From 26cdacbf49b5a4791f983c3b3d5bce326c4d8327 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 6 Sep 2022 23:42:32 +0900 Subject: [PATCH 001/180] wip --- packages/backend/package.json | 3 ++- .../src/server/api/endpoints/notes/create.ts | 4 +-- packages/backend/src/services/index.ts | 6 +++++ packages/backend/src/services/noteService.ts | 26 +++++++++++++++++++ .../backend/src/services/webhookService.ts | 12 +++++++++ packages/backend/yarn.lock | 13 +++++++--- 6 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 packages/backend/src/services/index.ts create mode 100644 packages/backend/src/services/noteService.ts create mode 100644 packages/backend/src/services/webhookService.ts diff --git a/packages/backend/package.json b/packages/backend/package.json index 9318bb085e75..96c16e04eabf 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -115,7 +115,8 @@ "tsc-alias": "1.7.0", "tsconfig-paths": "4.1.0", "twemoji-parser": "14.0.0", - "typeorm": "0.3.8", + "typedi": "0.10.0", + "typeorm": "0.3.9", "ulid": "2.3.0", "unzipper": "0.10.11", "uuid": "8.3.2", diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index a133294169ad..99f9883d1efb 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -1,12 +1,12 @@ import ms from 'ms'; import { In } from 'typeorm'; -import create from '@/services/note/create.js'; import { User } from '@/models/entities/user.js'; import { Users, DriveFiles, Notes, Channels, Blockings } from '@/models/index.js'; import { DriveFile } from '@/models/entities/drive-file.js'; import { Note } from '@/models/entities/note.js'; import { Channel } from '@/models/entities/channel.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; +import { noteService } from '@/services/index.js'; import { noteVisibilities } from '../../../../types.js'; import { ApiError } from '../../error.js'; import define from '../../define.js'; @@ -248,7 +248,7 @@ export default define(meta, paramDef, async (ps, user) => { } // 投稿を作成 - const note = await create(user, { + const note = await noteService.create(user, { createdAt: new Date(), files: files, poll: ps.poll ? { diff --git a/packages/backend/src/services/index.ts b/packages/backend/src/services/index.ts new file mode 100644 index 000000000000..47ba2741f55f --- /dev/null +++ b/packages/backend/src/services/index.ts @@ -0,0 +1,6 @@ +import { Container, Service } from 'typedi'; +import { Notes } from '@/models/index.js'; +import { NoteService } from './noteService.js'; + +Container.set('notesRepository', Notes); +export const noteService = Container.get(NoteService); diff --git a/packages/backend/src/services/noteService.ts b/packages/backend/src/services/noteService.ts new file mode 100644 index 000000000000..7dc38146553c --- /dev/null +++ b/packages/backend/src/services/noteService.ts @@ -0,0 +1,26 @@ +import { Container, Service, Inject } from 'typedi'; +import { Notes } from '@/models/index.js'; +import { User } from '@/models/entities/user.js'; +import { genId } from '@/misc/gen-id.js'; +import { WebhookService } from './webhookService.js'; + +@Service() +export class NoteService { + constructor( + @Inject('notesRepository') + public notesRepository: typeof Notes, + + public webhookService: WebhookService, + ) {} + + public async create(user: User, data: any) { + const created = await this.notesRepository.insert({ + ...data, + id: genId(), + userId: user.id, + text: data.text + '!!!', + }).then(x => this.notesRepository.findOneByOrFail(x.identifiers[0])); + this.webhookService.deliver(); + return created; + } +} diff --git a/packages/backend/src/services/webhookService.ts b/packages/backend/src/services/webhookService.ts new file mode 100644 index 000000000000..eb9b12c75836 --- /dev/null +++ b/packages/backend/src/services/webhookService.ts @@ -0,0 +1,12 @@ +import { Container, Service } from 'typedi'; + +@Service() +export class WebhookService { + constructor( + + ) {} + + public deliver() { + console.log('delivered'); + } +} diff --git a/packages/backend/yarn.lock b/packages/backend/yarn.lock index 607c4c2c61b6..01271aa82f2b 100644 --- a/packages/backend/yarn.lock +++ b/packages/backend/yarn.lock @@ -9046,10 +9046,15 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typeorm@0.3.8: - version "0.3.8" - resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.3.8.tgz#134ab0736592da5d5ca66d4eb8cea5999e34d5f9" - integrity sha512-77PNamypfwieZlhwbUAjMudX2T+5F6JCoNR18RdKenJ5Uu+9gc9AWa1kmz/2hH84MU23lGqPCu8u2lKnlfsN2g== +typedi@0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/typedi/-/typedi-0.10.0.tgz#e8f9a5ee100b84addbdfb57fe90d8d9ed2a21dea" + integrity sha512-v3UJF8xm68BBj6AF4oQML3ikrfK2c9EmZUyLOfShpJuItAqVBHWP/KtpGinkSsIiP6EZyyb6Z3NXyW9dgS9X1w== + +typeorm@0.3.9: + version "0.3.9" + resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.3.9.tgz#ad0f525d81c081fd11006f97030f47a55978ac81" + integrity sha512-xNcE44D4hn74n7pjuMog9hRgep+BiO3IBpjEaQZ8fb56zsDz7xHT1GAeWwmGuuU+4nDEELp2mIqgSCR+zxR7Jw== dependencies: "@sqltools/formatter" "^1.2.2" app-root-path "^3.0.0" From 7e98af575bbf2fd70979e19416bbd4d092e40cc3 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 7 Sep 2022 17:31:25 +0900 Subject: [PATCH 002/180] wip --- packages/backend/src/boot/worker.ts | 4 +++ packages/backend/src/services/fooService.ts | 11 +++++++ packages/backend/src/services/index.ts | 2 -- packages/backend/src/services/noteService.ts | 4 +-- .../backend/src/services/webhookService.ts | 4 ++- packages/backend/test/unit/noteService.ts | 31 +++++++++++++++++++ 6 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 packages/backend/src/services/fooService.ts create mode 100644 packages/backend/test/unit/noteService.ts diff --git a/packages/backend/src/boot/worker.ts b/packages/backend/src/boot/worker.ts index 8038e25631f8..5f56a194cec6 100644 --- a/packages/backend/src/boot/worker.ts +++ b/packages/backend/src/boot/worker.ts @@ -1,4 +1,6 @@ import cluster from 'node:cluster'; +import { Container } from 'typedi'; +import { Notes } from '@/models/index.js'; import { initDb } from '../db/postgre.js'; /** @@ -7,6 +9,8 @@ import { initDb } from '../db/postgre.js'; export async function workerMain() { await initDb(); + Container.set('notesRepository', Notes); + // start server await import('../server/index.js').then(x => x.default()); diff --git a/packages/backend/src/services/fooService.ts b/packages/backend/src/services/fooService.ts new file mode 100644 index 000000000000..83c5bac70c35 --- /dev/null +++ b/packages/backend/src/services/fooService.ts @@ -0,0 +1,11 @@ +import { Container, Service } from 'typedi'; + +@Service() +export class FooService { + constructor( + ) {} + + public foo() { + console.log('foo'); + } +} diff --git a/packages/backend/src/services/index.ts b/packages/backend/src/services/index.ts index 47ba2741f55f..dbdf5b22c84c 100644 --- a/packages/backend/src/services/index.ts +++ b/packages/backend/src/services/index.ts @@ -1,6 +1,4 @@ import { Container, Service } from 'typedi'; -import { Notes } from '@/models/index.js'; import { NoteService } from './noteService.js'; -Container.set('notesRepository', Notes); export const noteService = Container.get(NoteService); diff --git a/packages/backend/src/services/noteService.ts b/packages/backend/src/services/noteService.ts index 7dc38146553c..1ec9927bd5ff 100644 --- a/packages/backend/src/services/noteService.ts +++ b/packages/backend/src/services/noteService.ts @@ -8,9 +8,9 @@ import { WebhookService } from './webhookService.js'; export class NoteService { constructor( @Inject('notesRepository') - public notesRepository: typeof Notes, + private notesRepository: typeof Notes, - public webhookService: WebhookService, + private webhookService: WebhookService, ) {} public async create(user: User, data: any) { diff --git a/packages/backend/src/services/webhookService.ts b/packages/backend/src/services/webhookService.ts index eb9b12c75836..bf69d94409f6 100644 --- a/packages/backend/src/services/webhookService.ts +++ b/packages/backend/src/services/webhookService.ts @@ -1,12 +1,14 @@ import { Container, Service } from 'typedi'; +import { FooService } from './fooService.js'; @Service() export class WebhookService { constructor( - + private fooService: FooService, ) {} public deliver() { + this.fooService.foo(); console.log('delivered'); } } diff --git a/packages/backend/test/unit/noteService.ts b/packages/backend/test/unit/noteService.ts new file mode 100644 index 000000000000..12c4633e6ed9 --- /dev/null +++ b/packages/backend/test/unit/noteService.ts @@ -0,0 +1,31 @@ +process.env.NODE_ENV = 'test'; + +import * as assert from 'assert'; +import { jest } from '@jest/globals'; +import { Container, Service } from 'typedi'; +import { initDb } from '@/db/postgre.js'; +import { Notes } from '@/models/index.js'; +import { FooService } from '@/services/fooService.js'; +import { WebhookService } from '../../src/services/webhookService.js'; +import { NoteService } from '../../src/services/noteService.js'; + +describe('NoteService', () => { + beforeAll(async () => { + //await initTestDb(); + await initDb(); + + Container.set('notesRepository', Notes); + }); + + it('createしたときwebhookが配信される', async () => { + class WebhookService2 { + public deliver = jest.fn(() => {}); + } + + const webhookService = new WebhookService2(); + const noteService = new NoteService(Container.get('notesRepository'), webhookService as unknown as jest.Mocked); + + await noteService.create({ id: 'aaa' }, { text: 'test' }); + expect(webhookService.deliver).toBeCalled(); + }); +}); From 01df97d5bbc6b74f8bcb5e7f3c43b2af5bf53eed Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 9 Sep 2022 00:03:08 +0900 Subject: [PATCH 003/180] wip --- packages/backend/src/services/note/create.ts | 692 ----------------- packages/backend/src/services/noteService.ts | 713 +++++++++++++++++- .../test/{unit => tests}/noteService.ts | 0 3 files changed, 703 insertions(+), 702 deletions(-) delete mode 100644 packages/backend/src/services/note/create.ts rename packages/backend/test/{unit => tests}/noteService.ts (100%) diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts deleted file mode 100644 index e2bf9d5b594d..000000000000 --- a/packages/backend/src/services/note/create.ts +++ /dev/null @@ -1,692 +0,0 @@ -import * as mfm from 'mfm-js'; -import es from '../../db/elasticsearch.js'; -import { publishMainStream, publishNotesStream } from '@/services/stream.js'; -import DeliverManager from '@/remote/activitypub/deliver-manager.js'; -import renderNote from '@/remote/activitypub/renderer/note.js'; -import renderCreate from '@/remote/activitypub/renderer/create.js'; -import renderAnnounce from '@/remote/activitypub/renderer/announce.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import { resolveUser } from '@/remote/resolve-user.js'; -import config from '@/config/index.js'; -import { updateHashtags } from '../update-hashtag.js'; -import { concat } from '@/prelude/array.js'; -import { insertNoteUnread } from '@/services/note/unread.js'; -import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js'; -import { extractMentions } from '@/misc/extract-mentions.js'; -import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js'; -import { extractHashtags } from '@/misc/extract-hashtags.js'; -import { Note, IMentionedRemoteUsers } from '@/models/entities/note.js'; -import { Mutings, Users, NoteWatchings, Notes, Instances, UserProfiles, Antennas, Followings, MutedNotes, Channels, ChannelFollowings, Blockings, NoteThreadMutings } from '@/models/index.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; -import { App } from '@/models/entities/app.js'; -import { Not, In } from 'typeorm'; -import { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; -import { genId } from '@/misc/gen-id.js'; -import { notesChart, perUserNotesChart, activeUsersChart, instanceChart } from '@/services/chart/index.js'; -import { Poll, IPoll } from '@/models/entities/poll.js'; -import { createNotification } from '../create-notification.js'; -import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; -import { checkHitAntenna } from '@/misc/check-hit-antenna.js'; -import { checkWordMute } from '@/misc/check-word-mute.js'; -import { addNoteToAntenna } from '../add-note-to-antenna.js'; -import { countSameRenotes } from '@/misc/count-same-renotes.js'; -import { deliverToRelays } from '../relay.js'; -import { Channel } from '@/models/entities/channel.js'; -import { normalizeForSearch } from '@/misc/normalize-for-search.js'; -import { getAntennas } from '@/misc/antenna-cache.js'; -import { endedPollNotificationQueue } from '@/queue/queues.js'; -import { webhookDeliver } from '@/queue/index.js'; -import { Cache } from '@/misc/cache.js'; -import { UserProfile } from '@/models/entities/user-profile.js'; -import { db } from '@/db/postgre.js'; -import { getActiveWebhooks } from '@/misc/webhook-cache.js'; - -const mutedWordsCache = new Cache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5); - -type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; - -class NotificationManager { - private notifier: { id: User['id']; }; - private note: Note; - private queue: { - target: ILocalUser['id']; - reason: NotificationType; - }[]; - - constructor(notifier: { id: User['id']; }, note: Note) { - this.notifier = notifier; - this.note = note; - this.queue = []; - } - - public push(notifiee: ILocalUser['id'], reason: NotificationType) { - // 自分自身へは通知しない - if (this.notifier.id === notifiee) return; - - const exist = this.queue.find(x => x.target === notifiee); - - if (exist) { - // 「メンションされているかつ返信されている」場合は、メンションとしての通知ではなく返信としての通知にする - if (reason !== 'mention') { - exist.reason = reason; - } - } else { - this.queue.push({ - reason: reason, - target: notifiee, - }); - } - } - - public async deliver() { - for (const x of this.queue) { - // ミュート情報を取得 - const mentioneeMutes = await Mutings.findBy({ - muterId: x.target, - }); - - const mentioneesMutedUserIds = mentioneeMutes.map(m => m.muteeId); - - // 通知される側のユーザーが通知する側のユーザーをミュートしていない限りは通知する - if (!mentioneesMutedUserIds.includes(this.notifier.id)) { - createNotification(x.target, x.reason, { - notifierId: this.notifier.id, - noteId: this.note.id, - }); - } - } - } -} - -type MinimumUser = { - id: User['id']; - host: User['host']; - username: User['username']; - uri: User['uri']; -}; - -type Option = { - createdAt?: Date | null; - name?: string | null; - text?: string | null; - reply?: Note | null; - renote?: Note | null; - files?: DriveFile[] | null; - poll?: IPoll | null; - localOnly?: boolean | null; - cw?: string | null; - visibility?: string; - visibleUsers?: MinimumUser[] | null; - channel?: Channel | null; - apMentions?: MinimumUser[] | null; - apHashtags?: string[] | null; - apEmojis?: string[] | null; - uri?: string | null; - url?: string | null; - app?: App | null; -}; - -export default async (user: { id: User['id']; username: User['username']; host: User['host']; isSilenced: User['isSilenced']; createdAt: User['createdAt']; }, data: Option, silent = false) => new Promise(async (res, rej) => { - // チャンネル外にリプライしたら対象のスコープに合わせる - // (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで) - if (data.reply && data.channel && data.reply.channelId !== data.channel.id) { - if (data.reply.channelId) { - data.channel = await Channels.findOneBy({ id: data.reply.channelId }); - } else { - data.channel = null; - } - } - - // チャンネル内にリプライしたら対象のスコープに合わせる - // (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで) - if (data.reply && (data.channel == null) && data.reply.channelId) { - data.channel = await Channels.findOneBy({ id: data.reply.channelId }); - } - - if (data.createdAt == null) data.createdAt = 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 (user.isSilenced && data.visibility === 'public' && data.channel == null) { - data.visibility = 'home'; - } - - // Renote対象が「ホームまたは全体」以外の公開範囲ならreject - if (data.renote && data.renote.visibility !== 'public' && data.renote.visibility !== 'home' && data.renote.userId !== user.id) { - return rej('Renote target is not public or home'); - } - - // Renote対象がpublicではないならhomeにする - if (data.renote && data.renote.visibility !== 'public' && data.visibility === 'public') { - data.visibility = 'home'; - } - - // Renote対象がfollowersならfollowersにする - if (data.renote && data.renote.visibility === 'followers') { - data.visibility = 'followers'; - } - - // 返信対象がpublicではないならhomeにする - if (data.reply && data.reply.visibility !== 'public' && data.visibility === 'public') { - data.visibility = 'home'; - } - - // ローカルのみをRenoteしたらローカルのみにする - if (data.renote && data.renote.localOnly && data.channel == null) { - data.localOnly = true; - } - - // ローカルのみにリプライしたらローカルのみにする - if (data.reply && data.reply.localOnly && data.channel == null) { - data.localOnly = true; - } - - if (data.text) { - data.text = data.text.trim(); - } else { - data.text = null; - } - - let tags = data.apHashtags; - let emojis = data.apEmojis; - let mentionedUsers = data.apMentions; - - // 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 choiceTokens = data.poll && data.poll.choices - ? concat(data.poll.choices.map(choice => mfm.parse(choice)!)) - : []; - - const combinedTokens = tokens.concat(cwTokens).concat(choiceTokens); - - tags = data.apHashtags || extractHashtags(combinedTokens); - - emojis = data.apEmojis || extractCustomEmojisFromMfm(combinedTokens); - - mentionedUsers = data.apMentions || await extractMentionedUsers(user, combinedTokens); - } - - 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 Users.findOneByOrFail({ id: data.reply!.userId })); - } - - if (data.visibility === 'specified') { - if (data.visibleUsers == null) throw new Error('invalid param'); - - for (const u of data.visibleUsers) { - if (!mentionedUsers.some(x => x.id === u.id)) { - mentionedUsers.push(u); - } - } - - if (data.reply && !data.visibleUsers.some(x => x.id === data.reply!.userId)) { - data.visibleUsers.push(await Users.findOneByOrFail({ id: data.reply!.userId })); - } - } - - const note = await insertNote(user, data, tags, emojis, mentionedUsers); - - res(note); - - // 統計を更新 - notesChart.update(note, true); - perUserNotesChart.update(user, note, true); - - // Register host - if (Users.isRemoteUser(user)) { - registerOrFetchInstanceDoc(user.host).then(i => { - Instances.increment({ id: i.id }, 'notesCount', 1); - instanceChart.updateNote(i.host, note, true); - }); - } - - // ハッシュタグ更新 - if (data.visibility === 'public' || data.visibility === 'home') { - updateHashtags(user, tags); - } - - // Increment notes count (user) - incNotesCountOfUser(user); - - // Word mute - mutedWordsCache.fetch(null, () => UserProfiles.find({ - where: { - enableWordMute: true, - }, - select: ['userId', 'mutedWords'], - })).then(us => { - for (const u of us) { - checkWordMute(note, { id: u.userId }, u.mutedWords).then(shouldMute => { - if (shouldMute) { - MutedNotes.insert({ - id: genId(), - userId: u.userId, - noteId: note.id, - reason: 'word', - }); - } - }); - } - }); - - // Antenna - for (const antenna of (await getAntennas())) { - checkHitAntenna(antenna, note, user).then(hit => { - if (hit) { - addNoteToAntenna(antenna, note, user); - } - }); - } - - // Channel - if (note.channelId) { - ChannelFollowings.findBy({ followeeId: note.channelId }).then(followings => { - for (const following of followings) { - insertNoteUnread(following.followerId, note, { - isSpecified: false, - isMentioned: false, - }); - } - }); - } - - if (data.reply) { - saveReply(data.reply, note); - } - - // この投稿を除く指定したユーザーによる指定したノートのリノートが存在しないとき - if (data.renote && (await countSameRenotes(user.id, data.renote.id, note.id) === 0)) { - incRenoteCount(data.renote); - } - - if (data.poll && data.poll.expiresAt) { - const delay = data.poll.expiresAt.getTime() - Date.now(); - endedPollNotificationQueue.add({ - noteId: note.id, - }, { - delay, - removeOnComplete: true, - }); - } - - if (!silent) { - if (Users.isLocalUser(user)) activeUsersChart.write(user); - - // 未読通知を作成 - if (data.visibility === 'specified') { - if (data.visibleUsers == null) throw new Error('invalid param'); - - for (const u of data.visibleUsers) { - // ローカルユーザーのみ - if (!Users.isLocalUser(u)) continue; - - insertNoteUnread(u.id, note, { - isSpecified: true, - isMentioned: false, - }); - } - } else { - for (const u of mentionedUsers) { - // ローカルユーザーのみ - if (!Users.isLocalUser(u)) continue; - - insertNoteUnread(u.id, note, { - isSpecified: false, - isMentioned: true, - }); - } - } - - // Pack the note - const noteObj = await Notes.pack(note); - - publishNotesStream(noteObj); - - getActiveWebhooks().then(webhooks => { - webhooks = webhooks.filter(x => x.userId === user.id && x.on.includes('note')); - for (const webhook of webhooks) { - webhookDeliver(webhook, 'note', { - note: noteObj, - }); - } - }); - - const nm = new NotificationManager(user, note); - const nmRelatedPromises = []; - - await createMentionedEvents(mentionedUsers, note, nm); - - // If has in reply to note - if (data.reply) { - // Fetch watchers - nmRelatedPromises.push(notifyToWatchersOfReplyee(data.reply, user, nm)); - - // 通知 - if (data.reply.userHost === null) { - const threadMuted = await NoteThreadMutings.findOneBy({ - userId: data.reply.userId, - threadId: data.reply.threadId || data.reply.id, - }); - - if (!threadMuted) { - nm.push(data.reply.userId, 'reply'); - publishMainStream(data.reply.userId, 'reply', noteObj); - - const webhooks = (await getActiveWebhooks()).filter(x => x.userId === data.reply!.userId && x.on.includes('reply')); - for (const webhook of webhooks) { - webhookDeliver(webhook, 'reply', { - note: noteObj, - }); - } - } - } - } - - // If it is renote - if (data.renote) { - const type = data.text ? 'quote' : 'renote'; - - // Notify - if (data.renote.userHost === null) { - nm.push(data.renote.userId, type); - } - - // Fetch watchers - nmRelatedPromises.push(notifyToWatchersOfRenotee(data.renote, user, nm, type)); - - // Publish event - if ((user.id !== data.renote.userId) && data.renote.userHost === null) { - publishMainStream(data.renote.userId, 'renote', noteObj); - - const webhooks = (await getActiveWebhooks()).filter(x => x.userId === data.renote!.userId && x.on.includes('renote')); - for (const webhook of webhooks) { - webhookDeliver(webhook, 'renote', { - note: noteObj, - }); - } - } - } - - Promise.all(nmRelatedPromises).then(() => { - nm.deliver(); - }); - - //#region AP deliver - if (Users.isLocalUser(user)) { - (async () => { - const noteActivity = await renderNoteOrRenoteActivity(data, note); - const dm = new DeliverManager(user, noteActivity); - - // メンションされたリモートユーザーに配送 - for (const u of mentionedUsers.filter(u => Users.isRemoteUser(u))) { - dm.addDirectRecipe(u as IRemoteUser); - } - - // 投稿がリプライかつ投稿者がローカルユーザーかつリプライ先の投稿の投稿者がリモートユーザーなら配送 - if (data.reply && data.reply.userHost !== null) { - const u = await Users.findOneBy({ id: data.reply.userId }); - if (u && Users.isRemoteUser(u)) dm.addDirectRecipe(u); - } - - // 投稿がRenoteかつ投稿者がローカルユーザーかつRenote元の投稿の投稿者がリモートユーザーなら配送 - if (data.renote && data.renote.userHost !== null) { - const u = await Users.findOneBy({ id: data.renote.userId }); - if (u && Users.isRemoteUser(u)) dm.addDirectRecipe(u); - } - - // フォロワーに配送 - if (['public', 'home', 'followers'].includes(note.visibility)) { - dm.addFollowersRecipe(); - } - - if (['public'].includes(note.visibility)) { - deliverToRelays(user, noteActivity); - } - - dm.execute(); - })(); - } - //#endregion - } - - if (data.channel) { - Channels.increment({ id: data.channel.id }, 'notesCount', 1); - Channels.update(data.channel.id, { - lastNotedAt: new Date(), - }); - - Notes.countBy({ - userId: user.id, - channelId: data.channel.id, - }).then(count => { - // この処理が行われるのはノート作成後なので、ノートが一つしかなかったら最初の投稿だと判断できる - // TODO: とはいえノートを削除して何回も投稿すればその分だけインクリメントされる雑さもあるのでどうにかしたい - if (count === 1) { - Channels.increment({ id: data.channel!.id }, 'usersCount', 1); - } - }); - } - - // Register to search database - index(note); -}); - -async function renderNoteOrRenoteActivity(data: Option, note: Note) { - if (data.localOnly) return null; - - const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length === 0) - ? renderAnnounce(data.renote.uri ? data.renote.uri : `${config.url}/notes/${data.renote.id}`, note) - : renderCreate(await renderNote(note, false), note); - - return renderActivity(content); -} - -function incRenoteCount(renote: Note) { - Notes.createQueryBuilder().update() - .set({ - renoteCount: () => '"renoteCount" + 1', - score: () => '"score" + 1', - }) - .where('id = :id', { id: renote.id }) - .execute(); -} - -async function insertNote(user: { id: User['id']; host: User['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]) { - const insert = new Note({ - id: genId(data.createdAt!), - createdAt: data.createdAt!, - fileIds: data.files ? data.files.map(file => file.id) : [], - replyId: data.reply ? data.reply.id : null, - renoteId: data.renote ? data.renote.id : null, - channelId: data.channel ? data.channel.id : null, - threadId: data.reply - ? data.reply.threadId - ? data.reply.threadId - : data.reply.id - : null, - name: data.name, - text: data.text, - hasPoll: data.poll != null, - cw: data.cw == null ? null : data.cw, - tags: tags.map(tag => normalizeForSearch(tag)), - emojis, - userId: user.id, - localOnly: data.localOnly!, - visibility: data.visibility as any, - visibleUserIds: data.visibility === 'specified' - ? data.visibleUsers - ? data.visibleUsers.map(u => u.id) - : [] - : [], - - attachedFileTypes: data.files ? data.files.map(file => file.type) : [], - - // 以下非正規化データ - replyUserId: data.reply ? data.reply.userId : null, - replyUserHost: data.reply ? data.reply.userHost : null, - renoteUserId: data.renote ? data.renote.userId : null, - renoteUserHost: data.renote ? data.renote.userHost : null, - userHost: user.host, - }); - - if (data.uri != null) insert.uri = data.uri; - if (data.url != null) insert.url = data.url; - - // Append mentions data - if (mentionedUsers.length > 0) { - insert.mentions = mentionedUsers.map(u => u.id); - const profiles = await UserProfiles.findBy({ userId: In(insert.mentions) }); - insert.mentionedRemoteUsers = JSON.stringify(mentionedUsers.filter(u => Users.isRemoteUser(u)).map(u => { - const profile = profiles.find(p => p.userId === u.id); - const url = profile != null ? profile.url : null; - return { - uri: u.uri, - url: url == null ? undefined : url, - username: u.username, - host: u.host, - } as IMentionedRemoteUsers[0]; - })); - } - - // 投稿を作成 - try { - if (insert.hasPoll) { - // Start transaction - await db.transaction(async transactionalEntityManager => { - await transactionalEntityManager.insert(Note, insert); - - const poll = new Poll({ - noteId: insert.id, - choices: data.poll!.choices, - expiresAt: data.poll!.expiresAt, - multiple: data.poll!.multiple, - votes: new Array(data.poll!.choices.length).fill(0), - noteVisibility: insert.visibility, - userId: user.id, - userHost: user.host, - }); - - await transactionalEntityManager.insert(Poll, poll); - }); - } else { - await Notes.insert(insert); - } - - return insert; - } catch (e) { - // duplicate key error - if (isDuplicateKeyValueError(e)) { - const err = new Error('Duplicated note'); - err.name = 'duplicated'; - throw err; - } - - console.error(e); - - throw e; - } -} - -function index(note: Note) { - if (note.text == null || config.elasticsearch == null) return; - - es!.index({ - index: config.elasticsearch.index || 'misskey_note', - id: note.id.toString(), - body: { - text: normalizeForSearch(note.text), - userId: note.userId, - userHost: note.userHost, - }, - }); -} - -async function notifyToWatchersOfRenotee(renote: Note, user: { id: User['id']; }, nm: NotificationManager, type: NotificationType) { - const watchers = await NoteWatchings.findBy({ - noteId: renote.id, - userId: Not(user.id), - }); - - for (const watcher of watchers) { - nm.push(watcher.userId, type); - } -} - -async function notifyToWatchersOfReplyee(reply: Note, user: { id: User['id']; }, nm: NotificationManager) { - const watchers = await NoteWatchings.findBy({ - noteId: reply.id, - userId: Not(user.id), - }); - - for (const watcher of watchers) { - nm.push(watcher.userId, 'reply'); - } -} - -async function createMentionedEvents(mentionedUsers: MinimumUser[], note: Note, nm: NotificationManager) { - for (const u of mentionedUsers.filter(u => Users.isLocalUser(u))) { - const threadMuted = await NoteThreadMutings.findOneBy({ - userId: u.id, - threadId: note.threadId || note.id, - }); - - if (threadMuted) { - continue; - } - - const detailPackedNote = await Notes.pack(note, u, { - detail: true, - }); - - publishMainStream(u.id, 'mention', detailPackedNote); - - const webhooks = (await getActiveWebhooks()).filter(x => x.userId === u.id && x.on.includes('mention')); - for (const webhook of webhooks) { - webhookDeliver(webhook, 'mention', { - note: detailPackedNote, - }); - } - - // Create notification - nm.push(u.id, 'mention'); - } -} - -function saveReply(reply: Note, note: Note) { - Notes.increment({ id: reply.id }, 'repliesCount', 1); -} - -function incNotesCountOfUser(user: { id: User['id']; }) { - Users.createQueryBuilder().update() - .set({ - updatedAt: new Date(), - notesCount: () => '"notesCount" + 1', - }) - .where('id = :id', { id: user.id }) - .execute(); -} - -async function extractMentionedUsers(user: { host: User['host']; }, tokens: mfm.MfmNode[]): Promise { - if (tokens == null) return []; - - const mentions = extractMentions(tokens); - - let mentionedUsers = (await Promise.all(mentions.map(m => - resolveUser(m.username, m.host || user.host).catch(() => null) - ))).filter(x => x != null) as User[]; - - // Drop duplicate users - mentionedUsers = mentionedUsers.filter((u, i, self) => - i === self.findIndex(u2 => u.id === u2.id) - ); - - return mentionedUsers; -} diff --git a/packages/backend/src/services/noteService.ts b/packages/backend/src/services/noteService.ts index 1ec9927bd5ff..ffacae99a465 100644 --- a/packages/backend/src/services/noteService.ts +++ b/packages/backend/src/services/noteService.ts @@ -1,9 +1,133 @@ import { Container, Service, Inject } from 'typedi'; -import { Notes } from '@/models/index.js'; -import { User } from '@/models/entities/user.js'; +import * as mfm from 'mfm-js'; +import { Not, In } from 'typeorm'; +import { extractMentions } from '@/misc/extract-mentions.js'; +import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js'; +import { extractHashtags } from '@/misc/extract-hashtags.js'; +import { Note, IMentionedRemoteUsers } from '@/models/entities/note.js'; +import { Mutings, Users, NoteWatchings, Notes, Instances, UserProfiles, MutedNotes, Channels, ChannelFollowings, NoteThreadMutings } from '@/models/index.js'; +import { DriveFile } from '@/models/entities/drive-file.js'; +import { App } from '@/models/entities/app.js'; +import { insertNoteUnread } from '@/services/note/unread.js'; +import { concat } from '@/prelude/array.js'; +import config from '@/config/index.js'; +import { resolveUser } from '@/remote/resolve-user.js'; +import { renderActivity } from '@/remote/activitypub/renderer/index.js'; +import renderAnnounce from '@/remote/activitypub/renderer/announce.js'; +import renderCreate from '@/remote/activitypub/renderer/create.js'; +import renderNote from '@/remote/activitypub/renderer/note.js'; +import DeliverManager from '@/remote/activitypub/deliver-manager.js'; +import { publishMainStream, publishNotesStream } from '@/services/stream.js'; import { genId } from '@/misc/gen-id.js'; +import { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; +import { notesChart, perUserNotesChart, activeUsersChart, instanceChart } from '@/services/chart/index.js'; +import { Poll, IPoll } from '@/models/entities/poll.js'; +import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; +import { checkHitAntenna } from '@/misc/check-hit-antenna.js'; +import { checkWordMute } from '@/misc/check-word-mute.js'; +import { countSameRenotes } from '@/misc/count-same-renotes.js'; +import { Channel } from '@/models/entities/channel.js'; +import { normalizeForSearch } from '@/misc/normalize-for-search.js'; +import { getAntennas } from '@/misc/antenna-cache.js'; +import { endedPollNotificationQueue } from '@/queue/queues.js'; +import { webhookDeliver } from '@/queue/index.js'; +import { Cache } from '@/misc/cache.js'; +import { UserProfile } from '@/models/entities/user-profile.js'; +import { db } from '@/db/postgre.js'; +import { getActiveWebhooks } from '@/misc/webhook-cache.js'; +import es from '../db/elasticsearch.js'; +import { registerOrFetchInstanceDoc } from './register-or-fetch-instance-doc.js'; +import { updateHashtags } from './update-hashtag.js'; +import { deliverToRelays } from './relay.js'; +import { addNoteToAntenna } from './add-note-to-antenna.js'; +import { createNotification } from './create-notification.js'; import { WebhookService } from './webhookService.js'; +const mutedWordsCache = new Cache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5); + +type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; + +class NotificationManager { + private notifier: { id: User['id']; }; + private note: Note; + private queue: { + target: ILocalUser['id']; + reason: NotificationType; + }[]; + + constructor(notifier: { id: User['id']; }, note: Note) { + this.notifier = notifier; + this.note = note; + this.queue = []; + } + + public push(notifiee: ILocalUser['id'], reason: NotificationType) { + // 自分自身へは通知しない + if (this.notifier.id === notifiee) return; + + const exist = this.queue.find(x => x.target === notifiee); + + if (exist) { + // 「メンションされているかつ返信されている」場合は、メンションとしての通知ではなく返信としての通知にする + if (reason !== 'mention') { + exist.reason = reason; + } + } else { + this.queue.push({ + reason: reason, + target: notifiee, + }); + } + } + + public async deliver() { + for (const x of this.queue) { + // ミュート情報を取得 + const mentioneeMutes = await Mutings.findBy({ + muterId: x.target, + }); + + const mentioneesMutedUserIds = mentioneeMutes.map(m => m.muteeId); + + // 通知される側のユーザーが通知する側のユーザーをミュートしていない限りは通知する + if (!mentioneesMutedUserIds.includes(this.notifier.id)) { + createNotification(x.target, x.reason, { + notifierId: this.notifier.id, + noteId: this.note.id, + }); + } + } + } +} + +type MinimumUser = { + id: User['id']; + host: User['host']; + username: User['username']; + uri: User['uri']; +}; + +type Option = { + createdAt?: Date | null; + name?: string | null; + text?: string | null; + reply?: Note | null; + renote?: Note | null; + files?: DriveFile[] | null; + poll?: IPoll | null; + localOnly?: boolean | null; + cw?: string | null; + visibility?: string; + visibleUsers?: MinimumUser[] | null; + channel?: Channel | null; + apMentions?: MinimumUser[] | null; + apHashtags?: string[] | null; + apEmojis?: string[] | null; + uri?: string | null; + url?: string | null; + app?: App | null; +}; + @Service() export class NoteService { constructor( @@ -13,14 +137,583 @@ export class NoteService { private webhookService: WebhookService, ) {} - public async create(user: User, data: any) { - const created = await this.notesRepository.insert({ - ...data, - id: genId(), + public async create(user: { + id: User['id']; + username: User['username']; + host: User['host']; + isSilenced: User['isSilenced']; + createdAt: User['createdAt']; + }, data: Option, silent = false): Promise { + // チャンネル外にリプライしたら対象のスコープに合わせる + // (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで) + if (data.reply && data.channel && data.reply.channelId !== data.channel.id) { + if (data.reply.channelId) { + data.channel = await Channels.findOneBy({ id: data.reply.channelId }); + } else { + data.channel = null; + } + } + + // チャンネル内にリプライしたら対象のスコープに合わせる + // (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで) + if (data.reply && (data.channel == null) && data.reply.channelId) { + data.channel = await Channels.findOneBy({ id: data.reply.channelId }); + } + + if (data.createdAt == null) data.createdAt = 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 (user.isSilenced && data.visibility === 'public' && data.channel == null) { + data.visibility = 'home'; + } + + // Renote対象が「ホームまたは全体」以外の公開範囲ならreject + if (data.renote && data.renote.visibility !== 'public' && data.renote.visibility !== 'home' && data.renote.userId !== user.id) { + throw new Error('Renote target is not public or home'); + } + + // Renote対象がpublicではないならhomeにする + if (data.renote && data.renote.visibility !== 'public' && data.visibility === 'public') { + data.visibility = 'home'; + } + + // Renote対象がfollowersならfollowersにする + if (data.renote && data.renote.visibility === 'followers') { + data.visibility = 'followers'; + } + + // 返信対象がpublicではないならhomeにする + if (data.reply && data.reply.visibility !== 'public' && data.visibility === 'public') { + data.visibility = 'home'; + } + + // ローカルのみをRenoteしたらローカルのみにする + if (data.renote && data.renote.localOnly && data.channel == null) { + data.localOnly = true; + } + + // ローカルのみにリプライしたらローカルのみにする + if (data.reply && data.reply.localOnly && data.channel == null) { + data.localOnly = true; + } + + if (data.text) { + data.text = data.text.trim(); + } else { + data.text = null; + } + + let tags = data.apHashtags; + let emojis = data.apEmojis; + let mentionedUsers = data.apMentions; + + // 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 choiceTokens = data.poll && data.poll.choices + ? concat(data.poll.choices.map(choice => mfm.parse(choice)!)) + : []; + + const combinedTokens = tokens.concat(cwTokens).concat(choiceTokens); + + tags = data.apHashtags || extractHashtags(combinedTokens); + + emojis = data.apEmojis || extractCustomEmojisFromMfm(combinedTokens); + + mentionedUsers = data.apMentions || await this.#extractMentionedUsers(user, combinedTokens); + } + + 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 Users.findOneByOrFail({ id: data.reply!.userId })); + } + + if (data.visibility === 'specified') { + if (data.visibleUsers == null) throw new Error('invalid param'); + + for (const u of data.visibleUsers) { + if (!mentionedUsers.some(x => x.id === u.id)) { + mentionedUsers.push(u); + } + } + + if (data.reply && !data.visibleUsers.some(x => x.id === data.reply!.userId)) { + data.visibleUsers.push(await Users.findOneByOrFail({ id: data.reply!.userId })); + } + } + + const note = await this.#insertNote(user, data, tags, emojis, mentionedUsers); + + setImmediate(() => this.#postNoteCreated(note, user, data, silent, tags!, mentionedUsers!)); + + return note; + } + + async #insertNote(user: { id: User['id']; host: User['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]) { + const insert = new Note({ + id: genId(data.createdAt!), + createdAt: data.createdAt!, + fileIds: data.files ? data.files.map(file => file.id) : [], + replyId: data.reply ? data.reply.id : null, + renoteId: data.renote ? data.renote.id : null, + channelId: data.channel ? data.channel.id : null, + threadId: data.reply + ? data.reply.threadId + ? data.reply.threadId + : data.reply.id + : null, + name: data.name, + text: data.text, + hasPoll: data.poll != null, + cw: data.cw == null ? null : data.cw, + tags: tags.map(tag => normalizeForSearch(tag)), + emojis, userId: user.id, - text: data.text + '!!!', - }).then(x => this.notesRepository.findOneByOrFail(x.identifiers[0])); - this.webhookService.deliver(); - return created; + localOnly: data.localOnly!, + visibility: data.visibility as any, + visibleUserIds: data.visibility === 'specified' + ? data.visibleUsers + ? data.visibleUsers.map(u => u.id) + : [] + : [], + + attachedFileTypes: data.files ? data.files.map(file => file.type) : [], + + // 以下非正規化データ + replyUserId: data.reply ? data.reply.userId : null, + replyUserHost: data.reply ? data.reply.userHost : null, + renoteUserId: data.renote ? data.renote.userId : null, + renoteUserHost: data.renote ? data.renote.userHost : null, + userHost: user.host, + }); + + if (data.uri != null) insert.uri = data.uri; + if (data.url != null) insert.url = data.url; + + // Append mentions data + if (mentionedUsers.length > 0) { + insert.mentions = mentionedUsers.map(u => u.id); + const profiles = await UserProfiles.findBy({ userId: In(insert.mentions) }); + insert.mentionedRemoteUsers = JSON.stringify(mentionedUsers.filter(u => Users.isRemoteUser(u)).map(u => { + const profile = profiles.find(p => p.userId === u.id); + const url = profile != null ? profile.url : null; + return { + uri: u.uri, + url: url == null ? undefined : url, + username: u.username, + host: u.host, + } as IMentionedRemoteUsers[0]; + })); + } + + // 投稿を作成 + try { + if (insert.hasPoll) { + // Start transaction + await db.transaction(async transactionalEntityManager => { + await transactionalEntityManager.insert(Note, insert); + + const poll = new Poll({ + noteId: insert.id, + choices: data.poll!.choices, + expiresAt: data.poll!.expiresAt, + multiple: data.poll!.multiple, + votes: new Array(data.poll!.choices.length).fill(0), + noteVisibility: insert.visibility, + userId: user.id, + userHost: user.host, + }); + + await transactionalEntityManager.insert(Poll, poll); + }); + } else { + await this.notesRepository.insert(insert); + } + + return insert; + } catch (e) { + // duplicate key error + if (isDuplicateKeyValueError(e)) { + const err = new Error('Duplicated note'); + err.name = 'duplicated'; + throw err; + } + + console.error(e); + + throw e; + } + } + + async #postNoteCreated(note: Note, user: { + id: User['id']; + username: User['username']; + host: User['host']; + isSilenced: User['isSilenced']; + createdAt: User['createdAt']; + }, data: Option, silent: boolean, tags: string[], mentionedUsers: MinimumUser[]) { + // 統計を更新 + notesChart.update(note, true); + perUserNotesChart.update(user, note, true); + + // Register host + if (Users.isRemoteUser(user)) { + registerOrFetchInstanceDoc(user.host).then(i => { + Instances.increment({ id: i.id }, 'notesCount', 1); + instanceChart.updateNote(i.host, note, true); + }); + } + + // ハッシュタグ更新 + if (data.visibility === 'public' || data.visibility === 'home') { + updateHashtags(user, tags); + } + + // Increment notes count (user) + this.#incNotesCountOfUser(user); + + // Word mute + mutedWordsCache.fetch(null, () => UserProfiles.find({ + where: { + enableWordMute: true, + }, + select: ['userId', 'mutedWords'], + })).then(us => { + for (const u of us) { + checkWordMute(note, { id: u.userId }, u.mutedWords).then(shouldMute => { + if (shouldMute) { + MutedNotes.insert({ + id: genId(), + userId: u.userId, + noteId: note.id, + reason: 'word', + }); + } + }); + } + }); + + // Antenna + for (const antenna of (await getAntennas())) { + checkHitAntenna(antenna, note, user).then(hit => { + if (hit) { + addNoteToAntenna(antenna, note, user); + } + }); + } + + // Channel + if (note.channelId) { + ChannelFollowings.findBy({ followeeId: note.channelId }).then(followings => { + for (const following of followings) { + insertNoteUnread(following.followerId, note, { + isSpecified: false, + isMentioned: false, + }); + } + }); + } + + if (data.reply) { + this.#saveReply(data.reply, note); + } + + // この投稿を除く指定したユーザーによる指定したノートのリノートが存在しないとき + if (data.renote && (await countSameRenotes(user.id, data.renote.id, note.id) === 0)) { + this.#incRenoteCount(data.renote); + } + + if (data.poll && data.poll.expiresAt) { + const delay = data.poll.expiresAt.getTime() - Date.now(); + endedPollNotificationQueue.add({ + noteId: note.id, + }, { + delay, + removeOnComplete: true, + }); + } + + if (!silent) { + if (Users.isLocalUser(user)) activeUsersChart.write(user); + + // 未読通知を作成 + if (data.visibility === 'specified') { + if (data.visibleUsers == null) throw new Error('invalid param'); + + for (const u of data.visibleUsers) { + // ローカルユーザーのみ + if (!Users.isLocalUser(u)) continue; + + insertNoteUnread(u.id, note, { + isSpecified: true, + isMentioned: false, + }); + } + } else { + for (const u of mentionedUsers) { + // ローカルユーザーのみ + if (!Users.isLocalUser(u)) continue; + + insertNoteUnread(u.id, note, { + isSpecified: false, + isMentioned: true, + }); + } + } + + // Pack the note + const noteObj = await this.notesRepository.pack(note); + + publishNotesStream(noteObj); + + getActiveWebhooks().then(webhooks => { + webhooks = webhooks.filter(x => x.userId === user.id && x.on.includes('note')); + for (const webhook of webhooks) { + webhookDeliver(webhook, 'note', { + note: noteObj, + }); + } + }); + + const nm = new NotificationManager(user, note); + const nmRelatedPromises = []; + + await this.#createMentionedEvents(mentionedUsers, note, nm); + + // If has in reply to note + if (data.reply) { + // Fetch watchers + nmRelatedPromises.push(this.#notifyToWatchersOfReplyee(data.reply, user, nm)); + + // 通知 + if (data.reply.userHost === null) { + const threadMuted = await NoteThreadMutings.findOneBy({ + userId: data.reply.userId, + threadId: data.reply.threadId || data.reply.id, + }); + + if (!threadMuted) { + nm.push(data.reply.userId, 'reply'); + publishMainStream(data.reply.userId, 'reply', noteObj); + + const webhooks = (await getActiveWebhooks()).filter(x => x.userId === data.reply!.userId && x.on.includes('reply')); + for (const webhook of webhooks) { + webhookDeliver(webhook, 'reply', { + note: noteObj, + }); + } + } + } + } + + // If it is renote + if (data.renote) { + const type = data.text ? 'quote' : 'renote'; + + // Notify + if (data.renote.userHost === null) { + nm.push(data.renote.userId, type); + } + + // Fetch watchers + nmRelatedPromises.push(this.#notifyToWatchersOfRenotee(data.renote, user, nm, type)); + + // Publish event + if ((user.id !== data.renote.userId) && data.renote.userHost === null) { + publishMainStream(data.renote.userId, 'renote', noteObj); + + const webhooks = (await getActiveWebhooks()).filter(x => x.userId === data.renote!.userId && x.on.includes('renote')); + for (const webhook of webhooks) { + webhookDeliver(webhook, 'renote', { + note: noteObj, + }); + } + } + } + + Promise.all(nmRelatedPromises).then(() => { + nm.deliver(); + }); + + //#region AP deliver + if (Users.isLocalUser(user)) { + (async () => { + const noteActivity = await this.#renderNoteOrRenoteActivity(data, note); + const dm = new DeliverManager(user, noteActivity); + + // メンションされたリモートユーザーに配送 + for (const u of mentionedUsers.filter(u => Users.isRemoteUser(u))) { + dm.addDirectRecipe(u as IRemoteUser); + } + + // 投稿がリプライかつ投稿者がローカルユーザーかつリプライ先の投稿の投稿者がリモートユーザーなら配送 + if (data.reply && data.reply.userHost !== null) { + const u = await Users.findOneBy({ id: data.reply.userId }); + if (u && Users.isRemoteUser(u)) dm.addDirectRecipe(u); + } + + // 投稿がRenoteかつ投稿者がローカルユーザーかつRenote元の投稿の投稿者がリモートユーザーなら配送 + if (data.renote && data.renote.userHost !== null) { + const u = await Users.findOneBy({ id: data.renote.userId }); + if (u && Users.isRemoteUser(u)) dm.addDirectRecipe(u); + } + + // フォロワーに配送 + if (['public', 'home', 'followers'].includes(note.visibility)) { + dm.addFollowersRecipe(); + } + + if (['public'].includes(note.visibility)) { + deliverToRelays(user, noteActivity); + } + + dm.execute(); + })(); + } + //#endregion + } + + if (data.channel) { + Channels.increment({ id: data.channel.id }, 'notesCount', 1); + Channels.update(data.channel.id, { + lastNotedAt: new Date(), + }); + + this.notesRepository.countBy({ + userId: user.id, + channelId: data.channel.id, + }).then(count => { + // この処理が行われるのはノート作成後なので、ノートが一つしかなかったら最初の投稿だと判断できる + // TODO: とはいえノートを削除して何回も投稿すればその分だけインクリメントされる雑さもあるのでどうにかしたい + if (count === 1) { + Channels.increment({ id: data.channel!.id }, 'usersCount', 1); + } + }); + } + + // Register to search database + this.#index(note); + } + + #incRenoteCount(renote: Note) { + this.notesRepository.createQueryBuilder().update() + .set({ + renoteCount: () => '"renoteCount" + 1', + score: () => '"score" + 1', + }) + .where('id = :id', { id: renote.id }) + .execute(); + } + + async #createMentionedEvents(mentionedUsers: MinimumUser[], note: Note, nm: NotificationManager) { + for (const u of mentionedUsers.filter(u => Users.isLocalUser(u))) { + const threadMuted = await NoteThreadMutings.findOneBy({ + userId: u.id, + threadId: note.threadId || note.id, + }); + + if (threadMuted) { + continue; + } + + const detailPackedNote = await this.notesRepository.pack(note, u, { + detail: true, + }); + + publishMainStream(u.id, 'mention', detailPackedNote); + + const webhooks = (await getActiveWebhooks()).filter(x => x.userId === u.id && x.on.includes('mention')); + for (const webhook of webhooks) { + webhookDeliver(webhook, 'mention', { + note: detailPackedNote, + }); + } + + // Create notification + nm.push(u.id, 'mention'); + } + } + + #saveReply(reply: Note, note: Note) { + this.notesRepository.increment({ id: reply.id }, 'repliesCount', 1); + } + + async #renderNoteOrRenoteActivity(data: Option, note: Note) { + if (data.localOnly) return null; + + const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length === 0) + ? renderAnnounce(data.renote.uri ? data.renote.uri : `${config.url}/notes/${data.renote.id}`, note) + : renderCreate(await renderNote(note, false), note); + + return renderActivity(content); + } + + #index(note: Note) { + if (note.text == null || config.elasticsearch == null) return; + + es!.index({ + index: config.elasticsearch.index || 'misskey_note', + id: note.id.toString(), + body: { + text: normalizeForSearch(note.text), + userId: note.userId, + userHost: note.userHost, + }, + }); + } + + async #notifyToWatchersOfRenotee(renote: Note, user: { id: User['id']; }, nm: NotificationManager, type: NotificationType) { + const watchers = await NoteWatchings.findBy({ + noteId: renote.id, + userId: Not(user.id), + }); + + for (const watcher of watchers) { + nm.push(watcher.userId, type); + } + } + + async #notifyToWatchersOfReplyee(reply: Note, user: { id: User['id']; }, nm: NotificationManager) { + const watchers = await NoteWatchings.findBy({ + noteId: reply.id, + userId: Not(user.id), + }); + + for (const watcher of watchers) { + nm.push(watcher.userId, 'reply'); + } + } + + #incNotesCountOfUser(user: { id: User['id']; }) { + Users.createQueryBuilder().update() + .set({ + updatedAt: new Date(), + notesCount: () => '"notesCount" + 1', + }) + .where('id = :id', { id: user.id }) + .execute(); + } + + async #extractMentionedUsers(user: { host: User['host']; }, tokens: mfm.MfmNode[]): Promise { + if (tokens == null) return []; + + const mentions = extractMentions(tokens); + let mentionedUsers = (await Promise.all(mentions.map(m => + resolveUser(m.username, m.host || user.host).catch(() => null), + ))).filter(x => x != null) as User[]; + + // Drop duplicate users + mentionedUsers = mentionedUsers.filter((u, i, self) => + i === self.findIndex(u2 => u.id === u2.id), + ); + + return mentionedUsers; } } diff --git a/packages/backend/test/unit/noteService.ts b/packages/backend/test/tests/noteService.ts similarity index 100% rename from packages/backend/test/unit/noteService.ts rename to packages/backend/test/tests/noteService.ts From defa821aec4496979176eece7579aaf387bf9f71 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 9 Sep 2022 19:15:02 +0900 Subject: [PATCH 004/180] wip --- .../services/chart/ChartManagementService.ts | 60 +++++++ .../src/services/chart/charts/active-users.ts | 4 +- .../src/services/chart/charts/ap-request.ts | 2 + .../src/services/chart/charts/drive.ts | 6 +- .../src/services/chart/charts/federation.ts | 24 +-- .../src/services/chart/charts/hashtag.ts | 4 +- .../src/services/chart/charts/instance.ts | 4 +- .../src/services/chart/charts/notes.ts | 6 +- .../services/chart/charts/per-user-drive.ts | 4 +- .../chart/charts/per-user-following.ts | 6 +- .../services/chart/charts/per-user-notes.ts | 4 +- .../chart/charts/per-user-reactions.ts | 4 +- .../src/services/chart/charts/test-grouped.ts | 2 + .../chart/charts/test-intersection.ts | 2 + .../src/services/chart/charts/test-unique.ts | 2 + .../backend/src/services/chart/charts/test.ts | 2 + .../src/services/chart/charts/users.ts | 6 +- packages/backend/src/services/chart/index.ts | 51 ------ packages/backend/src/services/index.ts | 4 +- .../NoteCreateService.ts} | 16 +- .../src/services/note/NoteDeleteService.ts | 150 ++++++++++++++++++ packages/backend/src/services/note/delete.ts | 141 ---------------- packages/backend/test/tests/noteService.ts | 2 +- 23 files changed, 278 insertions(+), 228 deletions(-) create mode 100644 packages/backend/src/services/chart/ChartManagementService.ts delete mode 100644 packages/backend/src/services/chart/index.ts rename packages/backend/src/services/{noteService.ts => note/NoteCreateService.ts} (98%) create mode 100644 packages/backend/src/services/note/NoteDeleteService.ts delete mode 100644 packages/backend/src/services/note/delete.ts diff --git a/packages/backend/src/services/chart/ChartManagementService.ts b/packages/backend/src/services/chart/ChartManagementService.ts new file mode 100644 index 000000000000..1b2ea61834b3 --- /dev/null +++ b/packages/backend/src/services/chart/ChartManagementService.ts @@ -0,0 +1,60 @@ +import { Container, Service, Inject } from 'typedi'; +import { beforeShutdown } from '@/misc/before-shutdown.js'; + +import FederationChart from './charts/federation.js'; +import NotesChart from './charts/notes.js'; +import UsersChart from './charts/users.js'; +import ActiveUsersChart from './charts/active-users.js'; +import InstanceChart from './charts/instance.js'; +import PerUserNotesChart from './charts/per-user-notes.js'; +import DriveChart from './charts/drive.js'; +import PerUserReactionsChart from './charts/per-user-reactions.js'; +import HashtagChart from './charts/hashtag.js'; +import PerUserFollowingChart from './charts/per-user-following.js'; +import PerUserDriveChart from './charts/per-user-drive.js'; +import ApRequestChart from './charts/ap-request.js'; + +@Service() +export class ChartManagementService { + constructor( + private federationChart: FederationChart, + private notesChart: NotesChart, + private usersChart: UsersChart, + private activeUsersChart: ActiveUsersChart, + private instanceChart: InstanceChart, + private perUserNotesChart: PerUserNotesChart, + private driveChart: DriveChart, + private perUserReactionsChart: PerUserReactionsChart, + private hashtagChart: HashtagChart, + private perUserFollowingChart: PerUserFollowingChart, + private perUserDriveChart: PerUserDriveChart, + private apRequestChart: ApRequestChart, + ) {} + + public async run() { + const charts = [ + this.federationChart, + this.notesChart, + this.usersChart, + this.activeUsersChart, + this.instanceChart, + this.perUserNotesChart, + this.driveChart, + this.perUserReactionsChart, + this.hashtagChart, + this.perUserFollowingChart, + this.perUserDriveChart, + this.apRequestChart, + ]; + + // 20分おきにメモリ情報をDBに書き込み + // TODO: 専用のサービスに切り出す + setInterval(() => { + for (const chart of charts) { + chart.save(); + } + }, 1000 * 60 * 20); + + beforeShutdown(() => Promise.all(charts.map(chart => chart.save()))); + } +} diff --git a/packages/backend/src/services/chart/charts/active-users.ts b/packages/backend/src/services/chart/charts/active-users.ts index d952ea53bd2f..54574433effd 100644 --- a/packages/backend/src/services/chart/charts/active-users.ts +++ b/packages/backend/src/services/chart/charts/active-users.ts @@ -1,6 +1,7 @@ -import Chart, { KVs } from '../core.js'; +import { Container, Service, Inject } from 'typedi'; import { User } from '@/models/entities/user.js'; import { Users } from '@/models/index.js'; +import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/active-users.js'; const week = 1000 * 60 * 60 * 24 * 7; @@ -11,6 +12,7 @@ const year = 1000 * 60 * 60 * 24 * 365; * アクティブユーザーに関するチャート */ // eslint-disable-next-line import/no-default-export +@Service() export default class ActiveUsersChart extends Chart { constructor() { super(name, schema); diff --git a/packages/backend/src/services/chart/charts/ap-request.ts b/packages/backend/src/services/chart/charts/ap-request.ts index e9e42ade7f61..2e6da82eb664 100644 --- a/packages/backend/src/services/chart/charts/ap-request.ts +++ b/packages/backend/src/services/chart/charts/ap-request.ts @@ -1,3 +1,4 @@ +import { Container, Service, Inject } from 'typedi'; import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/ap-request.js'; @@ -5,6 +6,7 @@ import { name, schema } from './entities/ap-request.js'; * Chart about ActivityPub requests */ // eslint-disable-next-line import/no-default-export +@Service() export default class ApRequestChart extends Chart { constructor() { super(name, schema); diff --git a/packages/backend/src/services/chart/charts/drive.ts b/packages/backend/src/services/chart/charts/drive.ts index 0eeba90dd323..0f0b8c82bb56 100644 --- a/packages/backend/src/services/chart/charts/drive.ts +++ b/packages/backend/src/services/chart/charts/drive.ts @@ -1,13 +1,15 @@ -import Chart, { KVs } from '../core.js'; -import { DriveFiles } from '@/models/index.js'; +import { Container, Service, Inject } from 'typedi'; import { Not, IsNull } from 'typeorm'; +import { DriveFiles } from '@/models/index.js'; import { DriveFile } from '@/models/entities/drive-file.js'; +import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/drive.js'; /** * ドライブに関するチャート */ // eslint-disable-next-line import/no-default-export +@Service() export default class DriveChart extends Chart { constructor() { super(name, schema); diff --git a/packages/backend/src/services/chart/charts/federation.ts b/packages/backend/src/services/chart/charts/federation.ts index 10221ee1e3f8..1368653233e6 100644 --- a/packages/backend/src/services/chart/charts/federation.ts +++ b/packages/backend/src/services/chart/charts/federation.ts @@ -1,12 +1,14 @@ -import Chart, { KVs } from '../core.js'; +import { Container, Service, Inject } from 'typedi'; import { Followings, Instances } from '@/models/index.js'; -import { name, schema } from './entities/federation.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; +import Chart, { KVs } from '../core.js'; +import { name, schema } from './entities/federation.js'; /** * フェデレーションに関するチャート */ // eslint-disable-next-line import/no-default-export +@Service() export default class FederationChart extends Chart { constructor() { super(name, schema); @@ -40,21 +42,21 @@ export default class FederationChart extends Chart { Followings.createQueryBuilder('following') .select('COUNT(DISTINCT following.followeeHost)') .where('following.followeeHost IS NOT NULL') - .andWhere(meta.blockedHosts.length === 0 ? '1=1' : `following.followeeHost NOT IN (:...blocked)`, { blocked: meta.blockedHosts }) + .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'following.followeeHost NOT IN (:...blocked)', { blocked: meta.blockedHosts }) .andWhere(`following.followeeHost NOT IN (${ suspendedInstancesQuery.getQuery() })`) .getRawOne() .then(x => parseInt(x.count, 10)), Followings.createQueryBuilder('following') .select('COUNT(DISTINCT following.followerHost)') .where('following.followerHost IS NOT NULL') - .andWhere(meta.blockedHosts.length === 0 ? '1=1' : `following.followerHost NOT IN (:...blocked)`, { blocked: meta.blockedHosts }) + .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'following.followerHost NOT IN (:...blocked)', { blocked: meta.blockedHosts }) .andWhere(`following.followerHost NOT IN (${ suspendedInstancesQuery.getQuery() })`) .getRawOne() .then(x => parseInt(x.count, 10)), Followings.createQueryBuilder('following') .select('COUNT(DISTINCT following.followeeHost)') .where('following.followeeHost IS NOT NULL') - .andWhere(meta.blockedHosts.length === 0 ? '1=1' : `following.followeeHost NOT IN (:...blocked)`, { blocked: meta.blockedHosts }) + .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'following.followeeHost NOT IN (:...blocked)', { blocked: meta.blockedHosts }) .andWhere(`following.followeeHost NOT IN (${ suspendedInstancesQuery.getQuery() })`) .andWhere(`following.followeeHost IN (${ pubsubSubQuery.getQuery() })`) .setParameters(pubsubSubQuery.getParameters()) @@ -63,17 +65,17 @@ export default class FederationChart extends Chart { Instances.createQueryBuilder('instance') .select('COUNT(instance.id)') .where(`instance.host IN (${ subInstancesQuery.getQuery() })`) - .andWhere(meta.blockedHosts.length === 0 ? '1=1' : `instance.host NOT IN (:...blocked)`, { blocked: meta.blockedHosts }) - .andWhere(`instance.isSuspended = false`) - .andWhere(`instance.lastCommunicatedAt > :gt`, { gt: new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)) }) + .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'instance.host NOT IN (:...blocked)', { blocked: meta.blockedHosts }) + .andWhere('instance.isSuspended = false') + .andWhere('instance.lastCommunicatedAt > :gt', { gt: new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)) }) .getRawOne() .then(x => parseInt(x.count, 10)), Instances.createQueryBuilder('instance') .select('COUNT(instance.id)') .where(`instance.host IN (${ pubInstancesQuery.getQuery() })`) - .andWhere(meta.blockedHosts.length === 0 ? '1=1' : `instance.host NOT IN (:...blocked)`, { blocked: meta.blockedHosts }) - .andWhere(`instance.isSuspended = false`) - .andWhere(`instance.lastCommunicatedAt > :gt`, { gt: new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)) }) + .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'instance.host NOT IN (:...blocked)', { blocked: meta.blockedHosts }) + .andWhere('instance.isSuspended = false') + .andWhere('instance.lastCommunicatedAt > :gt', { gt: new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)) }) .getRawOne() .then(x => parseInt(x.count, 10)), ]); diff --git a/packages/backend/src/services/chart/charts/hashtag.ts b/packages/backend/src/services/chart/charts/hashtag.ts index 31f7fa95dccc..46fe51b10dd8 100644 --- a/packages/backend/src/services/chart/charts/hashtag.ts +++ b/packages/backend/src/services/chart/charts/hashtag.ts @@ -1,12 +1,14 @@ -import Chart, { KVs } from '../core.js'; +import { Container, Service, Inject } from 'typedi'; import { User } from '@/models/entities/user.js'; import { Users } from '@/models/index.js'; +import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/hashtag.js'; /** * ハッシュタグに関するチャート */ // eslint-disable-next-line import/no-default-export +@Service() export default class HashtagChart extends Chart { constructor() { super(name, schema, true); diff --git a/packages/backend/src/services/chart/charts/instance.ts b/packages/backend/src/services/chart/charts/instance.ts index fe29ba5228d9..16265aac2d73 100644 --- a/packages/backend/src/services/chart/charts/instance.ts +++ b/packages/backend/src/services/chart/charts/instance.ts @@ -1,14 +1,16 @@ -import Chart, { KVs } from '../core.js'; +import { Container, Service, Inject } from 'typedi'; import { DriveFiles, Followings, Users, Notes } from '@/models/index.js'; import { DriveFile } from '@/models/entities/drive-file.js'; import { Note } from '@/models/entities/note.js'; import { toPuny } from '@/misc/convert-host.js'; +import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/instance.js'; /** * インスタンスごとのチャート */ // eslint-disable-next-line import/no-default-export +@Service() export default class InstanceChart extends Chart { constructor() { super(name, schema, true); diff --git a/packages/backend/src/services/chart/charts/notes.ts b/packages/backend/src/services/chart/charts/notes.ts index bb14b62f3c8a..a32dfb49d893 100644 --- a/packages/backend/src/services/chart/charts/notes.ts +++ b/packages/backend/src/services/chart/charts/notes.ts @@ -1,13 +1,15 @@ -import Chart, { KVs } from '../core.js'; -import { Notes } from '@/models/index.js'; +import { Container, Service, Inject } from 'typedi'; import { Not, IsNull } from 'typeorm'; +import { Notes } from '@/models/index.js'; import { Note } from '@/models/entities/note.js'; +import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/notes.js'; /** * ノートに関するチャート */ // eslint-disable-next-line import/no-default-export +@Service() export default class NotesChart extends Chart { constructor() { super(name, schema); diff --git a/packages/backend/src/services/chart/charts/per-user-drive.ts b/packages/backend/src/services/chart/charts/per-user-drive.ts index 5f75dc6887b9..1926fa5f47fa 100644 --- a/packages/backend/src/services/chart/charts/per-user-drive.ts +++ b/packages/backend/src/services/chart/charts/per-user-drive.ts @@ -1,12 +1,14 @@ -import Chart, { KVs } from '../core.js'; +import { Container, Service, Inject } from 'typedi'; import { DriveFiles } from '@/models/index.js'; import { DriveFile } from '@/models/entities/drive-file.js'; +import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/per-user-drive.js'; /** * ユーザーごとのドライブに関するチャート */ // eslint-disable-next-line import/no-default-export +@Service() export default class PerUserDriveChart extends Chart { constructor() { super(name, schema, true); diff --git a/packages/backend/src/services/chart/charts/per-user-following.ts b/packages/backend/src/services/chart/charts/per-user-following.ts index 02b149f52aec..eb7f62b25fae 100644 --- a/packages/backend/src/services/chart/charts/per-user-following.ts +++ b/packages/backend/src/services/chart/charts/per-user-following.ts @@ -1,13 +1,15 @@ -import Chart, { KVs } from '../core.js'; -import { Followings, Users } from '@/models/index.js'; +import { Container, Service, Inject } from 'typedi'; import { Not, IsNull } from 'typeorm'; +import { Followings, Users } from '@/models/index.js'; import { User } from '@/models/entities/user.js'; +import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/per-user-following.js'; /** * ユーザーごとのフォローに関するチャート */ // eslint-disable-next-line import/no-default-export +@Service() export default class PerUserFollowingChart extends Chart { constructor() { super(name, schema, true); diff --git a/packages/backend/src/services/chart/charts/per-user-notes.ts b/packages/backend/src/services/chart/charts/per-user-notes.ts index b9191dd08885..6b9bd7f10406 100644 --- a/packages/backend/src/services/chart/charts/per-user-notes.ts +++ b/packages/backend/src/services/chart/charts/per-user-notes.ts @@ -1,13 +1,15 @@ -import Chart, { KVs } from '../core.js'; +import { Container, Service, Inject } from 'typedi'; import { User } from '@/models/entities/user.js'; import { Notes } from '@/models/index.js'; import { Note } from '@/models/entities/note.js'; +import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/per-user-notes.js'; /** * ユーザーごとのノートに関するチャート */ // eslint-disable-next-line import/no-default-export +@Service() export default class PerUserNotesChart extends Chart { constructor() { super(name, schema, true); diff --git a/packages/backend/src/services/chart/charts/per-user-reactions.ts b/packages/backend/src/services/chart/charts/per-user-reactions.ts index 3a830e118c7c..2a5d8061068b 100644 --- a/packages/backend/src/services/chart/charts/per-user-reactions.ts +++ b/packages/backend/src/services/chart/charts/per-user-reactions.ts @@ -1,13 +1,15 @@ -import Chart, { KVs } from '../core.js'; +import { Container, Service, Inject } from 'typedi'; import { User } from '@/models/entities/user.js'; import { Note } from '@/models/entities/note.js'; import { Users } from '@/models/index.js'; +import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/per-user-reactions.js'; /** * ユーザーごとのリアクションに関するチャート */ // eslint-disable-next-line import/no-default-export +@Service() export default class PerUserReactionsChart extends Chart { constructor() { super(name, schema, true); diff --git a/packages/backend/src/services/chart/charts/test-grouped.ts b/packages/backend/src/services/chart/charts/test-grouped.ts index d01c9fcbd6ab..52f1981c9777 100644 --- a/packages/backend/src/services/chart/charts/test-grouped.ts +++ b/packages/backend/src/services/chart/charts/test-grouped.ts @@ -1,3 +1,4 @@ +import { Container, Service, Inject } from 'typedi'; import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/test-grouped.js'; @@ -5,6 +6,7 @@ import { name, schema } from './entities/test-grouped.js'; * For testing */ // eslint-disable-next-line import/no-default-export +@Service() export default class TestGroupedChart extends Chart { private total = {} as Record; diff --git a/packages/backend/src/services/chart/charts/test-intersection.ts b/packages/backend/src/services/chart/charts/test-intersection.ts index 88b5a715c17d..e7bde55f4d46 100644 --- a/packages/backend/src/services/chart/charts/test-intersection.ts +++ b/packages/backend/src/services/chart/charts/test-intersection.ts @@ -1,3 +1,4 @@ +import { Container, Service, Inject } from 'typedi'; import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/test-intersection.js'; @@ -5,6 +6,7 @@ import { name, schema } from './entities/test-intersection.js'; * For testing */ // eslint-disable-next-line import/no-default-export +@Service() export default class TestIntersectionChart extends Chart { constructor() { super(name, schema); diff --git a/packages/backend/src/services/chart/charts/test-unique.ts b/packages/backend/src/services/chart/charts/test-unique.ts index d714f1d40c55..23c23d0a8329 100644 --- a/packages/backend/src/services/chart/charts/test-unique.ts +++ b/packages/backend/src/services/chart/charts/test-unique.ts @@ -1,3 +1,4 @@ +import { Container, Service, Inject } from 'typedi'; import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/test-unique.js'; @@ -5,6 +6,7 @@ import { name, schema } from './entities/test-unique.js'; * For testing */ // eslint-disable-next-line import/no-default-export +@Service() export default class TestUniqueChart extends Chart { constructor() { super(name, schema); diff --git a/packages/backend/src/services/chart/charts/test.ts b/packages/backend/src/services/chart/charts/test.ts index adb2b18c8750..10b928e31d1e 100644 --- a/packages/backend/src/services/chart/charts/test.ts +++ b/packages/backend/src/services/chart/charts/test.ts @@ -1,3 +1,4 @@ +import { Container, Service, Inject } from 'typedi'; import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/test.js'; @@ -5,6 +6,7 @@ import { name, schema } from './entities/test.js'; * For testing */ // eslint-disable-next-line import/no-default-export +@Service() export default class TestChart extends Chart { public total = 0; // publicにするのはテストのため diff --git a/packages/backend/src/services/chart/charts/users.ts b/packages/backend/src/services/chart/charts/users.ts index acb16ead87d2..137706ac469e 100644 --- a/packages/backend/src/services/chart/charts/users.ts +++ b/packages/backend/src/services/chart/charts/users.ts @@ -1,13 +1,15 @@ -import Chart, { KVs } from '../core.js'; -import { Users } from '@/models/index.js'; +import { Container, Service, Inject } from 'typedi'; import { Not, IsNull } from 'typeorm'; +import { Users } from '@/models/index.js'; import { User } from '@/models/entities/user.js'; +import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/users.js'; /** * ユーザー数に関するチャート */ // eslint-disable-next-line import/no-default-export +@Service() export default class UsersChart extends Chart { constructor() { super(name, schema); diff --git a/packages/backend/src/services/chart/index.ts b/packages/backend/src/services/chart/index.ts deleted file mode 100644 index 8bf2d8f65fb5..000000000000 --- a/packages/backend/src/services/chart/index.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { beforeShutdown } from '@/misc/before-shutdown.js'; - -import FederationChart from './charts/federation.js'; -import NotesChart from './charts/notes.js'; -import UsersChart from './charts/users.js'; -import ActiveUsersChart from './charts/active-users.js'; -import InstanceChart from './charts/instance.js'; -import PerUserNotesChart from './charts/per-user-notes.js'; -import DriveChart from './charts/drive.js'; -import PerUserReactionsChart from './charts/per-user-reactions.js'; -import HashtagChart from './charts/hashtag.js'; -import PerUserFollowingChart from './charts/per-user-following.js'; -import PerUserDriveChart from './charts/per-user-drive.js'; -import ApRequestChart from './charts/ap-request.js'; - -export const federationChart = new FederationChart(); -export const notesChart = new NotesChart(); -export const usersChart = new UsersChart(); -export const activeUsersChart = new ActiveUsersChart(); -export const instanceChart = new InstanceChart(); -export const perUserNotesChart = new PerUserNotesChart(); -export const driveChart = new DriveChart(); -export const perUserReactionsChart = new PerUserReactionsChart(); -export const hashtagChart = new HashtagChart(); -export const perUserFollowingChart = new PerUserFollowingChart(); -export const perUserDriveChart = new PerUserDriveChart(); -export const apRequestChart = new ApRequestChart(); - -const charts = [ - federationChart, - notesChart, - usersChart, - activeUsersChart, - instanceChart, - perUserNotesChart, - driveChart, - perUserReactionsChart, - hashtagChart, - perUserFollowingChart, - perUserDriveChart, - apRequestChart, -]; - -// 20分おきにメモリ情報をDBに書き込み -setInterval(() => { - for (const chart of charts) { - chart.save(); - } -}, 1000 * 60 * 20); - -beforeShutdown(() => Promise.all(charts.map(chart => chart.save()))); diff --git a/packages/backend/src/services/index.ts b/packages/backend/src/services/index.ts index dbdf5b22c84c..e4f8cc0fe110 100644 --- a/packages/backend/src/services/index.ts +++ b/packages/backend/src/services/index.ts @@ -1,4 +1,4 @@ import { Container, Service } from 'typedi'; -import { NoteService } from './noteService.js'; +import { NoteCreateService } from './note/NoteCreateService.js'; -export const noteService = Container.get(NoteService); +export const noteCreateService = Container.get(NoteCreateService); diff --git a/packages/backend/src/services/noteService.ts b/packages/backend/src/services/note/NoteCreateService.ts similarity index 98% rename from packages/backend/src/services/noteService.ts rename to packages/backend/src/services/note/NoteCreateService.ts index ffacae99a465..be1d6c0d5621 100644 --- a/packages/backend/src/services/noteService.ts +++ b/packages/backend/src/services/note/NoteCreateService.ts @@ -35,13 +35,13 @@ import { Cache } from '@/misc/cache.js'; import { UserProfile } from '@/models/entities/user-profile.js'; import { db } from '@/db/postgre.js'; import { getActiveWebhooks } from '@/misc/webhook-cache.js'; -import es from '../db/elasticsearch.js'; -import { registerOrFetchInstanceDoc } from './register-or-fetch-instance-doc.js'; -import { updateHashtags } from './update-hashtag.js'; -import { deliverToRelays } from './relay.js'; -import { addNoteToAntenna } from './add-note-to-antenna.js'; -import { createNotification } from './create-notification.js'; -import { WebhookService } from './webhookService.js'; +import es from '../../db/elasticsearch.js'; +import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js'; +import { updateHashtags } from '../update-hashtag.js'; +import { deliverToRelays } from '../relay.js'; +import { addNoteToAntenna } from '../add-note-to-antenna.js'; +import { createNotification } from '../create-notification.js'; +import { WebhookService } from '../webhookService.js'; const mutedWordsCache = new Cache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5); @@ -129,7 +129,7 @@ type Option = { }; @Service() -export class NoteService { +export class NoteCreateService { constructor( @Inject('notesRepository') private notesRepository: typeof Notes, diff --git a/packages/backend/src/services/note/NoteDeleteService.ts b/packages/backend/src/services/note/NoteDeleteService.ts new file mode 100644 index 000000000000..07b03b59eb86 --- /dev/null +++ b/packages/backend/src/services/note/NoteDeleteService.ts @@ -0,0 +1,150 @@ +import { Container, Service, Inject } from 'typedi'; +import { Brackets, In } from 'typeorm'; +import { publishNoteStream } from '@/services/stream.js'; +import renderDelete from '@/remote/activitypub/renderer/delete.js'; +import renderAnnounce from '@/remote/activitypub/renderer/announce.js'; +import renderUndo from '@/remote/activitypub/renderer/undo.js'; +import { renderActivity } from '@/remote/activitypub/renderer/index.js'; +import renderTombstone from '@/remote/activitypub/renderer/tombstone.js'; +import config from '@/config/index.js'; +import { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; +import { Note, IMentionedRemoteUsers } from '@/models/entities/note.js'; +import { Notes, Users, Instances } from '@/models/index.js'; +import { notesChart, perUserNotesChart, instanceChart } from '@/services/chart/index.js'; +import { deliverToFollowers, deliverToUser } from '@/remote/activitypub/deliver-manager.js'; +import { countSameRenotes } from '@/misc/count-same-renotes.js'; +import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js'; +import { deliverToRelays } from '../relay.js'; + +@Service() +export class NoteCreateService { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) {} + + /** + * 投稿を削除します。 + * @param user 投稿者 + * @param note 投稿 + */ + async delete(user: { id: User['id']; uri: User['uri']; host: User['host']; }, note: Note, quiet = false) { + const deletedAt = new Date(); + + // この投稿を除く指定したユーザーによる指定したノートのリノートが存在しないとき + if (note.renoteId && (await countSameRenotes(user.id, note.renoteId, note.id)) === 0) { + this.notesRepository.decrement({ id: note.renoteId }, 'renoteCount', 1); + this.notesRepository.decrement({ id: note.renoteId }, 'score', 1); + } + + if (note.replyId) { + await this.notesRepository.decrement({ id: note.replyId }, 'repliesCount', 1); + } + + if (!quiet) { + publishNoteStream(note.id, 'deleted', { + deletedAt: deletedAt, + }); + + //#region ローカルの投稿なら削除アクティビティを配送 + if (Users.isLocalUser(user) && !note.localOnly) { + let renote: Note | null = null; + + // if deletd note is renote + if (note.renoteId && note.text == null && !note.hasPoll && (note.fileIds == null || note.fileIds.length === 0)) { + renote = await this.notesRepository.findOneBy({ + id: note.renoteId, + }); + } + + const content = renderActivity(renote + ? renderUndo(renderAnnounce(renote.uri || `${config.url}/notes/${renote.id}`, note), user) + : renderDelete(renderTombstone(`${config.url}/notes/${note.id}`), user)); + + this.#deliverToConcerned(user, note, content); + } + + // also deliever delete activity to cascaded notes + const cascadingNotes = (await this.#findCascadingNotes(note)).filter(note => !note.localOnly); // filter out local-only notes + for (const cascadingNote of cascadingNotes) { + if (!cascadingNote.user) continue; + if (!Users.isLocalUser(cascadingNote.user)) continue; + const content = renderActivity(renderDelete(renderTombstone(`${config.url}/notes/${cascadingNote.id}`), cascadingNote.user)); + this.#deliverToConcerned(cascadingNote.user, cascadingNote, content); + } + //#endregion + + // 統計を更新 + notesChart.update(note, false); + perUserNotesChart.update(user, note, false); + + if (Users.isRemoteUser(user)) { + registerOrFetchInstanceDoc(user.host).then(i => { + Instances.decrement({ id: i.id }, 'notesCount', 1); + instanceChart.updateNote(i.host, note, false); + }); + } + } + + await this.notesRepository.delete({ + id: note.id, + userId: user.id, + }); + } + + async #findCascadingNotes(note: Note) { + const cascadingNotes: Note[] = []; + + const recursive = async (noteId: string) => { + const query = this.notesRepository.createQueryBuilder('note') + .where('note.replyId = :noteId', { noteId }) + .orWhere(new Brackets(q => { + q.where('note.renoteId = :noteId', { noteId }) + .andWhere('note.text IS NOT NULL'); + })) + .leftJoinAndSelect('note.user', 'user'); + const replies = await query.getMany(); + for (const reply of replies) { + cascadingNotes.push(reply); + await recursive(reply.id); + } + }; + await recursive(note.id); + + return cascadingNotes.filter(note => note.userHost === null); // filter out non-local users + } + + async #getMentionedRemoteUsers(note: Note) { + const where = [] as any[]; + + // mention / reply / dm + const uris = (JSON.parse(note.mentionedRemoteUsers) as IMentionedRemoteUsers).map(x => x.uri); + if (uris.length > 0) { + where.push( + { uri: In(uris) }, + ); + } + + // renote / quote + if (note.renoteUserId) { + where.push({ + id: note.renoteUserId, + }); + } + + if (where.length === 0) return []; + + return await Users.find({ + where, + }) as IRemoteUser[]; + } + + async #deliverToConcerned(user: { id: ILocalUser['id']; host: null; }, note: Note, content: any) { + deliverToFollowers(user, content); + deliverToRelays(user, content); + const remoteUsers = await this.#getMentionedRemoteUsers(note); + for (const remoteUser of remoteUsers) { + deliverToUser(user, content, remoteUser); + } + } +} diff --git a/packages/backend/src/services/note/delete.ts b/packages/backend/src/services/note/delete.ts deleted file mode 100644 index 4963200161e4..000000000000 --- a/packages/backend/src/services/note/delete.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { Brackets, In } from 'typeorm'; -import { publishNoteStream } from '@/services/stream.js'; -import renderDelete from '@/remote/activitypub/renderer/delete.js'; -import renderAnnounce from '@/remote/activitypub/renderer/announce.js'; -import renderUndo from '@/remote/activitypub/renderer/undo.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import renderTombstone from '@/remote/activitypub/renderer/tombstone.js'; -import config from '@/config/index.js'; -import { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; -import { Note, IMentionedRemoteUsers } from '@/models/entities/note.js'; -import { Notes, Users, Instances } from '@/models/index.js'; -import { notesChart, perUserNotesChart, instanceChart } from '@/services/chart/index.js'; -import { deliverToFollowers, deliverToUser } from '@/remote/activitypub/deliver-manager.js'; -import { countSameRenotes } from '@/misc/count-same-renotes.js'; -import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js'; -import { deliverToRelays } from '../relay.js'; - -/** - * 投稿を削除します。 - * @param user 投稿者 - * @param note 投稿 - */ -export default async function(user: { id: User['id']; uri: User['uri']; host: User['host']; }, note: Note, quiet = false) { - const deletedAt = new Date(); - - // この投稿を除く指定したユーザーによる指定したノートのリノートが存在しないとき - if (note.renoteId && (await countSameRenotes(user.id, note.renoteId, note.id)) === 0) { - Notes.decrement({ id: note.renoteId }, 'renoteCount', 1); - Notes.decrement({ id: note.renoteId }, 'score', 1); - } - - if (note.replyId) { - await Notes.decrement({ id: note.replyId }, 'repliesCount', 1); - } - - if (!quiet) { - publishNoteStream(note.id, 'deleted', { - deletedAt: deletedAt, - }); - - //#region ローカルの投稿なら削除アクティビティを配送 - if (Users.isLocalUser(user) && !note.localOnly) { - let renote: Note | null = null; - - // if deletd note is renote - if (note.renoteId && note.text == null && !note.hasPoll && (note.fileIds == null || note.fileIds.length === 0)) { - renote = await Notes.findOneBy({ - id: note.renoteId, - }); - } - - const content = renderActivity(renote - ? renderUndo(renderAnnounce(renote.uri || `${config.url}/notes/${renote.id}`, note), user) - : renderDelete(renderTombstone(`${config.url}/notes/${note.id}`), user)); - - deliverToConcerned(user, note, content); - } - - // also deliever delete activity to cascaded notes - const cascadingNotes = (await findCascadingNotes(note)).filter(note => !note.localOnly); // filter out local-only notes - for (const cascadingNote of cascadingNotes) { - if (!cascadingNote.user) continue; - if (!Users.isLocalUser(cascadingNote.user)) continue; - const content = renderActivity(renderDelete(renderTombstone(`${config.url}/notes/${cascadingNote.id}`), cascadingNote.user)); - deliverToConcerned(cascadingNote.user, cascadingNote, content); - } - //#endregion - - // 統計を更新 - notesChart.update(note, false); - perUserNotesChart.update(user, note, false); - - if (Users.isRemoteUser(user)) { - registerOrFetchInstanceDoc(user.host).then(i => { - Instances.decrement({ id: i.id }, 'notesCount', 1); - instanceChart.updateNote(i.host, note, false); - }); - } - } - - await Notes.delete({ - id: note.id, - userId: user.id, - }); -} - -async function findCascadingNotes(note: Note) { - const cascadingNotes: Note[] = []; - - const recursive = async (noteId: string) => { - const query = Notes.createQueryBuilder('note') - .where('note.replyId = :noteId', { noteId }) - .orWhere(new Brackets(q => { - q.where('note.renoteId = :noteId', { noteId }) - .andWhere('note.text IS NOT NULL'); - })) - .leftJoinAndSelect('note.user', 'user'); - const replies = await query.getMany(); - for (const reply of replies) { - cascadingNotes.push(reply); - await recursive(reply.id); - } - }; - await recursive(note.id); - - return cascadingNotes.filter(note => note.userHost === null); // filter out non-local users -} - -async function getMentionedRemoteUsers(note: Note) { - const where = [] as any[]; - - // mention / reply / dm - const uris = (JSON.parse(note.mentionedRemoteUsers) as IMentionedRemoteUsers).map(x => x.uri); - if (uris.length > 0) { - where.push( - { uri: In(uris) }, - ); - } - - // renote / quote - if (note.renoteUserId) { - where.push({ - id: note.renoteUserId, - }); - } - - if (where.length === 0) return []; - - return await Users.find({ - where, - }) as IRemoteUser[]; -} - -async function deliverToConcerned(user: { id: ILocalUser['id']; host: null; }, note: Note, content: any) { - deliverToFollowers(user, content); - deliverToRelays(user, content); - const remoteUsers = await getMentionedRemoteUsers(note); - for (const remoteUser of remoteUsers) { - deliverToUser(user, content, remoteUser); - } -} diff --git a/packages/backend/test/tests/noteService.ts b/packages/backend/test/tests/noteService.ts index 12c4633e6ed9..5f0a1c1e3d64 100644 --- a/packages/backend/test/tests/noteService.ts +++ b/packages/backend/test/tests/noteService.ts @@ -7,7 +7,7 @@ import { initDb } from '@/db/postgre.js'; import { Notes } from '@/models/index.js'; import { FooService } from '@/services/fooService.js'; import { WebhookService } from '../../src/services/webhookService.js'; -import { NoteService } from '../../src/services/noteService.js'; +import { NoteService } from '../../src/services/note/NoteCreateService.js'; describe('NoteService', () => { beforeAll(async () => { From 9b9a11b6900edefdbbb8e1cd6d76d1dea16f2908 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 10 Sep 2022 23:03:57 +0900 Subject: [PATCH 005/180] wip --- .../services/chart/ChartManagementService.ts | 1 - .../src/services/chart/charts/test-grouped.ts | 5 +- .../chart/charts/test-intersection.ts | 5 +- .../src/services/chart/charts/test-unique.ts | 5 +- .../backend/src/services/chart/charts/test.ts | 5 +- packages/backend/src/services/chart/core.ts | 5 +- packages/backend/test/tests/chart.ts | 50 +++++++++++++++---- 7 files changed, 54 insertions(+), 22 deletions(-) diff --git a/packages/backend/src/services/chart/ChartManagementService.ts b/packages/backend/src/services/chart/ChartManagementService.ts index 1b2ea61834b3..f7b315531083 100644 --- a/packages/backend/src/services/chart/ChartManagementService.ts +++ b/packages/backend/src/services/chart/ChartManagementService.ts @@ -48,7 +48,6 @@ export class ChartManagementService { ]; // 20分おきにメモリ情報をDBに書き込み - // TODO: 専用のサービスに切り出す setInterval(() => { for (const chart of charts) { chart.save(); diff --git a/packages/backend/src/services/chart/charts/test-grouped.ts b/packages/backend/src/services/chart/charts/test-grouped.ts index 52f1981c9777..54335d20a384 100644 --- a/packages/backend/src/services/chart/charts/test-grouped.ts +++ b/packages/backend/src/services/chart/charts/test-grouped.ts @@ -1,4 +1,5 @@ import { Container, Service, Inject } from 'typedi'; +import { DataSource } from 'typeorm'; import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/test-grouped.js'; @@ -10,8 +11,8 @@ import { name, schema } from './entities/test-grouped.js'; export default class TestGroupedChart extends Chart { private total = {} as Record; - constructor() { - super(name, schema, true); + constructor(db: DataSource) { + super(db, name, schema, true); } protected async tickMajor(group: string): Promise>> { diff --git a/packages/backend/src/services/chart/charts/test-intersection.ts b/packages/backend/src/services/chart/charts/test-intersection.ts index e7bde55f4d46..cb7a04b529fa 100644 --- a/packages/backend/src/services/chart/charts/test-intersection.ts +++ b/packages/backend/src/services/chart/charts/test-intersection.ts @@ -1,4 +1,5 @@ import { Container, Service, Inject } from 'typedi'; +import { DataSource } from 'typeorm'; import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/test-intersection.js'; @@ -8,8 +9,8 @@ import { name, schema } from './entities/test-intersection.js'; // eslint-disable-next-line import/no-default-export @Service() export default class TestIntersectionChart extends Chart { - constructor() { - super(name, schema); + constructor(db: DataSource) { + super(db, name, schema); } protected async tickMajor(): Promise>> { diff --git a/packages/backend/src/services/chart/charts/test-unique.ts b/packages/backend/src/services/chart/charts/test-unique.ts index 23c23d0a8329..368d4f0b16ec 100644 --- a/packages/backend/src/services/chart/charts/test-unique.ts +++ b/packages/backend/src/services/chart/charts/test-unique.ts @@ -1,4 +1,5 @@ import { Container, Service, Inject } from 'typedi'; +import { DataSource } from 'typeorm'; import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/test-unique.js'; @@ -8,8 +9,8 @@ import { name, schema } from './entities/test-unique.js'; // eslint-disable-next-line import/no-default-export @Service() export default class TestUniqueChart extends Chart { - constructor() { - super(name, schema); + constructor(db: DataSource) { + super(db, name, schema); } protected async tickMajor(): Promise>> { diff --git a/packages/backend/src/services/chart/charts/test.ts b/packages/backend/src/services/chart/charts/test.ts index 10b928e31d1e..b0ff5ea7b970 100644 --- a/packages/backend/src/services/chart/charts/test.ts +++ b/packages/backend/src/services/chart/charts/test.ts @@ -1,4 +1,5 @@ import { Container, Service, Inject } from 'typedi'; +import { DataSource } from 'typeorm'; import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/test.js'; @@ -10,8 +11,8 @@ import { name, schema } from './entities/test.js'; export default class TestChart extends Chart { public total = 0; // publicにするのはテストのため - constructor() { - super(name, schema); + constructor(db: DataSource) { + super(db, name, schema); } protected async tickMajor(): Promise>> { diff --git a/packages/backend/src/services/chart/core.ts b/packages/backend/src/services/chart/core.ts index f3e8fae01dfb..77b21ad6f8a1 100644 --- a/packages/backend/src/services/chart/core.ts +++ b/packages/backend/src/services/chart/core.ts @@ -5,10 +5,9 @@ */ import * as nestedProperty from 'nested-property'; -import { EntitySchema, Repository, LessThan, Between } from 'typeorm'; +import { EntitySchema, Repository, LessThan, Between, DataSource } from 'typeorm'; import { dateUTC, isTimeSame, isTimeBefore, subtractTime, addTime } from '@/prelude/time.js'; import { getChartInsertLock } from '@/misc/app-lock.js'; -import { db } from '@/db/postgre.js'; import Logger from '../logger.js'; const logger = new Logger('chart', 'white', process.env.NODE_ENV !== 'test'); @@ -230,7 +229,7 @@ export default abstract class Chart { }; } - constructor(name: string, schema: T, grouped = false) { + constructor(db: DataSource, name: string, schema: T, grouped = false) { this.name = name; this.schema = schema; diff --git a/packages/backend/test/tests/chart.ts b/packages/backend/test/tests/chart.ts index 277fed1bc61f..0728ed9d316a 100644 --- a/packages/backend/test/tests/chart.ts +++ b/packages/backend/test/tests/chart.ts @@ -2,13 +2,20 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import * as lolex from '@sinonjs/fake-timers'; -import TestChart from '../../src/services/chart/charts/test.js'; -import TestGroupedChart from '../../src/services/chart/charts/test-grouped.js'; -import TestUniqueChart from '../../src/services/chart/charts/test-unique.js'; -import TestIntersectionChart from '../../src/services/chart/charts/test-intersection.js'; -import { initDb } from '../../src/db/postgre.js'; +import { DataSource } from 'typeorm'; +import config from '@/config/index.js'; +import TestChart from '@/services/chart/charts/test.js'; +import TestGroupedChart from '@/services/chart/charts/test-grouped.js'; +import TestUniqueChart from '@/services/chart/charts/test-unique.js'; +import TestIntersectionChart from '@/services/chart/charts/test-intersection.js'; +import { entity as TestChartEntity } from '@/services/chart/charts/entities/test.js'; +import { entity as TestGroupedChartEntity } from '@/services/chart/charts/entities/test-grouped.js'; +import { entity as TestUniqueChartEntity } from '@/services/chart/charts/entities/test-unique.js'; +import { entity as TestIntersectionChartEntity } from '@/services/chart/charts/entities/test-intersection.js'; describe('Chart', () => { + let db: DataSource; + let testChart: TestChart; let testGroupedChart: TestGroupedChart; let testUniqueChart: TestUniqueChart; @@ -16,12 +23,35 @@ describe('Chart', () => { let clock: lolex.InstalledClock; beforeEach(async () => { - await initDb(true); + db = new DataSource({ + type: 'postgres', + host: config.db.host, + port: config.db.port, + username: config.db.user, + password: config.db.pass, + database: config.db.db, + extra: { + statement_timeout: 1000 * 10, + ...config.db.extra, + }, + synchronize: true, + dropSchema: true, + maxQueryExecutionTime: 300, + entities: [ + TestChartEntity.hour, TestChartEntity.day, + TestGroupedChartEntity.hour, TestGroupedChartEntity.day, + TestUniqueChartEntity.hour, TestUniqueChartEntity.day, + TestIntersectionChartEntity.hour, TestIntersectionChartEntity.day, + ], + migrations: ['../../migration/*.js'], + }); + + await db.initialize(); - testChart = new TestChart(); - testGroupedChart = new TestGroupedChart(); - testUniqueChart = new TestUniqueChart(); - testIntersectionChart = new TestIntersectionChart(); + testChart = new TestChart(db); + testGroupedChart = new TestGroupedChart(db); + testUniqueChart = new TestUniqueChart(db); + testIntersectionChart = new TestIntersectionChart(db); clock = lolex.install({ now: new Date(Date.UTC(2000, 0, 1, 0, 0, 0)), From 5b54fe4cbb6baaf45aa034c40f703afe793b8ad2 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 11 Sep 2022 00:39:58 +0900 Subject: [PATCH 006/180] Update chart.ts --- packages/backend/test/tests/chart.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/backend/test/tests/chart.ts b/packages/backend/test/tests/chart.ts index 0728ed9d316a..efd4442e91c4 100644 --- a/packages/backend/test/tests/chart.ts +++ b/packages/backend/test/tests/chart.ts @@ -14,7 +14,7 @@ import { entity as TestUniqueChartEntity } from '@/services/chart/charts/entitie import { entity as TestIntersectionChartEntity } from '@/services/chart/charts/entities/test-intersection.js'; describe('Chart', () => { - let db: DataSource; + let db: DataSource | undefined; let testChart: TestChart; let testGroupedChart: TestGroupedChart; @@ -23,6 +23,8 @@ describe('Chart', () => { let clock: lolex.InstalledClock; beforeEach(async () => { + if (db) db.destroy(); + db = new DataSource({ type: 'postgres', host: config.db.host, From 4e90b740d2163dbfa903dbde4ea4a7cbef8ab0d5 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 11 Sep 2022 02:48:21 +0900 Subject: [PATCH 007/180] wip --- packages/backend/package.json | 4 +- packages/backend/src/app.module.ts | 12 ++ packages/backend/src/boot/worker.ts | 9 +- packages/backend/src/server/api/define.ts | 59 ------ .../backend/src/server/api/endpoint-base.ts | 62 +++++++ .../backend/src/server/api/endpoints/notes.ts | 101 ++++++----- packages/backend/src/server/index.ts | 171 ++++++++---------- .../services/chart/ChartManagementService.ts | 4 +- .../src/services/chart/charts/active-users.ts | 4 +- .../src/services/chart/charts/ap-request.ts | 4 +- .../src/services/chart/charts/drive.ts | 4 +- .../src/services/chart/charts/federation.ts | 4 +- .../src/services/chart/charts/hashtag.ts | 4 +- .../src/services/chart/charts/instance.ts | 4 +- .../src/services/chart/charts/notes.ts | 4 +- .../services/chart/charts/per-user-drive.ts | 4 +- .../chart/charts/per-user-following.ts | 4 +- .../services/chart/charts/per-user-notes.ts | 4 +- .../chart/charts/per-user-reactions.ts | 4 +- .../src/services/chart/charts/test-grouped.ts | 4 +- .../chart/charts/test-intersection.ts | 4 +- .../src/services/chart/charts/test-unique.ts | 4 +- .../backend/src/services/chart/charts/test.ts | 4 +- .../src/services/chart/charts/users.ts | 4 +- packages/backend/src/services/fooService.ts | 2 +- .../src/services/note/NoteCreateService.ts | 22 ++- .../src/services/note/NoteDeleteService.ts | 4 +- .../backend/src/services/webhookService.ts | 2 +- .../test/tests/{noteService.ts => note.ts} | 8 +- packages/backend/yarn.lock | 83 ++++++++- 30 files changed, 345 insertions(+), 262 deletions(-) create mode 100644 packages/backend/src/app.module.ts delete mode 100644 packages/backend/src/server/api/define.ts create mode 100644 packages/backend/src/server/api/endpoint-base.ts rename packages/backend/test/tests/{noteService.ts => note.ts} (68%) diff --git a/packages/backend/package.json b/packages/backend/package.json index 3bf613ec49cf..3561ed451c4e 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -25,6 +25,8 @@ "@koa/cors": "3.1.0", "@koa/multer": "3.0.0", "@koa/router": "9.0.1", + "@nestjs/common": "9.0.11", + "@nestjs/core": "9.0.11", "@peertube/http-signature": "1.7.0", "@sinonjs/fake-timers": "9.1.2", "@syuilo/aiscript": "0.11.1", @@ -98,6 +100,7 @@ "rename": "1.0.4", "rndstr": "1.0.0", "rss-parser": "3.12.0", + "rxjs": "7.5.6", "s-age": "1.1.2", "sanitize-html": "2.7.1", "semver": "7.3.7", @@ -115,7 +118,6 @@ "tsc-alias": "1.7.0", "tsconfig-paths": "4.1.0", "twemoji-parser": "14.0.0", - "typedi": "0.10.0", "typeorm": "0.3.9", "ulid": "2.3.0", "unzipper": "0.10.11", diff --git a/packages/backend/src/app.module.ts b/packages/backend/src/app.module.ts new file mode 100644 index 000000000000..ee0cef6d9c41 --- /dev/null +++ b/packages/backend/src/app.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { Notes } from '@/models/index.js'; + +@Module({ + imports: [ + ], + providers: [{ + provide: 'notesRepository', + useValue: Notes, + }], + }) +export class AppModule {} diff --git a/packages/backend/src/boot/worker.ts b/packages/backend/src/boot/worker.ts index 5f56a194cec6..0a91cc5b1056 100644 --- a/packages/backend/src/boot/worker.ts +++ b/packages/backend/src/boot/worker.ts @@ -1,7 +1,8 @@ import cluster from 'node:cluster'; -import { Container } from 'typedi'; -import { Notes } from '@/models/index.js'; +import { NestFactory } from '@nestjs/core'; import { initDb } from '../db/postgre.js'; +import createServer from '../server/index.js'; +import { AppModule } from '../app.module.js'; /** * Init worker process @@ -9,10 +10,10 @@ import { initDb } from '../db/postgre.js'; export async function workerMain() { await initDb(); - Container.set('notesRepository', Notes); + const app = await NestFactory.createApplicationContext(AppModule); // start server - await import('../server/index.js').then(x => x.default()); + await createServer(app); // start job queue import('../queue/index.js').then(x => x.default()); diff --git a/packages/backend/src/server/api/define.ts b/packages/backend/src/server/api/define.ts deleted file mode 100644 index c1b56b8a83ec..000000000000 --- a/packages/backend/src/server/api/define.ts +++ /dev/null @@ -1,59 +0,0 @@ -import * as fs from 'node:fs'; -import Ajv from 'ajv'; -import { CacheableLocalUser, ILocalUser } from '@/models/entities/user.js'; -import { Schema, SchemaType } from '@/misc/schema.js'; -import { AccessToken } from '@/models/entities/access-token.js'; -import { IEndpointMeta } from './endpoints.js'; -import { ApiError } from './error.js'; - -export type Response = Record | void; - -// TODO: paramsの型をT['params']のスキーマ定義から推論する -type executor = - (params: SchemaType, user: T['requireCredential'] extends true ? CacheableLocalUser : CacheableLocalUser | null, token: AccessToken | null, file?: any, cleanup?: () => any, ip?: string | null, headers?: Record | null) => - Promise>>; - -const ajv = new Ajv({ - useDefaults: true, -}); - -ajv.addFormat('misskey:id', /^[a-zA-Z0-9]+$/); - -export default function (meta: T, paramDef: Ps, cb: executor) - : (params: any, user: T['requireCredential'] extends true ? CacheableLocalUser : CacheableLocalUser | null, token: AccessToken | null, file?: any, ip?: string | null, headers?: Record | null) => Promise { - const validate = ajv.compile(paramDef); - - return (params: any, user: T['requireCredential'] extends true ? CacheableLocalUser : CacheableLocalUser | null, token: AccessToken | null, file?: any, ip?: string | null, headers?: Record | null) => { - let cleanup: undefined | (() => void) = undefined; - - if (meta.requireFile) { - cleanup = () => { - fs.unlink(file.path, () => {}); - }; - - if (file == null) return Promise.reject(new ApiError({ - message: 'File required.', - code: 'FILE_REQUIRED', - id: '4267801e-70d1-416a-b011-4ee502885d8b', - })); - } - - const valid = validate(params); - if (!valid) { - if (file) cleanup!(); - - const errors = validate.errors!; - const err = new ApiError({ - message: 'Invalid param.', - code: 'INVALID_PARAM', - id: '3d81ceae-475f-4600-b2a8-2bc116157532', - }, { - param: errors[0].schemaPath, - reason: errors[0].message, - }); - return Promise.reject(err); - } - - return cb(params as SchemaType, user, token, file, cleanup, ip, headers); - }; -} diff --git a/packages/backend/src/server/api/endpoint-base.ts b/packages/backend/src/server/api/endpoint-base.ts new file mode 100644 index 000000000000..03034c3d93e1 --- /dev/null +++ b/packages/backend/src/server/api/endpoint-base.ts @@ -0,0 +1,62 @@ +import * as fs from 'node:fs'; +import Ajv from 'ajv'; +import type { Schema, SchemaType } from '@/misc/schema.js'; +import type { CacheableLocalUser } from '@/models/entities/user.js'; +import type { AccessToken } from '@/models/entities/access-token.js'; +import { ApiError } from './error.js'; +import type { IEndpointMeta } from './endpoints.js'; + +const ajv = new Ajv({ + useDefaults: true, +}); + +ajv.addFormat('misskey:id', /^[a-zA-Z0-9]+$/); + +export type Response = Record | void; + +// TODO: paramsの型をT['params']のスキーマ定義から推論する +type executor = + (params: SchemaType, user: T['requireCredential'] extends true ? CacheableLocalUser : CacheableLocalUser | null, token: AccessToken | null, file?: any, cleanup?: () => any, ip?: string | null, headers?: Record | null) => + Promise>>; + +export abstract class Endpoint { + public exec: (params: any, user: T['requireCredential'] extends true ? CacheableLocalUser : CacheableLocalUser | null, token: AccessToken | null, file?: any, ip?: string | null, headers?: Record | null) => Promise; + + constructor(meta: T, paramDef: Ps, cb: executor) { + const validate = ajv.compile(paramDef); + + this.exec = (params: any, user: T['requireCredential'] extends true ? CacheableLocalUser : CacheableLocalUser | null, token: AccessToken | null, file?: any, ip?: string | null, headers?: Record | null) => { + let cleanup: undefined | (() => void) = undefined; + + if (meta.requireFile) { + cleanup = () => { + fs.unlink(file.path, () => {}); + }; + + if (file == null) return Promise.reject(new ApiError({ + message: 'File required.', + code: 'FILE_REQUIRED', + id: '4267801e-70d1-416a-b011-4ee502885d8b', + })); + } + + const valid = validate(params); + if (!valid) { + if (file) cleanup!(); + + const errors = validate.errors!; + const err = new ApiError({ + message: 'Invalid param.', + code: 'INVALID_PARAM', + id: '3d81ceae-475f-4600-b2a8-2bc116157532', + }, { + param: errors[0].schemaPath, + reason: errors[0].message, + }); + return Promise.reject(err); + } + + return cb(params as SchemaType, user, token, file, cleanup, ip, headers); + }; + } +} diff --git a/packages/backend/src/server/api/endpoints/notes.ts b/packages/backend/src/server/api/endpoints/notes.ts index 015b0338e3f2..697b43cb3b7c 100644 --- a/packages/backend/src/server/api/endpoints/notes.ts +++ b/packages/backend/src/server/api/endpoints/notes.ts @@ -1,5 +1,6 @@ -import { Notes } from '@/models/index.js'; -import define from '../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import type { Notes } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../common/make-pagination-query.js'; export const meta = { @@ -32,48 +33,56 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { - const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .andWhere('note.visibility = \'public\'') - .andWhere('note.localOnly = FALSE') - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('user.avatar', 'avatar') - .leftJoinAndSelect('user.banner', 'banner') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') - .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') - .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - - if (ps.local) { - query.andWhere('note.userHost IS NULL'); - } - - if (ps.reply !== undefined) { - query.andWhere(ps.reply ? 'note.replyId IS NOT NULL' : 'note.replyId IS NULL'); - } - - if (ps.renote !== undefined) { - query.andWhere(ps.renote ? 'note.renoteId IS NOT NULL' : 'note.renoteId IS NULL'); - } - - if (ps.withFiles !== undefined) { - query.andWhere(ps.withFiles ? 'note.fileIds != \'{}\'' : 'note.fileIds = \'{}\''); - } - - if (ps.poll !== undefined) { - query.andWhere(ps.poll ? 'note.hasPoll = TRUE' : 'note.hasPoll = FALSE'); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps) => { + const query = makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) + .andWhere('note.visibility = \'public\'') + .andWhere('note.localOnly = FALSE') + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('user.avatar', 'avatar') + .leftJoinAndSelect('user.banner', 'banner') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); + + if (ps.local) { + query.andWhere('note.userHost IS NULL'); + } + + if (ps.reply !== undefined) { + query.andWhere(ps.reply ? 'note.replyId IS NOT NULL' : 'note.replyId IS NULL'); + } + + if (ps.renote !== undefined) { + query.andWhere(ps.renote ? 'note.renoteId IS NOT NULL' : 'note.renoteId IS NULL'); + } + + if (ps.withFiles !== undefined) { + query.andWhere(ps.withFiles ? 'note.fileIds != \'{}\'' : 'note.fileIds = \'{}\''); + } + + if (ps.poll !== undefined) { + query.andWhere(ps.poll ? 'note.hasPoll = TRUE' : 'note.hasPoll = FALSE'); + } + + // TODO + //if (bot != undefined) { + // query.isBot = bot; + //} + + const notes = await query.take(ps.limit).getMany(); + + return await this.notesRepository.packMany(notes); + }); } - - // TODO - //if (bot != undefined) { - // query.isBot = bot; - //} - - const notes = await query.take(ps.limit).getMany(); - - return await Notes.packMany(notes); -}); +} diff --git a/packages/backend/src/server/index.ts b/packages/backend/src/server/index.ts index f31de2b7f42a..ab631cc82e37 100644 --- a/packages/backend/src/server/index.ts +++ b/packages/backend/src/server/index.ts @@ -31,115 +31,100 @@ import { initializeStreamingServer } from './api/streaming.js'; export const serverLogger = new Logger('server', 'gray', false); -// Init app -const app = new Koa(); -app.proxy = true; - -if (!['production', 'test'].includes(process.env.NODE_ENV || '')) { - // Logger - app.use(koaLogger(str => { - serverLogger.info(str); - })); - - // Delay - if (envOption.slow) { - app.use(slow({ - delay: 3000, +export default () => new Promise(resolve => { + // Init app + const app = new Koa(); + app.proxy = true; + + if (!['production', 'test'].includes(process.env.NODE_ENV || '')) { + // Logger + app.use(koaLogger(str => { + serverLogger.info(str); })); + + // Delay + if (envOption.slow) { + app.use(slow({ + delay: 3000, + })); + } } -} - -// HSTS -// 6months (15552000sec) -if (config.url.startsWith('https') && !config.disableHsts) { - app.use(async (ctx, next) => { - ctx.set('strict-transport-security', 'max-age=15552000; preload'); - await next(); - }); -} - -app.use(mount('/api', apiServer)); -app.use(mount('/files', fileServer)); -app.use(mount('/proxy', proxyServer)); - -// Init router -const router = new Router(); - -// Routing -router.use(activityPub.routes()); -router.use(nodeinfo.routes()); -router.use(wellKnown.routes()); - -router.get('/avatar/@:acct', async ctx => { - const { username, host } = Acct.parse(ctx.params.acct); - const user = await Users.findOne({ - where: { - usernameLower: username.toLowerCase(), - host: (host == null) || (host === config.host) ? IsNull() : host, - isSuspended: false, - }, - relations: ['avatar'], - }); - if (user) { - ctx.redirect(Users.getAvatarUrlSync(user)); - } else { - ctx.redirect('/static-assets/user-unknown.png'); + // HSTS + // 6months (15552000sec) + if (config.url.startsWith('https') && !config.disableHsts) { + app.use(async (ctx, next) => { + ctx.set('strict-transport-security', 'max-age=15552000; preload'); + await next(); + }); } -}); -router.get('/identicon/:x', async ctx => { - const [temp, cleanup] = await createTemp(); - await genIdenticon(ctx.params.x, fs.createWriteStream(temp)); - ctx.set('Content-Type', 'image/png'); - ctx.body = fs.createReadStream(temp).on('close', () => cleanup()); -}); + app.use(mount('/api', apiServer)); + app.use(mount('/files', fileServer)); + app.use(mount('/proxy', proxyServer)); + + // Init router + const router = new Router(); + + // Routing + router.use(activityPub.routes()); + router.use(nodeinfo.routes()); + router.use(wellKnown.routes()); + + router.get('/avatar/@:acct', async ctx => { + const { username, host } = Acct.parse(ctx.params.acct); + const user = await Users.findOne({ + where: { + usernameLower: username.toLowerCase(), + host: (host == null) || (host === config.host) ? IsNull() : host, + isSuspended: false, + }, + relations: ['avatar'], + }); -router.get('/verify-email/:code', async ctx => { - const profile = await UserProfiles.findOneBy({ - emailVerifyCode: ctx.params.code, + if (user) { + ctx.redirect(Users.getAvatarUrlSync(user)); + } else { + ctx.redirect('/static-assets/user-unknown.png'); + } }); - if (profile != null) { - ctx.body = 'Verify succeeded!'; - ctx.status = 200; + router.get('/identicon/:x', async ctx => { + const [temp, cleanup] = await createTemp(); + await genIdenticon(ctx.params.x, fs.createWriteStream(temp)); + ctx.set('Content-Type', 'image/png'); + ctx.body = fs.createReadStream(temp).on('close', () => cleanup()); + }); - await UserProfiles.update({ userId: profile.userId }, { - emailVerified: true, - emailVerifyCode: null, + router.get('/verify-email/:code', async ctx => { + const profile = await UserProfiles.findOneBy({ + emailVerifyCode: ctx.params.code, }); - publishMainStream(profile.userId, 'meUpdated', await Users.pack(profile.userId, { id: profile.userId }, { - detail: true, - includeSecrets: true, - })); - } else { - ctx.status = 404; - } -}); - -// Register router -app.use(router.routes()); - -app.use(mount(webServer)); - -function createServer() { - return http.createServer(app.callback()); -} + if (profile != null) { + ctx.body = 'Verify succeeded!'; + ctx.status = 200; -// For testing -export const startServer = () => { - const server = createServer(); + await UserProfiles.update({ userId: profile.userId }, { + emailVerified: true, + emailVerifyCode: null, + }); - initializeStreamingServer(server); + publishMainStream(profile.userId, 'meUpdated', await Users.pack(profile.userId, { id: profile.userId }, { + detail: true, + includeSecrets: true, + })); + } else { + ctx.status = 404; + } + }); - server.listen(config.port); + // Register router + app.use(router.routes()); - return server; -}; + app.use(mount(webServer)); -export default () => new Promise(resolve => { - const server = createServer(); + const server = http.createServer(app.callback()); initializeStreamingServer(server); diff --git a/packages/backend/src/services/chart/ChartManagementService.ts b/packages/backend/src/services/chart/ChartManagementService.ts index f7b315531083..2020ca0ca6af 100644 --- a/packages/backend/src/services/chart/ChartManagementService.ts +++ b/packages/backend/src/services/chart/ChartManagementService.ts @@ -1,4 +1,4 @@ -import { Container, Service, Inject } from 'typedi'; +import { Injectable, Inject } from '@nestjs/common'; import { beforeShutdown } from '@/misc/before-shutdown.js'; import FederationChart from './charts/federation.js'; @@ -14,7 +14,7 @@ import PerUserFollowingChart from './charts/per-user-following.js'; import PerUserDriveChart from './charts/per-user-drive.js'; import ApRequestChart from './charts/ap-request.js'; -@Service() +@Injectable() export class ChartManagementService { constructor( private federationChart: FederationChart, diff --git a/packages/backend/src/services/chart/charts/active-users.ts b/packages/backend/src/services/chart/charts/active-users.ts index 54574433effd..f0a298bc321a 100644 --- a/packages/backend/src/services/chart/charts/active-users.ts +++ b/packages/backend/src/services/chart/charts/active-users.ts @@ -1,4 +1,4 @@ -import { Container, Service, Inject } from 'typedi'; +import { Injectable, Inject } from '@nestjs/common'; import { User } from '@/models/entities/user.js'; import { Users } from '@/models/index.js'; import Chart, { KVs } from '../core.js'; @@ -12,7 +12,7 @@ const year = 1000 * 60 * 60 * 24 * 365; * アクティブユーザーに関するチャート */ // eslint-disable-next-line import/no-default-export -@Service() +@Injectable() export default class ActiveUsersChart extends Chart { constructor() { super(name, schema); diff --git a/packages/backend/src/services/chart/charts/ap-request.ts b/packages/backend/src/services/chart/charts/ap-request.ts index 2e6da82eb664..2d047633c4ff 100644 --- a/packages/backend/src/services/chart/charts/ap-request.ts +++ b/packages/backend/src/services/chart/charts/ap-request.ts @@ -1,4 +1,4 @@ -import { Container, Service, Inject } from 'typedi'; +import { Injectable, Inject } from '@nestjs/common'; import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/ap-request.js'; @@ -6,7 +6,7 @@ import { name, schema } from './entities/ap-request.js'; * Chart about ActivityPub requests */ // eslint-disable-next-line import/no-default-export -@Service() +@Injectable() export default class ApRequestChart extends Chart { constructor() { super(name, schema); diff --git a/packages/backend/src/services/chart/charts/drive.ts b/packages/backend/src/services/chart/charts/drive.ts index 0f0b8c82bb56..1ce371563db4 100644 --- a/packages/backend/src/services/chart/charts/drive.ts +++ b/packages/backend/src/services/chart/charts/drive.ts @@ -1,4 +1,4 @@ -import { Container, Service, Inject } from 'typedi'; +import { Injectable, Inject } from '@nestjs/common'; import { Not, IsNull } from 'typeorm'; import { DriveFiles } from '@/models/index.js'; import { DriveFile } from '@/models/entities/drive-file.js'; @@ -9,7 +9,7 @@ import { name, schema } from './entities/drive.js'; * ドライブに関するチャート */ // eslint-disable-next-line import/no-default-export -@Service() +@Injectable() export default class DriveChart extends Chart { constructor() { super(name, schema); diff --git a/packages/backend/src/services/chart/charts/federation.ts b/packages/backend/src/services/chart/charts/federation.ts index 1368653233e6..da1f3ab1e76d 100644 --- a/packages/backend/src/services/chart/charts/federation.ts +++ b/packages/backend/src/services/chart/charts/federation.ts @@ -1,4 +1,4 @@ -import { Container, Service, Inject } from 'typedi'; +import { Injectable, Inject } from '@nestjs/common'; import { Followings, Instances } from '@/models/index.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import Chart, { KVs } from '../core.js'; @@ -8,7 +8,7 @@ import { name, schema } from './entities/federation.js'; * フェデレーションに関するチャート */ // eslint-disable-next-line import/no-default-export -@Service() +@Injectable() export default class FederationChart extends Chart { constructor() { super(name, schema); diff --git a/packages/backend/src/services/chart/charts/hashtag.ts b/packages/backend/src/services/chart/charts/hashtag.ts index 46fe51b10dd8..8b7feaf9288d 100644 --- a/packages/backend/src/services/chart/charts/hashtag.ts +++ b/packages/backend/src/services/chart/charts/hashtag.ts @@ -1,4 +1,4 @@ -import { Container, Service, Inject } from 'typedi'; +import { Injectable, Inject } from '@nestjs/common'; import { User } from '@/models/entities/user.js'; import { Users } from '@/models/index.js'; import Chart, { KVs } from '../core.js'; @@ -8,7 +8,7 @@ import { name, schema } from './entities/hashtag.js'; * ハッシュタグに関するチャート */ // eslint-disable-next-line import/no-default-export -@Service() +@Injectable() export default class HashtagChart extends Chart { constructor() { super(name, schema, true); diff --git a/packages/backend/src/services/chart/charts/instance.ts b/packages/backend/src/services/chart/charts/instance.ts index 16265aac2d73..73189d6630f8 100644 --- a/packages/backend/src/services/chart/charts/instance.ts +++ b/packages/backend/src/services/chart/charts/instance.ts @@ -1,4 +1,4 @@ -import { Container, Service, Inject } from 'typedi'; +import { Injectable, Inject } from '@nestjs/common'; import { DriveFiles, Followings, Users, Notes } from '@/models/index.js'; import { DriveFile } from '@/models/entities/drive-file.js'; import { Note } from '@/models/entities/note.js'; @@ -10,7 +10,7 @@ import { name, schema } from './entities/instance.js'; * インスタンスごとのチャート */ // eslint-disable-next-line import/no-default-export -@Service() +@Injectable() export default class InstanceChart extends Chart { constructor() { super(name, schema, true); diff --git a/packages/backend/src/services/chart/charts/notes.ts b/packages/backend/src/services/chart/charts/notes.ts index a32dfb49d893..ccacf06bbb08 100644 --- a/packages/backend/src/services/chart/charts/notes.ts +++ b/packages/backend/src/services/chart/charts/notes.ts @@ -1,4 +1,4 @@ -import { Container, Service, Inject } from 'typedi'; +import { Injectable, Inject } from '@nestjs/common'; import { Not, IsNull } from 'typeorm'; import { Notes } from '@/models/index.js'; import { Note } from '@/models/entities/note.js'; @@ -9,7 +9,7 @@ import { name, schema } from './entities/notes.js'; * ノートに関するチャート */ // eslint-disable-next-line import/no-default-export -@Service() +@Injectable() export default class NotesChart extends Chart { constructor() { super(name, schema); diff --git a/packages/backend/src/services/chart/charts/per-user-drive.ts b/packages/backend/src/services/chart/charts/per-user-drive.ts index 1926fa5f47fa..31a00adec8d8 100644 --- a/packages/backend/src/services/chart/charts/per-user-drive.ts +++ b/packages/backend/src/services/chart/charts/per-user-drive.ts @@ -1,4 +1,4 @@ -import { Container, Service, Inject } from 'typedi'; +import { Injectable, Inject } from '@nestjs/common'; import { DriveFiles } from '@/models/index.js'; import { DriveFile } from '@/models/entities/drive-file.js'; import Chart, { KVs } from '../core.js'; @@ -8,7 +8,7 @@ import { name, schema } from './entities/per-user-drive.js'; * ユーザーごとのドライブに関するチャート */ // eslint-disable-next-line import/no-default-export -@Service() +@Injectable() export default class PerUserDriveChart extends Chart { constructor() { super(name, schema, true); diff --git a/packages/backend/src/services/chart/charts/per-user-following.ts b/packages/backend/src/services/chart/charts/per-user-following.ts index eb7f62b25fae..f541383209ce 100644 --- a/packages/backend/src/services/chart/charts/per-user-following.ts +++ b/packages/backend/src/services/chart/charts/per-user-following.ts @@ -1,4 +1,4 @@ -import { Container, Service, Inject } from 'typedi'; +import { Injectable, Inject } from '@nestjs/common'; import { Not, IsNull } from 'typeorm'; import { Followings, Users } from '@/models/index.js'; import { User } from '@/models/entities/user.js'; @@ -9,7 +9,7 @@ import { name, schema } from './entities/per-user-following.js'; * ユーザーごとのフォローに関するチャート */ // eslint-disable-next-line import/no-default-export -@Service() +@Injectable() export default class PerUserFollowingChart extends Chart { constructor() { super(name, schema, true); diff --git a/packages/backend/src/services/chart/charts/per-user-notes.ts b/packages/backend/src/services/chart/charts/per-user-notes.ts index 6b9bd7f10406..a666f05141f3 100644 --- a/packages/backend/src/services/chart/charts/per-user-notes.ts +++ b/packages/backend/src/services/chart/charts/per-user-notes.ts @@ -1,4 +1,4 @@ -import { Container, Service, Inject } from 'typedi'; +import { Injectable, Inject } from '@nestjs/common'; import { User } from '@/models/entities/user.js'; import { Notes } from '@/models/index.js'; import { Note } from '@/models/entities/note.js'; @@ -9,7 +9,7 @@ import { name, schema } from './entities/per-user-notes.js'; * ユーザーごとのノートに関するチャート */ // eslint-disable-next-line import/no-default-export -@Service() +@Injectable() export default class PerUserNotesChart extends Chart { constructor() { super(name, schema, true); diff --git a/packages/backend/src/services/chart/charts/per-user-reactions.ts b/packages/backend/src/services/chart/charts/per-user-reactions.ts index 2a5d8061068b..bc4cf7e71bf3 100644 --- a/packages/backend/src/services/chart/charts/per-user-reactions.ts +++ b/packages/backend/src/services/chart/charts/per-user-reactions.ts @@ -1,4 +1,4 @@ -import { Container, Service, Inject } from 'typedi'; +import { Injectable, Inject } from '@nestjs/common'; import { User } from '@/models/entities/user.js'; import { Note } from '@/models/entities/note.js'; import { Users } from '@/models/index.js'; @@ -9,7 +9,7 @@ import { name, schema } from './entities/per-user-reactions.js'; * ユーザーごとのリアクションに関するチャート */ // eslint-disable-next-line import/no-default-export -@Service() +@Injectable() export default class PerUserReactionsChart extends Chart { constructor() { super(name, schema, true); diff --git a/packages/backend/src/services/chart/charts/test-grouped.ts b/packages/backend/src/services/chart/charts/test-grouped.ts index 54335d20a384..03956a9c4c56 100644 --- a/packages/backend/src/services/chart/charts/test-grouped.ts +++ b/packages/backend/src/services/chart/charts/test-grouped.ts @@ -1,4 +1,4 @@ -import { Container, Service, Inject } from 'typedi'; +import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/test-grouped.js'; @@ -7,7 +7,7 @@ import { name, schema } from './entities/test-grouped.js'; * For testing */ // eslint-disable-next-line import/no-default-export -@Service() +@Injectable() export default class TestGroupedChart extends Chart { private total = {} as Record; diff --git a/packages/backend/src/services/chart/charts/test-intersection.ts b/packages/backend/src/services/chart/charts/test-intersection.ts index cb7a04b529fa..ad2768a5875e 100644 --- a/packages/backend/src/services/chart/charts/test-intersection.ts +++ b/packages/backend/src/services/chart/charts/test-intersection.ts @@ -1,4 +1,4 @@ -import { Container, Service, Inject } from 'typedi'; +import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/test-intersection.js'; @@ -7,7 +7,7 @@ import { name, schema } from './entities/test-intersection.js'; * For testing */ // eslint-disable-next-line import/no-default-export -@Service() +@Injectable() export default class TestIntersectionChart extends Chart { constructor(db: DataSource) { super(db, name, schema); diff --git a/packages/backend/src/services/chart/charts/test-unique.ts b/packages/backend/src/services/chart/charts/test-unique.ts index 368d4f0b16ec..61624de632fc 100644 --- a/packages/backend/src/services/chart/charts/test-unique.ts +++ b/packages/backend/src/services/chart/charts/test-unique.ts @@ -1,4 +1,4 @@ -import { Container, Service, Inject } from 'typedi'; +import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/test-unique.js'; @@ -7,7 +7,7 @@ import { name, schema } from './entities/test-unique.js'; * For testing */ // eslint-disable-next-line import/no-default-export -@Service() +@Injectable() export default class TestUniqueChart extends Chart { constructor(db: DataSource) { super(db, name, schema); diff --git a/packages/backend/src/services/chart/charts/test.ts b/packages/backend/src/services/chart/charts/test.ts index b0ff5ea7b970..d2229b97b8e5 100644 --- a/packages/backend/src/services/chart/charts/test.ts +++ b/packages/backend/src/services/chart/charts/test.ts @@ -1,4 +1,4 @@ -import { Container, Service, Inject } from 'typedi'; +import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; import Chart, { KVs } from '../core.js'; import { name, schema } from './entities/test.js'; @@ -7,7 +7,7 @@ import { name, schema } from './entities/test.js'; * For testing */ // eslint-disable-next-line import/no-default-export -@Service() +@Injectable() export default class TestChart extends Chart { public total = 0; // publicにするのはテストのため diff --git a/packages/backend/src/services/chart/charts/users.ts b/packages/backend/src/services/chart/charts/users.ts index 137706ac469e..10d4fb5e3e77 100644 --- a/packages/backend/src/services/chart/charts/users.ts +++ b/packages/backend/src/services/chart/charts/users.ts @@ -1,4 +1,4 @@ -import { Container, Service, Inject } from 'typedi'; +import { Injectable, Inject } from '@nestjs/common'; import { Not, IsNull } from 'typeorm'; import { Users } from '@/models/index.js'; import { User } from '@/models/entities/user.js'; @@ -9,7 +9,7 @@ import { name, schema } from './entities/users.js'; * ユーザー数に関するチャート */ // eslint-disable-next-line import/no-default-export -@Service() +@Injectable() export default class UsersChart extends Chart { constructor() { super(name, schema); diff --git a/packages/backend/src/services/fooService.ts b/packages/backend/src/services/fooService.ts index 83c5bac70c35..5936da40dccb 100644 --- a/packages/backend/src/services/fooService.ts +++ b/packages/backend/src/services/fooService.ts @@ -1,6 +1,6 @@ import { Container, Service } from 'typedi'; -@Service() +@Injectable() export class FooService { constructor( ) {} diff --git a/packages/backend/src/services/note/NoteCreateService.ts b/packages/backend/src/services/note/NoteCreateService.ts index be1d6c0d5621..198428a0d9f0 100644 --- a/packages/backend/src/services/note/NoteCreateService.ts +++ b/packages/backend/src/services/note/NoteCreateService.ts @@ -1,6 +1,6 @@ -import { Container, Service, Inject } from 'typedi'; import * as mfm from 'mfm-js'; import { Not, In } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; import { extractMentions } from '@/misc/extract-mentions.js'; import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js'; import { extractHashtags } from '@/misc/extract-hashtags.js'; @@ -20,7 +20,6 @@ import DeliverManager from '@/remote/activitypub/deliver-manager.js'; import { publishMainStream, publishNotesStream } from '@/services/stream.js'; import { genId } from '@/misc/gen-id.js'; import { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; -import { notesChart, perUserNotesChart, activeUsersChart, instanceChart } from '@/services/chart/index.js'; import { Poll, IPoll } from '@/models/entities/poll.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import { checkHitAntenna } from '@/misc/check-hit-antenna.js'; @@ -42,6 +41,10 @@ import { deliverToRelays } from '../relay.js'; import { addNoteToAntenna } from '../add-note-to-antenna.js'; import { createNotification } from '../create-notification.js'; import { WebhookService } from '../webhookService.js'; +import type NotesChart from '../chart/charts/notes.js'; +import type PerUserNotesChart from '../chart/charts/per-user-notes.js'; +import type ActiveUsersChart from '../chart/charts/active-users.js'; +import type InstanceChart from '../chart/charts/instance.js'; const mutedWordsCache = new Cache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5); @@ -128,12 +131,17 @@ type Option = { app?: App | null; }; -@Service() +@Injectable() export class NoteCreateService { constructor( @Inject('notesRepository') private notesRepository: typeof Notes, + private notesChart: NotesChart, + private perUserNotesChart: PerUserNotesChart, + private activeUsersChart: ActiveUsersChart, + private instanceChart: InstanceChart, + private webhookService: WebhookService, ) {} @@ -360,14 +368,14 @@ export class NoteCreateService { createdAt: User['createdAt']; }, data: Option, silent: boolean, tags: string[], mentionedUsers: MinimumUser[]) { // 統計を更新 - notesChart.update(note, true); - perUserNotesChart.update(user, note, true); + this.notesChart.update(note, true); + this.perUserNotesChart.update(user, note, true); // Register host if (Users.isRemoteUser(user)) { registerOrFetchInstanceDoc(user.host).then(i => { Instances.increment({ id: i.id }, 'notesCount', 1); - instanceChart.updateNote(i.host, note, true); + this.instanceChart.updateNote(i.host, note, true); }); } @@ -441,7 +449,7 @@ export class NoteCreateService { } if (!silent) { - if (Users.isLocalUser(user)) activeUsersChart.write(user); + if (Users.isLocalUser(user)) this.activeUsersChart.write(user); // 未読通知を作成 if (data.visibility === 'specified') { diff --git a/packages/backend/src/services/note/NoteDeleteService.ts b/packages/backend/src/services/note/NoteDeleteService.ts index 07b03b59eb86..72b6f1ba6355 100644 --- a/packages/backend/src/services/note/NoteDeleteService.ts +++ b/packages/backend/src/services/note/NoteDeleteService.ts @@ -1,5 +1,5 @@ -import { Container, Service, Inject } from 'typedi'; import { Brackets, In } from 'typeorm'; +import { Injectable, Inject } from '@nestjs/common'; import { publishNoteStream } from '@/services/stream.js'; import renderDelete from '@/remote/activitypub/renderer/delete.js'; import renderAnnounce from '@/remote/activitypub/renderer/announce.js'; @@ -16,7 +16,7 @@ import { countSameRenotes } from '@/misc/count-same-renotes.js'; import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js'; import { deliverToRelays } from '../relay.js'; -@Service() +@Injectable() export class NoteCreateService { constructor( @Inject('notesRepository') diff --git a/packages/backend/src/services/webhookService.ts b/packages/backend/src/services/webhookService.ts index bf69d94409f6..b8cf19000a9e 100644 --- a/packages/backend/src/services/webhookService.ts +++ b/packages/backend/src/services/webhookService.ts @@ -1,7 +1,7 @@ import { Container, Service } from 'typedi'; import { FooService } from './fooService.js'; -@Service() +@Injectable() export class WebhookService { constructor( private fooService: FooService, diff --git a/packages/backend/test/tests/noteService.ts b/packages/backend/test/tests/note.ts similarity index 68% rename from packages/backend/test/tests/noteService.ts rename to packages/backend/test/tests/note.ts index 5f0a1c1e3d64..71608173e5f2 100644 --- a/packages/backend/test/tests/noteService.ts +++ b/packages/backend/test/tests/note.ts @@ -7,9 +7,9 @@ import { initDb } from '@/db/postgre.js'; import { Notes } from '@/models/index.js'; import { FooService } from '@/services/fooService.js'; import { WebhookService } from '../../src/services/webhookService.js'; -import { NoteService } from '../../src/services/note/NoteCreateService.js'; +import { NoteCreateService } from '../../src/services/note/NoteCreateService.js'; -describe('NoteService', () => { +describe('NoteCreateService', () => { beforeAll(async () => { //await initTestDb(); await initDb(); @@ -23,9 +23,9 @@ describe('NoteService', () => { } const webhookService = new WebhookService2(); - const noteService = new NoteService(Container.get('notesRepository'), webhookService as unknown as jest.Mocked); + const noteCreateService = new NoteCreateService(Container.get('notesRepository'), webhookService as unknown as jest.Mocked); - await noteService.create({ id: 'aaa' }, { text: 'test' }); + await noteCreateService.create({ id: 'aaa' }, { text: 'test' }); expect(webhookService.deliver).toBeCalled(); }); }); diff --git a/packages/backend/yarn.lock b/packages/backend/yarn.lock index a49ee7a40e3d..a501d5392103 100644 --- a/packages/backend/yarn.lock +++ b/packages/backend/yarn.lock @@ -744,6 +744,28 @@ semver "^7.3.5" tar "^6.1.11" +"@nestjs/common@9.0.11": + version "9.0.11" + resolved "https://registry.yarnpkg.com/@nestjs/common/-/common-9.0.11.tgz#5747cfbb5d94d909bc2894bf2a5bec83fca809c5" + integrity sha512-oYLIcOal3QOwcqt6goXovRNg8ZkalyOMjH0oYYzfJLrait6P7c6nAeWHu4qFDThY7GoZHEanLgji1qlqVEW09g== + dependencies: + iterare "1.2.1" + tslib "2.4.0" + uuid "8.3.2" + +"@nestjs/core@9.0.11": + version "9.0.11" + resolved "https://registry.yarnpkg.com/@nestjs/core/-/core-9.0.11.tgz#1bed969d81685f17d4a01f854c43a222dd3e13ce" + integrity sha512-DYyoiWSGebDAG8WSfG/ue88HBU39kAJTi2YXftWdVSl1LFveV+pwKY83P2qX0ND38TS8WktFYpaMkXslf97BBQ== + dependencies: + "@nuxtjs/opencollective" "0.3.2" + fast-safe-stringify "2.1.1" + iterare "1.2.1" + object-hash "3.0.0" + path-to-regexp "3.2.0" + tslib "2.4.0" + uuid "8.3.2" + "@node-redis/bloom@^1.0.0": version "1.0.1" resolved "https://registry.yarnpkg.com/@node-redis/bloom/-/bloom-1.0.1.tgz#144474a0b7dc4a4b91badea2cfa9538ce0a1854e" @@ -831,6 +853,15 @@ pngjs-nozlib "1.0.0" through "2.3.4" +"@nuxtjs/opencollective@0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz#620ce1044f7ac77185e825e1936115bb38e2681c" + integrity sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA== + dependencies: + chalk "^4.1.0" + consola "^2.15.0" + node-fetch "^2.6.1" + "@peertube/http-signature@1.7.0": version "1.7.0" resolved "https://registry.yarnpkg.com/@peertube/http-signature/-/http-signature-1.7.0.tgz#12a84f3fc62e786aa3a2eb09426417bad65736dc" @@ -2887,6 +2918,11 @@ config-chain@^1.1.12: ini "^1.3.4" proto-list "~1.2.1" +consola@^2.15.0: + version "2.15.3" + resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550" + integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== + console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" @@ -3997,6 +4033,11 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fast-safe-stringify@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" + integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== + fast-xml-parser@^3.19.0: version "3.19.0" resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.19.0.tgz#cb637ec3f3999f51406dd8ff0e6fc4d83e520d01" @@ -5289,6 +5330,11 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +iterare@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/iterare/-/iterare-1.2.1.tgz#139c400ff7363690e33abffa33cbba8920f00042" + integrity sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q== + jake@^10.8.5: version "10.8.5" resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" @@ -6952,6 +6998,11 @@ object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= +object-hash@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== + object-inspect@^1.11.0, object-inspect@^1.9.0: version "1.11.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" @@ -7274,6 +7325,11 @@ path-parse@^1.0.6, path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-to-regexp@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-3.2.0.tgz#fa7877ecbc495c601907562222453c43cc204a5f" + integrity sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA== + path-to-regexp@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.1.0.tgz#0b18f88b7a0ce0bfae6a25990c909ab86f512427" @@ -8093,6 +8149,13 @@ run-parallel@^1.1.9: resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== +rxjs@7.5.6: + version "7.5.6" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.6.tgz#0446577557862afd6903517ce7cae79ecb9662bc" + integrity sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw== + dependencies: + tslib "^2.1.0" + s-age@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/s-age/-/s-age-1.1.2.tgz#c0cf15233ccc93f41de92ea42c36d957977d1ea2" @@ -8961,6 +9024,11 @@ tsconfig-paths@^3.14.1: minimist "^1.2.6" strip-bom "^3.0.0" +tslib@2.4.0, tslib@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + tslib@^1.8.1: version "1.11.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" @@ -9059,11 +9127,6 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typedi@0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/typedi/-/typedi-0.10.0.tgz#e8f9a5ee100b84addbdfb57fe90d8d9ed2a21dea" - integrity sha512-v3UJF8xm68BBj6AF4oQML3ikrfK2c9EmZUyLOfShpJuItAqVBHWP/KtpGinkSsIiP6EZyyb6Z3NXyW9dgS9X1w== - typeorm@0.3.9: version "0.3.9" resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.3.9.tgz#ad0f525d81c081fd11006f97030f47a55978ac81" @@ -9229,6 +9292,11 @@ uuid@8.0.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c" integrity sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw== +uuid@8.3.2, uuid@^8.3.0, uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + uuid@9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" @@ -9239,11 +9307,6 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^8.3.0, uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" From 5ac68fbcd5c38a257d03fc64524528e0654e9208 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 11 Sep 2022 03:34:11 +0900 Subject: [PATCH 008/180] wip --- .../api/endpoints/admin/abuse-user-reports.ts | 11 +- .../api/endpoints/admin/accounts/create.ts | 11 +- .../api/endpoints/admin/accounts/delete.ts | 11 +- .../server/api/endpoints/admin/ad/create.ts | 11 +- .../server/api/endpoints/admin/ad/delete.ts | 11 +- .../src/server/api/endpoints/admin/ad/list.ts | 11 +- .../server/api/endpoints/admin/ad/update.ts | 11 +- .../endpoints/admin/announcements/create.ts | 11 +- .../endpoints/admin/announcements/delete.ts | 11 +- .../api/endpoints/admin/announcements/list.ts | 11 +- .../endpoints/admin/announcements/update.ts | 11 +- .../api/endpoints/admin/delete-account.ts | 11 +- .../admin/delete-all-files-of-a-user.ts | 11 +- .../admin/drive-capacity-override.ts | 11 +- .../admin/drive/clean-remote-files.ts | 11 +- .../api/endpoints/admin/drive/cleanup.ts | 11 +- .../server/api/endpoints/admin/drive/files.ts | 11 +- .../api/endpoints/admin/drive/show-file.ts | 11 +- .../endpoints/admin/emoji/add-aliases-bulk.ts | 11 +- .../server/api/endpoints/admin/emoji/add.ts | 11 +- .../server/api/endpoints/admin/emoji/copy.ts | 11 +- .../api/endpoints/admin/emoji/delete-bulk.ts | 11 +- .../api/endpoints/admin/emoji/delete.ts | 11 +- .../api/endpoints/admin/emoji/import-zip.ts | 11 +- .../api/endpoints/admin/emoji/list-remote.ts | 11 +- .../server/api/endpoints/admin/emoji/list.ts | 11 +- .../admin/emoji/remove-aliases-bulk.ts | 11 +- .../endpoints/admin/emoji/set-aliases-bulk.ts | 11 +- .../admin/emoji/set-category-bulk.ts | 11 +- .../api/endpoints/admin/emoji/update.ts | 11 +- .../admin/federation/delete-all-files.ts | 11 +- .../refresh-remote-instance-metadata.ts | 11 +- .../admin/federation/remove-all-following.ts | 11 +- .../admin/federation/update-instance.ts | 11 +- .../api/endpoints/admin/get-index-stats.ts | 3 +- .../api/endpoints/admin/get-table-stats.ts | 3 +- .../api/endpoints/admin/get-user-ips.ts | 11 +- .../src/server/api/endpoints/admin/invite.ts | 3 +- .../src/server/api/endpoints/admin/meta.ts | 11 +- .../api/endpoints/admin/moderators/add.ts | 11 +- .../api/endpoints/admin/moderators/remove.ts | 11 +- .../api/endpoints/admin/promo/create.ts | 11 +- .../server/api/endpoints/admin/queue/clear.ts | 11 +- .../endpoints/admin/queue/deliver-delayed.ts | 11 +- .../endpoints/admin/queue/inbox-delayed.ts | 11 +- .../server/api/endpoints/admin/queue/stats.ts | 11 +- .../server/api/endpoints/admin/relays/add.ts | 11 +- .../server/api/endpoints/admin/relays/list.ts | 11 +- .../api/endpoints/admin/relays/remove.ts | 11 +- .../api/endpoints/admin/reset-password.ts | 11 +- .../admin/resolve-abuse-user-report.ts | 11 +- .../server/api/endpoints/admin/send-email.ts | 11 +- .../server/api/endpoints/admin/server-info.ts | 3 +- .../endpoints/admin/show-moderation-logs.ts | 11 +- .../server/api/endpoints/admin/show-user.ts | 11 +- .../server/api/endpoints/admin/show-users.ts | 11 +- .../api/endpoints/admin/silence-user.ts | 11 +- .../api/endpoints/admin/suspend-user.ts | 11 +- .../api/endpoints/admin/unsilence-user.ts | 11 +- .../api/endpoints/admin/unsuspend-user.ts | 11 +- .../server/api/endpoints/admin/update-meta.ts | 11 +- .../api/endpoints/admin/update-user-note.ts | 11 +- .../src/server/api/endpoints/admin/vacuum.ts | 11 +- .../src/server/api/endpoints/announcements.ts | 11 +- .../server/api/endpoints/antennas/create.ts | 91 +++++----- .../server/api/endpoints/antennas/delete.ts | 39 ++-- .../src/server/api/endpoints/antennas/list.ts | 11 +- .../server/api/endpoints/antennas/notes.ts | 49 ++--- .../src/server/api/endpoints/antennas/show.ts | 11 +- .../server/api/endpoints/antennas/update.ts | 113 ++++++------ .../src/server/api/endpoints/ap/get.ts | 11 +- .../src/server/api/endpoints/ap/show.ts | 11 +- .../src/server/api/endpoints/app/create.ts | 11 +- .../src/server/api/endpoints/app/show.ts | 3 +- .../src/server/api/endpoints/auth/accept.ts | 11 +- .../api/endpoints/auth/session/generate.ts | 11 +- .../server/api/endpoints/auth/session/show.ts | 11 +- .../api/endpoints/auth/session/userkey.ts | 11 +- .../server/api/endpoints/blocking/create.ts | 81 +++++---- .../server/api/endpoints/blocking/delete.ts | 73 ++++---- .../src/server/api/endpoints/blocking/list.ts | 25 ++- .../server/api/endpoints/channels/create.ts | 59 ++++--- .../server/api/endpoints/channels/featured.ts | 21 ++- .../server/api/endpoints/channels/follow.ts | 47 +++-- .../server/api/endpoints/channels/followed.ts | 21 ++- .../server/api/endpoints/channels/owned.ts | 21 ++- .../src/server/api/endpoints/channels/show.ts | 33 ++-- .../server/api/endpoints/channels/timeline.ts | 43 +++-- .../server/api/endpoints/channels/unfollow.ts | 43 +++-- .../server/api/endpoints/channels/update.ts | 73 ++++---- .../api/endpoints/charts/active-users.ts | 17 +- .../server/api/endpoints/charts/ap-request.ts | 17 +- .../src/server/api/endpoints/charts/drive.ts | 17 +- .../server/api/endpoints/charts/federation.ts | 17 +- .../server/api/endpoints/charts/hashtag.ts | 17 +- .../server/api/endpoints/charts/instance.ts | 17 +- .../src/server/api/endpoints/charts/notes.ts | 17 +- .../server/api/endpoints/charts/user/drive.ts | 17 +- .../api/endpoints/charts/user/following.ts | 17 +- .../server/api/endpoints/charts/user/notes.ts | 17 +- .../api/endpoints/charts/user/reactions.ts | 17 +- .../src/server/api/endpoints/charts/users.ts | 17 +- .../server/api/endpoints/clips/add-note.ts | 63 ++++--- .../src/server/api/endpoints/clips/create.ts | 33 ++-- .../src/server/api/endpoints/clips/delete.ts | 35 ++-- .../src/server/api/endpoints/clips/list.ts | 23 ++- .../src/server/api/endpoints/clips/notes.ts | 49 ++--- .../server/api/endpoints/clips/remove-note.ts | 47 +++-- .../src/server/api/endpoints/clips/show.ts | 43 +++-- .../src/server/api/endpoints/clips/update.ts | 47 +++-- .../backend/src/server/api/endpoints/drive.ts | 11 +- .../src/server/api/endpoints/drive/files.ts | 45 +++-- .../endpoints/drive/files/attached-notes.ts | 43 +++-- .../endpoints/drive/files/check-existence.ts | 27 ++- .../api/endpoints/drive/files/create.ts | 3 +- .../api/endpoints/drive/files/delete.ts | 39 ++-- .../api/endpoints/drive/files/find-by-hash.ts | 27 ++- .../server/api/endpoints/drive/files/find.ts | 31 ++-- .../server/api/endpoints/drive/files/show.ts | 67 ++++--- .../api/endpoints/drive/files/update.ts | 89 +++++----- .../endpoints/drive/files/upload-from-url.ts | 3 +- .../src/server/api/endpoints/drive/folders.ts | 31 ++-- .../api/endpoints/drive/folders/create.ts | 67 ++++--- .../api/endpoints/drive/folders/delete.ts | 55 +++--- .../api/endpoints/drive/folders/find.ts | 29 +-- .../api/endpoints/drive/folders/show.ts | 41 +++-- .../api/endpoints/drive/folders/update.ts | 123 +++++++------ .../src/server/api/endpoints/drive/stream.ts | 35 ++-- .../api/endpoints/email-address/available.ts | 17 +- .../src/server/api/endpoints/endpoint.ts | 11 +- .../src/server/api/endpoints/endpoints.ts | 3 +- .../api/endpoints/export-custom-emojis.ts | 11 +- .../api/endpoints/federation/followers.ts | 23 ++- .../api/endpoints/federation/following.ts | 23 ++- .../api/endpoints/federation/instances.ts | 167 +++++++++--------- .../api/endpoints/federation/show-instance.ts | 19 +- .../server/api/endpoints/federation/stats.ts | 91 +++++----- .../federation/update-remote-user.ts | 21 ++- .../server/api/endpoints/federation/users.ts | 23 ++- .../src/server/api/endpoints/fetch-rss.ts | 11 +- .../server/api/endpoints/following/create.ts | 85 +++++---- .../server/api/endpoints/following/delete.ts | 67 ++++--- .../api/endpoints/following/invalidate.ts | 67 ++++--- .../endpoints/following/requests/accept.ts | 39 ++-- .../endpoints/following/requests/cancel.ts | 49 ++--- .../api/endpoints/following/requests/list.ts | 23 ++- .../endpoints/following/requests/reject.ts | 33 ++-- .../server/api/endpoints/gallery/featured.ts | 27 ++- .../server/api/endpoints/gallery/popular.ts | 21 ++- .../src/server/api/endpoints/gallery/posts.ts | 23 ++- .../api/endpoints/gallery/posts/create.ts | 59 ++++--- .../api/endpoints/gallery/posts/delete.ts | 35 ++-- .../api/endpoints/gallery/posts/like.ts | 63 ++++--- .../api/endpoints/gallery/posts/show.ts | 33 ++-- .../api/endpoints/gallery/posts/unlike.ts | 45 +++-- .../api/endpoints/gallery/posts/update.ts | 59 ++++--- .../api/endpoints/get-online-users-count.ts | 3 +- .../src/server/api/endpoints/hashtags/list.ts | 73 ++++---- .../server/api/endpoints/hashtags/search.ts | 19 +- .../src/server/api/endpoints/hashtags/show.ts | 27 ++- .../server/api/endpoints/hashtags/trend.ts | 5 +- .../server/api/endpoints/hashtags/users.ts | 59 ++++--- .../backend/src/server/api/endpoints/i.ts | 3 +- .../src/server/api/endpoints/i/2fa/done.ts | 11 +- .../server/api/endpoints/i/2fa/key-done.ts | 11 +- .../api/endpoints/i/2fa/password-less.ts | 11 +- .../api/endpoints/i/2fa/register-key.ts | 11 +- .../server/api/endpoints/i/2fa/register.ts | 11 +- .../server/api/endpoints/i/2fa/remove-key.ts | 11 +- .../server/api/endpoints/i/2fa/unregister.ts | 11 +- .../src/server/api/endpoints/i/apps.ts | 11 +- .../server/api/endpoints/i/authorized-apps.ts | 11 +- .../server/api/endpoints/i/change-password.ts | 11 +- .../server/api/endpoints/i/delete-account.ts | 11 +- .../server/api/endpoints/i/export-blocking.ts | 11 +- .../api/endpoints/i/export-following.ts | 11 +- .../src/server/api/endpoints/i/export-mute.ts | 11 +- .../server/api/endpoints/i/export-notes.ts | 11 +- .../api/endpoints/i/export-user-lists.ts | 11 +- .../src/server/api/endpoints/i/favorites.ts | 11 +- .../server/api/endpoints/i/gallery/likes.ts | 11 +- .../server/api/endpoints/i/gallery/posts.ts | 11 +- .../endpoints/i/get-word-muted-notes-count.ts | 11 +- .../server/api/endpoints/i/import-blocking.ts | 11 +- .../api/endpoints/i/import-following.ts | 11 +- .../server/api/endpoints/i/import-muting.ts | 11 +- .../api/endpoints/i/import-user-lists.ts | 11 +- .../server/api/endpoints/i/notifications.ts | 11 +- .../src/server/api/endpoints/i/page-likes.ts | 11 +- .../src/server/api/endpoints/i/pages.ts | 11 +- .../backend/src/server/api/endpoints/i/pin.ts | 11 +- .../i/read-all-messaging-messages.ts | 11 +- .../api/endpoints/i/read-all-unread-notes.ts | 11 +- .../api/endpoints/i/read-announcement.ts | 11 +- .../api/endpoints/i/regenerate-token.ts | 11 +- .../api/endpoints/i/registry/get-all.ts | 11 +- .../api/endpoints/i/registry/get-detail.ts | 11 +- .../server/api/endpoints/i/registry/get.ts | 11 +- .../endpoints/i/registry/keys-with-type.ts | 11 +- .../server/api/endpoints/i/registry/keys.ts | 11 +- .../server/api/endpoints/i/registry/remove.ts | 11 +- .../server/api/endpoints/i/registry/scopes.ts | 11 +- .../server/api/endpoints/i/registry/set.ts | 11 +- .../server/api/endpoints/i/revoke-token.ts | 11 +- .../server/api/endpoints/i/signin-history.ts | 11 +- .../src/server/api/endpoints/i/unpin.ts | 11 +- .../server/api/endpoints/i/update-email.ts | 11 +- .../src/server/api/endpoints/i/update.ts | 3 +- .../api/endpoints/i/user-group-invites.ts | 11 +- .../server/api/endpoints/i/webhooks/create.ts | 11 +- .../server/api/endpoints/i/webhooks/delete.ts | 11 +- .../server/api/endpoints/i/webhooks/list.ts | 11 +- .../server/api/endpoints/i/webhooks/show.ts | 11 +- .../server/api/endpoints/i/webhooks/update.ts | 11 +- .../server/api/endpoints/messaging/history.ts | 11 +- .../api/endpoints/messaging/messages.ts | 11 +- .../endpoints/messaging/messages/create.ts | 11 +- .../endpoints/messaging/messages/delete.ts | 11 +- .../api/endpoints/messaging/messages/read.ts | 11 +- .../backend/src/server/api/endpoints/meta.ts | 11 +- .../server/api/endpoints/miauth/gen-token.ts | 11 +- .../src/server/api/endpoints/mute/create.ts | 11 +- .../src/server/api/endpoints/mute/delete.ts | 11 +- .../src/server/api/endpoints/mute/list.ts | 11 +- .../src/server/api/endpoints/my/apps.ts | 11 +- .../backend/src/server/api/endpoints/notes.ts | 2 +- .../server/api/endpoints/notes/children.ts | 11 +- .../src/server/api/endpoints/notes/clips.ts | 11 +- .../api/endpoints/notes/conversation.ts | 11 +- .../src/server/api/endpoints/notes/create.ts | 11 +- .../src/server/api/endpoints/notes/delete.ts | 11 +- .../api/endpoints/notes/favorites/create.ts | 11 +- .../api/endpoints/notes/favorites/delete.ts | 11 +- .../server/api/endpoints/notes/featured.ts | 11 +- .../api/endpoints/notes/global-timeline.ts | 11 +- .../api/endpoints/notes/hybrid-timeline.ts | 11 +- .../api/endpoints/notes/local-timeline.ts | 11 +- .../server/api/endpoints/notes/mentions.ts | 11 +- .../endpoints/notes/polls/recommendation.ts | 11 +- .../server/api/endpoints/notes/polls/vote.ts | 11 +- .../server/api/endpoints/notes/reactions.ts | 11 +- .../api/endpoints/notes/reactions/create.ts | 11 +- .../api/endpoints/notes/reactions/delete.ts | 11 +- .../src/server/api/endpoints/notes/renotes.ts | 11 +- .../src/server/api/endpoints/notes/replies.ts | 11 +- .../api/endpoints/notes/search-by-tag.ts | 11 +- .../src/server/api/endpoints/notes/search.ts | 11 +- .../src/server/api/endpoints/notes/show.ts | 11 +- .../src/server/api/endpoints/notes/state.ts | 11 +- .../endpoints/notes/thread-muting/create.ts | 11 +- .../endpoints/notes/thread-muting/delete.ts | 11 +- .../server/api/endpoints/notes/timeline.ts | 11 +- .../server/api/endpoints/notes/translate.ts | 11 +- .../server/api/endpoints/notes/unrenote.ts | 11 +- .../api/endpoints/notes/user-list-timeline.ts | 11 +- .../api/endpoints/notes/watching/create.ts | 11 +- .../api/endpoints/notes/watching/delete.ts | 11 +- .../api/endpoints/notifications/create.ts | 3 +- .../notifications/mark-all-as-read.ts | 11 +- .../api/endpoints/notifications/read.ts | 11 +- .../src/server/api/endpoints/page-push.ts | 11 +- .../src/server/api/endpoints/pages/create.ts | 11 +- .../src/server/api/endpoints/pages/delete.ts | 11 +- .../server/api/endpoints/pages/featured.ts | 11 +- .../src/server/api/endpoints/pages/like.ts | 11 +- .../src/server/api/endpoints/pages/show.ts | 11 +- .../src/server/api/endpoints/pages/unlike.ts | 11 +- .../src/server/api/endpoints/pages/update.ts | 11 +- .../backend/src/server/api/endpoints/ping.ts | 3 +- .../src/server/api/endpoints/pinned-users.ts | 11 +- .../src/server/api/endpoints/promo/read.ts | 11 +- .../api/endpoints/request-reset-password.ts | 11 +- .../src/server/api/endpoints/reset-db.ts | 11 +- .../server/api/endpoints/reset-password.ts | 11 +- .../src/server/api/endpoints/server-info.ts | 3 +- .../backend/src/server/api/endpoints/stats.ts | 3 +- .../src/server/api/endpoints/sw/register.ts | 11 +- .../src/server/api/endpoints/sw/unregister.ts | 11 +- .../backend/src/server/api/endpoints/test.ts | 11 +- .../api/endpoints/username/available.ts | 11 +- .../backend/src/server/api/endpoints/users.ts | 11 +- .../src/server/api/endpoints/users/clips.ts | 11 +- .../server/api/endpoints/users/followers.ts | 11 +- .../server/api/endpoints/users/following.ts | 11 +- .../api/endpoints/users/gallery/posts.ts | 11 +- .../users/get-frequently-replied-users.ts | 11 +- .../api/endpoints/users/groups/create.ts | 11 +- .../api/endpoints/users/groups/delete.ts | 11 +- .../users/groups/invitations/accept.ts | 11 +- .../users/groups/invitations/reject.ts | 11 +- .../api/endpoints/users/groups/invite.ts | 11 +- .../api/endpoints/users/groups/joined.ts | 11 +- .../api/endpoints/users/groups/leave.ts | 11 +- .../api/endpoints/users/groups/owned.ts | 11 +- .../server/api/endpoints/users/groups/pull.ts | 11 +- .../server/api/endpoints/users/groups/show.ts | 11 +- .../api/endpoints/users/groups/transfer.ts | 11 +- .../api/endpoints/users/groups/update.ts | 11 +- .../api/endpoints/users/lists/create.ts | 11 +- .../api/endpoints/users/lists/delete.ts | 11 +- .../server/api/endpoints/users/lists/list.ts | 11 +- .../server/api/endpoints/users/lists/pull.ts | 11 +- .../server/api/endpoints/users/lists/push.ts | 11 +- .../server/api/endpoints/users/lists/show.ts | 11 +- .../api/endpoints/users/lists/update.ts | 11 +- .../src/server/api/endpoints/users/notes.ts | 11 +- .../src/server/api/endpoints/users/pages.ts | 11 +- .../server/api/endpoints/users/reactions.ts | 11 +- .../api/endpoints/users/recommendation.ts | 11 +- .../server/api/endpoints/users/relation.ts | 11 +- .../api/endpoints/users/report-abuse.ts | 11 +- .../users/search-by-username-and-host.ts | 11 +- .../src/server/api/endpoints/users/search.ts | 11 +- .../src/server/api/endpoints/users/show.ts | 11 +- .../src/server/api/endpoints/users/stats.ts | 11 +- 315 files changed, 4069 insertions(+), 1807 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts index 333746f4239c..0b0645d08ff7 100644 --- a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts +++ b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { AbuseUserReports } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -85,7 +86,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(AbuseUserReports.createQueryBuilder('report'), ps.sinceId, ps.untilId); switch (ps.state) { diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts index 5f892199914d..ab4f774cee13 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Users } from '@/models/index.js'; import { signup } from '../../../common/signup.js'; import { IsNull } from 'typeorm'; @@ -29,7 +30,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, _me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, _me) => { const me = _me ? await Users.findOneByOrFail({ id: _me.id }) : null; const noUsers = (await Users.countBy({ host: IsNull(), diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts index 629d70058288..4920285b3ee5 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Users } from '@/models/index.js'; import { doPostSuspend } from '@/services/suspend-user.js'; import { publishUserEvent } from '@/services/stream.js'; @@ -20,7 +21,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const user = await Users.findOneBy({ id: ps.userId }); if (user == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts index ab2c50b50f72..8549d3312802 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Ads } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; @@ -24,7 +25,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { await Ads.insert({ id: genId(), createdAt: new Date(), diff --git a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts index 0ead2be0054e..8b0ff2fdb2f6 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Ads } from '@/models/index.js'; import { ApiError } from '../../../error.js'; @@ -26,7 +27,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const ad = await Ads.findOneBy({ id: ps.id }); if (ad == null) throw new ApiError(meta.errors.noSuchAd); diff --git a/packages/backend/src/server/api/endpoints/admin/ad/list.ts b/packages/backend/src/server/api/endpoints/admin/ad/list.ts index 74f154f272f7..a32107be0082 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/list.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Ads } from '@/models/index.js'; import { makePaginationQuery } from '../../../common/make-pagination-query.js'; @@ -20,7 +21,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(Ads.createQueryBuilder('ad'), ps.sinceId, ps.untilId) .andWhere('ad.expiresAt > :now', { now: new Date() }); diff --git a/packages/backend/src/server/api/endpoints/admin/ad/update.ts b/packages/backend/src/server/api/endpoints/admin/ad/update.ts index 650f8670e331..a5ac584d0901 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/update.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Ads } from '@/models/index.js'; import { ApiError } from '../../../error.js'; @@ -33,7 +34,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const ad = await Ads.findOneBy({ id: ps.id }); if (ad == null) throw new ApiError(meta.errors.noSuchAd); diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts index 33076b6d3027..44fd1fb8aac7 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Announcements } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; @@ -55,7 +56,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const announcement = await Announcements.insert({ id: genId(), createdAt: new Date(), diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts b/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts index c17765f4fc46..51b497006727 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Announcements } from '@/models/index.js'; import { ApiError } from '../../../error.js'; @@ -26,7 +27,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const announcement = await Announcements.findOneBy({ id: ps.id }); if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement); diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts index 7a5758d75b74..92786fa3f179 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts @@ -1,6 +1,7 @@ import { Announcements, AnnouncementReads } from '@/models/index.js'; import { Announcement } from '@/models/entities/announcement.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../../../common/make-pagination-query.js'; export const meta = { @@ -64,7 +65,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(Announcements.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); const announcements = await query.take(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts index 61ce106d88f7..7b253418ffa7 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Announcements } from '@/models/index.js'; import { ApiError } from '../../../error.js'; @@ -29,7 +30,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const announcement = await Announcements.findOneBy({ id: ps.id }); if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement); diff --git a/packages/backend/src/server/api/endpoints/admin/delete-account.ts b/packages/backend/src/server/api/endpoints/admin/delete-account.ts index 2d7ef2f23674..25654c7dadc2 100644 --- a/packages/backend/src/server/api/endpoints/admin/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/admin/delete-account.ts @@ -1,6 +1,7 @@ import { Users } from '@/models/index.js'; import { deleteAccount } from '@/services/delete-account.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['admin'], @@ -21,7 +22,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const user = await Users.findOneByOrFail({ id: ps.userId }); if (user.isDeleted) { return; diff --git a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts index dc1976624de4..bd6ab82a79af 100644 --- a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { deleteFile } from '@/services/drive/delete-file.js'; import { DriveFiles } from '@/models/index.js'; @@ -18,7 +19,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const files = await DriveFiles.findBy({ userId: ps.userId, }); diff --git a/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts b/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts index a4b29770e1fa..4914a51cf36c 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Users } from '@/models/index.js'; import { User } from '@/models/entities/user.js'; import { insertModerationLog } from '@/services/insert-moderation-log.js'; @@ -19,7 +20,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const user = await Users.findOneBy({ id: ps.userId }); if (user == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts b/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts index bab149532ec1..7749f7a88d5b 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { createCleanRemoteFilesJob } from '@/queue/index.js'; export const meta = { @@ -15,6 +16,12 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { createCleanRemoteFilesJob(); }); diff --git a/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts b/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts index 3db942e6cd8a..f02106efa008 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts @@ -1,5 +1,6 @@ import { IsNull } from 'typeorm'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { deleteFile } from '@/services/drive/delete-file.js'; import { DriveFiles } from '@/models/index.js'; @@ -17,7 +18,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const files = await DriveFiles.findBy({ userId: IsNull(), }); diff --git a/packages/backend/src/server/api/endpoints/admin/drive/files.ts b/packages/backend/src/server/api/endpoints/admin/drive/files.ts index ba32aac4319d..06a49bab19f5 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/files.ts @@ -1,5 +1,6 @@ import { DriveFiles } from '@/models/index.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../../../common/make-pagination-query.js'; export const meta = { @@ -39,7 +40,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId); if (ps.userId) { diff --git a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts index e9117a23c8e6..aee8439ebdd3 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts @@ -1,5 +1,6 @@ import { DriveFiles } from '@/models/index.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -169,7 +170,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const file = ps.fileId ? await DriveFiles.findOneBy({ id: ps.fileId }) : await DriveFiles.findOne({ where: [{ url: ps.url, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts index 232fbbd57301..263bd56c4b37 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; import { In } from 'typeorm'; import { ApiError } from '../../../error.js'; @@ -25,7 +26,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const emojis = await Emojis.findBy({ id: In(ps.ids), }); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 67349c24e0b8..ed3521d8493b 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis, DriveFiles } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { insertModerationLog } from '@/services/insert-moderation-log.js'; @@ -31,7 +32,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const file = await DriveFiles.findOneBy({ id: ps.fileId }); if (file == null) throw new ApiError(meta.errors.noSuchFile); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index 7010ade0d871..23183fd8151f 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { ApiError } from '../../../error.js'; @@ -43,7 +44,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const emoji = await Emojis.findOneBy({ id: ps.emojiId }); if (emoji == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts index 93a6c4e4e2a7..552559820399 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; import { In } from 'typeorm'; import { insertModerationLog } from '@/services/insert-moderation-log.js'; @@ -23,7 +24,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const emojis = await Emojis.findBy({ id: In(ps.ids), }); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts index 67dbf28d8577..b82c2af401a5 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; import { insertModerationLog } from '@/services/insert-moderation-log.js'; import { ApiError } from '../../../error.js'; @@ -28,7 +29,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const emoji = await Emojis.findOneBy({ id: ps.id }); if (emoji == null) throw new ApiError(meta.errors.noSuchEmoji); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts index 3f03dc2da469..699876dff0f9 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { createImportCustomEmojisJob } from '@/queue/index.js'; import ms from 'ms'; @@ -17,6 +18,12 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { createImportCustomEmojisJob(user, ps.fileId); }); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts index d16689a280c2..29e731c676c9 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; import { toPuny } from '@/misc/convert-host.js'; import { makePaginationQuery } from '../../../common/make-pagination-query.js'; @@ -69,7 +70,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const q = makePaginationQuery(Emojis.createQueryBuilder('emoji'), ps.sinceId, ps.untilId); if (ps.host == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts index 6192978fadf4..c3f5cfc67fc4 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; import { makePaginationQuery } from '../../../common/make-pagination-query.js'; import { Emoji } from '@/models/entities/emoji.js'; @@ -63,7 +64,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const q = makePaginationQuery(Emojis.createQueryBuilder('emoji'), ps.sinceId, ps.untilId) .andWhere(`emoji.host IS NULL`); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts index a4da40fffd8b..254add60a210 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; import { In } from 'typeorm'; import { ApiError } from '../../../error.js'; @@ -25,7 +26,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const emojis = await Emojis.findBy({ id: In(ps.ids), }); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts index ae3b190f40bc..466444b61756 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; import { In } from 'typeorm'; import { ApiError } from '../../../error.js'; @@ -25,7 +26,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { await Emojis.update({ id: In(ps.ids), }, { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts index cff58d617068..cfdaf52c8999 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; import { In } from 'typeorm'; import { ApiError } from '../../../error.js'; @@ -27,7 +28,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { await Emojis.update({ id: In(ps.ids), }, { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index 5b547b3b7937..d322ca8e0bfb 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; import { ApiError } from '../../../error.js'; import { db } from '@/db/postgre.js'; @@ -36,7 +37,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const emoji = await Emojis.findOneBy({ id: ps.id }); if (emoji == null) throw new ApiError(meta.errors.noSuchEmoji); diff --git a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts index da54201473df..e1bab7504c5c 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { deleteFile } from '@/services/drive/delete-file.js'; import { DriveFiles } from '@/models/index.js'; @@ -18,7 +19,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const files = await DriveFiles.findBy({ userHost: ps.host, }); diff --git a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts index cb2be5ab37fe..ce169f6b1bdb 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Instances } from '@/models/index.js'; import { toPuny } from '@/misc/convert-host.js'; import { fetchInstanceMetadata } from '@/services/fetch-instance-metadata.js'; @@ -19,7 +20,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const instance = await Instances.findOneBy({ host: toPuny(ps.host) }); if (instance == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts index b7ee27db6458..4c420f41e2dc 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import deleteFollowing from '@/services/following/delete.js'; import { Followings, Users } from '@/models/index.js'; @@ -18,7 +19,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const followings = await Followings.findBy({ followerHost: ps.host, }); diff --git a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts index 278131fb3752..828966fc7c55 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Instances } from '@/models/index.js'; import { toPuny } from '@/misc/convert-host.js'; @@ -19,7 +20,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const instance = await Instances.findOneBy({ host: toPuny(ps.host) }); if (instance == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts index dd16473f302a..9be4e6ac53f0 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { db } from '@/db/postgre.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts index aca2540fd527..efbf39259595 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts @@ -1,5 +1,6 @@ import { db } from '@/db/postgre.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { requireCredential: true, diff --git a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts index e8b9cb3b0931..9195e8a87966 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts @@ -1,5 +1,6 @@ import { UserIps } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['admin'], @@ -17,7 +18,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const ips = await UserIps.find({ where: { userId: ps.userId }, order: { createdAt: 'DESC' }, diff --git a/packages/backend/src/server/api/endpoints/admin/invite.ts b/packages/backend/src/server/api/endpoints/admin/invite.ts index 7e950cf87bc6..10dd44c857bd 100644 --- a/packages/backend/src/server/api/endpoints/admin/invite.ts +++ b/packages/backend/src/server/api/endpoints/admin/invite.ts @@ -1,5 +1,6 @@ import rndstr from 'rndstr'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { RegistrationTickets } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 87461196874a..4a3b099eed6a 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -1,7 +1,8 @@ import config from '@/config/index.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['meta'], @@ -340,7 +341,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const instance = await fetchMeta(true); return { diff --git a/packages/backend/src/server/api/endpoints/admin/moderators/add.ts b/packages/backend/src/server/api/endpoints/admin/moderators/add.ts index 7b209c2d99f9..d85d98c85805 100644 --- a/packages/backend/src/server/api/endpoints/admin/moderators/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/moderators/add.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Users } from '@/models/index.js'; import { publishInternalEvent } from '@/services/stream.js'; @@ -18,7 +19,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const user = await Users.findOneBy({ id: ps.userId }); if (user == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts b/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts index a01e9f3c69b2..4f61634ca001 100644 --- a/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts +++ b/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Users } from '@/models/index.js'; import { publishInternalEvent } from '@/services/stream.js'; @@ -18,7 +19,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const user = await Users.findOneBy({ id: ps.userId }); if (user == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/promo/create.ts b/packages/backend/src/server/api/endpoints/admin/promo/create.ts index 68a17867b22d..52fdd411facf 100644 --- a/packages/backend/src/server/api/endpoints/admin/promo/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/promo/create.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { getNote } from '../../../common/getters.js'; import { PromoNotes } from '@/models/index.js'; @@ -34,7 +35,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts index 8f015c280aa6..311d8c3818fa 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { destroy } from '@/queue/index.js'; import { insertModerationLog } from '@/services/insert-moderation-log.js'; @@ -16,7 +17,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { destroy(); insertModerationLog(me, 'clearQueue'); diff --git a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts index 70f7d77de401..b26c18a5ecbe 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts @@ -1,6 +1,7 @@ import { deliverQueue } from '@/queue/queues.js'; import { URL } from 'node:url'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['admin'], @@ -39,7 +40,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const jobs = await deliverQueue.getJobs(['delayed']); const res = [] as [string, number][]; diff --git a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts index 2235ce8f970a..68f335dbc4c4 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts @@ -1,5 +1,6 @@ import { URL } from 'node:url'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { inboxQueue } from '@/queue/queues.js'; export const meta = { @@ -39,7 +40,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const jobs = await inboxQueue.getJobs(['delayed']); const res = [] as [string, number][]; diff --git a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts index 988b5a5e3596..103bd08511d4 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts @@ -1,5 +1,6 @@ import { deliverQueue, inboxQueue, dbQueue, objectStorageQueue } from '@/queue/queues.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['admin'], @@ -38,7 +39,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const deliverJobCounts = await deliverQueue.getJobCounts(); const inboxJobCounts = await inboxQueue.getJobCounts(); const dbJobCounts = await dbQueue.getJobCounts(); diff --git a/packages/backend/src/server/api/endpoints/admin/relays/add.ts b/packages/backend/src/server/api/endpoints/admin/relays/add.ts index 348e9baca1ff..bf5ce2c1f283 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/add.ts @@ -1,5 +1,6 @@ import { URL } from 'node:url'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { addRelay } from '@/services/relay.js'; import { ApiError } from '../../../error.js'; @@ -54,7 +55,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { try { if (new URL(ps.inbox).protocol !== 'https:') throw 'https only'; } catch { diff --git a/packages/backend/src/server/api/endpoints/admin/relays/list.ts b/packages/backend/src/server/api/endpoints/admin/relays/list.ts index 89ec651e611b..ac0df8d8394a 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/list.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { listRelay } from '@/services/relay.js'; export const meta = { @@ -46,6 +47,12 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { return await listRelay(); }); diff --git a/packages/backend/src/server/api/endpoints/admin/relays/remove.ts b/packages/backend/src/server/api/endpoints/admin/relays/remove.ts index b59cf72c5804..43ed2f1d9fd9 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/remove.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/remove.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { removeRelay } from '@/services/relay.js'; export const meta = { @@ -17,6 +18,12 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { return await removeRelay(ps.inbox); }); diff --git a/packages/backend/src/server/api/endpoints/admin/reset-password.ts b/packages/backend/src/server/api/endpoints/admin/reset-password.ts index be4c2dceed35..bee86d40a34e 100644 --- a/packages/backend/src/server/api/endpoints/admin/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/admin/reset-password.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import bcrypt from 'bcryptjs'; import rndstr from 'rndstr'; import { Users, UserProfiles } from '@/models/index.js'; @@ -32,7 +33,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const user = await Users.findOneBy({ id: ps.userId }); if (user == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts index 3edae4a85fa1..ceff7cac5b0d 100644 --- a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts +++ b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { AbuseUserReports, Users } from '@/models/index.js'; import { getInstanceActor } from '@/services/instance-actor.js'; import { deliver } from '@/queue/index.js'; @@ -22,7 +23,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const report = await AbuseUserReports.findOneByOrFail({ id: ps.reportId }); if (report == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/send-email.ts b/packages/backend/src/server/api/endpoints/admin/send-email.ts index bbdd66e4c92a..2071117d4c1c 100644 --- a/packages/backend/src/server/api/endpoints/admin/send-email.ts +++ b/packages/backend/src/server/api/endpoints/admin/send-email.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { sendEmail } from '@/services/send-email.js'; export const meta = { @@ -19,6 +20,12 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { await sendEmail(ps.to, ps.subject, ps.text, ps.text); }); diff --git a/packages/backend/src/server/api/endpoints/admin/server-info.ts b/packages/backend/src/server/api/endpoints/admin/server-info.ts index 85c6fb82e7e6..f15852b30504 100644 --- a/packages/backend/src/server/api/endpoints/admin/server-info.ts +++ b/packages/backend/src/server/api/endpoints/admin/server-info.ts @@ -1,6 +1,7 @@ import * as os from 'node:os'; import si from 'systeminformation'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { redisClient } from '../../../../db/redis.js'; import { db } from '@/db/postgre.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts index 3545536aa255..53cdc14be114 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ModerationLogs } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -59,7 +60,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(ModerationLogs.createQueryBuilder('report'), ps.sinceId, ps.untilId); const reports = await query.take(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index 0d866b311382..62c0e2e8df7a 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -1,5 +1,6 @@ import { Signins, UserProfiles, Users } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['admin'], @@ -22,7 +23,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const [user, profile] = await Promise.all([ Users.findOneBy({ id: ps.userId }), UserProfiles.findOneBy({ userId: ps.userId }), diff --git a/packages/backend/src/server/api/endpoints/admin/show-users.ts b/packages/backend/src/server/api/endpoints/admin/show-users.ts index 8e09e72d5b2b..23b04a6ebada 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-users.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts @@ -1,5 +1,6 @@ import { Users } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['admin'], @@ -38,7 +39,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const query = Users.createQueryBuilder('user'); switch (ps.state) { diff --git a/packages/backend/src/server/api/endpoints/admin/silence-user.ts b/packages/backend/src/server/api/endpoints/admin/silence-user.ts index 17b9f3b5a046..7347c631cd92 100644 --- a/packages/backend/src/server/api/endpoints/admin/silence-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/silence-user.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Users } from '@/models/index.js'; import { insertModerationLog } from '@/services/insert-moderation-log.js'; import { publishInternalEvent } from '@/services/stream.js'; @@ -19,7 +20,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const user = await Users.findOneBy({ id: ps.userId }); if (user == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts index ed513eda0835..07be0be6fd92 100644 --- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import deleteFollowing from '@/services/following/delete.js'; import { Users, Followings, Notifications } from '@/models/index.js'; import { User } from '@/models/entities/user.js'; @@ -22,7 +23,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const user = await Users.findOneBy({ id: ps.userId }); if (user == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts b/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts index a4b373f5c787..8eab0de57e28 100644 --- a/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Users } from '@/models/index.js'; import { insertModerationLog } from '@/services/insert-moderation-log.js'; import { publishInternalEvent } from '@/services/stream.js'; @@ -19,7 +20,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const user = await Users.findOneBy({ id: ps.userId }); if (user == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts index 5cf26251bed5..cb97dc1dc34f 100644 --- a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Users } from '@/models/index.js'; import { insertModerationLog } from '@/services/insert-moderation-log.js'; import { doPostUnsuspend } from '@/services/unsuspend-user.js'; @@ -19,7 +20,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const user = await Users.findOneBy({ id: ps.userId }); if (user == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index f14aa41050f6..4620bff99c7b 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -2,7 +2,8 @@ import { Meta } from '@/models/entities/meta.js'; import { insertModerationLog } from '@/services/insert-moderation-log.js'; import { DB_MAX_NOTE_TEXT_LENGTH } from '@/misc/hard-limits.js'; import { db } from '@/db/postgre.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['admin'], @@ -107,7 +108,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const set = {} as Partial; if (typeof ps.disableRegistration === 'boolean') { diff --git a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts index fa21ab78334c..069eba501a8f 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts @@ -1,5 +1,6 @@ import { UserProfiles, Users } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['admin'], @@ -18,7 +19,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const user = await Users.findOneBy({ id: ps.userId }); if (user == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/vacuum.ts b/packages/backend/src/server/api/endpoints/admin/vacuum.ts index 0546acfacbd1..dc0d2ce3c9b5 100644 --- a/packages/backend/src/server/api/endpoints/admin/vacuum.ts +++ b/packages/backend/src/server/api/endpoints/admin/vacuum.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { insertModerationLog } from '@/services/insert-moderation-log.js'; import { db } from '@/db/postgre.js'; @@ -19,7 +20,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const params: string[] = []; if (ps.full) { diff --git a/packages/backend/src/server/api/endpoints/announcements.ts b/packages/backend/src/server/api/endpoints/announcements.ts index 23cb93c9a52a..2a8d57476e19 100644 --- a/packages/backend/src/server/api/endpoints/announcements.ts +++ b/packages/backend/src/server/api/endpoints/announcements.ts @@ -1,5 +1,6 @@ import { Announcements, AnnouncementReads } from '@/models/index.js'; -import define from '../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../common/make-pagination-query.js'; export const meta = { @@ -63,7 +64,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(Announcements.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); const announcements = await query.take(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index 7a4923b944fb..63e8c6984a99 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -1,8 +1,9 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { genId } from '@/misc/gen-id.js'; import { Antennas, UserLists, UserGroupJoinings } from '@/models/index.js'; -import { ApiError } from '../../error.js'; import { publishInternalEvent } from '@/services/stream.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['antennas'], @@ -61,48 +62,56 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - let userList; - let userGroupJoining; +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + let userList; + let userGroupJoining; - if (ps.src === 'list' && ps.userListId) { - userList = await UserLists.findOneBy({ - id: ps.userListId, - userId: user.id, - }); + if (ps.src === 'list' && ps.userListId) { + userList = await UserLists.findOneBy({ + id: ps.userListId, + userId: user.id, + }); - if (userList == null) { - throw new ApiError(meta.errors.noSuchUserList); - } - } else if (ps.src === 'group' && ps.userGroupId) { - userGroupJoining = await UserGroupJoinings.findOneBy({ - userGroupId: ps.userGroupId, - userId: user.id, - }); + if (userList == null) { + throw new ApiError(meta.errors.noSuchUserList); + } + } else if (ps.src === 'group' && ps.userGroupId) { + userGroupJoining = await UserGroupJoinings.findOneBy({ + userGroupId: ps.userGroupId, + userId: user.id, + }); - if (userGroupJoining == null) { - throw new ApiError(meta.errors.noSuchUserGroup); - } - } + if (userGroupJoining == null) { + throw new ApiError(meta.errors.noSuchUserGroup); + } + } - const antenna = await Antennas.insert({ - id: genId(), - createdAt: new Date(), - userId: user.id, - name: ps.name, - src: ps.src, - userListId: userList ? userList.id : null, - userGroupJoiningId: userGroupJoining ? userGroupJoining.id : null, - keywords: ps.keywords, - excludeKeywords: ps.excludeKeywords, - users: ps.users, - caseSensitive: ps.caseSensitive, - withReplies: ps.withReplies, - withFile: ps.withFile, - notify: ps.notify, - }).then(x => Antennas.findOneByOrFail(x.identifiers[0])); + const antenna = await Antennas.insert({ + id: genId(), + createdAt: new Date(), + userId: user.id, + name: ps.name, + src: ps.src, + userListId: userList ? userList.id : null, + userGroupJoiningId: userGroupJoining ? userGroupJoining.id : null, + keywords: ps.keywords, + excludeKeywords: ps.excludeKeywords, + users: ps.users, + caseSensitive: ps.caseSensitive, + withReplies: ps.withReplies, + withFile: ps.withFile, + notify: ps.notify, + }).then(x => Antennas.findOneByOrFail(x.identifiers[0])); - publishInternalEvent('antennaCreated', antenna); + publishInternalEvent('antennaCreated', antenna); - return await Antennas.pack(antenna); -}); + return await Antennas.pack(antenna); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/antennas/delete.ts b/packages/backend/src/server/api/endpoints/antennas/delete.ts index ced34ba313db..5eaa4752ed84 100644 --- a/packages/backend/src/server/api/endpoints/antennas/delete.ts +++ b/packages/backend/src/server/api/endpoints/antennas/delete.ts @@ -1,7 +1,8 @@ -import define from '../../define.js'; -import { ApiError } from '../../error.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Antennas } from '@/models/index.js'; import { publishInternalEvent } from '@/services/stream.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['antennas'], @@ -28,17 +29,25 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const antenna = await Antennas.findOneBy({ - id: ps.antennaId, - userId: user.id, - }); - - if (antenna == null) { - throw new ApiError(meta.errors.noSuchAntenna); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const antenna = await Antennas.findOneBy({ + id: ps.antennaId, + userId: user.id, + }); + + if (antenna == null) { + throw new ApiError(meta.errors.noSuchAntenna); + } + + await Antennas.delete(antenna.id); + + publishInternalEvent('antennaDeleted', antenna); + }); } - - await Antennas.delete(antenna.id); - - publishInternalEvent('antennaDeleted', antenna); -}); +} diff --git a/packages/backend/src/server/api/endpoints/antennas/list.ts b/packages/backend/src/server/api/endpoints/antennas/list.ts index c519b452effc..a19e8ebf00bc 100644 --- a/packages/backend/src/server/api/endpoints/antennas/list.ts +++ b/packages/backend/src/server/api/endpoints/antennas/list.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Antennas } from '@/models/index.js'; export const meta = { @@ -26,7 +27,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const antennas = await Antennas.findBy({ userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index 8aac55b4a041..f26d7ef40500 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import readNote from '@/services/note/read.js'; import { Antennas, Notes, AntennaNotes } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -47,18 +48,24 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const antenna = await Antennas.findOneBy({ - id: ps.antennaId, - userId: user.id, - }); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const antenna = await Antennas.findOneBy({ + id: ps.antennaId, + userId: user.id, + }); - if (antenna == null) { - throw new ApiError(meta.errors.noSuchAntenna); - } + if (antenna == null) { + throw new ApiError(meta.errors.noSuchAntenna); + } - const query = makePaginationQuery(Notes.createQueryBuilder('note'), - ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + const query = makePaginationQuery(Notes.createQueryBuilder('note'), + ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .innerJoin(AntennaNotes.metadata.targetName, 'antennaNote', 'antennaNote.noteId = note.id') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') @@ -73,17 +80,19 @@ export default define(meta, paramDef, async (ps, user) => { .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') .andWhere('antennaNote.antennaId = :antennaId', { antennaId: antenna.id }); - generateVisibilityQuery(query, user); - generateMutedUserQuery(query, user); - generateBlockedUserQuery(query, user); + generateVisibilityQuery(query, user); + generateMutedUserQuery(query, user); + generateBlockedUserQuery(query, user); - const notes = await query + const notes = await query .take(ps.limit) .getMany(); - if (notes.length > 0) { - readNote(user.id, notes); - } + if (notes.length > 0) { + readNote(user.id, notes); + } - return await Notes.packMany(notes, user); -}); + return await Notes.packMany(notes, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/antennas/show.ts b/packages/backend/src/server/api/endpoints/antennas/show.ts index dd693789cba1..440e15e313f2 100644 --- a/packages/backend/src/server/api/endpoints/antennas/show.ts +++ b/packages/backend/src/server/api/endpoints/antennas/show.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { Antennas } from '@/models/index.js'; @@ -33,7 +34,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { // Fetch the antenna const antenna = await Antennas.findOneBy({ id: ps.antennaId, diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts index edfedc1752df..749d6b158717 100644 --- a/packages/backend/src/server/api/endpoints/antennas/update.ts +++ b/packages/backend/src/server/api/endpoints/antennas/update.ts @@ -1,7 +1,8 @@ -import define from '../../define.js'; -import { ApiError } from '../../error.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Antennas, UserLists, UserGroupJoinings } from '@/models/index.js'; import { publishInternalEvent } from '@/services/stream.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['antennas'], @@ -67,55 +68,63 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - // Fetch the antenna - const antenna = await Antennas.findOneBy({ - id: ps.antennaId, - userId: user.id, - }); - - if (antenna == null) { - throw new ApiError(meta.errors.noSuchAntenna); - } - - let userList; - let userGroupJoining; - - if (ps.src === 'list' && ps.userListId) { - userList = await UserLists.findOneBy({ - id: ps.userListId, - userId: user.id, +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + // Fetch the antenna + const antenna = await Antennas.findOneBy({ + id: ps.antennaId, + userId: user.id, + }); + + if (antenna == null) { + throw new ApiError(meta.errors.noSuchAntenna); + } + + let userList; + let userGroupJoining; + + if (ps.src === 'list' && ps.userListId) { + userList = await UserLists.findOneBy({ + id: ps.userListId, + userId: user.id, + }); + + if (userList == null) { + throw new ApiError(meta.errors.noSuchUserList); + } + } else if (ps.src === 'group' && ps.userGroupId) { + userGroupJoining = await UserGroupJoinings.findOneBy({ + userGroupId: ps.userGroupId, + userId: user.id, + }); + + if (userGroupJoining == null) { + throw new ApiError(meta.errors.noSuchUserGroup); + } + } + + await Antennas.update(antenna.id, { + name: ps.name, + src: ps.src, + userListId: userList ? userList.id : null, + userGroupJoiningId: userGroupJoining ? userGroupJoining.id : null, + keywords: ps.keywords, + excludeKeywords: ps.excludeKeywords, + users: ps.users, + caseSensitive: ps.caseSensitive, + withReplies: ps.withReplies, + withFile: ps.withFile, + notify: ps.notify, + }); + + publishInternalEvent('antennaUpdated', await Antennas.findOneByOrFail({ id: antenna.id })); + + return await Antennas.pack(antenna.id); }); - - if (userList == null) { - throw new ApiError(meta.errors.noSuchUserList); - } - } else if (ps.src === 'group' && ps.userGroupId) { - userGroupJoining = await UserGroupJoinings.findOneBy({ - userGroupId: ps.userGroupId, - userId: user.id, - }); - - if (userGroupJoining == null) { - throw new ApiError(meta.errors.noSuchUserGroup); - } } - - await Antennas.update(antenna.id, { - name: ps.name, - src: ps.src, - userListId: userList ? userList.id : null, - userGroupJoiningId: userGroupJoining ? userGroupJoining.id : null, - keywords: ps.keywords, - excludeKeywords: ps.excludeKeywords, - users: ps.users, - caseSensitive: ps.caseSensitive, - withReplies: ps.withReplies, - withFile: ps.withFile, - notify: ps.notify, - }); - - publishInternalEvent('antennaUpdated', await Antennas.findOneByOrFail({ id: antenna.id })); - - return await Antennas.pack(antenna.id); -}); +} diff --git a/packages/backend/src/server/api/endpoints/ap/get.ts b/packages/backend/src/server/api/endpoints/ap/get.ts index 0cbe7ebc6715..4e1173f5d641 100644 --- a/packages/backend/src/server/api/endpoints/ap/get.ts +++ b/packages/backend/src/server/api/endpoints/ap/get.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import Resolver from '@/remote/activitypub/resolver.js'; import { ApiError } from '../../error.js'; import ms from 'ms'; @@ -31,7 +32,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const resolver = new Resolver(); const object = await resolver.resolve(ps.uri); return object; diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index 6442a1412c8c..2b84aebef87e 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import config from '@/config/index.js'; import { createPerson } from '@/remote/activitypub/models/person.js'; import { createNote } from '@/remote/activitypub/models/note.js'; @@ -78,7 +79,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const object = await fetchAny(ps.uri, me); if (object) { return object; diff --git a/packages/backend/src/server/api/endpoints/app/create.ts b/packages/backend/src/server/api/endpoints/app/create.ts index a0a735082229..133207d82150 100644 --- a/packages/backend/src/server/api/endpoints/app/create.ts +++ b/packages/backend/src/server/api/endpoints/app/create.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Apps } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { unique } from '@/prelude/array.js'; @@ -30,7 +31,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { // Generate secret const secret = secureRndstr(32, true); diff --git a/packages/backend/src/server/api/endpoints/app/show.ts b/packages/backend/src/server/api/endpoints/app/show.ts index 451969d97147..f8505c815ef2 100644 --- a/packages/backend/src/server/api/endpoints/app/show.ts +++ b/packages/backend/src/server/api/endpoints/app/show.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { Apps } from '@/models/index.js'; diff --git a/packages/backend/src/server/api/endpoints/auth/accept.ts b/packages/backend/src/server/api/endpoints/auth/accept.ts index b5c06792bb36..6b2cdb13f3a1 100644 --- a/packages/backend/src/server/api/endpoints/auth/accept.ts +++ b/packages/backend/src/server/api/endpoints/auth/accept.ts @@ -1,5 +1,6 @@ import * as crypto from 'node:crypto'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { AuthSessions, AccessTokens, Apps } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; @@ -30,7 +31,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { // Fetch token const session = await AuthSessions .findOneBy({ token: ps.token }); diff --git a/packages/backend/src/server/api/endpoints/auth/session/generate.ts b/packages/backend/src/server/api/endpoints/auth/session/generate.ts index 717c3e508639..a50f00e810bc 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/generate.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/generate.ts @@ -1,6 +1,7 @@ import { v4 as uuid } from 'uuid'; import config from '@/config/index.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { Apps, AuthSessions } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; @@ -44,7 +45,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { // Lookup app const app = await Apps.findOneBy({ secret: ps.appSecret, diff --git a/packages/backend/src/server/api/endpoints/auth/session/show.ts b/packages/backend/src/server/api/endpoints/auth/session/show.ts index 3f3a4d14272e..3582a09339e1 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/show.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/show.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { AuthSessions } from '@/models/index.js'; @@ -46,7 +47,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { // Lookup session const session = await AuthSessions.findOneBy({ token: ps.token, diff --git a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts index 89884ed38a95..327eb7daab09 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { Apps, AuthSessions, AccessTokens, Users } from '@/models/index.js'; @@ -55,7 +56,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { // Lookup app const app = await Apps.findOneBy({ secret: ps.appSecret, diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts index 0540e6ab0f69..eee748d8e3c0 100644 --- a/packages/backend/src/server/api/endpoints/blocking/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking/create.ts @@ -1,9 +1,10 @@ import ms from 'ms'; +import { Inject, Injectable } from '@nestjs/common'; import create from '@/services/blocking/create.js'; -import define from '../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { Blockings, NoteWatchings, Users } from '@/models/index.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; -import { Blockings, NoteWatchings, Users } from '@/models/index.js'; export const meta = { tags: ['account'], @@ -53,38 +54,46 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const blocker = await Users.findOneByOrFail({ id: user.id }); - - // 自分自身 - if (user.id === ps.userId) { - throw new ApiError(meta.errors.blockeeIsYourself); - } - - // Get blockee - const blockee = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; - }); - - // Check if already blocking - const exist = await Blockings.findOneBy({ - blockerId: blocker.id, - blockeeId: blockee.id, - }); - - if (exist != null) { - throw new ApiError(meta.errors.alreadyBlocking); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const blocker = await Users.findOneByOrFail({ id: user.id }); + + // 自分自身 + if (user.id === ps.userId) { + throw new ApiError(meta.errors.blockeeIsYourself); + } + + // Get blockee + const blockee = await getUser(ps.userId).catch(e => { + if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw e; + }); + + // Check if already blocking + const exist = await Blockings.findOneBy({ + blockerId: blocker.id, + blockeeId: blockee.id, + }); + + if (exist != null) { + throw new ApiError(meta.errors.alreadyBlocking); + } + + await create(blocker, blockee); + + NoteWatchings.delete({ + userId: blocker.id, + noteUserId: blockee.id, + }); + + return await Users.pack(blockee.id, blocker, { + detail: true, + }); + }); } - - await create(blocker, blockee); - - NoteWatchings.delete({ - userId: blocker.id, - noteUserId: blockee.id, - }); - - return await Users.pack(blockee.id, blocker, { - detail: true, - }); -}); +} diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts index 77e17b3ba988..cd776fcf8650 100644 --- a/packages/backend/src/server/api/endpoints/blocking/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts @@ -1,9 +1,10 @@ import ms from 'ms'; +import { Inject, Injectable } from '@nestjs/common'; import deleteBlocking from '@/services/blocking/delete.js'; -import define from '../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { Blockings, Users } from '@/models/index.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; -import { Blockings, Users } from '@/models/index.js'; export const meta = { tags: ['account'], @@ -53,34 +54,42 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const blocker = await Users.findOneByOrFail({ id: user.id }); - - // Check if the blockee is yourself - if (user.id === ps.userId) { - throw new ApiError(meta.errors.blockeeIsYourself); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const blocker = await Users.findOneByOrFail({ id: user.id }); + + // Check if the blockee is yourself + if (user.id === ps.userId) { + throw new ApiError(meta.errors.blockeeIsYourself); + } + + // Get blockee + const blockee = await getUser(ps.userId).catch(e => { + if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw e; + }); + + // Check not blocking + const exist = await Blockings.findOneBy({ + blockerId: blocker.id, + blockeeId: blockee.id, + }); + + if (exist == null) { + throw new ApiError(meta.errors.notBlocking); + } + + // Delete blocking + await deleteBlocking(blocker, blockee); + + return await Users.pack(blockee.id, blocker, { + detail: true, + }); + }); } - - // Get blockee - const blockee = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; - }); - - // Check not blocking - const exist = await Blockings.findOneBy({ - blockerId: blocker.id, - blockeeId: blockee.id, - }); - - if (exist == null) { - throw new ApiError(meta.errors.notBlocking); - } - - // Delete blocking - await deleteBlocking(blocker, blockee); - - return await Users.pack(blockee.id, blocker, { - detail: true, - }); -}); +} diff --git a/packages/backend/src/server/api/endpoints/blocking/list.ts b/packages/backend/src/server/api/endpoints/blocking/list.ts index 29095ebe21d1..19829173c8c4 100644 --- a/packages/backend/src/server/api/endpoints/blocking/list.ts +++ b/packages/backend/src/server/api/endpoints/blocking/list.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Blockings } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -31,13 +32,21 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Blockings.createQueryBuilder('blocking'), ps.sinceId, ps.untilId) - .andWhere(`blocking.blockerId = :meId`, { meId: me.id }); - - const blockings = await query +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { + const query = makePaginationQuery(Blockings.createQueryBuilder('blocking'), ps.sinceId, ps.untilId) + .andWhere('blocking.blockerId = :meId', { meId: me.id }); + + const blockings = await query .take(ps.limit) .getMany(); - return await Blockings.packMany(blockings, me); -}); + return await Blockings.packMany(blockings, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/channels/create.ts b/packages/backend/src/server/api/endpoints/channels/create.ts index 94dcfe502377..8ed41f189c44 100644 --- a/packages/backend/src/server/api/endpoints/channels/create.ts +++ b/packages/backend/src/server/api/endpoints/channels/create.ts @@ -1,8 +1,9 @@ -import define from '../../define.js'; -import { ApiError } from '../../error.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Channels, DriveFiles } from '@/models/index.js'; -import { Channel } from '@/models/entities/channel.js'; +import type { Channel } from '@/models/entities/channel.js'; import { genId } from '@/misc/gen-id.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['channels'], @@ -37,27 +38,35 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - let banner = null; - if (ps.bannerId != null) { - banner = await DriveFiles.findOneBy({ - id: ps.bannerId, - userId: user.id, - }); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + let banner = null; + if (ps.bannerId != null) { + banner = await DriveFiles.findOneBy({ + id: ps.bannerId, + userId: user.id, + }); - if (banner == null) { - throw new ApiError(meta.errors.noSuchFile); - } - } + if (banner == null) { + throw new ApiError(meta.errors.noSuchFile); + } + } - const channel = await Channels.insert({ - id: genId(), - createdAt: new Date(), - userId: user.id, - name: ps.name, - description: ps.description || null, - bannerId: banner ? banner.id : null, - } as Channel).then(x => Channels.findOneByOrFail(x.identifiers[0])); - - return await Channels.pack(channel, user); -}); + const channel = await Channels.insert({ + id: genId(), + createdAt: new Date(), + userId: user.id, + name: ps.name, + description: ps.description || null, + bannerId: banner ? banner.id : null, + } as Channel).then(x => Channels.findOneByOrFail(x.identifiers[0])); + + return await Channels.pack(channel, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/channels/featured.ts b/packages/backend/src/server/api/endpoints/channels/featured.ts index 73980c0fadfe..cfaeef492e15 100644 --- a/packages/backend/src/server/api/endpoints/channels/featured.ts +++ b/packages/backend/src/server/api/endpoints/channels/featured.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Channels } from '@/models/index.js'; export const meta = { @@ -24,12 +25,20 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const query = Channels.createQueryBuilder('channel') +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { + const query = Channels.createQueryBuilder('channel') .where('channel.lastNotedAt IS NOT NULL') .orderBy('channel.lastNotedAt', 'DESC'); - const channels = await query.take(10).getMany(); + const channels = await query.take(10).getMany(); - return await Promise.all(channels.map(x => Channels.pack(x, me))); -}); + return await Promise.all(channels.map(x => Channels.pack(x, me))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/channels/follow.ts b/packages/backend/src/server/api/endpoints/channels/follow.ts index 895ffed0bdcf..adb53347edc0 100644 --- a/packages/backend/src/server/api/endpoints/channels/follow.ts +++ b/packages/backend/src/server/api/endpoints/channels/follow.ts @@ -1,8 +1,9 @@ -import define from '../../define.js'; -import { ApiError } from '../../error.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Channels, ChannelFollowings } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { publishUserEvent } from '@/services/stream.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['channels'], @@ -29,21 +30,29 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const channel = await Channels.findOneBy({ - id: ps.channelId, - }); - - if (channel == null) { - throw new ApiError(meta.errors.noSuchChannel); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const channel = await Channels.findOneBy({ + id: ps.channelId, + }); + + if (channel == null) { + throw new ApiError(meta.errors.noSuchChannel); + } + + await ChannelFollowings.insert({ + id: genId(), + createdAt: new Date(), + followerId: user.id, + followeeId: channel.id, + }); + + publishUserEvent(user.id, 'followChannel', channel); + }); } - - await ChannelFollowings.insert({ - id: genId(), - createdAt: new Date(), - followerId: user.id, - followeeId: channel.id, - }); - - publishUserEvent(user.id, 'followChannel', channel); -}); +} diff --git a/packages/backend/src/server/api/endpoints/channels/followed.ts b/packages/backend/src/server/api/endpoints/channels/followed.ts index e4aa4d1614d9..86c8a52bde9b 100644 --- a/packages/backend/src/server/api/endpoints/channels/followed.ts +++ b/packages/backend/src/server/api/endpoints/channels/followed.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Channels, ChannelFollowings } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -31,13 +32,21 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(ChannelFollowings.createQueryBuilder(), ps.sinceId, ps.untilId) +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { + const query = makePaginationQuery(ChannelFollowings.createQueryBuilder(), ps.sinceId, ps.untilId) .andWhere({ followerId: me.id }); - const followings = await query + const followings = await query .take(ps.limit) .getMany(); - return await Promise.all(followings.map(x => Channels.pack(x.followeeId, me))); -}); + return await Promise.all(followings.map(x => Channels.pack(x.followeeId, me))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/channels/owned.ts b/packages/backend/src/server/api/endpoints/channels/owned.ts index ed7e41cac2cb..ef87a1521381 100644 --- a/packages/backend/src/server/api/endpoints/channels/owned.ts +++ b/packages/backend/src/server/api/endpoints/channels/owned.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Channels } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -31,13 +32,21 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Channels.createQueryBuilder(), ps.sinceId, ps.untilId) +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { + const query = makePaginationQuery(Channels.createQueryBuilder(), ps.sinceId, ps.untilId) .andWhere({ userId: me.id }); - const channels = await query + const channels = await query .take(ps.limit) .getMany(); - return await Promise.all(channels.map(x => Channels.pack(x, me))); -}); + return await Promise.all(channels.map(x => Channels.pack(x, me))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/channels/show.ts b/packages/backend/src/server/api/endpoints/channels/show.ts index 87665a9865ee..d0dd33f023d6 100644 --- a/packages/backend/src/server/api/endpoints/channels/show.ts +++ b/packages/backend/src/server/api/endpoints/channels/show.ts @@ -1,6 +1,7 @@ -import define from '../../define.js'; -import { ApiError } from '../../error.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Channels } from '@/models/index.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['channels'], @@ -31,14 +32,22 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const channel = await Channels.findOneBy({ - id: ps.channelId, - }); - - if (channel == null) { - throw new ApiError(meta.errors.noSuchChannel); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { + const channel = await Channels.findOneBy({ + id: ps.channelId, + }); + + if (channel == null) { + throw new ApiError(meta.errors.noSuchChannel); + } + + return await Channels.pack(channel, me); + }); } - - return await Channels.pack(channel, me); -}); +} diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index deaa29901312..9f7f9c87ab26 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -1,8 +1,9 @@ -import define from '../../define.js'; -import { ApiError } from '../../error.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Notes, Channels } from '@/models/index.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { activeUsersChart } from '@/services/chart/index.js'; +import { ApiError } from '../../error.js'; +import { makePaginationQuery } from '../../common/make-pagination-query.js'; export const meta = { tags: ['notes', 'channels'], @@ -42,17 +43,23 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const channel = await Channels.findOneBy({ - id: ps.channelId, - }); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const channel = await Channels.findOneBy({ + id: ps.channelId, + }); - if (channel == null) { - throw new ApiError(meta.errors.noSuchChannel); - } + if (channel == null) { + throw new ApiError(meta.errors.noSuchChannel); + } - //#region Construct query - const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + //#region Construct query + const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.channelId = :channelId', { channelId: channel.id }) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') @@ -66,11 +73,13 @@ export default define(meta, paramDef, async (ps, user) => { .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') .leftJoinAndSelect('note.channel', 'channel'); - //#endregion + //#endregion - const timeline = await query.take(ps.limit).getMany(); + const timeline = await query.take(ps.limit).getMany(); - if (user) activeUsersChart.read(user); + if (user) activeUsersChart.read(user); - return await Notes.packMany(timeline, user); -}); + return await Notes.packMany(timeline, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/channels/unfollow.ts b/packages/backend/src/server/api/endpoints/channels/unfollow.ts index e065d897a56d..094bba578d09 100644 --- a/packages/backend/src/server/api/endpoints/channels/unfollow.ts +++ b/packages/backend/src/server/api/endpoints/channels/unfollow.ts @@ -1,7 +1,8 @@ -import define from '../../define.js'; -import { ApiError } from '../../error.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Channels, ChannelFollowings } from '@/models/index.js'; import { publishUserEvent } from '@/services/stream.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['channels'], @@ -28,19 +29,27 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const channel = await Channels.findOneBy({ - id: ps.channelId, - }); - - if (channel == null) { - throw new ApiError(meta.errors.noSuchChannel); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const channel = await Channels.findOneBy({ + id: ps.channelId, + }); + + if (channel == null) { + throw new ApiError(meta.errors.noSuchChannel); + } + + await ChannelFollowings.delete({ + followerId: user.id, + followeeId: channel.id, + }); + + publishUserEvent(user.id, 'unfollowChannel', channel); + }); } - - await ChannelFollowings.delete({ - followerId: user.id, - followeeId: channel.id, - }); - - publishUserEvent(user.id, 'unfollowChannel', channel); -}); +} diff --git a/packages/backend/src/server/api/endpoints/channels/update.ts b/packages/backend/src/server/api/endpoints/channels/update.ts index 13104f324ff2..d8d224eee4ee 100644 --- a/packages/backend/src/server/api/endpoints/channels/update.ts +++ b/packages/backend/src/server/api/endpoints/channels/update.ts @@ -1,6 +1,7 @@ -import define from '../../define.js'; -import { ApiError } from '../../error.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Channels, DriveFiles } from '@/models/index.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['channels'], @@ -48,39 +49,47 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const channel = await Channels.findOneBy({ - id: ps.channelId, - }); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { + const channel = await Channels.findOneBy({ + id: ps.channelId, + }); - if (channel == null) { - throw new ApiError(meta.errors.noSuchChannel); - } + if (channel == null) { + throw new ApiError(meta.errors.noSuchChannel); + } - if (channel.userId !== me.id) { - throw new ApiError(meta.errors.accessDenied); - } + if (channel.userId !== me.id) { + throw new ApiError(meta.errors.accessDenied); + } - // eslint:disable-next-line:no-unnecessary-initializer - let banner = undefined; - if (ps.bannerId != null) { - banner = await DriveFiles.findOneBy({ - id: ps.bannerId, - userId: me.id, - }); + // eslint:disable-next-line:no-unnecessary-initializer + let banner = undefined; + if (ps.bannerId != null) { + banner = await DriveFiles.findOneBy({ + id: ps.bannerId, + userId: me.id, + }); - if (banner == null) { - throw new ApiError(meta.errors.noSuchFile); - } - } else if (ps.bannerId === null) { - banner = null; - } + if (banner == null) { + throw new ApiError(meta.errors.noSuchFile); + } + } else if (ps.bannerId === null) { + banner = null; + } - await Channels.update(channel.id, { - ...(ps.name !== undefined ? { name: ps.name } : {}), - ...(ps.description !== undefined ? { description: ps.description } : {}), - ...(banner ? { bannerId: banner.id } : {}), - }); + await Channels.update(channel.id, { + ...(ps.name !== undefined ? { name: ps.name } : {}), + ...(ps.description !== undefined ? { description: ps.description } : {}), + ...(banner ? { bannerId: banner.id } : {}), + }); - return await Channels.pack(channel.id, me); -}); + return await Channels.pack(channel.id, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/charts/active-users.ts b/packages/backend/src/server/api/endpoints/charts/active-users.ts index ea2379429659..1a1e3e878c62 100644 --- a/packages/backend/src/server/api/endpoints/charts/active-users.ts +++ b/packages/backend/src/server/api/endpoints/charts/active-users.ts @@ -1,6 +1,7 @@ +import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { activeUsersChart } from '@/services/chart/index.js'; -import define from '../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['charts', 'users'], @@ -22,6 +23,14 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { - return await activeUsersChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + return await activeUsersChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/charts/ap-request.ts b/packages/backend/src/server/api/endpoints/charts/ap-request.ts index 06dee250ee9b..0a5dabea68e6 100644 --- a/packages/backend/src/server/api/endpoints/charts/ap-request.ts +++ b/packages/backend/src/server/api/endpoints/charts/ap-request.ts @@ -1,6 +1,7 @@ +import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { apRequestChart } from '@/services/chart/index.js'; -import define from '../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['charts'], @@ -22,6 +23,14 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { - return await apRequestChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + return await apRequestChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/charts/drive.ts b/packages/backend/src/server/api/endpoints/charts/drive.ts index dd2c2d6838f0..22e366d1dc89 100644 --- a/packages/backend/src/server/api/endpoints/charts/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/drive.ts @@ -1,6 +1,7 @@ +import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { driveChart } from '@/services/chart/index.js'; -import define from '../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['charts', 'drive'], @@ -22,6 +23,14 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { - return await driveChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + return await driveChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/charts/federation.ts b/packages/backend/src/server/api/endpoints/charts/federation.ts index 8c35b3c46d45..2cc4456d59e6 100644 --- a/packages/backend/src/server/api/endpoints/charts/federation.ts +++ b/packages/backend/src/server/api/endpoints/charts/federation.ts @@ -1,6 +1,7 @@ +import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { federationChart } from '@/services/chart/index.js'; -import define from '../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['charts'], @@ -22,6 +23,14 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { - return await federationChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + return await federationChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/charts/hashtag.ts b/packages/backend/src/server/api/endpoints/charts/hashtag.ts index 77e24a62c3f0..b147212714a9 100644 --- a/packages/backend/src/server/api/endpoints/charts/hashtag.ts +++ b/packages/backend/src/server/api/endpoints/charts/hashtag.ts @@ -1,6 +1,7 @@ +import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { hashtagChart } from '@/services/chart/index.js'; -import define from '../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['charts', 'hashtags'], @@ -23,6 +24,14 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { - return await hashtagChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.tag); -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + return await hashtagChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.tag); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/charts/instance.ts b/packages/backend/src/server/api/endpoints/charts/instance.ts index 817d51ad014f..91a83249b24c 100644 --- a/packages/backend/src/server/api/endpoints/charts/instance.ts +++ b/packages/backend/src/server/api/endpoints/charts/instance.ts @@ -1,6 +1,7 @@ +import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { instanceChart } from '@/services/chart/index.js'; -import define from '../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['charts'], @@ -23,6 +24,14 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { - return await instanceChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.host); -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + return await instanceChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.host); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/charts/notes.ts b/packages/backend/src/server/api/endpoints/charts/notes.ts index 951adf540883..4150bf61bb1d 100644 --- a/packages/backend/src/server/api/endpoints/charts/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/notes.ts @@ -1,6 +1,7 @@ +import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { notesChart } from '@/services/chart/index.js'; -import define from '../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['charts', 'notes'], @@ -22,6 +23,14 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { - return await notesChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + return await notesChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/charts/user/drive.ts b/packages/backend/src/server/api/endpoints/charts/user/drive.ts index f165b40224a6..a71602cd7352 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/drive.ts @@ -1,6 +1,7 @@ +import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { perUserDriveChart } from '@/services/chart/index.js'; -import define from '../../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['charts', 'drive', 'users'], @@ -23,6 +24,14 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { - return await perUserDriveChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId); -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + return await perUserDriveChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/charts/user/following.ts b/packages/backend/src/server/api/endpoints/charts/user/following.ts index f5d42e21c224..b18d4fc744ac 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/following.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/following.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { getJsonSchema } from '@/services/chart/core.js'; import { perUserFollowingChart } from '@/services/chart/index.js'; @@ -23,6 +24,14 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { - return await perUserFollowingChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId); -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + return await perUserFollowingChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/charts/user/notes.ts b/packages/backend/src/server/api/endpoints/charts/user/notes.ts index aefe550d4365..2875c31f0e8c 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/notes.ts @@ -1,6 +1,7 @@ +import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { perUserNotesChart } from '@/services/chart/index.js'; -import define from '../../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['charts', 'users', 'notes'], @@ -23,6 +24,14 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { - return await perUserNotesChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId); -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + return await perUserNotesChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts index 6bc6b56bf090..2fada9336b83 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts @@ -1,6 +1,7 @@ +import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { perUserReactionsChart } from '@/services/chart/index.js'; -import define from '../../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['charts', 'users', 'reactions'], @@ -23,6 +24,14 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { - return await perUserReactionsChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId); -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + return await perUserReactionsChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/charts/users.ts b/packages/backend/src/server/api/endpoints/charts/users.ts index 338e8fd338c8..2d8197eaa8c3 100644 --- a/packages/backend/src/server/api/endpoints/charts/users.ts +++ b/packages/backend/src/server/api/endpoints/charts/users.ts @@ -1,6 +1,7 @@ +import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { usersChart } from '@/services/chart/index.js'; -import define from '../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['charts', 'users'], @@ -22,6 +23,14 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { - return await usersChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + return await usersChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/clips/add-note.ts b/packages/backend/src/server/api/endpoints/clips/add-note.ts index 5d72f5c1bff3..1157f43c813b 100644 --- a/packages/backend/src/server/api/endpoints/clips/add-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/add-note.ts @@ -1,7 +1,8 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ClipNotes, Clips } from '@/models/index.js'; -import { ApiError } from '../../error.js'; import { genId } from '@/misc/gen-id.js'; +import { ApiError } from '../../error.js'; import { getNote } from '../../common/getters.js'; export const meta = { @@ -42,33 +43,41 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const clip = await Clips.findOneBy({ - id: ps.clipId, - userId: user.id, - }); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const clip = await Clips.findOneBy({ + id: ps.clipId, + userId: user.id, + }); - if (clip == null) { - throw new ApiError(meta.errors.noSuchClip); - } + if (clip == null) { + throw new ApiError(meta.errors.noSuchClip); + } - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); + const note = await getNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); - const exist = await ClipNotes.findOneBy({ - noteId: note.id, - clipId: clip.id, - }); + const exist = await ClipNotes.findOneBy({ + noteId: note.id, + clipId: clip.id, + }); - if (exist != null) { - throw new ApiError(meta.errors.alreadyClipped); - } + if (exist != null) { + throw new ApiError(meta.errors.alreadyClipped); + } - await ClipNotes.insert({ - id: genId(), - noteId: note.id, - clipId: clip.id, - }); -}); + await ClipNotes.insert({ + id: genId(), + noteId: note.id, + clipId: clip.id, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/clips/create.ts b/packages/backend/src/server/api/endpoints/clips/create.ts index 4afe4222a101..059bae8e646a 100644 --- a/packages/backend/src/server/api/endpoints/clips/create.ts +++ b/packages/backend/src/server/api/endpoints/clips/create.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { genId } from '@/misc/gen-id.js'; import { Clips } from '@/models/index.js'; @@ -27,15 +28,23 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const clip = await Clips.insert({ - id: genId(), - createdAt: new Date(), - userId: user.id, - name: ps.name, - isPublic: ps.isPublic, - description: ps.description, - }).then(x => Clips.findOneByOrFail(x.identifiers[0])); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const clip = await Clips.insert({ + id: genId(), + createdAt: new Date(), + userId: user.id, + name: ps.name, + isPublic: ps.isPublic, + description: ps.description, + }).then(x => Clips.findOneByOrFail(x.identifiers[0])); - return await Clips.pack(clip); -}); + return await Clips.pack(clip); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/clips/delete.ts b/packages/backend/src/server/api/endpoints/clips/delete.ts index b6c0eb702a91..1f469503a63f 100644 --- a/packages/backend/src/server/api/endpoints/clips/delete.ts +++ b/packages/backend/src/server/api/endpoints/clips/delete.ts @@ -1,6 +1,7 @@ -import define from '../../define.js'; -import { ApiError } from '../../error.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Clips } from '@/models/index.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['clips'], @@ -27,15 +28,23 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const clip = await Clips.findOneBy({ - id: ps.clipId, - userId: user.id, - }); - - if (clip == null) { - throw new ApiError(meta.errors.noSuchClip); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const clip = await Clips.findOneBy({ + id: ps.clipId, + userId: user.id, + }); + + if (clip == null) { + throw new ApiError(meta.errors.noSuchClip); + } + + await Clips.delete(clip.id); + }); } - - await Clips.delete(clip.id); -}); +} diff --git a/packages/backend/src/server/api/endpoints/clips/list.ts b/packages/backend/src/server/api/endpoints/clips/list.ts index 378811eba050..c26c598d2122 100644 --- a/packages/backend/src/server/api/endpoints/clips/list.ts +++ b/packages/backend/src/server/api/endpoints/clips/list.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Clips } from '@/models/index.js'; export const meta = { @@ -26,10 +27,18 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const clips = await Clips.findBy({ - userId: me.id, - }); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { + const clips = await Clips.findBy({ + userId: me.id, + }); - return await Promise.all(clips.map(x => Clips.pack(x))); -}); + return await Promise.all(clips.map(x => Clips.pack(x))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts index 4ace747efe54..3e6722c68c29 100644 --- a/packages/backend/src/server/api/endpoints/clips/notes.ts +++ b/packages/backend/src/server/api/endpoints/clips/notes.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ClipNotes, Clips, Notes } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; @@ -44,20 +45,26 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const clip = await Clips.findOneBy({ - id: ps.clipId, - }); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const clip = await Clips.findOneBy({ + id: ps.clipId, + }); - if (clip == null) { - throw new ApiError(meta.errors.noSuchClip); - } + if (clip == null) { + throw new ApiError(meta.errors.noSuchClip); + } - if (!clip.isPublic && (user == null || (clip.userId !== user.id))) { - throw new ApiError(meta.errors.noSuchClip); - } + if (!clip.isPublic && (user == null || (clip.userId !== user.id))) { + throw new ApiError(meta.errors.noSuchClip); + } - const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .innerJoin(ClipNotes.metadata.targetName, 'clipNote', 'clipNote.noteId = note.id') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') @@ -72,15 +79,17 @@ export default define(meta, paramDef, async (ps, user) => { .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') .andWhere('clipNote.clipId = :clipId', { clipId: clip.id }); - if (user) { - generateVisibilityQuery(query, user); - generateMutedUserQuery(query, user); - generateBlockedUserQuery(query, user); - } + if (user) { + generateVisibilityQuery(query, user); + generateMutedUserQuery(query, user); + generateBlockedUserQuery(query, user); + } - const notes = await query + const notes = await query .take(ps.limit) .getMany(); - return await Notes.packMany(notes, user); -}); + return await Notes.packMany(notes, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/clips/remove-note.ts b/packages/backend/src/server/api/endpoints/clips/remove-note.ts index 8b90e31f653a..710fbd4dfe8e 100644 --- a/packages/backend/src/server/api/endpoints/clips/remove-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/remove-note.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ClipNotes, Clips } from '@/models/index.js'; import { ApiError } from '../../error.js'; import { getNote } from '../../common/getters.js'; @@ -35,23 +36,31 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const clip = await Clips.findOneBy({ - id: ps.clipId, - userId: user.id, - }); - - if (clip == null) { - throw new ApiError(meta.errors.noSuchClip); - } +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const clip = await Clips.findOneBy({ + id: ps.clipId, + userId: user.id, + }); + + if (clip == null) { + throw new ApiError(meta.errors.noSuchClip); + } - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); + const note = await getNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); - await ClipNotes.delete({ - noteId: note.id, - clipId: clip.id, - }); -}); + await ClipNotes.delete({ + noteId: note.id, + clipId: clip.id, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/clips/show.ts b/packages/backend/src/server/api/endpoints/clips/show.ts index c3d73c168d28..64e59514fb19 100644 --- a/packages/backend/src/server/api/endpoints/clips/show.ts +++ b/packages/backend/src/server/api/endpoints/clips/show.ts @@ -1,6 +1,7 @@ -import define from '../../define.js'; -import { ApiError } from '../../error.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Clips } from '@/models/index.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['clips', 'account'], @@ -33,19 +34,27 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - // Fetch the clip - const clip = await Clips.findOneBy({ - id: ps.clipId, - }); - - if (clip == null) { - throw new ApiError(meta.errors.noSuchClip); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { + // Fetch the clip + const clip = await Clips.findOneBy({ + id: ps.clipId, + }); + + if (clip == null) { + throw new ApiError(meta.errors.noSuchClip); + } + + if (!clip.isPublic && (me == null || (clip.userId !== me.id))) { + throw new ApiError(meta.errors.noSuchClip); + } + + return await Clips.pack(clip); + }); } - - if (!clip.isPublic && (me == null || (clip.userId !== me.id))) { - throw new ApiError(meta.errors.noSuchClip); - } - - return await Clips.pack(clip); -}); +} diff --git a/packages/backend/src/server/api/endpoints/clips/update.ts b/packages/backend/src/server/api/endpoints/clips/update.ts index b67d844f6e74..cec36e48bcba 100644 --- a/packages/backend/src/server/api/endpoints/clips/update.ts +++ b/packages/backend/src/server/api/endpoints/clips/update.ts @@ -1,6 +1,7 @@ -import define from '../../define.js'; -import { ApiError } from '../../error.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Clips } from '@/models/index.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['clips'], @@ -36,22 +37,30 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - // Fetch the clip - const clip = await Clips.findOneBy({ - id: ps.clipId, - userId: user.id, - }); - - if (clip == null) { - throw new ApiError(meta.errors.noSuchClip); - } +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + // Fetch the clip + const clip = await Clips.findOneBy({ + id: ps.clipId, + userId: user.id, + }); - await Clips.update(clip.id, { - name: ps.name, - description: ps.description, - isPublic: ps.isPublic, - }); + if (clip == null) { + throw new ApiError(meta.errors.noSuchClip); + } - return await Clips.pack(clip.id); -}); + await Clips.update(clip.id, { + name: ps.name, + description: ps.description, + isPublic: ps.isPublic, + }); + + return await Clips.pack(clip.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/drive.ts b/packages/backend/src/server/api/endpoints/drive.ts index 82497adefa4b..a52b3a04ccf4 100644 --- a/packages/backend/src/server/api/endpoints/drive.ts +++ b/packages/backend/src/server/api/endpoints/drive.ts @@ -1,6 +1,7 @@ import { fetchMeta } from '@/misc/fetch-meta.js'; import { DriveFiles } from '@/models/index.js'; -import define from '../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['drive', 'account'], @@ -32,7 +33,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const instance = await fetchMeta(true); // Calculate drive usage diff --git a/packages/backend/src/server/api/endpoints/drive/files.ts b/packages/backend/src/server/api/endpoints/drive/files.ts index 40e6c16c9ca3..1ef358fb9243 100644 --- a/packages/backend/src/server/api/endpoints/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/drive/files.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFiles } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -33,25 +34,33 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId) +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const query = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId) .andWhere('file.userId = :userId', { userId: user.id }); - if (ps.folderId) { - query.andWhere('file.folderId = :folderId', { folderId: ps.folderId }); - } else { - query.andWhere('file.folderId IS NULL'); - } + if (ps.folderId) { + query.andWhere('file.folderId = :folderId', { folderId: ps.folderId }); + } else { + query.andWhere('file.folderId IS NULL'); + } - if (ps.type) { - if (ps.type.endsWith('/*')) { - query.andWhere('file.type like :type', { type: ps.type.replace('/*', '/') + '%' }); - } else { - query.andWhere('file.type = :type', { type: ps.type }); - } - } + if (ps.type) { + if (ps.type.endsWith('/*')) { + query.andWhere('file.type like :type', { type: ps.type.replace('/*', '/') + '%' }); + } else { + query.andWhere('file.type = :type', { type: ps.type }); + } + } - const files = await query.take(ps.limit).getMany(); + const files = await query.take(ps.limit).getMany(); - return await DriveFiles.packMany(files, { detail: false, self: true }); -}); + return await DriveFiles.packMany(files, { detail: false, self: true }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts index 415a8cc69366..d0ef15b84ab4 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts @@ -1,6 +1,7 @@ -import define from '../../../define.js'; -import { ApiError } from '../../../error.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFiles, Notes } from '@/models/index.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['drive', 'notes'], @@ -39,22 +40,30 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - // Fetch file - const file = await DriveFiles.findOneBy({ - id: ps.fileId, - userId: user.id, - }); - - if (file == null) { - throw new ApiError(meta.errors.noSuchFile); - } +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + // Fetch file + const file = await DriveFiles.findOneBy({ + id: ps.fileId, + userId: user.id, + }); - const notes = await Notes.createQueryBuilder('note') + if (file == null) { + throw new ApiError(meta.errors.noSuchFile); + } + + const notes = await Notes.createQueryBuilder('note') .where(':file = ANY(note.fileIds)', { file: file.id }) .getMany(); - return await Notes.packMany(notes, user, { - detail: true, - }); -}); + return await Notes.packMany(notes, user, { + detail: true, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts index bbae9bf4e4dc..60dd0336be25 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFiles } from '@/models/index.js'; export const meta = { @@ -25,11 +26,19 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const file = await DriveFiles.findOneBy({ - md5: ps.md5, - userId: user.id, - }); - - return file != null; -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const file = await DriveFiles.findOneBy({ + md5: ps.md5, + userId: user.id, + }); + + return file != null; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/drive/files/create.ts b/packages/backend/src/server/api/endpoints/drive/files/create.ts index ddcbd62889a9..ccee5236e339 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/create.ts @@ -4,7 +4,8 @@ import { DriveFiles } from '@/models/index.js'; import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { apiLogger } from '../../../logger.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/drive/files/delete.ts b/packages/backend/src/server/api/endpoints/drive/files/delete.ts index 6108ae7da9f8..085168f9a3c0 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/delete.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/delete.ts @@ -1,8 +1,9 @@ +import { Inject, Injectable } from '@nestjs/common'; import { deleteFile } from '@/services/drive/delete-file.js'; import { publishDriveStream } from '@/services/stream.js'; -import define from '../../../define.js'; -import { ApiError } from '../../../error.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFiles, Users } from '@/models/index.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['drive'], @@ -37,20 +38,28 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const file = await DriveFiles.findOneBy({ id: ps.fileId }); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const file = await DriveFiles.findOneBy({ id: ps.fileId }); - if (file == null) { - throw new ApiError(meta.errors.noSuchFile); - } + if (file == null) { + throw new ApiError(meta.errors.noSuchFile); + } - if ((!user.isAdmin && !user.isModerator) && (file.userId !== user.id)) { - throw new ApiError(meta.errors.accessDenied); - } + if ((!user.isAdmin && !user.isModerator) && (file.userId !== user.id)) { + throw new ApiError(meta.errors.accessDenied); + } - // Delete - await deleteFile(file); + // Delete + await deleteFile(file); - // Publish fileDeleted event - publishDriveStream(user.id, 'fileDeleted', file.id); -}); + // Publish fileDeleted event + publishDriveStream(user.id, 'fileDeleted', file.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts index f2bc7348c615..ffae36293d9f 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts @@ -1,5 +1,6 @@ +import { Inject, Injectable } from '@nestjs/common'; import { DriveFiles } from '@/models/index.js'; -import define from '../../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['drive'], @@ -30,11 +31,19 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const files = await DriveFiles.findBy({ - md5: ps.md5, - userId: user.id, - }); - - return await DriveFiles.packMany(files, { self: true }); -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const files = await DriveFiles.findBy({ + md5: ps.md5, + userId: user.id, + }); + + return await DriveFiles.packMany(files, { self: true }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/drive/files/find.ts b/packages/backend/src/server/api/endpoints/drive/files/find.ts index 245fb45a6552..ddb87a15228e 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/find.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/find.ts @@ -1,6 +1,7 @@ -import define from '../../../define.js'; -import { DriveFiles } from '@/models/index.js'; +import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DriveFiles } from '@/models/index.js'; export const meta = { requireCredential: true, @@ -32,12 +33,20 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const files = await DriveFiles.findBy({ - name: ps.name, - userId: user.id, - folderId: ps.folderId ?? IsNull(), - }); - - return await Promise.all(files.map(file => DriveFiles.pack(file, { self: true }))); -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const files = await DriveFiles.findBy({ + name: ps.name, + userId: user.id, + folderId: ps.folderId ?? IsNull(), + }); + + return await Promise.all(files.map(file => DriveFiles.pack(file, { self: true }))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/drive/files/show.ts b/packages/backend/src/server/api/endpoints/drive/files/show.ts index 2c604c54c857..f2c39cc4e1cb 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/show.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/show.ts @@ -1,6 +1,7 @@ -import { DriveFile } from '@/models/entities/drive-file.js'; +import { Inject, Injectable } from '@nestjs/common'; +import type { DriveFile } from '@/models/entities/drive-file.js'; import { DriveFiles, Users } from '@/models/index.js'; -import define from '../../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -52,34 +53,42 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - let file: DriveFile | null = null; +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + let file: DriveFile | null = null; - if (ps.fileId) { - file = await DriveFiles.findOneBy({ id: ps.fileId }); - } else if (ps.url) { - file = await DriveFiles.findOne({ - where: [{ - url: ps.url, - }, { - webpublicUrl: ps.url, - }, { - thumbnailUrl: ps.url, - }], - }); - } + if (ps.fileId) { + file = await DriveFiles.findOneBy({ id: ps.fileId }); + } else if (ps.url) { + file = await DriveFiles.findOne({ + where: [{ + url: ps.url, + }, { + webpublicUrl: ps.url, + }, { + thumbnailUrl: ps.url, + }], + }); + } - if (file == null) { - throw new ApiError(meta.errors.noSuchFile); - } + if (file == null) { + throw new ApiError(meta.errors.noSuchFile); + } - if ((!user.isAdmin && !user.isModerator) && (file.userId !== user.id)) { - throw new ApiError(meta.errors.accessDenied); - } + if ((!user.isAdmin && !user.isModerator) && (file.userId !== user.id)) { + throw new ApiError(meta.errors.accessDenied); + } - return await DriveFiles.pack(file, { - detail: true, - withUser: true, - self: true, - }); -}); + return await DriveFiles.pack(file, { + detail: true, + withUser: true, + self: true, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/drive/files/update.ts b/packages/backend/src/server/api/endpoints/drive/files/update.ts index fa2ec8519c5a..57950ceffe65 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts @@ -1,7 +1,8 @@ +import { Inject, Injectable } from '@nestjs/common'; import { publishDriveStream } from '@/services/stream.js'; import { DriveFiles, DriveFolders, Users } from '@/models/index.js'; import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js'; -import define from '../../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -59,54 +60,62 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const file = await DriveFiles.findOneBy({ id: ps.fileId }); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const file = await DriveFiles.findOneBy({ id: ps.fileId }); + + if (file == null) { + throw new ApiError(meta.errors.noSuchFile); + } - if (file == null) { - throw new ApiError(meta.errors.noSuchFile); - } + if ((!user.isAdmin && !user.isModerator) && (file.userId !== user.id)) { + throw new ApiError(meta.errors.accessDenied); + } - if ((!user.isAdmin && !user.isModerator) && (file.userId !== user.id)) { - throw new ApiError(meta.errors.accessDenied); - } + if (ps.name) file.name = ps.name; + if (!DriveFiles.validateFileName(file.name)) { + throw new ApiError(meta.errors.invalidFileName); + } - if (ps.name) file.name = ps.name; - if (!DriveFiles.validateFileName(file.name)) { - throw new ApiError(meta.errors.invalidFileName); - } + if (ps.comment !== undefined) file.comment = ps.comment; - if (ps.comment !== undefined) file.comment = ps.comment; + if (ps.isSensitive !== undefined) file.isSensitive = ps.isSensitive; - if (ps.isSensitive !== undefined) file.isSensitive = ps.isSensitive; + if (ps.folderId !== undefined) { + if (ps.folderId === null) { + file.folderId = null; + } else { + const folder = await DriveFolders.findOneBy({ + id: ps.folderId, + userId: user.id, + }); - if (ps.folderId !== undefined) { - if (ps.folderId === null) { - file.folderId = null; - } else { - const folder = await DriveFolders.findOneBy({ - id: ps.folderId, - userId: user.id, - }); + if (folder == null) { + throw new ApiError(meta.errors.noSuchFolder); + } - if (folder == null) { - throw new ApiError(meta.errors.noSuchFolder); + file.folderId = folder.id; + } } - file.folderId = folder.id; - } - } - - await DriveFiles.update(file.id, { - name: file.name, - comment: file.comment, - folderId: file.folderId, - isSensitive: file.isSensitive, - }); + await DriveFiles.update(file.id, { + name: file.name, + comment: file.comment, + folderId: file.folderId, + isSensitive: file.isSensitive, + }); - const fileObj = await DriveFiles.pack(file, { self: true }); + const fileObj = await DriveFiles.pack(file, { self: true }); - // Publish fileUpdated event - publishDriveStream(user.id, 'fileUpdated', fileObj); + // Publish fileUpdated event + publishDriveStream(user.id, 'fileUpdated', fileObj); - return fileObj; -}); + return fileObj; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts b/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts index eb8071c3c983..652f730b336d 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts @@ -3,7 +3,8 @@ import { uploadFromUrl } from '@/services/drive/upload-from-url.js'; import { DriveFiles } from '@/models/index.js'; import { publishMainStream } from '@/services/stream.js'; import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['drive'], diff --git a/packages/backend/src/server/api/endpoints/drive/folders.ts b/packages/backend/src/server/api/endpoints/drive/folders.ts index d4d530ba9e9d..07fbb3e65293 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFolders } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -32,17 +33,25 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(DriveFolders.createQueryBuilder('folder'), ps.sinceId, ps.untilId) +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const query = makePaginationQuery(DriveFolders.createQueryBuilder('folder'), ps.sinceId, ps.untilId) .andWhere('folder.userId = :userId', { userId: user.id }); - if (ps.folderId) { - query.andWhere('folder.parentId = :parentId', { parentId: ps.folderId }); - } else { - query.andWhere('folder.parentId IS NULL'); - } + if (ps.folderId) { + query.andWhere('folder.parentId = :parentId', { parentId: ps.folderId }); + } else { + query.andWhere('folder.parentId IS NULL'); + } - const folders = await query.take(ps.limit).getMany(); + const folders = await query.take(ps.limit).getMany(); - return await Promise.all(folders.map(folder => DriveFolders.pack(folder))); -}); + return await Promise.all(folders.map(folder => DriveFolders.pack(folder))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/drive/folders/create.ts b/packages/backend/src/server/api/endpoints/drive/folders/create.ts index 3d7f514c85e9..b193555f2393 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/create.ts @@ -1,8 +1,9 @@ +import { Inject, Injectable } from '@nestjs/common'; import { publishDriveStream } from '@/services/stream.js'; -import define from '../../../define.js'; -import { ApiError } from '../../../error.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFolders } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['drive'], @@ -29,41 +30,49 @@ export const meta = { export const paramDef = { type: 'object', properties: { - name: { type: 'string', default: "Untitled", maxLength: 200 }, + name: { type: 'string', default: 'Untitled', maxLength: 200 }, parentId: { type: 'string', format: 'misskey:id', nullable: true }, }, required: [], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - // If the parent folder is specified - let parent = null; - if (ps.parentId) { - // Fetch parent folder - parent = await DriveFolders.findOneBy({ - id: ps.parentId, - userId: user.id, - }); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + // If the parent folder is specified + let parent = null; + if (ps.parentId) { + // Fetch parent folder + parent = await DriveFolders.findOneBy({ + id: ps.parentId, + userId: user.id, + }); - if (parent == null) { - throw new ApiError(meta.errors.noSuchFolder); - } - } + if (parent == null) { + throw new ApiError(meta.errors.noSuchFolder); + } + } - // Create folder - const folder = await DriveFolders.insert({ - id: genId(), - createdAt: new Date(), - name: ps.name, - parentId: parent !== null ? parent.id : null, - userId: user.id, - }).then(x => DriveFolders.findOneByOrFail(x.identifiers[0])); + // Create folder + const folder = await DriveFolders.insert({ + id: genId(), + createdAt: new Date(), + name: ps.name, + parentId: parent !== null ? parent.id : null, + userId: user.id, + }).then(x => DriveFolders.findOneByOrFail(x.identifiers[0])); - const folderObj = await DriveFolders.pack(folder); + const folderObj = await DriveFolders.pack(folder); - // Publish folderCreated event - publishDriveStream(user.id, 'folderCreated', folderObj); + // Publish folderCreated event + publishDriveStream(user.id, 'folderCreated', folderObj); - return folderObj; -}); + return folderObj; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/drive/folders/delete.ts b/packages/backend/src/server/api/endpoints/drive/folders/delete.ts index ab9d411ec0fd..8ea7a378420e 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/delete.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/delete.ts @@ -1,7 +1,8 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { publishDriveStream } from '@/services/stream.js'; -import { ApiError } from '../../../error.js'; import { DriveFolders, DriveFiles } from '@/models/index.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['drive'], @@ -34,28 +35,36 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - // Get folder - const folder = await DriveFolders.findOneBy({ - id: ps.folderId, - userId: user.id, - }); - - if (folder == null) { - throw new ApiError(meta.errors.noSuchFolder); - } +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + // Get folder + const folder = await DriveFolders.findOneBy({ + id: ps.folderId, + userId: user.id, + }); - const [childFoldersCount, childFilesCount] = await Promise.all([ - DriveFolders.countBy({ parentId: folder.id }), - DriveFiles.countBy({ folderId: folder.id }), - ]); + if (folder == null) { + throw new ApiError(meta.errors.noSuchFolder); + } - if (childFoldersCount !== 0 || childFilesCount !== 0) { - throw new ApiError(meta.errors.hasChildFilesOrFolders); - } + const [childFoldersCount, childFilesCount] = await Promise.all([ + DriveFolders.countBy({ parentId: folder.id }), + DriveFiles.countBy({ folderId: folder.id }), + ]); + + if (childFoldersCount !== 0 || childFilesCount !== 0) { + throw new ApiError(meta.errors.hasChildFilesOrFolders); + } - await DriveFolders.delete(folder.id); + await DriveFolders.delete(folder.id); - // Publish folderCreated event - publishDriveStream(user.id, 'folderDeleted', folder.id); -}); + // Publish folderCreated event + publishDriveStream(user.id, 'folderDeleted', folder.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/drive/folders/find.ts b/packages/backend/src/server/api/endpoints/drive/folders/find.ts index 1feab273a17c..3e3462be4e5f 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/find.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/find.ts @@ -1,6 +1,7 @@ -import define from '../../../define.js'; -import { DriveFolders } from '@/models/index.js'; +import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DriveFolders } from '@/models/index.js'; export const meta = { tags: ['drive'], @@ -30,12 +31,20 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const folders = await DriveFolders.findBy({ - name: ps.name, - userId: user.id, - parentId: ps.parentId ?? IsNull(), - }); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const folders = await DriveFolders.findBy({ + name: ps.name, + userId: user.id, + parentId: ps.parentId ?? IsNull(), + }); - return await Promise.all(folders.map(folder => DriveFolders.pack(folder))); -}); + return await Promise.all(folders.map(folder => DriveFolders.pack(folder))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/drive/folders/show.ts b/packages/backend/src/server/api/endpoints/drive/folders/show.ts index 1e7aa2b16c9c..0d3e67cba2ae 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/show.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/show.ts @@ -1,6 +1,7 @@ -import define from '../../../define.js'; -import { ApiError } from '../../../error.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFolders } from '@/models/index.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['drive'], @@ -33,18 +34,26 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - // Get folder - const folder = await DriveFolders.findOneBy({ - id: ps.folderId, - userId: user.id, - }); - - if (folder == null) { - throw new ApiError(meta.errors.noSuchFolder); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + // Get folder + const folder = await DriveFolders.findOneBy({ + id: ps.folderId, + userId: user.id, + }); + + if (folder == null) { + throw new ApiError(meta.errors.noSuchFolder); + } + + return await DriveFolders.pack(folder, { + detail: true, + }); + }); } - - return await DriveFolders.pack(folder, { - detail: true, - }); -}); +} diff --git a/packages/backend/src/server/api/endpoints/drive/folders/update.ts b/packages/backend/src/server/api/endpoints/drive/folders/update.ts index 1aa2e8429220..4e3bd2e69211 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/update.ts @@ -1,7 +1,8 @@ +import { Inject, Injectable } from '@nestjs/common'; import { publishDriveStream } from '@/services/stream.js'; -import define from '../../../define.js'; -import { ApiError } from '../../../error.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFolders } from '@/models/index.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['drive'], @@ -48,71 +49,79 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - // Fetch folder - const folder = await DriveFolders.findOneBy({ - id: ps.folderId, - userId: user.id, - }); - - if (folder == null) { - throw new ApiError(meta.errors.noSuchFolder); - } - - if (ps.name) folder.name = ps.name; - - if (ps.parentId !== undefined) { - if (ps.parentId === folder.id) { - throw new ApiError(meta.errors.recursiveNesting); - } else if (ps.parentId === null) { - folder.parentId = null; - } else { - // Get parent folder - const parent = await DriveFolders.findOneBy({ - id: ps.parentId, +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + // Fetch folder + const folder = await DriveFolders.findOneBy({ + id: ps.folderId, userId: user.id, }); - if (parent == null) { - throw new ApiError(meta.errors.noSuchParentFolder); + if (folder == null) { + throw new ApiError(meta.errors.noSuchFolder); } - // Check if the circular reference will occur - async function checkCircle(folderId: string): Promise { - // Fetch folder - const folder2 = await DriveFolders.findOneBy({ - id: folderId, - }); - - if (folder2!.id === folder!.id) { - return true; - } else if (folder2!.parentId) { - return await checkCircle(folder2!.parentId); - } else { - return false; - } - } + if (ps.name) folder.name = ps.name; - if (parent.parentId !== null) { - if (await checkCircle(parent.parentId)) { + if (ps.parentId !== undefined) { + if (ps.parentId === folder.id) { throw new ApiError(meta.errors.recursiveNesting); + } else if (ps.parentId === null) { + folder.parentId = null; + } else { + // Get parent folder + const parent = await DriveFolders.findOneBy({ + id: ps.parentId, + userId: user.id, + }); + + if (parent == null) { + throw new ApiError(meta.errors.noSuchParentFolder); + } + + // Check if the circular reference will occur + async function checkCircle(folderId: string): Promise { + // Fetch folder + const folder2 = await DriveFolders.findOneBy({ + id: folderId, + }); + + if (folder2!.id === folder!.id) { + return true; + } else if (folder2!.parentId) { + return await checkCircle(folder2!.parentId); + } else { + return false; + } + } + + if (parent.parentId !== null) { + if (await checkCircle(parent.parentId)) { + throw new ApiError(meta.errors.recursiveNesting); + } + } + + folder.parentId = parent.id; } } - folder.parentId = parent.id; - } - } - - // Update - DriveFolders.update(folder.id, { - name: folder.name, - parentId: folder.parentId, - }); + // Update + DriveFolders.update(folder.id, { + name: folder.name, + parentId: folder.parentId, + }); - const folderObj = await DriveFolders.pack(folder); + const folderObj = await DriveFolders.pack(folder); - // Publish folderUpdated event - publishDriveStream(user.id, 'folderUpdated', folderObj); + // Publish folderUpdated event + publishDriveStream(user.id, 'folderUpdated', folderObj); - return folderObj; -}); + return folderObj; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/drive/stream.ts b/packages/backend/src/server/api/endpoints/drive/stream.ts index 99e8d024fb5a..35314bdc5293 100644 --- a/packages/backend/src/server/api/endpoints/drive/stream.ts +++ b/packages/backend/src/server/api/endpoints/drive/stream.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFiles } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -32,19 +33,27 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId) +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const query = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId) .andWhere('file.userId = :userId', { userId: user.id }); - if (ps.type) { - if (ps.type.endsWith('/*')) { - query.andWhere('file.type like :type', { type: ps.type.replace('/*', '/') + '%' }); - } else { - query.andWhere('file.type = :type', { type: ps.type }); - } - } + if (ps.type) { + if (ps.type.endsWith('/*')) { + query.andWhere('file.type like :type', { type: ps.type.replace('/*', '/') + '%' }); + } else { + query.andWhere('file.type = :type', { type: ps.type }); + } + } - const files = await query.take(ps.limit).getMany(); + const files = await query.take(ps.limit).getMany(); - return await DriveFiles.packMany(files, { detail: false, self: true }); -}); + return await DriveFiles.packMany(files, { detail: false, self: true }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/email-address/available.ts b/packages/backend/src/server/api/endpoints/email-address/available.ts index 07064ce9fa61..f10dc4abeef5 100644 --- a/packages/backend/src/server/api/endpoints/email-address/available.ts +++ b/packages/backend/src/server/api/endpoints/email-address/available.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { validateEmailForAccount } from '@/services/validate-email-for-account.js'; export const meta = { @@ -31,6 +32,14 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { - return await validateEmailForAccount(ps.emailAddress); -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + return await validateEmailForAccount(ps.emailAddress); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/endpoint.ts b/packages/backend/src/server/api/endpoints/endpoint.ts index c174126779c8..cc5115429aa5 100644 --- a/packages/backend/src/server/api/endpoints/endpoint.ts +++ b/packages/backend/src/server/api/endpoints/endpoint.ts @@ -1,4 +1,5 @@ -import define from '../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import endpoints from '../endpoints.js'; export const meta = { @@ -16,7 +17,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const ep = endpoints.find(x => x.name === ps.endpoint); if (ep == null) return null; return { diff --git a/packages/backend/src/server/api/endpoints/endpoints.ts b/packages/backend/src/server/api/endpoints/endpoints.ts index b20da96eb30d..b6ebf6bf769d 100644 --- a/packages/backend/src/server/api/endpoints/endpoints.ts +++ b/packages/backend/src/server/api/endpoints/endpoints.ts @@ -1,4 +1,5 @@ -import define from '../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import endpoints from '../endpoints.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/export-custom-emojis.ts b/packages/backend/src/server/api/endpoints/export-custom-emojis.ts index 5fe622932d88..6cdcc4628af1 100644 --- a/packages/backend/src/server/api/endpoints/export-custom-emojis.ts +++ b/packages/backend/src/server/api/endpoints/export-custom-emojis.ts @@ -1,6 +1,7 @@ import ms from 'ms'; import { createExportCustomEmojisJob } from '@/queue/index.js'; -import define from '../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { secure: true, @@ -18,6 +19,12 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { createExportCustomEmojisJob(user); }); diff --git a/packages/backend/src/server/api/endpoints/federation/followers.ts b/packages/backend/src/server/api/endpoints/federation/followers.ts index 7b1197d1e5fd..5d57d7602c6b 100644 --- a/packages/backend/src/server/api/endpoints/federation/followers.ts +++ b/packages/backend/src/server/api/endpoints/federation/followers.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Followings } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -30,13 +31,21 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) - .andWhere(`following.followeeHost = :host`, { host: ps.host }); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { + const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) + .andWhere('following.followeeHost = :host', { host: ps.host }); - const followings = await query + const followings = await query .take(ps.limit) .getMany(); - return await Followings.packMany(followings, me, { populateFollowee: true }); -}); + return await Followings.packMany(followings, me, { populateFollowee: true }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/federation/following.ts b/packages/backend/src/server/api/endpoints/federation/following.ts index ed1f142d885a..84499fdaf976 100644 --- a/packages/backend/src/server/api/endpoints/federation/following.ts +++ b/packages/backend/src/server/api/endpoints/federation/following.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Followings } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -30,13 +31,21 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) - .andWhere(`following.followerHost = :host`, { host: ps.host }); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { + const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) + .andWhere('following.followerHost = :host', { host: ps.host }); - const followings = await query + const followings = await query .take(ps.limit) .getMany(); - return await Followings.packMany(followings, me, { populateFollowee: true }); -}); + return await Followings.packMany(followings, me, { populateFollowee: true }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts index 07e5c07c6a64..6d7b29b4e688 100644 --- a/packages/backend/src/server/api/endpoints/federation/instances.ts +++ b/packages/backend/src/server/api/endpoints/federation/instances.ts @@ -1,5 +1,6 @@ +import { Inject, Injectable } from '@nestjs/common'; import config from '@/config/index.js'; -import define from '../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Instances } from '@/models/index.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; @@ -37,82 +38,90 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const query = Instances.createQueryBuilder('instance'); - - switch (ps.sort) { - case '+pubSub': query.orderBy('instance.followingCount', 'DESC').orderBy('instance.followersCount', 'DESC'); break; - case '-pubSub': query.orderBy('instance.followingCount', 'ASC').orderBy('instance.followersCount', 'ASC'); break; - case '+notes': query.orderBy('instance.notesCount', 'DESC'); break; - case '-notes': query.orderBy('instance.notesCount', 'ASC'); break; - case '+users': query.orderBy('instance.usersCount', 'DESC'); break; - case '-users': query.orderBy('instance.usersCount', 'ASC'); break; - case '+following': query.orderBy('instance.followingCount', 'DESC'); break; - case '-following': query.orderBy('instance.followingCount', 'ASC'); break; - case '+followers': query.orderBy('instance.followersCount', 'DESC'); break; - case '-followers': query.orderBy('instance.followersCount', 'ASC'); break; - case '+caughtAt': query.orderBy('instance.caughtAt', 'DESC'); break; - case '-caughtAt': query.orderBy('instance.caughtAt', 'ASC'); break; - case '+lastCommunicatedAt': query.orderBy('instance.lastCommunicatedAt', 'DESC'); break; - case '-lastCommunicatedAt': query.orderBy('instance.lastCommunicatedAt', 'ASC'); break; - - default: query.orderBy('instance.id', 'DESC'); break; +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { + const query = Instances.createQueryBuilder('instance'); + + switch (ps.sort) { + case '+pubSub': query.orderBy('instance.followingCount', 'DESC').orderBy('instance.followersCount', 'DESC'); break; + case '-pubSub': query.orderBy('instance.followingCount', 'ASC').orderBy('instance.followersCount', 'ASC'); break; + case '+notes': query.orderBy('instance.notesCount', 'DESC'); break; + case '-notes': query.orderBy('instance.notesCount', 'ASC'); break; + case '+users': query.orderBy('instance.usersCount', 'DESC'); break; + case '-users': query.orderBy('instance.usersCount', 'ASC'); break; + case '+following': query.orderBy('instance.followingCount', 'DESC'); break; + case '-following': query.orderBy('instance.followingCount', 'ASC'); break; + case '+followers': query.orderBy('instance.followersCount', 'DESC'); break; + case '-followers': query.orderBy('instance.followersCount', 'ASC'); break; + case '+caughtAt': query.orderBy('instance.caughtAt', 'DESC'); break; + case '-caughtAt': query.orderBy('instance.caughtAt', 'ASC'); break; + case '+lastCommunicatedAt': query.orderBy('instance.lastCommunicatedAt', 'DESC'); break; + case '-lastCommunicatedAt': query.orderBy('instance.lastCommunicatedAt', 'ASC'); break; + + default: query.orderBy('instance.id', 'DESC'); break; + } + + if (typeof ps.blocked === 'boolean') { + const meta = await fetchMeta(true); + if (ps.blocked) { + query.andWhere('instance.host IN (:...blocks)', { blocks: meta.blockedHosts }); + } else { + query.andWhere('instance.host NOT IN (:...blocks)', { blocks: meta.blockedHosts }); + } + } + + if (typeof ps.notResponding === 'boolean') { + if (ps.notResponding) { + query.andWhere('instance.isNotResponding = TRUE'); + } else { + query.andWhere('instance.isNotResponding = FALSE'); + } + } + + if (typeof ps.suspended === 'boolean') { + if (ps.suspended) { + query.andWhere('instance.isSuspended = TRUE'); + } else { + query.andWhere('instance.isSuspended = FALSE'); + } + } + + if (typeof ps.federating === 'boolean') { + if (ps.federating) { + query.andWhere('((instance.followingCount > 0) OR (instance.followersCount > 0))'); + } else { + query.andWhere('((instance.followingCount = 0) AND (instance.followersCount = 0))'); + } + } + + if (typeof ps.subscribing === 'boolean') { + if (ps.subscribing) { + query.andWhere('instance.followersCount > 0'); + } else { + query.andWhere('instance.followersCount = 0'); + } + } + + if (typeof ps.publishing === 'boolean') { + if (ps.publishing) { + query.andWhere('instance.followingCount > 0'); + } else { + query.andWhere('instance.followingCount = 0'); + } + } + + if (ps.host) { + query.andWhere('instance.host like :host', { host: '%' + ps.host.toLowerCase() + '%' }); + } + + const instances = await query.take(ps.limit).skip(ps.offset).getMany(); + + return await Instances.packMany(instances); + }); } - - if (typeof ps.blocked === 'boolean') { - const meta = await fetchMeta(true); - if (ps.blocked) { - query.andWhere('instance.host IN (:...blocks)', { blocks: meta.blockedHosts }); - } else { - query.andWhere('instance.host NOT IN (:...blocks)', { blocks: meta.blockedHosts }); - } - } - - if (typeof ps.notResponding === 'boolean') { - if (ps.notResponding) { - query.andWhere('instance.isNotResponding = TRUE'); - } else { - query.andWhere('instance.isNotResponding = FALSE'); - } - } - - if (typeof ps.suspended === 'boolean') { - if (ps.suspended) { - query.andWhere('instance.isSuspended = TRUE'); - } else { - query.andWhere('instance.isSuspended = FALSE'); - } - } - - if (typeof ps.federating === 'boolean') { - if (ps.federating) { - query.andWhere('((instance.followingCount > 0) OR (instance.followersCount > 0))'); - } else { - query.andWhere('((instance.followingCount = 0) AND (instance.followersCount = 0))'); - } - } - - if (typeof ps.subscribing === 'boolean') { - if (ps.subscribing) { - query.andWhere('instance.followersCount > 0'); - } else { - query.andWhere('instance.followersCount = 0'); - } - } - - if (typeof ps.publishing === 'boolean') { - if (ps.publishing) { - query.andWhere('instance.followingCount > 0'); - } else { - query.andWhere('instance.followingCount = 0'); - } - } - - if (ps.host) { - query.andWhere('instance.host like :host', { host: '%' + ps.host.toLowerCase() + '%' }); - } - - const instances = await query.take(ps.limit).skip(ps.offset).getMany(); - - return await Instances.packMany(instances); -}); +} diff --git a/packages/backend/src/server/api/endpoints/federation/show-instance.ts b/packages/backend/src/server/api/endpoints/federation/show-instance.ts index 2fbb8a15cb48..297e2f243ece 100644 --- a/packages/backend/src/server/api/endpoints/federation/show-instance.ts +++ b/packages/backend/src/server/api/endpoints/federation/show-instance.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Instances } from '@/models/index.js'; import { toPuny } from '@/misc/convert-host.js'; @@ -26,9 +27,17 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const instance = await Instances +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { + const instance = await Instances .findOneBy({ host: toPuny(ps.host) }); - return instance ? await Instances.pack(instance) : null; -}); + return instance ? await Instances.pack(instance) : null; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/federation/stats.ts b/packages/backend/src/server/api/endpoints/federation/stats.ts index e02c7b97e0a5..b796805d2061 100644 --- a/packages/backend/src/server/api/endpoints/federation/stats.ts +++ b/packages/backend/src/server/api/endpoints/federation/stats.ts @@ -1,7 +1,8 @@ import { IsNull, MoreThan, Not } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; import { Followings, Instances } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; -import define from '../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['federation'], @@ -21,45 +22,53 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { - const [topSubInstances, topPubInstances, allSubCount, allPubCount] = await Promise.all([ - Instances.find({ - where: { - followersCount: MoreThan(0), - }, - order: { - followersCount: 'DESC', - }, - take: ps.limit, - }), - Instances.find({ - where: { - followingCount: MoreThan(0), - }, - order: { - followingCount: 'DESC', - }, - take: ps.limit, - }), - Followings.count({ - where: { - followeeHost: Not(IsNull()), - }, - }), - Followings.count({ - where: { - followerHost: Not(IsNull()), - }, - }), - ]); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const [topSubInstances, topPubInstances, allSubCount, allPubCount] = await Promise.all([ + Instances.find({ + where: { + followersCount: MoreThan(0), + }, + order: { + followersCount: 'DESC', + }, + take: ps.limit, + }), + Instances.find({ + where: { + followingCount: MoreThan(0), + }, + order: { + followingCount: 'DESC', + }, + take: ps.limit, + }), + Followings.count({ + where: { + followeeHost: Not(IsNull()), + }, + }), + Followings.count({ + where: { + followerHost: Not(IsNull()), + }, + }), + ]); - const gotSubCount = topSubInstances.map(x => x.followersCount).reduce((a, b) => a + b, 0); - const gotPubCount = topPubInstances.map(x => x.followingCount).reduce((a, b) => a + b, 0); + const gotSubCount = topSubInstances.map(x => x.followersCount).reduce((a, b) => a + b, 0); + const gotPubCount = topPubInstances.map(x => x.followingCount).reduce((a, b) => a + b, 0); - return await awaitAll({ - topSubInstances: Instances.packMany(topSubInstances), - otherFollowersCount: Math.max(0, allSubCount - gotSubCount), - topPubInstances: Instances.packMany(topPubInstances), - otherFollowingCount: Math.max(0, allPubCount - gotPubCount), - }); -}); + return await awaitAll({ + topSubInstances: Instances.packMany(topSubInstances), + otherFollowersCount: Math.max(0, allSubCount - gotSubCount), + topPubInstances: Instances.packMany(topPubInstances), + otherFollowingCount: Math.max(0, allPubCount - gotPubCount), + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts index 409cc7695e37..ca494922ac56 100644 --- a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts +++ b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts @@ -1,6 +1,7 @@ -import define from '../../define.js'; -import { getRemoteUser } from '../../common/getters.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { updatePerson } from '@/remote/activitypub/models/person.js'; +import { getRemoteUser } from '../../common/getters.js'; export const meta = { tags: ['federation'], @@ -17,7 +18,15 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { - const user = await getRemoteUser(ps.userId); - await updatePerson(user.uri!); -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const user = await getRemoteUser(ps.userId); + await updatePerson(user.uri!); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/federation/users.ts b/packages/backend/src/server/api/endpoints/federation/users.ts index 65ad9f88d3e2..72021aa715d8 100644 --- a/packages/backend/src/server/api/endpoints/federation/users.ts +++ b/packages/backend/src/server/api/endpoints/federation/users.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Users } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -30,13 +31,21 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Users.createQueryBuilder('user'), ps.sinceId, ps.untilId) - .andWhere(`user.host = :host`, { host: ps.host }); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { + const query = makePaginationQuery(Users.createQueryBuilder('user'), ps.sinceId, ps.untilId) + .andWhere('user.host = :host', { host: ps.host }); - const users = await query + const users = await query .take(ps.limit) .getMany(); - return await Users.packMany(users, me, { detail: true }); -}); + return await Users.packMany(users, me, { detail: true }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/fetch-rss.ts b/packages/backend/src/server/api/endpoints/fetch-rss.ts index 05fa22a9e4c7..a3e6d46b3106 100644 --- a/packages/backend/src/server/api/endpoints/fetch-rss.ts +++ b/packages/backend/src/server/api/endpoints/fetch-rss.ts @@ -1,7 +1,8 @@ import Parser from 'rss-parser'; import { getResponse } from '@/misc/fetch.js'; import config from '@/config/index.js'; -import define from '../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; const rssParser = new Parser(); @@ -22,7 +23,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const res = await getResponse({ url: ps.url, method: 'GET', diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts index 02a030cd5e96..ba49262a587c 100644 --- a/packages/backend/src/server/api/endpoints/following/create.ts +++ b/packages/backend/src/server/api/endpoints/following/create.ts @@ -1,10 +1,11 @@ import ms from 'ms'; +import { Inject, Injectable } from '@nestjs/common'; import create from '@/services/following/create.js'; -import define from '../../define.js'; -import { ApiError } from '../../error.js'; -import { getUser } from '../../common/getters.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Followings, Users } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; +import { ApiError } from '../../error.js'; +import { getUser } from '../../common/getters.js'; export const meta = { tags: ['following', 'users'], @@ -66,39 +67,47 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const follower = user; - - // 自分自身 - if (user.id === ps.userId) { - throw new ApiError(meta.errors.followeeIsYourself); - } - - // Get followee - const followee = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; - }); - - // Check if already following - const exist = await Followings.findOneBy({ - followerId: follower.id, - followeeId: followee.id, - }); - - if (exist != null) { - throw new ApiError(meta.errors.alreadyFollowing); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const follower = user; + + // 自分自身 + if (user.id === ps.userId) { + throw new ApiError(meta.errors.followeeIsYourself); + } + + // Get followee + const followee = await getUser(ps.userId).catch(e => { + if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw e; + }); + + // Check if already following + const exist = await Followings.findOneBy({ + followerId: follower.id, + followeeId: followee.id, + }); + + if (exist != null) { + throw new ApiError(meta.errors.alreadyFollowing); + } + + try { + await create(follower, followee); + } catch (e) { + if (e instanceof IdentifiableError) { + if (e.id === '710e8fb0-b8c3-4922-be49-d5d93d8e6a6e') throw new ApiError(meta.errors.blocking); + if (e.id === '3338392a-f764-498d-8855-db939dcf8c48') throw new ApiError(meta.errors.blocked); + } + throw e; + } + + return await Users.pack(followee.id, user); + }); } - - try { - await create(follower, followee); - } catch (e) { - if (e instanceof IdentifiableError) { - if (e.id === '710e8fb0-b8c3-4922-be49-d5d93d8e6a6e') throw new ApiError(meta.errors.blocking); - if (e.id === '3338392a-f764-498d-8855-db939dcf8c48') throw new ApiError(meta.errors.blocked); - } - throw e; - } - - return await Users.pack(followee.id, user); -}); +} diff --git a/packages/backend/src/server/api/endpoints/following/delete.ts b/packages/backend/src/server/api/endpoints/following/delete.ts index 2f41b16e9a04..5d62f12b20a6 100644 --- a/packages/backend/src/server/api/endpoints/following/delete.ts +++ b/packages/backend/src/server/api/endpoints/following/delete.ts @@ -1,9 +1,10 @@ import ms from 'ms'; +import { Inject, Injectable } from '@nestjs/common'; import deleteFollowing from '@/services/following/delete.js'; -import define from '../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { Followings, Users } from '@/models/index.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; -import { Followings, Users } from '@/models/index.js'; export const meta = { tags: ['following', 'users'], @@ -53,31 +54,39 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const follower = user; - - // Check if the followee is yourself - if (user.id === ps.userId) { - throw new ApiError(meta.errors.followeeIsYourself); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const follower = user; + + // Check if the followee is yourself + if (user.id === ps.userId) { + throw new ApiError(meta.errors.followeeIsYourself); + } + + // Get followee + const followee = await getUser(ps.userId).catch(e => { + if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw e; + }); + + // Check not following + const exist = await Followings.findOneBy({ + followerId: follower.id, + followeeId: followee.id, + }); + + if (exist == null) { + throw new ApiError(meta.errors.notFollowing); + } + + await deleteFollowing(follower, followee); + + return await Users.pack(followee.id, user); + }); } - - // Get followee - const followee = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; - }); - - // Check not following - const exist = await Followings.findOneBy({ - followerId: follower.id, - followeeId: followee.id, - }); - - if (exist == null) { - throw new ApiError(meta.errors.notFollowing); - } - - await deleteFollowing(follower, followee); - - return await Users.pack(followee.id, user); -}); +} diff --git a/packages/backend/src/server/api/endpoints/following/invalidate.ts b/packages/backend/src/server/api/endpoints/following/invalidate.ts index 18ec5affe8c9..3fdad31a7328 100644 --- a/packages/backend/src/server/api/endpoints/following/invalidate.ts +++ b/packages/backend/src/server/api/endpoints/following/invalidate.ts @@ -1,9 +1,10 @@ import ms from 'ms'; +import { Inject, Injectable } from '@nestjs/common'; import deleteFollowing from '@/services/following/delete.js'; -import define from '../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { Followings, Users } from '@/models/index.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; -import { Followings, Users } from '@/models/index.js'; export const meta = { tags: ['following', 'users'], @@ -53,31 +54,39 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const followee = user; - - // Check if the follower is yourself - if (user.id === ps.userId) { - throw new ApiError(meta.errors.followerIsYourself); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const followee = user; + + // Check if the follower is yourself + if (user.id === ps.userId) { + throw new ApiError(meta.errors.followerIsYourself); + } + + // Get follower + const follower = await getUser(ps.userId).catch(e => { + if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw e; + }); + + // Check not following + const exist = await Followings.findOneBy({ + followerId: follower.id, + followeeId: followee.id, + }); + + if (exist == null) { + throw new ApiError(meta.errors.notFollowing); + } + + await deleteFollowing(follower, followee); + + return await Users.pack(followee.id, user); + }); } - - // Get follower - const follower = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; - }); - - // Check not following - const exist = await Followings.findOneBy({ - followerId: follower.id, - followeeId: followee.id, - }); - - if (exist == null) { - throw new ApiError(meta.errors.notFollowing); - } - - await deleteFollowing(follower, followee); - - return await Users.pack(followee.id, user); -}); +} diff --git a/packages/backend/src/server/api/endpoints/following/requests/accept.ts b/packages/backend/src/server/api/endpoints/following/requests/accept.ts index e5df55375ec9..6cc7927535a4 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/accept.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/accept.ts @@ -1,5 +1,6 @@ +import { Inject, Injectable } from '@nestjs/common'; import acceptFollowRequest from '@/services/following/requests/accept.js'; -import define from '../../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { getUser } from '../../../common/getters.js'; @@ -33,17 +34,25 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - // Fetch follower - const follower = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; - }); - - await acceptFollowRequest(user, follower).catch(e => { - if (e.id === '8884c2dd-5795-4ac9-b27e-6a01d38190f9') throw new ApiError(meta.errors.noFollowRequest); - throw e; - }); - - return; -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + // Fetch follower + const follower = await getUser(ps.userId).catch(e => { + if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw e; + }); + + await acceptFollowRequest(user, follower).catch(e => { + if (e.id === '8884c2dd-5795-4ac9-b27e-6a01d38190f9') throw new ApiError(meta.errors.noFollowRequest); + throw e; + }); + + return; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts index 80d37fb07543..b558320271e4 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts @@ -1,9 +1,10 @@ +import { Inject, Injectable } from '@nestjs/common'; import cancelFollowRequest from '@/services/following/requests/cancel.js'; -import define from '../../../define.js'; -import { ApiError } from '../../../error.js'; -import { getUser } from '../../../common/getters.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Users } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; +import { ApiError } from '../../../error.js'; +import { getUser } from '../../../common/getters.js'; export const meta = { tags: ['following', 'account'], @@ -42,21 +43,29 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - // Fetch followee - const followee = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; - }); - - try { - await cancelFollowRequest(followee, user); - } catch (e) { - if (e instanceof IdentifiableError) { - if (e.id === '17447091-ce07-46dd-b331-c1fd4f15b1e7') throw new ApiError(meta.errors.followRequestNotFound); - } - throw e; - } +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + // Fetch followee + const followee = await getUser(ps.userId).catch(e => { + if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw e; + }); + + try { + await cancelFollowRequest(followee, user); + } catch (e) { + if (e instanceof IdentifiableError) { + if (e.id === '17447091-ce07-46dd-b331-c1fd4f15b1e7') throw new ApiError(meta.errors.followRequestNotFound); + } + throw e; + } - return await Users.pack(followee.id, user); -}); + return await Users.pack(followee.id, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/following/requests/list.ts b/packages/backend/src/server/api/endpoints/following/requests/list.ts index a8f42c481d27..d8c5fea47067 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/list.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/list.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { FollowRequests } from '@/models/index.js'; export const meta = { @@ -42,10 +43,18 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const reqs = await FollowRequests.findBy({ - followeeId: user.id, - }); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const reqs = await FollowRequests.findBy({ + followeeId: user.id, + }); - return await Promise.all(reqs.map(req => FollowRequests.pack(req))); -}); + return await Promise.all(reqs.map(req => FollowRequests.pack(req))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/following/requests/reject.ts b/packages/backend/src/server/api/endpoints/following/requests/reject.ts index cebe604284cd..26d9f7f02740 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/reject.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/reject.ts @@ -1,5 +1,6 @@ +import { Inject, Injectable } from '@nestjs/common'; import { rejectFollowRequest } from '@/services/following/reject.js'; -import define from '../../../define.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { getUser } from '../../../common/getters.js'; @@ -28,14 +29,22 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - // Fetch follower - const follower = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; - }); - - await rejectFollowRequest(user, follower); - - return; -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + // Fetch follower + const follower = await getUser(ps.userId).catch(e => { + if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw e; + }); + + await rejectFollowRequest(user, follower); + + return; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/gallery/featured.ts b/packages/backend/src/server/api/endpoints/gallery/featured.ts index e6acd36911b3..1a5c2039dc06 100644 --- a/packages/backend/src/server/api/endpoints/gallery/featured.ts +++ b/packages/backend/src/server/api/endpoints/gallery/featured.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { GalleryPosts } from '@/models/index.js'; export const meta = { @@ -24,13 +25,21 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const query = GalleryPosts.createQueryBuilder('post') - .andWhere('post.createdAt > :date', { date: new Date(Date.now() - (1000 * 60 * 60 * 24 * 3)) }) - .andWhere('post.likedCount > 0') - .orderBy('post.likedCount', 'DESC'); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { + const query = GalleryPosts.createQueryBuilder('post') + .andWhere('post.createdAt > :date', { date: new Date(Date.now() - (1000 * 60 * 60 * 24 * 3)) }) + .andWhere('post.likedCount > 0') + .orderBy('post.likedCount', 'DESC'); - const posts = await query.take(10).getMany(); + const posts = await query.take(10).getMany(); - return await GalleryPosts.packMany(posts, me); -}); + return await GalleryPosts.packMany(posts, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/gallery/popular.ts b/packages/backend/src/server/api/endpoints/gallery/popular.ts index c4c8982fcc0a..dbf712f61911 100644 --- a/packages/backend/src/server/api/endpoints/gallery/popular.ts +++ b/packages/backend/src/server/api/endpoints/gallery/popular.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { GalleryPosts } from '@/models/index.js'; export const meta = { @@ -24,12 +25,20 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const query = GalleryPosts.createQueryBuilder('post') +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { + const query = GalleryPosts.createQueryBuilder('post') .andWhere('post.likedCount > 0') .orderBy('post.likedCount', 'DESC'); - const posts = await query.take(10).getMany(); + const posts = await query.take(10).getMany(); - return await GalleryPosts.packMany(posts, me); -}); + return await GalleryPosts.packMany(posts, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/gallery/posts.ts b/packages/backend/src/server/api/endpoints/gallery/posts.ts index 428ba9cc71a1..18d764d73734 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts.ts @@ -1,6 +1,7 @@ -import define from '../../define.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { GalleryPosts } from '@/models/index.js'; +import { makePaginationQuery } from '../../common/make-pagination-query.js'; export const meta = { tags: ['gallery'], @@ -27,11 +28,19 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { + const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) .innerJoinAndSelect('post.user', 'user'); - const posts = await query.take(ps.limit).getMany(); + const posts = await query.take(ps.limit).getMany(); - return await GalleryPosts.packMany(posts, me); -}); + return await GalleryPosts.packMany(posts, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts index 8074a3b34f0b..aee094a50c05 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts @@ -1,10 +1,11 @@ import ms from 'ms'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFiles, GalleryPosts } from '@/models/index.js'; -import { genId } from '../../../../../misc/gen-id.js'; import { GalleryPost } from '@/models/entities/gallery-post.js'; +import type { DriveFile } from '@/models/entities/drive-file.js'; +import { genId } from '../../../../../misc/gen-id.js'; import { ApiError } from '../../../error.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; export const meta = { tags: ['gallery'], @@ -43,28 +44,36 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const files = (await Promise.all(ps.fileIds.map(fileId => - DriveFiles.findOneBy({ - id: fileId, - userId: user.id, - }) - ))).filter((file): file is DriveFile => file != null); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const files = (await Promise.all(ps.fileIds.map(fileId => + DriveFiles.findOneBy({ + id: fileId, + userId: user.id, + }), + ))).filter((file): file is DriveFile => file != null); - if (files.length === 0) { - throw new Error(); - } + if (files.length === 0) { + throw new Error(); + } - const post = await GalleryPosts.insert(new GalleryPost({ - id: genId(), - createdAt: new Date(), - updatedAt: new Date(), - title: ps.title, - description: ps.description, - userId: user.id, - isSensitive: ps.isSensitive, - fileIds: files.map(file => file.id), - })).then(x => GalleryPosts.findOneByOrFail(x.identifiers[0])); + const post = await GalleryPosts.insert(new GalleryPost({ + id: genId(), + createdAt: new Date(), + updatedAt: new Date(), + title: ps.title, + description: ps.description, + userId: user.id, + isSensitive: ps.isSensitive, + fileIds: files.map(file => file.id), + })).then(x => GalleryPosts.findOneByOrFail(x.identifiers[0])); - return await GalleryPosts.pack(post, user); -}); + return await GalleryPosts.pack(post, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts b/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts index b00ee0e2ae7f..34a0f48a51f8 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts @@ -1,6 +1,7 @@ -import define from '../../../define.js'; -import { ApiError } from '../../../error.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { GalleryPosts } from '@/models/index.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['gallery'], @@ -27,15 +28,23 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const post = await GalleryPosts.findOneBy({ - id: ps.postId, - userId: user.id, - }); - - if (post == null) { - throw new ApiError(meta.errors.noSuchPost); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const post = await GalleryPosts.findOneBy({ + id: ps.postId, + userId: user.id, + }); + + if (post == null) { + throw new ApiError(meta.errors.noSuchPost); + } + + await GalleryPosts.delete(post.id); + }); } - - await GalleryPosts.delete(post.id); -}); +} diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts index b858114aec14..f4740bdaa3f6 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts @@ -1,7 +1,8 @@ -import define from '../../../define.js'; -import { ApiError } from '../../../error.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { GalleryPosts, GalleryLikes } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['gallery'], @@ -40,33 +41,41 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const post = await GalleryPosts.findOneBy({ id: ps.postId }); - if (post == null) { - throw new ApiError(meta.errors.noSuchPost); - } +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const post = await GalleryPosts.findOneBy({ id: ps.postId }); + if (post == null) { + throw new ApiError(meta.errors.noSuchPost); + } - if (post.userId === user.id) { - throw new ApiError(meta.errors.yourPost); - } + if (post.userId === user.id) { + throw new ApiError(meta.errors.yourPost); + } - // if already liked - const exist = await GalleryLikes.findOneBy({ - postId: post.id, - userId: user.id, - }); + // if already liked + const exist = await GalleryLikes.findOneBy({ + postId: post.id, + userId: user.id, + }); - if (exist != null) { - throw new ApiError(meta.errors.alreadyLiked); - } + if (exist != null) { + throw new ApiError(meta.errors.alreadyLiked); + } - // Create like - await GalleryLikes.insert({ - id: genId(), - createdAt: new Date(), - postId: post.id, - userId: user.id, - }); + // Create like + await GalleryLikes.insert({ + id: genId(), + createdAt: new Date(), + postId: post.id, + userId: user.id, + }); - GalleryPosts.increment({ id: post.id }, 'likedCount', 1); -}); + GalleryPosts.increment({ id: post.id }, 'likedCount', 1); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts b/packages/backend/src/server/api/endpoints/gallery/posts/show.ts index 4f6dafd7cbfe..e69e7ce6998f 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/show.ts @@ -1,6 +1,7 @@ -import define from '../../../define.js'; -import { ApiError } from '../../../error.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { GalleryPosts } from '@/models/index.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['gallery'], @@ -31,14 +32,22 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const post = await GalleryPosts.findOneBy({ - id: ps.postId, - }); - - if (post == null) { - throw new ApiError(meta.errors.noSuchPost); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { + const post = await GalleryPosts.findOneBy({ + id: ps.postId, + }); + + if (post == null) { + throw new ApiError(meta.errors.noSuchPost); + } + + return await GalleryPosts.pack(post, me); + }); } - - return await GalleryPosts.pack(post, me); -}); +} diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts index d136239e5ecf..b401d54dff88 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts @@ -1,6 +1,7 @@ -import define from '../../../define.js'; -import { ApiError } from '../../../error.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { GalleryPosts, GalleryLikes } from '@/models/index.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['gallery'], @@ -33,23 +34,31 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const post = await GalleryPosts.findOneBy({ id: ps.postId }); - if (post == null) { - throw new ApiError(meta.errors.noSuchPost); - } +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const post = await GalleryPosts.findOneBy({ id: ps.postId }); + if (post == null) { + throw new ApiError(meta.errors.noSuchPost); + } - const exist = await GalleryLikes.findOneBy({ - postId: post.id, - userId: user.id, - }); + const exist = await GalleryLikes.findOneBy({ + postId: post.id, + userId: user.id, + }); - if (exist == null) { - throw new ApiError(meta.errors.notLiked); - } + if (exist == null) { + throw new ApiError(meta.errors.notLiked); + } - // Delete like - await GalleryLikes.delete(exist.id); + // Delete like + await GalleryLikes.delete(exist.id); - GalleryPosts.decrement({ id: post.id }, 'likedCount', 1); -}); + GalleryPosts.decrement({ id: post.id }, 'likedCount', 1); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts index 82fe38078e56..9c008cc6c7f3 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts @@ -1,9 +1,10 @@ import ms from 'ms'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFiles, GalleryPosts } from '@/models/index.js'; import { GalleryPost } from '@/models/entities/gallery-post.js'; +import type { DriveFile } from '@/models/entities/drive-file.js'; import { ApiError } from '../../../error.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; export const meta = { tags: ['gallery'], @@ -43,30 +44,38 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const files = (await Promise.all(ps.fileIds.map(fileId => - DriveFiles.findOneBy({ - id: fileId, - userId: user.id, - }) - ))).filter((file): file is DriveFile => file != null); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const files = (await Promise.all(ps.fileIds.map(fileId => + DriveFiles.findOneBy({ + id: fileId, + userId: user.id, + }), + ))).filter((file): file is DriveFile => file != null); - if (files.length === 0) { - throw new Error(); - } + if (files.length === 0) { + throw new Error(); + } - await GalleryPosts.update({ - id: ps.postId, - userId: user.id, - }, { - updatedAt: new Date(), - title: ps.title, - description: ps.description, - isSensitive: ps.isSensitive, - fileIds: files.map(file => file.id), - }); + await GalleryPosts.update({ + id: ps.postId, + userId: user.id, + }, { + updatedAt: new Date(), + title: ps.title, + description: ps.description, + isSensitive: ps.isSensitive, + fileIds: files.map(file => file.id), + }); - const post = await GalleryPosts.findOneByOrFail({ id: ps.postId }); + const post = await GalleryPosts.findOneByOrFail({ id: ps.postId }); - return await GalleryPosts.pack(post, user); -}); + return await GalleryPosts.pack(post, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/get-online-users-count.ts b/packages/backend/src/server/api/endpoints/get-online-users-count.ts index 56c550297815..4dff275046cd 100644 --- a/packages/backend/src/server/api/endpoints/get-online-users-count.ts +++ b/packages/backend/src/server/api/endpoints/get-online-users-count.ts @@ -1,7 +1,8 @@ import { MoreThan } from 'typeorm'; import { USER_ONLINE_THRESHOLD } from '@/const.js'; import { Users } from '@/models/index.js'; -import define from '../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['meta'], diff --git a/packages/backend/src/server/api/endpoints/hashtags/list.ts b/packages/backend/src/server/api/endpoints/hashtags/list.ts index 50e36386cf85..f30661546a56 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/list.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/list.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Hashtags } from '@/models/index.js'; export const meta = { @@ -30,39 +31,47 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const query = Hashtags.createQueryBuilder('tag'); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { + const query = Hashtags.createQueryBuilder('tag'); - if (ps.attachedToUserOnly) query.andWhere('tag.attachedUsersCount != 0'); - if (ps.attachedToLocalUserOnly) query.andWhere('tag.attachedLocalUsersCount != 0'); - if (ps.attachedToRemoteUserOnly) query.andWhere('tag.attachedRemoteUsersCount != 0'); + if (ps.attachedToUserOnly) query.andWhere('tag.attachedUsersCount != 0'); + if (ps.attachedToLocalUserOnly) query.andWhere('tag.attachedLocalUsersCount != 0'); + if (ps.attachedToRemoteUserOnly) query.andWhere('tag.attachedRemoteUsersCount != 0'); - switch (ps.sort) { - case '+mentionedUsers': query.orderBy('tag.mentionedUsersCount', 'DESC'); break; - case '-mentionedUsers': query.orderBy('tag.mentionedUsersCount', 'ASC'); break; - case '+mentionedLocalUsers': query.orderBy('tag.mentionedLocalUsersCount', 'DESC'); break; - case '-mentionedLocalUsers': query.orderBy('tag.mentionedLocalUsersCount', 'ASC'); break; - case '+mentionedRemoteUsers': query.orderBy('tag.mentionedRemoteUsersCount', 'DESC'); break; - case '-mentionedRemoteUsers': query.orderBy('tag.mentionedRemoteUsersCount', 'ASC'); break; - case '+attachedUsers': query.orderBy('tag.attachedUsersCount', 'DESC'); break; - case '-attachedUsers': query.orderBy('tag.attachedUsersCount', 'ASC'); break; - case '+attachedLocalUsers': query.orderBy('tag.attachedLocalUsersCount', 'DESC'); break; - case '-attachedLocalUsers': query.orderBy('tag.attachedLocalUsersCount', 'ASC'); break; - case '+attachedRemoteUsers': query.orderBy('tag.attachedRemoteUsersCount', 'DESC'); break; - case '-attachedRemoteUsers': query.orderBy('tag.attachedRemoteUsersCount', 'ASC'); break; - } + switch (ps.sort) { + case '+mentionedUsers': query.orderBy('tag.mentionedUsersCount', 'DESC'); break; + case '-mentionedUsers': query.orderBy('tag.mentionedUsersCount', 'ASC'); break; + case '+mentionedLocalUsers': query.orderBy('tag.mentionedLocalUsersCount', 'DESC'); break; + case '-mentionedLocalUsers': query.orderBy('tag.mentionedLocalUsersCount', 'ASC'); break; + case '+mentionedRemoteUsers': query.orderBy('tag.mentionedRemoteUsersCount', 'DESC'); break; + case '-mentionedRemoteUsers': query.orderBy('tag.mentionedRemoteUsersCount', 'ASC'); break; + case '+attachedUsers': query.orderBy('tag.attachedUsersCount', 'DESC'); break; + case '-attachedUsers': query.orderBy('tag.attachedUsersCount', 'ASC'); break; + case '+attachedLocalUsers': query.orderBy('tag.attachedLocalUsersCount', 'DESC'); break; + case '-attachedLocalUsers': query.orderBy('tag.attachedLocalUsersCount', 'ASC'); break; + case '+attachedRemoteUsers': query.orderBy('tag.attachedRemoteUsersCount', 'DESC'); break; + case '-attachedRemoteUsers': query.orderBy('tag.attachedRemoteUsersCount', 'ASC'); break; + } - query.select([ - 'tag.name', - 'tag.mentionedUsersCount', - 'tag.mentionedLocalUsersCount', - 'tag.mentionedRemoteUsersCount', - 'tag.attachedUsersCount', - 'tag.attachedLocalUsersCount', - 'tag.attachedRemoteUsersCount', - ]); + query.select([ + 'tag.name', + 'tag.mentionedUsersCount', + 'tag.mentionedLocalUsersCount', + 'tag.mentionedRemoteUsersCount', + 'tag.attachedUsersCount', + 'tag.attachedLocalUsersCount', + 'tag.attachedRemoteUsersCount', + ]); - const tags = await query.take(ps.limit).getMany(); + const tags = await query.take(ps.limit).getMany(); - return Hashtags.packMany(tags); -}); + return Hashtags.packMany(tags); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/hashtags/search.ts b/packages/backend/src/server/api/endpoints/hashtags/search.ts index c28984477582..d101c8607db6 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/search.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/search.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Hashtags } from '@/models/index.js'; export const meta = { @@ -27,8 +28,14 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { - const hashtags = await Hashtags.createQueryBuilder('tag') +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const hashtags = await Hashtags.createQueryBuilder('tag') .where('tag.name like :q', { q: ps.query.toLowerCase() + '%' }) .orderBy('tag.count', 'DESC') .groupBy('tag.id') @@ -36,5 +43,7 @@ export default define(meta, paramDef, async (ps) => { .skip(ps.offset) .getMany(); - return hashtags.map(tag => tag.name); -}); + return hashtags.map(tag => tag.name); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/hashtags/show.ts b/packages/backend/src/server/api/endpoints/hashtags/show.ts index 5b78f6ac7f00..fbf815891381 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/show.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/show.ts @@ -1,7 +1,8 @@ -import define from '../../define.js'; -import { ApiError } from '../../error.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Hashtags } from '@/models/index.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['hashtags'], @@ -32,11 +33,19 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { - const hashtag = await Hashtags.findOneBy({ name: normalizeForSearch(ps.tag) }); - if (hashtag == null) { - throw new ApiError(meta.errors.noSuchHashtag); - } +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { + const hashtag = await Hashtags.findOneBy({ name: normalizeForSearch(ps.tag) }); + if (hashtag == null) { + throw new ApiError(meta.errors.noSuchHashtag); + } - return await Hashtags.pack(hashtag); -}); + return await Hashtags.pack(hashtag); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/hashtags/trend.ts b/packages/backend/src/server/api/endpoints/hashtags/trend.ts index 9cdbc8941c7e..ee3a93657b58 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/trend.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/trend.ts @@ -1,5 +1,6 @@ import { Brackets } from 'typeorm'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { Notes } from '@/models/index.js'; import { Note } from '@/models/entities/note.js'; @@ -151,3 +152,5 @@ export default define(meta, paramDef, async () => { return stats; }); +} +} diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts index a5df21a7e3dd..130cb7ce3109 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/users.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Users } from '@/models/index.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; @@ -24,39 +25,47 @@ export const paramDef = { tag: { type: 'string' }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sort: { type: 'string', enum: ['+follower', '-follower', '+createdAt', '-createdAt', '+updatedAt', '-updatedAt'] }, - state: { type: 'string', enum: ['all', 'alive'], default: "all" }, - origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "local" }, + state: { type: 'string', enum: ['all', 'alive'], default: 'all' }, + origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: 'local' }, }, required: ['tag', 'sort'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { - const query = Users.createQueryBuilder('user') +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { + const query = Users.createQueryBuilder('user') .where(':tag = ANY(user.tags)', { tag: normalizeForSearch(ps.tag) }); - const recent = new Date(Date.now() - (1000 * 60 * 60 * 24 * 5)); + const recent = new Date(Date.now() - (1000 * 60 * 60 * 24 * 5)); - if (ps.state === 'alive') { - query.andWhere('user.updatedAt > :date', { date: recent }); - } + if (ps.state === 'alive') { + query.andWhere('user.updatedAt > :date', { date: recent }); + } - if (ps.origin === 'local') { - query.andWhere('user.host IS NULL'); - } else if (ps.origin === 'remote') { - query.andWhere('user.host IS NOT NULL'); - } + if (ps.origin === 'local') { + query.andWhere('user.host IS NULL'); + } else if (ps.origin === 'remote') { + query.andWhere('user.host IS NOT NULL'); + } - switch (ps.sort) { - case '+follower': query.orderBy('user.followersCount', 'DESC'); break; - case '-follower': query.orderBy('user.followersCount', 'ASC'); break; - case '+createdAt': query.orderBy('user.createdAt', 'DESC'); break; - case '-createdAt': query.orderBy('user.createdAt', 'ASC'); break; - case '+updatedAt': query.orderBy('user.updatedAt', 'DESC'); break; - case '-updatedAt': query.orderBy('user.updatedAt', 'ASC'); break; - } + switch (ps.sort) { + case '+follower': query.orderBy('user.followersCount', 'DESC'); break; + case '-follower': query.orderBy('user.followersCount', 'ASC'); break; + case '+createdAt': query.orderBy('user.createdAt', 'DESC'); break; + case '-createdAt': query.orderBy('user.createdAt', 'ASC'); break; + case '+updatedAt': query.orderBy('user.updatedAt', 'DESC'); break; + case '-updatedAt': query.orderBy('user.updatedAt', 'ASC'); break; + } - const users = await query.take(ps.limit).getMany(); + const users = await query.take(ps.limit).getMany(); - return await Users.packMany(users, me, { detail: true }); -}); + return await Users.packMany(users, me, { detail: true }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index 22aedfeee8c7..9d63089049ef 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -1,5 +1,6 @@ import { Users } from '@/models/index.js'; -import define from '../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['account'], diff --git a/packages/backend/src/server/api/endpoints/i/2fa/done.ts b/packages/backend/src/server/api/endpoints/i/2fa/done.ts index 35806b2bc39f..a676d0e65266 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/done.ts @@ -1,5 +1,6 @@ import * as speakeasy from 'speakeasy'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserProfiles } from '@/models/index.js'; export const meta = { @@ -17,7 +18,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const token = ps.token.replace(/\s/g, ''); const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts index 1afb34bfda34..9d88b450d957 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts @@ -1,7 +1,8 @@ import bcrypt from 'bcryptjs'; import { promisify } from 'node:util'; import * as cbor from 'cbor'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserProfiles, UserSecurityKeys, @@ -34,7 +35,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); // Compare password diff --git a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts index 4bfa24f97f5d..9d5cccd14af9 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserProfiles } from '@/models/index.js'; export const meta = { @@ -16,7 +17,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { await UserProfiles.update(user.id, { usePasswordLessLogin: ps.value, }); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts index e906b8204351..8767603725bf 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts @@ -1,5 +1,6 @@ import bcrypt from 'bcryptjs'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserProfiles, AttestationChallenges } from '@/models/index.js'; import { promisify } from 'node:util'; import * as crypto from 'node:crypto'; @@ -23,7 +24,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); // Compare password diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register.ts b/packages/backend/src/server/api/endpoints/i/2fa/register.ts index 33f5717728ab..6ebe67a56259 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts @@ -3,7 +3,8 @@ import * as speakeasy from 'speakeasy'; import * as QRCode from 'qrcode'; import config from '@/config/index.js'; import { UserProfiles } from '@/models/index.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { requireCredential: true, @@ -20,7 +21,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); // Compare password diff --git a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts index eb2f75308d30..25eafaed2005 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts @@ -1,5 +1,6 @@ import bcrypt from 'bcryptjs'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserProfiles, UserSecurityKeys, Users } from '@/models/index.js'; import { publishMainStream } from '@/services/stream.js'; @@ -19,7 +20,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); // Compare password diff --git a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts index 45e7a98639ec..75601c9d1c02 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts @@ -1,5 +1,6 @@ import bcrypt from 'bcryptjs'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserProfiles } from '@/models/index.js'; export const meta = { @@ -17,7 +18,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); // Compare password diff --git a/packages/backend/src/server/api/endpoints/i/apps.ts b/packages/backend/src/server/api/endpoints/i/apps.ts index eca9558847f5..de2f4ec3fc81 100644 --- a/packages/backend/src/server/api/endpoints/i/apps.ts +++ b/packages/backend/src/server/api/endpoints/i/apps.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { AccessTokens } from '@/models/index.js'; export const meta = { @@ -16,7 +17,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = AccessTokens.createQueryBuilder('token') .where('token.userId = :userId', { userId: user.id }); diff --git a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts index 68bd103a6d4d..34db84f13c60 100644 --- a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts +++ b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { AccessTokens, Apps } from '@/models/index.js'; export const meta = { @@ -18,7 +19,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { // Get tokens const tokens = await AccessTokens.find({ where: { diff --git a/packages/backend/src/server/api/endpoints/i/change-password.ts b/packages/backend/src/server/api/endpoints/i/change-password.ts index f9f6a33a80f8..1134cb7630f5 100644 --- a/packages/backend/src/server/api/endpoints/i/change-password.ts +++ b/packages/backend/src/server/api/endpoints/i/change-password.ts @@ -1,5 +1,6 @@ import bcrypt from 'bcryptjs'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserProfiles } from '@/models/index.js'; export const meta = { @@ -18,7 +19,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); // Compare password diff --git a/packages/backend/src/server/api/endpoints/i/delete-account.ts b/packages/backend/src/server/api/endpoints/i/delete-account.ts index ede4a9d03b44..b6cc3b8c21d2 100644 --- a/packages/backend/src/server/api/endpoints/i/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts @@ -1,7 +1,8 @@ import bcrypt from 'bcryptjs'; import { UserProfiles, Users } from '@/models/index.js'; import { deleteAccount } from '@/services/delete-account.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { requireCredential: true, @@ -18,7 +19,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); const userDetailed = await Users.findOneByOrFail({ id: user.id }); if (userDetailed.isDeleted) { diff --git a/packages/backend/src/server/api/endpoints/i/export-blocking.ts b/packages/backend/src/server/api/endpoints/i/export-blocking.ts index aed4c2e0a3de..8c8a971586a6 100644 --- a/packages/backend/src/server/api/endpoints/i/export-blocking.ts +++ b/packages/backend/src/server/api/endpoints/i/export-blocking.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { createExportBlockingJob } from '@/queue/index.js'; import ms from 'ms'; @@ -18,6 +19,12 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { createExportBlockingJob(user); }); diff --git a/packages/backend/src/server/api/endpoints/i/export-following.ts b/packages/backend/src/server/api/endpoints/i/export-following.ts index 058d77b3c2ad..a661e55c0db9 100644 --- a/packages/backend/src/server/api/endpoints/i/export-following.ts +++ b/packages/backend/src/server/api/endpoints/i/export-following.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { createExportFollowingJob } from '@/queue/index.js'; import ms from 'ms'; @@ -21,6 +22,12 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { createExportFollowingJob(user, ps.excludeMuting, ps.excludeInactive); }); diff --git a/packages/backend/src/server/api/endpoints/i/export-mute.ts b/packages/backend/src/server/api/endpoints/i/export-mute.ts index c0216fac0cf4..811d7789f83d 100644 --- a/packages/backend/src/server/api/endpoints/i/export-mute.ts +++ b/packages/backend/src/server/api/endpoints/i/export-mute.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { createExportMuteJob } from '@/queue/index.js'; import ms from 'ms'; @@ -18,6 +19,12 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { createExportMuteJob(user); }); diff --git a/packages/backend/src/server/api/endpoints/i/export-notes.ts b/packages/backend/src/server/api/endpoints/i/export-notes.ts index 4b85a4555486..15ff13016213 100644 --- a/packages/backend/src/server/api/endpoints/i/export-notes.ts +++ b/packages/backend/src/server/api/endpoints/i/export-notes.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { createExportNotesJob } from '@/queue/index.js'; import ms from 'ms'; @@ -18,6 +19,12 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { createExportNotesJob(user); }); diff --git a/packages/backend/src/server/api/endpoints/i/export-user-lists.ts b/packages/backend/src/server/api/endpoints/i/export-user-lists.ts index fa5c1f5e5ab6..ac4ec9b3eb6f 100644 --- a/packages/backend/src/server/api/endpoints/i/export-user-lists.ts +++ b/packages/backend/src/server/api/endpoints/i/export-user-lists.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { createExportUserListsJob } from '@/queue/index.js'; import ms from 'ms'; @@ -18,6 +19,12 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { createExportUserListsJob(user); }); diff --git a/packages/backend/src/server/api/endpoints/i/favorites.ts b/packages/backend/src/server/api/endpoints/i/favorites.ts index 3c420e4d0f67..391660ad1710 100644 --- a/packages/backend/src/server/api/endpoints/i/favorites.ts +++ b/packages/backend/src/server/api/endpoints/i/favorites.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteFavorites } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -31,7 +32,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(NoteFavorites.createQueryBuilder('favorite'), ps.sinceId, ps.untilId) .andWhere(`favorite.userId = :meId`, { meId: user.id }) .leftJoinAndSelect('favorite.note', 'note'); diff --git a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts index a38383f30e8e..45c94469466c 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { GalleryLikes } from '@/models/index.js'; import { makePaginationQuery } from '../../../common/make-pagination-query.js'; @@ -42,7 +43,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(GalleryLikes.createQueryBuilder('like'), ps.sinceId, ps.untilId) .andWhere(`like.userId = :meId`, { meId: user.id }) .leftJoinAndSelect('like.post', 'post'); diff --git a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts index b4edb5f73dd1..8aec20f793ec 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { GalleryPosts } from '@/models/index.js'; import { makePaginationQuery } from '../../../common/make-pagination-query.js'; @@ -31,7 +32,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) .andWhere(`post.userId = :meId`, { meId: user.id }); diff --git a/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts b/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts index e7d7518c5bc5..a29e204ead49 100644 --- a/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts +++ b/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { MutedNotes } from '@/models/index.js'; export const meta = { @@ -27,7 +28,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { return { count: await MutedNotes.countBy({ userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/i/import-blocking.ts b/packages/backend/src/server/api/endpoints/i/import-blocking.ts index 0bcbf37ddd88..c165efed89ad 100644 --- a/packages/backend/src/server/api/endpoints/i/import-blocking.ts +++ b/packages/backend/src/server/api/endpoints/i/import-blocking.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { createImportBlockingJob } from '@/queue/index.js'; import ms from 'ms'; import { ApiError } from '../../error.js'; @@ -49,7 +50,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const file = await DriveFiles.findOneBy({ id: ps.fileId }); if (file == null) throw new ApiError(meta.errors.noSuchFile); diff --git a/packages/backend/src/server/api/endpoints/i/import-following.ts b/packages/backend/src/server/api/endpoints/i/import-following.ts index ee2abbea1936..485a84e8d1b7 100644 --- a/packages/backend/src/server/api/endpoints/i/import-following.ts +++ b/packages/backend/src/server/api/endpoints/i/import-following.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { createImportFollowingJob } from '@/queue/index.js'; import ms from 'ms'; import { ApiError } from '../../error.js'; @@ -48,7 +49,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const file = await DriveFiles.findOneBy({ id: ps.fileId }); if (file == null) throw new ApiError(meta.errors.noSuchFile); diff --git a/packages/backend/src/server/api/endpoints/i/import-muting.ts b/packages/backend/src/server/api/endpoints/i/import-muting.ts index b3b3b3923897..9ae28e97d20b 100644 --- a/packages/backend/src/server/api/endpoints/i/import-muting.ts +++ b/packages/backend/src/server/api/endpoints/i/import-muting.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { createImportMutingJob } from '@/queue/index.js'; import ms from 'ms'; import { ApiError } from '../../error.js'; @@ -49,7 +50,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const file = await DriveFiles.findOneBy({ id: ps.fileId }); if (file == null) throw new ApiError(meta.errors.noSuchFile); diff --git a/packages/backend/src/server/api/endpoints/i/import-user-lists.ts b/packages/backend/src/server/api/endpoints/i/import-user-lists.ts index 64f5ec05fdc5..acbcbaa364e2 100644 --- a/packages/backend/src/server/api/endpoints/i/import-user-lists.ts +++ b/packages/backend/src/server/api/endpoints/i/import-user-lists.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { createImportUserListsJob } from '@/queue/index.js'; import ms from 'ms'; import { ApiError } from '../../error.js'; @@ -48,7 +49,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const file = await DriveFiles.findOneBy({ id: ps.fileId }); if (file == null) throw new ApiError(meta.errors.noSuchFile); diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index 2b343dabddb7..27cdc8ee0ab8 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -3,7 +3,8 @@ import { Notifications, Followings, Mutings, Users, UserProfiles } from '@/model import { notificationTypes } from '@/types.js'; import read from '@/services/note/read.js'; import { readNotification } from '../../common/read-notification.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; export const meta = { @@ -49,7 +50,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { // includeTypes が空の場合はクエリしない if (ps.includeTypes && ps.includeTypes.length === 0) { return []; diff --git a/packages/backend/src/server/api/endpoints/i/page-likes.ts b/packages/backend/src/server/api/endpoints/i/page-likes.ts index 71e326e2f0ea..57b12d76b217 100644 --- a/packages/backend/src/server/api/endpoints/i/page-likes.ts +++ b/packages/backend/src/server/api/endpoints/i/page-likes.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { PageLikes } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -41,7 +42,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(PageLikes.createQueryBuilder('like'), ps.sinceId, ps.untilId) .andWhere(`like.userId = :meId`, { meId: user.id }) .leftJoinAndSelect('like.page', 'page'); diff --git a/packages/backend/src/server/api/endpoints/i/pages.ts b/packages/backend/src/server/api/endpoints/i/pages.ts index f28aed3fd45f..2434e49d089c 100644 --- a/packages/backend/src/server/api/endpoints/i/pages.ts +++ b/packages/backend/src/server/api/endpoints/i/pages.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Pages } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -31,7 +32,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(Pages.createQueryBuilder('page'), ps.sinceId, ps.untilId) .andWhere(`page.userId = :meId`, { meId: user.id }); diff --git a/packages/backend/src/server/api/endpoints/i/pin.ts b/packages/backend/src/server/api/endpoints/i/pin.ts index 67b7026be1ce..5a7a7b2cfd9d 100644 --- a/packages/backend/src/server/api/endpoints/i/pin.ts +++ b/packages/backend/src/server/api/endpoints/i/pin.ts @@ -1,5 +1,6 @@ import { addPinned } from '@/services/i/pin.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { Users } from '@/models/index.js'; @@ -46,7 +47,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { await addPinned(user, ps.noteId).catch(e => { if (e.id === '70c4e51f-5bea-449c-a030-53bee3cce202') throw new ApiError(meta.errors.noSuchNote); if (e.id === '15a018eb-58e5-4da1-93be-330fcc5e4e1a') throw new ApiError(meta.errors.pinLimitExceeded); diff --git a/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts b/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts index 7ff6409caf51..69ece1a1c0ec 100644 --- a/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts +++ b/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts @@ -1,5 +1,6 @@ import { publishMainStream } from '@/services/stream.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { MessagingMessages, UserGroupJoinings } from '@/models/index.js'; export const meta = { @@ -17,7 +18,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { // Update documents await MessagingMessages.update({ recipientId: user.id, diff --git a/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts b/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts index 49f3deb331da..d7dc91b1b08d 100644 --- a/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts +++ b/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts @@ -1,5 +1,6 @@ import { publishMainStream } from '@/services/stream.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteUnreads } from '@/models/index.js'; export const meta = { @@ -17,7 +18,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { // Remove documents await NoteUnreads.delete({ userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/i/read-announcement.ts b/packages/backend/src/server/api/endpoints/i/read-announcement.ts index 45b6e98c86b6..f808def1de1b 100644 --- a/packages/backend/src/server/api/endpoints/i/read-announcement.ts +++ b/packages/backend/src/server/api/endpoints/i/read-announcement.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { genId } from '@/misc/gen-id.js'; import { AnnouncementReads, Announcements, Users } from '@/models/index.js'; @@ -29,7 +30,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { // Check if announcement exists const announcement = await Announcements.findOneBy({ id: ps.announcementId }); diff --git a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts index af929b04e8fc..1756cad156b7 100644 --- a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts +++ b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts @@ -1,7 +1,8 @@ import bcrypt from 'bcryptjs'; import { publishInternalEvent, publishMainStream, publishUserEvent } from '@/services/stream.js'; import generateUserToken from '../../common/generate-native-user-token.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Users, UserProfiles } from '@/models/index.js'; export const meta = { @@ -19,7 +20,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const freshUser = await Users.findOneByOrFail({ id: user.id }); const oldToken = freshUser.token; diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts index d0b16dbc4893..15ccdd32ed28 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { RegistryItems } from '@/models/index.js'; export const meta = { @@ -18,7 +19,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = RegistryItems.createQueryBuilder('item') .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: user.id }) diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts index cc5d5a8c6f7d..fbf6fbee905a 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { RegistryItems } from '@/models/index.js'; import { ApiError } from '../../../error.js'; @@ -28,7 +29,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = RegistryItems.createQueryBuilder('item') .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: user.id }) diff --git a/packages/backend/src/server/api/endpoints/i/registry/get.ts b/packages/backend/src/server/api/endpoints/i/registry/get.ts index a79319744c33..bc03f42538cf 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { RegistryItems } from '@/models/index.js'; import { ApiError } from '../../../error.js'; @@ -28,7 +29,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = RegistryItems.createQueryBuilder('item') .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: user.id }) diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts index ac209c06a629..27a49e1d4e36 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { RegistryItems } from '@/models/index.js'; export const meta = { @@ -18,7 +19,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = RegistryItems.createQueryBuilder('item') .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: user.id }) diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys.ts b/packages/backend/src/server/api/endpoints/i/registry/keys.ts index 5ea1a9d344e0..8d1d3e67ec8f 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/keys.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/keys.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { RegistryItems } from '@/models/index.js'; export const meta = { @@ -18,7 +19,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = RegistryItems.createQueryBuilder('item') .select('item.key') .where('item.domain IS NULL') diff --git a/packages/backend/src/server/api/endpoints/i/registry/remove.ts b/packages/backend/src/server/api/endpoints/i/registry/remove.ts index 92473654c67e..9af1b75f6bba 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/remove.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/remove.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { RegistryItems } from '@/models/index.js'; import { ApiError } from '../../../error.js'; @@ -28,7 +29,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = RegistryItems.createQueryBuilder('item') .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: user.id }) diff --git a/packages/backend/src/server/api/endpoints/i/registry/scopes.ts b/packages/backend/src/server/api/endpoints/i/registry/scopes.ts index de4b313e2594..3905cbbd2a00 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/scopes.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/scopes.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { RegistryItems } from '@/models/index.js'; export const meta = { @@ -14,7 +15,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = RegistryItems.createQueryBuilder('item') .select('item.scope') .where('item.domain IS NULL') diff --git a/packages/backend/src/server/api/endpoints/i/registry/set.ts b/packages/backend/src/server/api/endpoints/i/registry/set.ts index d380b428a3d1..60120b3701c8 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/set.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/set.ts @@ -1,5 +1,6 @@ import { publishMainStream } from '@/services/stream.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { RegistryItems } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; @@ -22,7 +23,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = RegistryItems.createQueryBuilder('item') .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: user.id }) diff --git a/packages/backend/src/server/api/endpoints/i/revoke-token.ts b/packages/backend/src/server/api/endpoints/i/revoke-token.ts index c69245379406..adce9a773182 100644 --- a/packages/backend/src/server/api/endpoints/i/revoke-token.ts +++ b/packages/backend/src/server/api/endpoints/i/revoke-token.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { AccessTokens } from '@/models/index.js'; import { publishUserEvent } from '@/services/stream.js'; @@ -17,7 +18,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const token = await AccessTokens.findOneBy({ id: ps.tokenId }); if (token) { diff --git a/packages/backend/src/server/api/endpoints/i/signin-history.ts b/packages/backend/src/server/api/endpoints/i/signin-history.ts index ca37411662c7..4d73fe7c49e6 100644 --- a/packages/backend/src/server/api/endpoints/i/signin-history.ts +++ b/packages/backend/src/server/api/endpoints/i/signin-history.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Signins } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -19,7 +20,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(Signins.createQueryBuilder('signin'), ps.sinceId, ps.untilId) .andWhere(`signin.userId = :meId`, { meId: user.id }); diff --git a/packages/backend/src/server/api/endpoints/i/unpin.ts b/packages/backend/src/server/api/endpoints/i/unpin.ts index 9912689da523..dcc662469f15 100644 --- a/packages/backend/src/server/api/endpoints/i/unpin.ts +++ b/packages/backend/src/server/api/endpoints/i/unpin.ts @@ -1,5 +1,6 @@ import { removePinned } from '@/services/i/pin.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { Users } from '@/models/index.js'; @@ -34,7 +35,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { await removePinned(user, ps.noteId).catch(e => { if (e.id === 'b302d4cf-c050-400a-bbb3-be208681f40c') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts index 33180785230d..cd432e373053 100644 --- a/packages/backend/src/server/api/endpoints/i/update-email.ts +++ b/packages/backend/src/server/api/endpoints/i/update-email.ts @@ -1,5 +1,6 @@ import { publishMainStream } from '@/services/stream.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import rndstr from 'rndstr'; import config from '@/config/index.js'; import ms from 'ms'; @@ -44,7 +45,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); // Compare password diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 3c2f1cea0d28..bc3928b5c685 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -13,7 +13,8 @@ import { notificationTypes } from '@/types.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { langmap } from '@/misc/langmap.js'; import { ApiError } from '../../error.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['account'], diff --git a/packages/backend/src/server/api/endpoints/i/user-group-invites.ts b/packages/backend/src/server/api/endpoints/i/user-group-invites.ts index 1d7e4a16b3bc..e530a065f1f9 100644 --- a/packages/backend/src/server/api/endpoints/i/user-group-invites.ts +++ b/packages/backend/src/server/api/endpoints/i/user-group-invites.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserGroupInvitations } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -42,7 +43,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(UserGroupInvitations.createQueryBuilder('invitation'), ps.sinceId, ps.untilId) .andWhere(`invitation.userId = :meId`, { meId: user.id }) .leftJoinAndSelect('invitation.userGroup', 'user_group'); diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts index 2e2fd00b8ce6..aabe267369f7 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { genId } from '@/misc/gen-id.js'; import { Webhooks } from '@/models/index.js'; import { publishInternalEvent } from '@/services/stream.js'; @@ -26,7 +27,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const webhook = await Webhooks.insert({ id: genId(), createdAt: new Date(), diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts b/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts index 2821eaa5f13c..6373f3992c7f 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { Webhooks } from '@/models/index.js'; import { publishInternalEvent } from '@/services/stream.js'; @@ -28,7 +29,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const webhook = await Webhooks.findOneBy({ id: ps.webhookId, userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/list.ts b/packages/backend/src/server/api/endpoints/i/webhooks/list.ts index 54e4563732c2..6ec37cb2a1c4 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/list.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/list.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Webhooks } from '@/models/index.js'; export const meta = { @@ -16,7 +17,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const webhooks = await Webhooks.findBy({ userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/show.ts b/packages/backend/src/server/api/endpoints/i/webhooks/show.ts index 02fa1edb5e09..e544e215e1ae 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/show.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/show.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { Webhooks } from '@/models/index.js'; @@ -27,7 +28,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const webhook = await Webhooks.findOneBy({ id: ps.webhookId, userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/update.ts b/packages/backend/src/server/api/endpoints/i/webhooks/update.ts index f87b9753fb71..b2e84cab2dcb 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/update.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/update.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { Webhooks } from '@/models/index.js'; import { publishInternalEvent } from '@/services/stream.js'; @@ -37,7 +38,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const webhook = await Webhooks.findOneBy({ id: ps.webhookId, userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/messaging/history.ts b/packages/backend/src/server/api/endpoints/messaging/history.ts index ea0600d0e4b0..3104a2342d4b 100644 --- a/packages/backend/src/server/api/endpoints/messaging/history.ts +++ b/packages/backend/src/server/api/endpoints/messaging/history.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { MessagingMessage } from '@/models/entities/messaging-message.js'; import { MessagingMessages, Mutings, UserGroupJoinings } from '@/models/index.js'; import { Brackets } from 'typeorm'; @@ -31,7 +32,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const mute = await Mutings.findBy({ muterId: user.id, }); diff --git a/packages/backend/src/server/api/endpoints/messaging/messages.ts b/packages/backend/src/server/api/endpoints/messaging/messages.ts index dbf1f6c86892..5d882942b6db 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; import { MessagingMessages, UserGroups, UserGroupJoinings, Users } from '@/models/index.js'; @@ -69,7 +70,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { if (ps.userId != null) { // Fetch recipient (user) const recipient = await getUser(ps.userId).catch(e => { diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts index 405af5ec17dc..d7e2248ab147 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { getUser } from '../../../common/getters.js'; import { MessagingMessages, DriveFiles, UserGroups, UserGroupJoinings, Blockings } from '@/models/index.js'; @@ -87,7 +88,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { let recipientUser: User | null; let recipientGroup: UserGroup | null; diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts b/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts index f66d75873c05..3eb6dcaccbba 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import ms from 'ms'; import { ApiError } from '../../../error.js'; import { MessagingMessages } from '@/models/index.js'; @@ -35,7 +36,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const message = await MessagingMessages.findOneBy({ id: ps.messageId, userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/read.ts b/packages/backend/src/server/api/endpoints/messaging/messages/read.ts index db12ae922cdc..ef33d8e453df 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/read.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/read.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { MessagingMessages } from '@/models/index.js'; import { readUserMessagingMessage, readGroupMessagingMessage } from '../../../common/read-messaging-message.js'; @@ -28,7 +29,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const message = await MessagingMessages.findOneBy({ id: ps.messageId }); if (message == null) { diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 5b624842c3ef..f2938307217c 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -4,7 +4,8 @@ import { fetchMeta } from '@/misc/fetch-meta.js'; import { Ads, Emojis, Users } from '@/models/index.js'; import { DB_MAX_NOTE_TEXT_LENGTH } from '@/misc/hard-limits.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; -import define from '../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['meta'], @@ -304,7 +305,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const instance = await fetchMeta(true); const emojis = await Emojis.find({ diff --git a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts index 73ecdaeb0302..62a1f919b2e0 100644 --- a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts +++ b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { AccessTokens } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; @@ -37,7 +38,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { // Generate access token const accessToken = secureRndstr(32, true); diff --git a/packages/backend/src/server/api/endpoints/mute/create.ts b/packages/backend/src/server/api/endpoints/mute/create.ts index 7e857e673116..57f14db559dd 100644 --- a/packages/backend/src/server/api/endpoints/mute/create.ts +++ b/packages/backend/src/server/api/endpoints/mute/create.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; import { genId } from '@/misc/gen-id.js'; @@ -48,7 +49,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const muter = user; // 自分自身 diff --git a/packages/backend/src/server/api/endpoints/mute/delete.ts b/packages/backend/src/server/api/endpoints/mute/delete.ts index 0b173dbe24be..11e4607a22a2 100644 --- a/packages/backend/src/server/api/endpoints/mute/delete.ts +++ b/packages/backend/src/server/api/endpoints/mute/delete.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; import { Mutings } from '@/models/index.js'; @@ -41,7 +42,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const muter = user; // Check if the mutee is yourself diff --git a/packages/backend/src/server/api/endpoints/mute/list.ts b/packages/backend/src/server/api/endpoints/mute/list.ts index 31283cf4c136..ed1321605425 100644 --- a/packages/backend/src/server/api/endpoints/mute/list.ts +++ b/packages/backend/src/server/api/endpoints/mute/list.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { Mutings } from '@/models/index.js'; @@ -31,7 +32,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(Mutings.createQueryBuilder('muting'), ps.sinceId, ps.untilId) .andWhere(`muting.muterId = :meId`, { meId: me.id }); diff --git a/packages/backend/src/server/api/endpoints/my/apps.ts b/packages/backend/src/server/api/endpoints/my/apps.ts index 85b75c15df6a..46498cc5f332 100644 --- a/packages/backend/src/server/api/endpoints/my/apps.ts +++ b/packages/backend/src/server/api/endpoints/my/apps.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Apps } from '@/models/index.js'; export const meta = { @@ -27,7 +28,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = { userId: user.id, }; diff --git a/packages/backend/src/server/api/endpoints/notes.ts b/packages/backend/src/server/api/endpoints/notes.ts index 697b43cb3b7c..40b148ce5021 100644 --- a/packages/backend/src/server/api/endpoints/notes.ts +++ b/packages/backend/src/server/api/endpoints/notes.ts @@ -39,7 +39,7 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps) => { + super(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere('note.visibility = \'public\'') .andWhere('note.localOnly = FALSE') diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index efc109105c42..f6838843d419 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -1,6 +1,7 @@ import { Brackets } from 'typeorm'; import { Notes } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; @@ -34,7 +35,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(new Brackets(qb => { qb .where('note.replyId = :noteId', { noteId: ps.noteId }) diff --git a/packages/backend/src/server/api/endpoints/notes/clips.ts b/packages/backend/src/server/api/endpoints/notes/clips.ts index e79f8563e84e..63800271040d 100644 --- a/packages/backend/src/server/api/endpoints/notes/clips.ts +++ b/packages/backend/src/server/api/endpoints/notes/clips.ts @@ -1,6 +1,7 @@ import { In } from 'typeorm'; import { ClipNotes, Clips } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../common/getters.js'; import { ApiError } from '../../error.js'; @@ -37,7 +38,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/conversation.ts b/packages/backend/src/server/api/endpoints/notes/conversation.ts index b731d18248dc..de868832fc03 100644 --- a/packages/backend/src/server/api/endpoints/notes/conversation.ts +++ b/packages/backend/src/server/api/endpoints/notes/conversation.ts @@ -1,6 +1,7 @@ import { Note } from '@/models/entities/note.js'; import { Notes } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { getNote } from '../../common/getters.js'; @@ -39,7 +40,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 99f9883d1efb..fbd1fa0e9773 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -9,7 +9,8 @@ import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { noteService } from '@/services/index.js'; import { noteVisibilities } from '../../../../types.js'; import { ApiError } from '../../error.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['notes'], @@ -161,7 +162,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { let visibleUsers: User[] = []; if (ps.visibleUserIds) { visibleUsers = await Users.findBy({ diff --git a/packages/backend/src/server/api/endpoints/notes/delete.ts b/packages/backend/src/server/api/endpoints/notes/delete.ts index c23ceeb5bf7e..06ba6f77645f 100644 --- a/packages/backend/src/server/api/endpoints/notes/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/delete.ts @@ -1,7 +1,8 @@ import ms from 'ms'; import deleteNote from '@/services/note/delete.js'; import { Users } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../common/getters.js'; import { ApiError } from '../../error.js'; @@ -42,7 +43,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts index 097371a425a2..2fadcd19c62d 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts @@ -1,6 +1,7 @@ import { NoteFavorites } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { getNote } from '../../../common/getters.js'; @@ -35,7 +36,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { // Get favoritee const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts index 82ef4fa19702..1d5744b44a93 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts @@ -1,5 +1,6 @@ import { NoteFavorites } from '@/models/index.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { getNote } from '../../../common/getters.js'; @@ -34,7 +35,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { // Get favoritee const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index dd9cc581aa3c..d6c4447f0814 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -1,5 +1,6 @@ import { Notes } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; @@ -29,7 +30,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const max = 30; const day = 1000 * 60 * 60 * 24 * 3; // 3日前まで diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index 925318f54456..5bab0bcfa822 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -1,7 +1,8 @@ import { fetchMeta } from '@/misc/fetch-meta.js'; import { Notes } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; @@ -49,7 +50,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const m = await fetchMeta(); if (m.disableGlobalTimeline) { if (user == null || (!user.isAdmin && !user.isModerator)) { diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 2dc98c4c9faf..c5d457dccdbe 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -2,7 +2,8 @@ import { Brackets } from 'typeorm'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { Followings, Notes } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; @@ -57,7 +58,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const m = await fetchMeta(); if (m.disableLocalTimeline && (!user.isAdmin && !user.isModerator)) { throw new ApiError(meta.errors.stlDisabled); diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index aac2a3749c84..66f44a2f1509 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -2,7 +2,8 @@ import { Brackets } from 'typeorm'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { Notes, Users } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -56,7 +57,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const m = await fetchMeta(); if (m.disableLocalTimeline) { if (user == null || (!user.isAdmin && !user.isModerator)) { diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index 9b41544523e6..b8b2a3d5a1b6 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -1,7 +1,8 @@ import { Brackets } from 'typeorm'; import read from '@/services/note/read.js'; import { Notes, Followings } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -37,7 +38,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const followingQuery = Followings.createQueryBuilder('following') .select('following.followeeId') .where('following.followerId = :followerId', { followerId: user.id }); diff --git a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts index 5a04d68f3e41..c3eda14ec751 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts @@ -1,6 +1,7 @@ import { Brackets, In } from 'typeorm'; import { Polls, Mutings, Notes, PollVotes } from '@/models/index.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['notes'], @@ -28,7 +29,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = Polls.createQueryBuilder('poll') .where('poll.userHost IS NULL') .andWhere('poll.userId != :meId', { meId: user.id }) 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 45a832cbd215..4824e6befd73 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -10,7 +10,8 @@ import { IRemoteUser } from '@/models/entities/user.js'; import { genId } from '@/misc/gen-id.js'; import { getNote } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['notes'], @@ -68,7 +69,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const createdAt = new Date(); // Get votee diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts index 15a62d394d32..3256aa750698 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts @@ -1,7 +1,8 @@ import { DeepPartial, FindOptionsWhere } from 'typeorm'; import { NoteReactions } from '@/models/index.js'; import { NoteReaction } from '@/models/entities/note-reaction.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -45,7 +46,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = { noteId: ps.noteId, } as FindOptionsWhere; diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts index 07e52a92660f..88f9faa31cfb 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts @@ -1,5 +1,6 @@ import createReaction from '@/services/note/reaction/create.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; @@ -41,7 +42,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts index c13cafa21de8..607db7c23383 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts @@ -1,6 +1,7 @@ import ms from 'ms'; import deleteReaction from '@/services/note/reaction/delete.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; @@ -41,7 +42,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index 28be360763e0..031f510b5741 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -1,5 +1,6 @@ import { Notes } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../common/getters.js'; import { ApiError } from '../../error.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; @@ -43,7 +44,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index ab0018f58e04..07f9c80c3afe 100644 --- a/packages/backend/src/server/api/endpoints/notes/replies.ts +++ b/packages/backend/src/server/api/endpoints/notes/replies.ts @@ -1,5 +1,6 @@ import { Notes } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; @@ -33,7 +34,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere('note.replyId = :replyId', { replyId: ps.noteId }) .innerJoinAndSelect('note.user', 'user') diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts index 777de7221c27..d51d36cdf9e3 100644 --- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts +++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts @@ -2,7 +2,8 @@ import { Brackets } from 'typeorm'; import { Notes } from '@/models/index.js'; import { safeForSql } from '@/misc/safe-for-sql.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; @@ -66,7 +67,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index 4e2cdae801dc..226b5bce6d5e 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -2,7 +2,8 @@ import { In } from 'typeorm'; import { Notes } from '@/models/index.js'; import config from '@/config/index.js'; import es from '../../../../db/elasticsearch.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; @@ -47,7 +48,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { if (es == null) { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId); diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts index 5cd74bd2cac3..114ef929ff78 100644 --- a/packages/backend/src/server/api/endpoints/notes/show.ts +++ b/packages/backend/src/server/api/endpoints/notes/show.ts @@ -1,5 +1,6 @@ import { Notes } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../common/getters.js'; import { ApiError } from '../../error.js'; @@ -32,7 +33,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/state.ts b/packages/backend/src/server/api/endpoints/notes/state.ts index 01afa5add2e6..c08fbb0713b9 100644 --- a/packages/backend/src/server/api/endpoints/notes/state.ts +++ b/packages/backend/src/server/api/endpoints/notes/state.ts @@ -1,5 +1,6 @@ import { NoteFavorites, Notes, NoteThreadMutings, NoteWatchings } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['notes'], @@ -35,7 +36,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const note = await Notes.findOneByOrFail({ id: ps.noteId }); const [favorite, watching, threadMuting] = await Promise.all([ diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts index cf360526d3be..1721ae3a4b86 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts @@ -1,7 +1,8 @@ import { Notes, NoteThreadMutings } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import readNote from '@/services/note/read.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; @@ -30,7 +31,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts index ac310d0fe67a..fed00a8169eb 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts @@ -1,5 +1,6 @@ import { NoteThreadMutings } from '@/models/index.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; @@ -28,7 +29,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 22f492517512..d1dd09366f8b 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -1,7 +1,8 @@ import { Brackets } from 'typeorm'; import { Notes, Followings } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; @@ -47,7 +48,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const hasFollowing = (await Followings.count({ where: { followerId: user.id, diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts index 5e40e7106f09..d980fae068b6 100644 --- a/packages/backend/src/server/api/endpoints/notes/translate.ts +++ b/packages/backend/src/server/api/endpoints/notes/translate.ts @@ -6,7 +6,8 @@ import { fetchMeta } from '@/misc/fetch-meta.js'; import { Notes } from '@/models/index.js'; import { ApiError } from '../../error.js'; import { getNote } from '../../common/getters.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['notes'], @@ -37,7 +38,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/unrenote.ts b/packages/backend/src/server/api/endpoints/notes/unrenote.ts index 3fba0efe0c7c..0b20ce3a658f 100644 --- a/packages/backend/src/server/api/endpoints/notes/unrenote.ts +++ b/packages/backend/src/server/api/endpoints/notes/unrenote.ts @@ -1,7 +1,8 @@ import ms from 'ms'; import deleteNote from '@/services/note/delete.js'; import { Notes, Users } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../common/getters.js'; import { ApiError } from '../../error.js'; @@ -36,7 +37,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index e603a8f62598..99033ac88c2f 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -1,7 +1,8 @@ import { Brackets } from 'typeorm'; import { UserLists, UserListJoinings, Notes } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; @@ -52,7 +53,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const list = await UserLists.findOneBy({ id: ps.listId, userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/notes/watching/create.ts b/packages/backend/src/server/api/endpoints/notes/watching/create.ts index 7d482b073287..f33961c9fee7 100644 --- a/packages/backend/src/server/api/endpoints/notes/watching/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/watching/create.ts @@ -1,5 +1,6 @@ import watch from '@/services/note/watch.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; @@ -28,7 +29,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/watching/delete.ts b/packages/backend/src/server/api/endpoints/notes/watching/delete.ts index 2c1a2e5fbd2b..9488016b3f86 100644 --- a/packages/backend/src/server/api/endpoints/notes/watching/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/watching/delete.ts @@ -1,5 +1,6 @@ import unwatch from '@/services/note/unwatch.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; @@ -28,7 +29,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notifications/create.ts b/packages/backend/src/server/api/endpoints/notifications/create.ts index 80d513d8da93..2afbc2347fc1 100644 --- a/packages/backend/src/server/api/endpoints/notifications/create.ts +++ b/packages/backend/src/server/api/endpoints/notifications/create.ts @@ -1,5 +1,6 @@ import { createNotification } from '@/services/create-notification.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['notifications'], diff --git a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts index d169afbb35d6..2e17bd6155af 100644 --- a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts @@ -1,7 +1,8 @@ import { publishMainStream } from '@/services/stream.js'; import { pushNotification } from '@/services/push-notification.js'; import { Notifications } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['notifications', 'account'], @@ -18,7 +19,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { // Update documents await Notifications.update({ notifieeId: user.id, diff --git a/packages/backend/src/server/api/endpoints/notifications/read.ts b/packages/backend/src/server/api/endpoints/notifications/read.ts index 7bce525a55fa..de48cee31cbd 100644 --- a/packages/backend/src/server/api/endpoints/notifications/read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/read.ts @@ -1,4 +1,5 @@ -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { readNotification } from '../../common/read-notification.js'; export const meta = { @@ -43,7 +44,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { if ('notificationId' in ps) return readNotification(user.id, [ps.notificationId]); return readNotification(user.id, ps.notificationIds); }); diff --git a/packages/backend/src/server/api/endpoints/page-push.ts b/packages/backend/src/server/api/endpoints/page-push.ts index 6dd3ede85a05..d7a9b347c3bd 100644 --- a/packages/backend/src/server/api/endpoints/page-push.ts +++ b/packages/backend/src/server/api/endpoints/page-push.ts @@ -1,6 +1,7 @@ import { publishMainStream } from '@/services/stream.js'; import { Users, Pages } from '@/models/index.js'; -import define from '../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../error.js'; export const meta = { @@ -27,7 +28,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const page = await Pages.findOneBy({ id: ps.pageId }); if (page == null) { throw new ApiError(meta.errors.noSuchPage); diff --git a/packages/backend/src/server/api/endpoints/pages/create.ts b/packages/backend/src/server/api/endpoints/pages/create.ts index b008cde84e97..969714511734 100644 --- a/packages/backend/src/server/api/endpoints/pages/create.ts +++ b/packages/backend/src/server/api/endpoints/pages/create.ts @@ -2,7 +2,8 @@ import ms from 'ms'; import { Pages, DriveFiles } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { Page } from '@/models/entities/page.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -59,7 +60,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { let eyeCatchingImage = null; if (ps.eyeCatchingImageId != null) { eyeCatchingImage = await DriveFiles.findOneBy({ diff --git a/packages/backend/src/server/api/endpoints/pages/delete.ts b/packages/backend/src/server/api/endpoints/pages/delete.ts index a7708e658572..e70585f87812 100644 --- a/packages/backend/src/server/api/endpoints/pages/delete.ts +++ b/packages/backend/src/server/api/endpoints/pages/delete.ts @@ -1,5 +1,6 @@ import { Pages } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -33,7 +34,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const page = await Pages.findOneBy({ id: ps.pageId }); if (page == null) { throw new ApiError(meta.errors.noSuchPage); diff --git a/packages/backend/src/server/api/endpoints/pages/featured.ts b/packages/backend/src/server/api/endpoints/pages/featured.ts index 5a149a626e53..b6c632c3150a 100644 --- a/packages/backend/src/server/api/endpoints/pages/featured.ts +++ b/packages/backend/src/server/api/endpoints/pages/featured.ts @@ -1,5 +1,6 @@ import { Pages } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['pages'], @@ -24,7 +25,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const query = Pages.createQueryBuilder('page') .where('page.visibility = \'public\'') .andWhere('page.likedCount > 0') diff --git a/packages/backend/src/server/api/endpoints/pages/like.ts b/packages/backend/src/server/api/endpoints/pages/like.ts index 269b539f743c..763836f6edbf 100644 --- a/packages/backend/src/server/api/endpoints/pages/like.ts +++ b/packages/backend/src/server/api/endpoints/pages/like.ts @@ -1,6 +1,7 @@ import { Pages, PageLikes } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -40,7 +41,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const page = await Pages.findOneBy({ id: ps.pageId }); if (page == null) { throw new ApiError(meta.errors.noSuchPage); diff --git a/packages/backend/src/server/api/endpoints/pages/show.ts b/packages/backend/src/server/api/endpoints/pages/show.ts index 5d37e86b9182..a978f4ddcd00 100644 --- a/packages/backend/src/server/api/endpoints/pages/show.ts +++ b/packages/backend/src/server/api/endpoints/pages/show.ts @@ -1,7 +1,8 @@ import { IsNull } from 'typeorm'; import { Pages, Users } from '@/models/index.js'; import { Page } from '@/models/entities/page.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -44,7 +45,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { let page: Page | null = null; if (ps.pageId) { diff --git a/packages/backend/src/server/api/endpoints/pages/unlike.ts b/packages/backend/src/server/api/endpoints/pages/unlike.ts index 6b3a2bec1069..f7f6a3aa39a8 100644 --- a/packages/backend/src/server/api/endpoints/pages/unlike.ts +++ b/packages/backend/src/server/api/endpoints/pages/unlike.ts @@ -1,5 +1,6 @@ import { Pages, PageLikes } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -33,7 +34,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const page = await Pages.findOneBy({ id: ps.pageId }); if (page == null) { throw new ApiError(meta.errors.noSuchPage); diff --git a/packages/backend/src/server/api/endpoints/pages/update.ts b/packages/backend/src/server/api/endpoints/pages/update.ts index d241f585aa8a..36066d965a16 100644 --- a/packages/backend/src/server/api/endpoints/pages/update.ts +++ b/packages/backend/src/server/api/endpoints/pages/update.ts @@ -1,7 +1,8 @@ import ms from 'ms'; import { Not } from 'typeorm'; import { Pages, DriveFiles } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -65,7 +66,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const page = await Pages.findOneBy({ id: ps.pageId }); if (page == null) { throw new ApiError(meta.errors.noSuchPage); diff --git a/packages/backend/src/server/api/endpoints/ping.ts b/packages/backend/src/server/api/endpoints/ping.ts index 2891a0860a20..a9b6bdbbaa91 100644 --- a/packages/backend/src/server/api/endpoints/ping.ts +++ b/packages/backend/src/server/api/endpoints/ping.ts @@ -1,4 +1,5 @@ -import define from '../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { requireCredential: false, diff --git a/packages/backend/src/server/api/endpoints/pinned-users.ts b/packages/backend/src/server/api/endpoints/pinned-users.ts index 41595b47d9e8..a72db47201ae 100644 --- a/packages/backend/src/server/api/endpoints/pinned-users.ts +++ b/packages/backend/src/server/api/endpoints/pinned-users.ts @@ -3,7 +3,8 @@ import { Users } from '@/models/index.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import * as Acct from '@/misc/acct.js'; import { User } from '@/models/entities/user.js'; -import define from '../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['users'], @@ -28,7 +29,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const meta = await fetchMeta(); const users = await Promise.all(meta.pinnedUsers.map(acct => Acct.parse(acct)).map(acct => Users.findOneBy({ diff --git a/packages/backend/src/server/api/endpoints/promo/read.ts b/packages/backend/src/server/api/endpoints/promo/read.ts index c6a940c65e62..0a405ba02de0 100644 --- a/packages/backend/src/server/api/endpoints/promo/read.ts +++ b/packages/backend/src/server/api/endpoints/promo/read.ts @@ -1,6 +1,7 @@ import { PromoReads } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { getNote } from '../../common/getters.js'; @@ -27,7 +28,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/request-reset-password.ts b/packages/backend/src/server/api/endpoints/request-reset-password.ts index 511a6bbb53c8..9eb819fa1a98 100644 --- a/packages/backend/src/server/api/endpoints/request-reset-password.ts +++ b/packages/backend/src/server/api/endpoints/request-reset-password.ts @@ -7,7 +7,8 @@ import { Users, UserProfiles, PasswordResetRequests } from '@/models/index.js'; import { sendEmail } from '@/services/send-email.js'; import { genId } from '@/misc/gen-id.js'; import { ApiError } from '../error.js'; -import define from '../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['reset password'], @@ -36,7 +37,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const user = await Users.findOneBy({ usernameLower: ps.username.toLowerCase(), host: IsNull(), diff --git a/packages/backend/src/server/api/endpoints/reset-db.ts b/packages/backend/src/server/api/endpoints/reset-db.ts index 140f96d579e7..89fb74017d37 100644 --- a/packages/backend/src/server/api/endpoints/reset-db.ts +++ b/packages/backend/src/server/api/endpoints/reset-db.ts @@ -1,5 +1,6 @@ import { resetDb } from '@/db/postgre.js'; -import define from '../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../error.js'; export const meta = { @@ -21,7 +22,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { if (process.env.NODE_ENV !== 'test') throw 'NODE_ENV is not a test'; await resetDb(); diff --git a/packages/backend/src/server/api/endpoints/reset-password.ts b/packages/backend/src/server/api/endpoints/reset-password.ts index 797169c2c3ea..da5ab929039e 100644 --- a/packages/backend/src/server/api/endpoints/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/reset-password.ts @@ -1,7 +1,8 @@ import bcrypt from 'bcryptjs'; import { publishMainStream } from '@/services/stream.js'; import { Users, UserProfiles, PasswordResetRequests } from '@/models/index.js'; -import define from '../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../error.js'; export const meta = { @@ -26,7 +27,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const req = await PasswordResetRequests.findOneByOrFail({ token: ps.token, }); diff --git a/packages/backend/src/server/api/endpoints/server-info.ts b/packages/backend/src/server/api/endpoints/server-info.ts index 99f3730e9785..2b2a1c2650e0 100644 --- a/packages/backend/src/server/api/endpoints/server-info.ts +++ b/packages/backend/src/server/api/endpoints/server-info.ts @@ -1,6 +1,7 @@ import * as os from 'node:os'; import si from 'systeminformation'; -import define from '../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { requireCredential: false, diff --git a/packages/backend/src/server/api/endpoints/stats.ts b/packages/backend/src/server/api/endpoints/stats.ts index cc94f8bf2691..4a35631d8123 100644 --- a/packages/backend/src/server/api/endpoints/stats.ts +++ b/packages/backend/src/server/api/endpoints/stats.ts @@ -1,5 +1,6 @@ import { Instances, NoteReactions, Notes, Users } from '@/models/index.js'; -import define from '../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { } from '@/services/chart/index.js'; import { IsNull } from 'typeorm'; diff --git a/packages/backend/src/server/api/endpoints/sw/register.ts b/packages/backend/src/server/api/endpoints/sw/register.ts index 437f8874ffd6..8c030d90e5ee 100644 --- a/packages/backend/src/server/api/endpoints/sw/register.ts +++ b/packages/backend/src/server/api/endpoints/sw/register.ts @@ -1,7 +1,8 @@ import { fetchMeta } from '@/misc/fetch-meta.js'; import { genId } from '@/misc/gen-id.js'; import { SwSubscriptions } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['account'], @@ -38,7 +39,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { // if already subscribed const exist = await SwSubscriptions.findOneBy({ userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/sw/unregister.ts b/packages/backend/src/server/api/endpoints/sw/unregister.ts index c19e06b879a8..5666b13ef92a 100644 --- a/packages/backend/src/server/api/endpoints/sw/unregister.ts +++ b/packages/backend/src/server/api/endpoints/sw/unregister.ts @@ -1,5 +1,6 @@ import { SwSubscriptions } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['account'], @@ -18,7 +19,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { await SwSubscriptions.delete({ userId: user.id, endpoint: ps.endpoint, diff --git a/packages/backend/src/server/api/endpoints/test.ts b/packages/backend/src/server/api/endpoints/test.ts index 9949237a7e3e..d8a240859931 100644 --- a/packages/backend/src/server/api/endpoints/test.ts +++ b/packages/backend/src/server/api/endpoints/test.ts @@ -1,4 +1,5 @@ -import define from '../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['non-productive'], @@ -21,6 +22,12 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { return ps; }); diff --git a/packages/backend/src/server/api/endpoints/username/available.ts b/packages/backend/src/server/api/endpoints/username/available.ts index 3e41aeaed881..0e9ebfe5fdbb 100644 --- a/packages/backend/src/server/api/endpoints/username/available.ts +++ b/packages/backend/src/server/api/endpoints/username/available.ts @@ -1,6 +1,7 @@ import { IsNull } from 'typeorm'; import { Users, UsedUsernames } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['users'], @@ -28,7 +29,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { // Get exist const exist = await Users.countBy({ host: IsNull(), diff --git a/packages/backend/src/server/api/endpoints/users.ts b/packages/backend/src/server/api/endpoints/users.ts index 3a8211374bb7..e1c71a8214a2 100644 --- a/packages/backend/src/server/api/endpoints/users.ts +++ b/packages/backend/src/server/api/endpoints/users.ts @@ -1,5 +1,6 @@ import { Users } from '@/models/index.js'; -import define from '../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { generateMutedUserQueryForUsers } from '../common/generate-muted-user-query.js'; import { generateBlockQueryForUsers } from '../common/generate-block-query.js'; @@ -38,7 +39,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const query = Users.createQueryBuilder('user'); query.where('user.isExplorable = TRUE'); diff --git a/packages/backend/src/server/api/endpoints/users/clips.ts b/packages/backend/src/server/api/endpoints/users/clips.ts index 09fdf27c2339..1b34ce8ceb21 100644 --- a/packages/backend/src/server/api/endpoints/users/clips.ts +++ b/packages/backend/src/server/api/endpoints/users/clips.ts @@ -1,5 +1,6 @@ import { Clips } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; export const meta = { @@ -30,7 +31,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(Clips.createQueryBuilder('clip'), ps.sinceId, ps.untilId) .andWhere('clip.userId = :userId', { userId: ps.userId }) .andWhere('clip.isPublic = true'); diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index 7f9f980764af..a9191b4d07dc 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -1,7 +1,8 @@ import { IsNull } from 'typeorm'; import { Users, Followings, UserProfiles } from '@/models/index.js'; import { toPunyNullable } from '@/misc/convert-host.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -66,7 +67,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const user = await Users.findOneBy(ps.userId != null ? { id: ps.userId } : { usernameLower: ps.username!.toLowerCase(), host: toPunyNullable(ps.host) ?? IsNull() }); diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index 0aaa810f76df..e5cea984341b 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -1,7 +1,8 @@ import { IsNull } from 'typeorm'; import { Users, Followings, UserProfiles } from '@/models/index.js'; import { toPunyNullable } from '@/misc/convert-host.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -66,7 +67,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const user = await Users.findOneBy(ps.userId != null ? { id: ps.userId } : { usernameLower: ps.username!.toLowerCase(), host: toPunyNullable(ps.host) ?? IsNull() }); diff --git a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts index 35bf2df59879..da61ac211e98 100644 --- a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts @@ -1,4 +1,5 @@ -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { GalleryPosts } from '@/models/index.js'; import { makePaginationQuery } from '../../../common/make-pagination-query.js'; @@ -30,7 +31,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) .andWhere(`post.userId = :userId`, { userId: ps.userId }); diff --git a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts index 56965d30661e..376427da7cb6 100644 --- a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts +++ b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts @@ -1,7 +1,8 @@ import { Not, In, IsNull } from 'typeorm'; import { maximum } from '@/prelude/array.js'; import { Notes, Users } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; @@ -51,7 +52,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { // Lookup user const user = await getUser(ps.userId).catch(e => { if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); diff --git a/packages/backend/src/server/api/endpoints/users/groups/create.ts b/packages/backend/src/server/api/endpoints/users/groups/create.ts index 4a6362a3c621..8845359ea8b8 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/create.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/create.ts @@ -2,7 +2,8 @@ import { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { UserGroup } from '@/models/entities/user-group.js'; import { UserGroupJoining } from '@/models/entities/user-group-joining.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['groups'], @@ -29,7 +30,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const userGroup = await UserGroups.insert({ id: genId(), createdAt: new Date(), diff --git a/packages/backend/src/server/api/endpoints/users/groups/delete.ts b/packages/backend/src/server/api/endpoints/users/groups/delete.ts index 2ff1f9aec1c9..eff72d061a4c 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/delete.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/delete.ts @@ -1,5 +1,6 @@ import { UserGroups } from '@/models/index.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -29,7 +30,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const userGroup = await UserGroups.findOneBy({ id: ps.groupId, userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts b/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts index 220fff5f3e92..178b94531a67 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts @@ -2,7 +2,8 @@ import { UserGroupJoinings, UserGroupInvitations } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { UserGroupJoining } from '@/models/entities/user-group-joining.js'; import { ApiError } from '../../../../error.js'; -import define from '../../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['groups', 'users'], @@ -31,7 +32,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { // Fetch the invitation const invitation = await UserGroupInvitations.findOneBy({ id: ps.invitationId, diff --git a/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts b/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts index 8d1d3db734ca..73a359e1f5fa 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts @@ -1,5 +1,6 @@ import { UserGroupInvitations } from '@/models/index.js'; -import define from '../../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../../error.js'; export const meta = { @@ -29,7 +30,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { // Fetch the invitation const invitation = await UserGroupInvitations.findOneBy({ id: ps.invitationId, diff --git a/packages/backend/src/server/api/endpoints/users/groups/invite.ts b/packages/backend/src/server/api/endpoints/users/groups/invite.ts index 1a8d320f3ac3..78c922c6f215 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invite.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invite.ts @@ -4,7 +4,8 @@ import { UserGroupInvitation } from '@/models/entities/user-group-invitation.js' import { createNotification } from '@/services/create-notification.js'; import { getUser } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['groups', 'users'], @@ -52,7 +53,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { // Fetch the group const userGroup = await UserGroups.findOneBy({ id: ps.groupId, diff --git a/packages/backend/src/server/api/endpoints/users/groups/joined.ts b/packages/backend/src/server/api/endpoints/users/groups/joined.ts index 16c6e544e5b0..fddec53fe58f 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/joined.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/joined.ts @@ -1,6 +1,7 @@ import { Not, In } from 'typeorm'; import { UserGroups, UserGroupJoinings } from '@/models/index.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['groups', 'account'], @@ -29,7 +30,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const ownedGroups = await UserGroups.findBy({ userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/users/groups/leave.ts b/packages/backend/src/server/api/endpoints/users/groups/leave.ts index 83dc757db11f..bb0ad57fe794 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/leave.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/leave.ts @@ -1,5 +1,6 @@ import { UserGroups, UserGroupJoinings } from '@/models/index.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -35,7 +36,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { // Fetch the group const userGroup = await UserGroups.findOneBy({ id: ps.groupId, diff --git a/packages/backend/src/server/api/endpoints/users/groups/owned.ts b/packages/backend/src/server/api/endpoints/users/groups/owned.ts index d77cf1a52e0c..cde934d8e459 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/owned.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/owned.ts @@ -1,5 +1,6 @@ import { UserGroups } from '@/models/index.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['groups', 'account'], @@ -28,7 +29,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const userGroups = await UserGroups.findBy({ userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/users/groups/pull.ts b/packages/backend/src/server/api/endpoints/users/groups/pull.ts index ba67a1e5c9cb..77a0321b6d27 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/pull.ts @@ -1,5 +1,6 @@ import { UserGroups, UserGroupJoinings } from '@/models/index.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { getUser } from '../../../common/getters.js'; @@ -43,7 +44,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { // Fetch the group const userGroup = await UserGroups.findOneBy({ id: ps.groupId, diff --git a/packages/backend/src/server/api/endpoints/users/groups/show.ts b/packages/backend/src/server/api/endpoints/users/groups/show.ts index 21e3d9da265c..39fb982c9a73 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/show.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/show.ts @@ -1,5 +1,6 @@ import { UserGroups, UserGroupJoinings } from '@/models/index.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -35,7 +36,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { // Fetch the group const userGroup = await UserGroups.findOneBy({ id: ps.groupId, diff --git a/packages/backend/src/server/api/endpoints/users/groups/transfer.ts b/packages/backend/src/server/api/endpoints/users/groups/transfer.ts index 6456e70dd5f6..56ae222706d8 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/transfer.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/transfer.ts @@ -1,5 +1,6 @@ import { UserGroups, UserGroupJoinings } from '@/models/index.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { getUser } from '../../../common/getters.js'; @@ -49,7 +50,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { // Fetch the group const userGroup = await UserGroups.findOneBy({ id: ps.groupId, diff --git a/packages/backend/src/server/api/endpoints/users/groups/update.ts b/packages/backend/src/server/api/endpoints/users/groups/update.ts index 0a96165fc4be..c4587bf7f9be 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/update.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/update.ts @@ -1,5 +1,6 @@ import { UserGroups } from '@/models/index.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -36,7 +37,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { // Fetch the group const userGroup = await UserGroups.findOneBy({ id: ps.groupId, diff --git a/packages/backend/src/server/api/endpoints/users/lists/create.ts b/packages/backend/src/server/api/endpoints/users/lists/create.ts index 783e63f5dea1..8fa710a4860f 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts @@ -1,7 +1,8 @@ import { UserLists } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { UserList } from '@/models/entities/user-list.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['lists'], @@ -28,7 +29,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const userList = await UserLists.insert({ id: genId(), createdAt: new Date(), diff --git a/packages/backend/src/server/api/endpoints/users/lists/delete.ts b/packages/backend/src/server/api/endpoints/users/lists/delete.ts index 5a7613c98a48..e7df202ae74e 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/delete.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/delete.ts @@ -1,5 +1,6 @@ import { UserLists } from '@/models/index.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -29,7 +30,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const userList = await UserLists.findOneBy({ id: ps.listId, userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/users/lists/list.ts b/packages/backend/src/server/api/endpoints/users/lists/list.ts index 889052fa3014..4dad0524150d 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/list.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/list.ts @@ -1,5 +1,6 @@ import { UserLists } from '@/models/index.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['lists', 'account'], @@ -28,7 +29,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const userLists = await UserLists.findBy({ userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/users/lists/pull.ts b/packages/backend/src/server/api/endpoints/users/lists/pull.ts index d3d1d6555c9e..f4d0ea30aeb6 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/pull.ts @@ -1,6 +1,7 @@ import { publishUserListStream } from '@/services/stream.js'; import { UserLists, UserListJoinings, Users } from '@/models/index.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { getUser } from '../../../common/getters.js'; @@ -38,7 +39,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { // Fetch the list const userList = await UserLists.findOneBy({ id: ps.listId, diff --git a/packages/backend/src/server/api/endpoints/users/lists/push.ts b/packages/backend/src/server/api/endpoints/users/lists/push.ts index 12b7b86342b4..b6e37686441e 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/push.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/push.ts @@ -1,6 +1,7 @@ import { pushUserToUserList } from '@/services/user-list/push.js'; import { UserLists, UserListJoinings, Blockings } from '@/models/index.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { getUser } from '../../../common/getters.js'; @@ -50,7 +51,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { // Fetch the list const userList = await UserLists.findOneBy({ id: ps.listId, diff --git a/packages/backend/src/server/api/endpoints/users/lists/show.ts b/packages/backend/src/server/api/endpoints/users/lists/show.ts index fd0612f7351f..40823ce72a81 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/show.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/show.ts @@ -1,5 +1,6 @@ import { UserLists } from '@/models/index.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -35,7 +36,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { // Fetch the list const userList = await UserLists.findOneBy({ id: ps.listId, diff --git a/packages/backend/src/server/api/endpoints/users/lists/update.ts b/packages/backend/src/server/api/endpoints/users/lists/update.ts index 65e708b95998..b783c8b574f6 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/update.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/update.ts @@ -1,5 +1,6 @@ import { UserLists } from '@/models/index.js'; -import define from '../../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -36,7 +37,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { // Fetch the list const userList = await UserLists.findOneBy({ id: ps.listId, diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 9fa56fe83ac1..28f071445277 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -1,6 +1,7 @@ import { Brackets } from 'typeorm'; import { Notes } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -53,7 +54,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { // Lookup user const user = await getUser(ps.userId).catch(e => { if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); diff --git a/packages/backend/src/server/api/endpoints/users/pages.ts b/packages/backend/src/server/api/endpoints/users/pages.ts index b1d28af8458a..9c86851f9f51 100644 --- a/packages/backend/src/server/api/endpoints/users/pages.ts +++ b/packages/backend/src/server/api/endpoints/users/pages.ts @@ -1,5 +1,6 @@ import { Pages } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; export const meta = { @@ -30,7 +31,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(Pages.createQueryBuilder('page'), ps.sinceId, ps.untilId) .andWhere('page.userId = :userId', { userId: ps.userId }) .andWhere('page.visibility = \'public\''); diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts index 9668bd21b8ba..d5a1b383adf8 100644 --- a/packages/backend/src/server/api/endpoints/users/reactions.ts +++ b/packages/backend/src/server/api/endpoints/users/reactions.ts @@ -1,5 +1,6 @@ import { NoteReactions, UserProfiles } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { ApiError } from '../../error.js'; @@ -44,7 +45,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const profile = await UserProfiles.findOneByOrFail({ userId: ps.userId }); if (me == null || (me.id !== ps.userId && !profile.publicReactions)) { diff --git a/packages/backend/src/server/api/endpoints/users/recommendation.ts b/packages/backend/src/server/api/endpoints/users/recommendation.ts index e7654e1714b0..7f7a83b1a8d7 100644 --- a/packages/backend/src/server/api/endpoints/users/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/users/recommendation.ts @@ -1,6 +1,7 @@ import ms from 'ms'; import { Users, Followings } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { generateMutedUserQueryForUsers } from '../../common/generate-muted-user-query.js'; import { generateBlockedUserQuery, generateBlockQueryForUsers } from '../../common/generate-block-query.js'; @@ -34,7 +35,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const query = Users.createQueryBuilder('user') .where('user.isLocked = FALSE') .andWhere('user.isExplorable = TRUE') diff --git a/packages/backend/src/server/api/endpoints/users/relation.ts b/packages/backend/src/server/api/endpoints/users/relation.ts index 233a6a90b4c3..374368d566bd 100644 --- a/packages/backend/src/server/api/endpoints/users/relation.ts +++ b/packages/backend/src/server/api/endpoints/users/relation.ts @@ -1,5 +1,6 @@ import { Users } from '@/models/index.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['users'], @@ -112,7 +113,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const ids = Array.isArray(ps.userId) ? ps.userId : [ps.userId]; const relations = await Promise.all(ids.map(id => Users.getRelation(me.id, id))); diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts index a9987eafa998..f7b87bfc850b 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -6,7 +6,8 @@ import { sendEmail } from '@/services/send-email.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { getUser } from '../../common/getters.js'; import { ApiError } from '../../error.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['users'], @@ -46,7 +47,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { // Lookup user const user = await getUser(ps.userId).catch(e => { if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index 6e5bc46bb582..d2c67ef24fb0 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -2,7 +2,8 @@ import { Brackets } from 'typeorm'; import { Followings, Users } from '@/models/index.js'; import { USER_ACTIVE_THRESHOLD } from '@/const.js'; import { User } from '@/models/entities/user.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['users'], @@ -39,7 +40,13 @@ export const paramDef = { // TODO: avatar,bannerをJOINしたいけどエラーになる // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日 if (ps.host) { diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index 01729de66751..7ec144af504d 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -1,7 +1,8 @@ import { Brackets } from 'typeorm'; import { UserProfiles, Users } from '@/models/index.js'; import { User } from '@/models/entities/user.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['users'], @@ -34,7 +35,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日 const isUsername = ps.query.startsWith('@'); diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts index 846d83b49fb7..2be5404ba085 100644 --- a/packages/backend/src/server/api/endpoints/users/show.ts +++ b/packages/backend/src/server/api/endpoints/users/show.ts @@ -2,7 +2,8 @@ import { FindOptionsWhere, In, IsNull } from 'typeorm'; import { resolveUser } from '@/remote/resolve-user.js'; import { Users } from '@/models/index.js'; import { User } from '@/models/entities/user.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { apiLogger } from '../../logger.js'; import { ApiError } from '../../error.js'; @@ -78,7 +79,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { let user; const isAdminOrModerator = me && (me.isAdmin || me.isModerator); diff --git a/packages/backend/src/server/api/endpoints/users/stats.ts b/packages/backend/src/server/api/endpoints/users/stats.ts index 47f322ee9b65..5952460a3228 100644 --- a/packages/backend/src/server/api/endpoints/users/stats.ts +++ b/packages/backend/src/server/api/endpoints/users/stats.ts @@ -1,6 +1,7 @@ import { DriveFiles, Followings, NoteFavorites, NoteReactions, Notes, PageLikes, PollVotes, Users } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; -import define from '../../define.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -116,7 +117,13 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, me) => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, me) => { const user = await Users.findOneBy({ id: ps.userId }); if (user == null) { throw new ApiError(meta.errors.noSuchUser); From 6b30a62655845dcbf9cef1233b751b736dc1b21f Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 11 Sep 2022 03:58:46 +0900 Subject: [PATCH 009/180] wip --- .../api/endpoints/admin/abuse-user-reports.ts | 41 +- .../api/endpoints/admin/accounts/create.ts | 47 +- .../api/endpoints/admin/accounts/delete.ts | 61 +- .../server/api/endpoints/admin/ad/create.ts | 29 +- .../server/api/endpoints/admin/ad/delete.ts | 13 +- .../src/server/api/endpoints/admin/ad/list.ts | 15 +- .../server/api/endpoints/admin/ad/update.ts | 33 +- .../endpoints/admin/announcements/create.ts | 25 +- .../endpoints/admin/announcements/delete.ts | 13 +- .../api/endpoints/admin/announcements/list.ts | 45 +- .../endpoints/admin/announcements/update.ts | 23 +- .../api/endpoints/admin/delete-account.ts | 19 +- .../admin/delete-all-files-of-a-user.ts | 17 +- .../admin/drive-capacity-override.ts | 35 +- .../admin/drive/clean-remote-files.ts | 9 +- .../api/endpoints/admin/drive/cleanup.ts | 17 +- .../server/api/endpoints/admin/drive/files.ts | 53 +- .../api/endpoints/admin/drive/show-file.ts | 43 +- .../endpoints/admin/emoji/add-aliases-bulk.ts | 29 +- .../server/api/endpoints/admin/emoji/add.ts | 59 +- .../server/api/endpoints/admin/emoji/copy.ts | 67 ++- .../api/endpoints/admin/emoji/delete-bulk.ts | 27 +- .../api/endpoints/admin/emoji/delete.ts | 23 +- .../api/endpoints/admin/emoji/import-zip.ts | 11 +- .../api/endpoints/admin/emoji/list-remote.ts | 35 +- .../server/api/endpoints/admin/emoji/list.ts | 37 +- .../admin/emoji/remove-aliases-bulk.ts | 29 +- .../endpoints/admin/emoji/set-aliases-bulk.ts | 25 +- .../admin/emoji/set-category-bulk.ts | 25 +- .../api/endpoints/admin/emoji/update.ts | 27 +- .../admin/federation/delete-all-files.ts | 17 +- .../refresh-remote-instance-metadata.ts | 17 +- .../admin/federation/remove-all-following.ts | 25 +- .../admin/federation/update-instance.ts | 21 +- .../api/endpoints/admin/get-index-stats.ts | 31 +- .../api/endpoints/admin/get-table-stats.ts | 43 +- .../api/endpoints/admin/get-user-ips.ts | 27 +- .../src/server/api/endpoints/admin/invite.ts | 43 +- .../src/server/api/endpoints/admin/meta.ts | 179 +++--- .../api/endpoints/admin/moderators/add.ts | 29 +- .../api/endpoints/admin/moderators/remove.ts | 23 +- .../api/endpoints/admin/promo/create.ts | 35 +- .../server/api/endpoints/admin/queue/clear.ts | 11 +- .../endpoints/admin/queue/deliver-delayed.ts | 31 +- .../endpoints/admin/queue/inbox-delayed.ts | 29 +- .../server/api/endpoints/admin/queue/stats.ts | 29 +- .../server/api/endpoints/admin/relays/add.ts | 19 +- .../server/api/endpoints/admin/relays/list.ts | 9 +- .../api/endpoints/admin/relays/remove.ts | 9 +- .../api/endpoints/admin/reset-password.ts | 45 +- .../admin/resolve-abuse-user-report.ts | 35 +- .../server/api/endpoints/admin/send-email.ts | 9 +- .../server/api/endpoints/admin/server-info.ts | 71 ++- .../endpoints/admin/show-moderation-logs.ts | 13 +- .../server/api/endpoints/admin/show-user.ts | 97 +-- .../server/api/endpoints/admin/show-users.ts | 75 +-- .../api/endpoints/admin/silence-user.ts | 35 +- .../api/endpoints/admin/suspend-user.ts | 71 ++- .../api/endpoints/admin/unsilence-user.ts | 29 +- .../api/endpoints/admin/unsuspend-user.ts | 29 +- .../server/api/endpoints/admin/update-meta.ts | 567 +++++++++--------- .../api/endpoints/admin/update-user-note.ts | 23 +- .../src/server/api/endpoints/admin/vacuum.ts | 25 +- .../src/server/api/endpoints/announcements.ts | 39 +- .../server/api/endpoints/antennas/create.ts | 3 + .../server/api/endpoints/antennas/delete.ts | 3 + .../src/server/api/endpoints/antennas/list.ts | 3 + .../server/api/endpoints/antennas/notes.ts | 3 + .../src/server/api/endpoints/antennas/show.ts | 3 + .../server/api/endpoints/antennas/update.ts | 3 + .../src/server/api/endpoints/ap/get.ts | 15 +- .../src/server/api/endpoints/ap/show.ts | 37 +- .../src/server/api/endpoints/app/create.ts | 51 +- .../src/server/api/endpoints/app/show.ts | 41 +- .../src/server/api/endpoints/auth/accept.ts | 79 +-- .../api/endpoints/auth/session/generate.ts | 51 +- .../server/api/endpoints/auth/session/show.ts | 25 +- .../api/endpoints/auth/session/userkey.ts | 83 +-- .../server/api/endpoints/blocking/create.ts | 3 + .../server/api/endpoints/blocking/delete.ts | 3 + .../src/server/api/endpoints/blocking/list.ts | 3 + .../server/api/endpoints/channels/create.ts | 3 + .../server/api/endpoints/channels/featured.ts | 3 + .../server/api/endpoints/channels/follow.ts | 3 + .../server/api/endpoints/channels/followed.ts | 3 + .../server/api/endpoints/channels/owned.ts | 3 + .../src/server/api/endpoints/channels/show.ts | 3 + .../server/api/endpoints/channels/timeline.ts | 3 + .../server/api/endpoints/channels/unfollow.ts | 3 + .../server/api/endpoints/channels/update.ts | 3 + .../api/endpoints/charts/active-users.ts | 3 + .../server/api/endpoints/charts/ap-request.ts | 3 + .../src/server/api/endpoints/charts/drive.ts | 3 + .../server/api/endpoints/charts/federation.ts | 3 + .../server/api/endpoints/charts/hashtag.ts | 3 + .../server/api/endpoints/charts/instance.ts | 3 + .../src/server/api/endpoints/charts/notes.ts | 3 + .../server/api/endpoints/charts/user/drive.ts | 3 + .../api/endpoints/charts/user/following.ts | 3 + .../server/api/endpoints/charts/user/notes.ts | 3 + .../api/endpoints/charts/user/reactions.ts | 3 + .../src/server/api/endpoints/charts/users.ts | 3 + .../server/api/endpoints/clips/add-note.ts | 3 + .../src/server/api/endpoints/clips/create.ts | 3 + .../src/server/api/endpoints/clips/delete.ts | 3 + .../src/server/api/endpoints/clips/list.ts | 3 + .../src/server/api/endpoints/clips/notes.ts | 3 + .../server/api/endpoints/clips/remove-note.ts | 3 + .../src/server/api/endpoints/clips/show.ts | 3 + .../src/server/api/endpoints/clips/update.ts | 3 + .../backend/src/server/api/endpoints/drive.ts | 27 +- .../src/server/api/endpoints/drive/files.ts | 3 + .../endpoints/drive/files/attached-notes.ts | 3 + .../endpoints/drive/files/check-existence.ts | 3 + .../api/endpoints/drive/files/delete.ts | 3 + .../api/endpoints/drive/files/find-by-hash.ts | 3 + .../server/api/endpoints/drive/files/find.ts | 3 + .../server/api/endpoints/drive/files/show.ts | 3 + .../api/endpoints/drive/files/update.ts | 3 + .../src/server/api/endpoints/drive/folders.ts | 3 + .../api/endpoints/drive/folders/create.ts | 3 + .../api/endpoints/drive/folders/delete.ts | 3 + .../api/endpoints/drive/folders/find.ts | 3 + .../api/endpoints/drive/folders/show.ts | 3 + .../api/endpoints/drive/folders/update.ts | 3 + .../src/server/api/endpoints/drive/stream.ts | 3 + .../api/endpoints/email-address/available.ts | 3 + .../src/server/api/endpoints/endpoint.ts | 23 +- .../src/server/api/endpoints/endpoints.ts | 17 +- .../api/endpoints/export-custom-emojis.ts | 11 +- .../api/endpoints/federation/followers.ts | 3 + .../api/endpoints/federation/following.ts | 3 + .../api/endpoints/federation/instances.ts | 3 + .../api/endpoints/federation/show-instance.ts | 3 + .../server/api/endpoints/federation/stats.ts | 3 + .../federation/update-remote-user.ts | 3 + .../server/api/endpoints/federation/users.ts | 3 + .../src/server/api/endpoints/fetch-rss.ts | 35 +- .../server/api/endpoints/following/create.ts | 3 + .../server/api/endpoints/following/delete.ts | 3 + .../api/endpoints/following/invalidate.ts | 3 + .../endpoints/following/requests/accept.ts | 3 + .../endpoints/following/requests/cancel.ts | 3 + .../api/endpoints/following/requests/list.ts | 3 + .../endpoints/following/requests/reject.ts | 3 + .../server/api/endpoints/gallery/featured.ts | 3 + .../server/api/endpoints/gallery/popular.ts | 3 + .../src/server/api/endpoints/gallery/posts.ts | 3 + .../api/endpoints/gallery/posts/create.ts | 3 + .../api/endpoints/gallery/posts/delete.ts | 3 + .../api/endpoints/gallery/posts/like.ts | 3 + .../api/endpoints/gallery/posts/show.ts | 3 + .../api/endpoints/gallery/posts/unlike.ts | 3 + .../api/endpoints/gallery/posts/update.ts | 3 + .../api/endpoints/get-online-users-count.ts | 29 +- .../src/server/api/endpoints/hashtags/list.ts | 3 + .../server/api/endpoints/hashtags/search.ts | 3 + .../src/server/api/endpoints/hashtags/show.ts | 3 + .../server/api/endpoints/hashtags/trend.ts | 11 +- .../server/api/endpoints/hashtags/users.ts | 3 + .../backend/src/server/api/endpoints/i.ts | 29 +- .../src/server/api/endpoints/i/2fa/done.ts | 41 +- .../server/api/endpoints/i/2fa/key-done.ts | 217 +++---- .../api/endpoints/i/2fa/password-less.ts | 13 +- .../api/endpoints/i/2fa/register-key.ts | 65 +- .../server/api/endpoints/i/2fa/register.ts | 65 +- .../server/api/endpoints/i/2fa/remove-key.ts | 41 +- .../server/api/endpoints/i/2fa/unregister.ts | 27 +- .../src/server/api/endpoints/i/apps.ts | 43 +- .../server/api/endpoints/i/authorized-apps.ts | 37 +- .../server/api/endpoints/i/change-password.ts | 31 +- .../server/api/endpoints/i/delete-account.ts | 31 +- .../server/api/endpoints/i/export-blocking.ts | 11 +- .../api/endpoints/i/export-following.ts | 11 +- .../src/server/api/endpoints/i/export-mute.ts | 11 +- .../server/api/endpoints/i/export-notes.ts | 11 +- .../api/endpoints/i/export-user-lists.ts | 11 +- .../src/server/api/endpoints/i/favorites.ts | 25 +- .../server/api/endpoints/i/gallery/likes.ts | 23 +- .../server/api/endpoints/i/gallery/posts.ts | 19 +- .../endpoints/i/get-word-muted-notes-count.ts | 19 +- .../server/api/endpoints/i/import-blocking.ts | 23 +- .../api/endpoints/i/import-following.ts | 23 +- .../server/api/endpoints/i/import-muting.ts | 23 +- .../api/endpoints/i/import-user-lists.ts | 23 +- .../server/api/endpoints/i/notifications.ts | 191 +++--- .../src/server/api/endpoints/i/page-likes.ts | 23 +- .../src/server/api/endpoints/i/pages.ts | 19 +- .../backend/src/server/api/endpoints/i/pin.ts | 29 +- .../i/read-all-messaging-messages.ts | 49 +- .../api/endpoints/i/read-all-unread-notes.ts | 23 +- .../api/endpoints/i/read-announcement.ts | 53 +- .../api/endpoints/i/regenerate-token.ts | 49 +- .../api/endpoints/i/registry/get-all.ts | 27 +- .../api/endpoints/i/registry/get-detail.ts | 37 +- .../server/api/endpoints/i/registry/get.ts | 27 +- .../endpoints/i/registry/keys-with-type.ts | 29 +- .../server/api/endpoints/i/registry/keys.ts | 21 +- .../server/api/endpoints/i/registry/remove.ts | 27 +- .../server/api/endpoints/i/registry/scopes.ts | 29 +- .../server/api/endpoints/i/registry/set.ts | 65 +- .../server/api/endpoints/i/revoke-token.ts | 23 +- .../server/api/endpoints/i/signin-history.ts | 15 +- .../src/server/api/endpoints/i/unpin.ts | 27 +- .../server/api/endpoints/i/update-email.ts | 83 +-- .../src/server/api/endpoints/i/update.ts | 241 ++++---- .../api/endpoints/i/user-group-invites.ts | 21 +- .../server/api/endpoints/i/webhooks/create.ts | 33 +- .../server/api/endpoints/i/webhooks/delete.ts | 27 +- .../server/api/endpoints/i/webhooks/list.ts | 15 +- .../server/api/endpoints/i/webhooks/show.ts | 25 +- .../server/api/endpoints/i/webhooks/update.ts | 39 +- .../server/api/endpoints/messaging/history.ts | 109 ++-- .../api/endpoints/messaging/messages.ts | 141 ++--- .../endpoints/messaging/messages/create.ts | 131 ++-- .../endpoints/messaging/messages/delete.ts | 27 +- .../api/endpoints/messaging/messages/read.ts | 39 +- .../backend/src/server/api/endpoints/meta.ts | 197 +++--- .../server/api/endpoints/miauth/gen-token.ts | 47 +- .../src/server/api/endpoints/mute/create.ts | 95 +-- .../src/server/api/endpoints/mute/delete.ts | 57 +- .../src/server/api/endpoints/mute/list.ts | 21 +- .../src/server/api/endpoints/my/apps.ts | 33 +- .../backend/src/server/api/endpoints/notes.ts | 3 + .../server/api/endpoints/notes/children.ts | 67 ++- .../src/server/api/endpoints/notes/clips.ts | 39 +- .../api/endpoints/notes/conversation.ts | 59 +- .../src/server/api/endpoints/notes/create.ts | 209 +++---- .../src/server/api/endpoints/notes/delete.ts | 27 +- .../api/endpoints/notes/favorites/create.ts | 49 +- .../api/endpoints/notes/favorites/delete.ts | 43 +- .../server/api/endpoints/notes/featured.ts | 65 +- .../api/endpoints/notes/global-timeline.ts | 87 +-- .../api/endpoints/notes/hybrid-timeline.ts | 163 ++--- .../api/endpoints/notes/local-timeline.ts | 114 ++-- .../server/api/endpoints/notes/mentions.ts | 75 +-- .../endpoints/notes/polls/recommendation.ts | 109 ++-- .../server/api/endpoints/notes/polls/vote.ts | 173 +++--- .../server/api/endpoints/notes/reactions.ts | 54 +- .../api/endpoints/notes/reactions/create.ts | 29 +- .../api/endpoints/notes/reactions/delete.ts | 25 +- .../src/server/api/endpoints/notes/renotes.ts | 53 +- .../src/server/api/endpoints/notes/replies.ts | 45 +- .../api/endpoints/notes/search-by-tag.ts | 127 ++-- .../src/server/api/endpoints/notes/search.ts | 177 +++--- .../src/server/api/endpoints/notes/show.ts | 25 +- .../src/server/api/endpoints/notes/state.ts | 67 ++- .../endpoints/notes/thread-muting/create.ts | 51 +- .../endpoints/notes/thread-muting/delete.ts | 27 +- .../server/api/endpoints/notes/timeline.ts | 157 ++--- .../server/api/endpoints/notes/translate.ts | 105 ++-- .../server/api/endpoints/notes/unrenote.ts | 33 +- .../api/endpoints/notes/user-list-timeline.ts | 141 ++--- .../api/endpoints/notes/watching/create.ts | 19 +- .../api/endpoints/notes/watching/delete.ts | 19 +- .../api/endpoints/notifications/create.ts | 29 +- .../notifications/mark-all-as-read.ts | 29 +- .../api/endpoints/notifications/read.ts | 11 +- .../src/server/api/endpoints/page-push.ts | 35 +- .../src/server/api/endpoints/pages/create.ts | 81 +-- .../src/server/api/endpoints/pages/delete.ts | 25 +- .../server/api/endpoints/pages/featured.ts | 21 +- .../src/server/api/endpoints/pages/like.ts | 55 +- .../src/server/api/endpoints/pages/show.ts | 49 +- .../src/server/api/endpoints/pages/unlike.ts | 37 +- .../src/server/api/endpoints/pages/update.ts | 95 +-- .../backend/src/server/api/endpoints/ping.ts | 21 +- .../src/server/api/endpoints/pinned-users.ts | 23 +- .../src/server/api/endpoints/promo/read.ts | 47 +- .../api/endpoints/request-reset-password.ts | 81 +-- .../src/server/api/endpoints/reset-db.ts | 15 +- .../server/api/endpoints/reset-password.ts | 40 +- .../src/server/api/endpoints/server-info.ts | 47 +- .../backend/src/server/api/endpoints/stats.ts | 73 ++- .../src/server/api/endpoints/sw/register.ts | 61 +- .../src/server/api/endpoints/sw/unregister.ts | 17 +- .../backend/src/server/api/endpoints/test.ts | 9 +- .../api/endpoints/username/available.ts | 31 +- .../backend/src/server/api/endpoints/users.ts | 69 ++- .../src/server/api/endpoints/users/clips.ts | 23 +- .../server/api/endpoints/users/followers.ts | 69 ++- .../server/api/endpoints/users/following.ts | 69 ++- .../api/endpoints/users/gallery/posts.ts | 19 +- .../users/get-frequently-replied-users.ts | 113 ++-- .../api/endpoints/users/groups/create.ts | 45 +- .../api/endpoints/users/groups/delete.ts | 25 +- .../users/groups/invitations/accept.ts | 49 +- .../users/groups/invitations/reject.ts | 31 +- .../api/endpoints/users/groups/invite.ts | 103 ++-- .../api/endpoints/users/groups/joined.ts | 33 +- .../api/endpoints/users/groups/leave.ts | 31 +- .../api/endpoints/users/groups/owned.ts | 19 +- .../server/api/endpoints/users/groups/pull.ts | 45 +- .../server/api/endpoints/users/groups/show.ts | 39 +- .../api/endpoints/users/groups/transfer.ts | 67 ++- .../api/endpoints/users/groups/update.ts | 35 +- .../api/endpoints/users/lists/create.ts | 27 +- .../api/endpoints/users/lists/delete.ts | 25 +- .../server/api/endpoints/users/lists/list.ts | 19 +- .../server/api/endpoints/users/lists/pull.ts | 41 +- .../server/api/endpoints/users/lists/push.ts | 81 +-- .../server/api/endpoints/users/lists/show.ts | 29 +- .../api/endpoints/users/lists/update.ts | 35 +- .../src/server/api/endpoints/users/notes.ts | 123 ++-- .../src/server/api/endpoints/users/pages.ts | 23 +- .../server/api/endpoints/users/reactions.ts | 35 +- .../api/endpoints/users/recommendation.ts | 45 +- .../server/api/endpoints/users/relation.ts | 15 +- .../api/endpoints/users/report-abuse.ts | 99 +-- .../users/search-by-username-and-host.ts | 153 ++--- .../src/server/api/endpoints/users/search.ts | 171 +++--- .../src/server/api/endpoints/users/show.ts | 106 ++-- .../src/server/api/endpoints/users/stats.ts | 149 ++--- 313 files changed, 6716 insertions(+), 5229 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts index 0b0645d08ff7..035df998797f 100644 --- a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts +++ b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts @@ -78,8 +78,8 @@ export const paramDef = { sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, state: { type: 'string', nullable: true, default: null }, - reporterOrigin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "combined" }, - targetUserOrigin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "combined" }, + reporterOrigin: { type: 'string', enum: ['combined', 'local', 'remote'], default: 'combined' }, + targetUserOrigin: { type: 'string', enum: ['combined', 'local', 'remote'], default: 'combined' }, forwarded: { type: 'boolean', default: false }, }, required: [], @@ -89,28 +89,33 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(AbuseUserReports.createQueryBuilder('report'), ps.sinceId, ps.untilId); + const query = makePaginationQuery(AbuseUserReports.createQueryBuilder('report'), ps.sinceId, ps.untilId); - switch (ps.state) { - case 'resolved': query.andWhere('report.resolved = TRUE'); break; - case 'unresolved': query.andWhere('report.resolved = FALSE'); break; - } + switch (ps.state) { + case 'resolved': query.andWhere('report.resolved = TRUE'); break; + case 'unresolved': query.andWhere('report.resolved = FALSE'); break; + } - switch (ps.reporterOrigin) { - case 'local': query.andWhere('report.reporterHost IS NULL'); break; - case 'remote': query.andWhere('report.reporterHost IS NOT NULL'); break; - } + switch (ps.reporterOrigin) { + case 'local': query.andWhere('report.reporterHost IS NULL'); break; + case 'remote': query.andWhere('report.reporterHost IS NOT NULL'); break; + } - switch (ps.targetUserOrigin) { - case 'local': query.andWhere('report.targetUserHost IS NULL'); break; - case 'remote': query.andWhere('report.targetUserHost IS NOT NULL'); break; - } + switch (ps.targetUserOrigin) { + case 'local': query.andWhere('report.targetUserHost IS NULL'); break; + case 'remote': query.andWhere('report.targetUserHost IS NOT NULL'); break; + } - const reports = await query.take(ps.limit).getMany(); + const reports = await query.take(ps.limit).getMany(); - return await AbuseUserReports.packMany(reports); -}); + return await AbuseUserReports.packMany(reports); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts index ab4f774cee13..c48f92151c35 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; +import { IsNull } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Users } from '@/models/index.js'; import { signup } from '../../../common/signup.js'; -import { IsNull } from 'typeorm'; export const meta = { tags: ['admin'], @@ -33,27 +33,32 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, _me) => { - const me = _me ? await Users.findOneByOrFail({ id: _me.id }) : null; - const noUsers = (await Users.countBy({ - host: IsNull(), - })) === 0; - if (!noUsers && !me?.isAdmin) throw new Error('access denied'); - - const { account, secret } = await signup({ - username: ps.username, - password: ps.password, - }); - - const res = await Users.pack(account, account, { - detail: true, - includeSecrets: true, - }); - - (res as any).token = secret; - - return res; -}); + const me = _me ? await Users.findOneByOrFail({ id: _me.id }) : null; + const noUsers = (await Users.countBy({ + host: IsNull(), + })) === 0; + if (!noUsers && !me?.isAdmin) throw new Error('access denied'); + + const { account, secret } = await signup({ + username: ps.username, + password: ps.password, + }); + + const res = await Users.pack(account, account, { + detail: true, + includeSecrets: true, + }); + + (res as any).token = secret; + + return res; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts index 4920285b3ee5..1273d172f6b7 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts @@ -24,43 +24,48 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const user = await Users.findOneBy({ id: ps.userId }); + const user = await Users.findOneBy({ id: ps.userId }); - if (user == null) { - throw new Error('user not found'); - } + if (user == null) { + throw new Error('user not found'); + } - if (user.isAdmin) { - throw new Error('cannot suspend admin'); - } + if (user.isAdmin) { + throw new Error('cannot suspend admin'); + } - if (user.isModerator) { - throw new Error('cannot suspend moderator'); - } + if (user.isModerator) { + throw new Error('cannot suspend moderator'); + } - if (Users.isLocalUser(user)) { - // 物理削除する前にDelete activityを送信する - await doPostSuspend(user).catch(e => {}); + if (Users.isLocalUser(user)) { + // 物理削除する前にDelete activityを送信する + await doPostSuspend(user).catch(e => {}); - createDeleteAccountJob(user, { - soft: false, - }); - } else { - createDeleteAccountJob(user, { - soft: true, // リモートユーザーの削除は、完全にDBから物理削除してしまうと再度連合してきてアカウントが復活する可能性があるため、soft指定する - }); - } + createDeleteAccountJob(user, { + soft: false, + }); + } else { + createDeleteAccountJob(user, { + soft: true, // リモートユーザーの削除は、完全にDBから物理削除してしまうと再度連合してきてアカウントが復活する可能性があるため、soft指定する + }); + } - await Users.update(user.id, { - isDeleted: true, - }); + await Users.update(user.id, { + isDeleted: true, + }); - if (Users.isLocalUser(user)) { - // Terminate streaming - publishUserEvent(user.id, 'terminate', {}); + if (Users.isLocalUser(user)) { + // Terminate streaming + publishUserEvent(user.id, 'terminate', {}); + } + }); } -}); +} diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts index 8549d3312802..4895b589fda3 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts @@ -28,19 +28,24 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - await Ads.insert({ - id: genId(), - createdAt: new Date(), - expiresAt: new Date(ps.expiresAt), - url: ps.url, - imageUrl: ps.imageUrl, - priority: ps.priority, - ratio: ps.ratio, - place: ps.place, - memo: ps.memo, - }); -}); + await Ads.insert({ + id: genId(), + createdAt: new Date(), + expiresAt: new Date(ps.expiresAt), + url: ps.url, + imageUrl: ps.imageUrl, + priority: ps.priority, + ratio: ps.ratio, + place: ps.place, + memo: ps.memo, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts index 8b0ff2fdb2f6..f4acb53a68d5 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts @@ -30,13 +30,18 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const ad = await Ads.findOneBy({ id: ps.id }); + const ad = await Ads.findOneBy({ id: ps.id }); - if (ad == null) throw new ApiError(meta.errors.noSuchAd); + if (ad == null) throw new ApiError(meta.errors.noSuchAd); - await Ads.delete(ad.id); -}); + await Ads.delete(ad.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/ad/list.ts b/packages/backend/src/server/api/endpoints/admin/ad/list.ts index a32107be0082..955a6c8cf940 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/list.ts @@ -24,14 +24,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(Ads.createQueryBuilder('ad'), ps.sinceId, ps.untilId) - .andWhere('ad.expiresAt > :now', { now: new Date() }); + const query = makePaginationQuery(Ads.createQueryBuilder('ad'), ps.sinceId, ps.untilId) + .andWhere('ad.expiresAt > :now', { now: new Date() }); - const ads = await query.take(ps.limit).getMany(); + const ads = await query.take(ps.limit).getMany(); - return ads; -}); + return ads; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/ad/update.ts b/packages/backend/src/server/api/endpoints/admin/ad/update.ts index a5ac584d0901..5889b7c9c048 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/update.ts @@ -37,21 +37,26 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const ad = await Ads.findOneBy({ id: ps.id }); - - if (ad == null) throw new ApiError(meta.errors.noSuchAd); - - await Ads.update(ad.id, { - url: ps.url, - place: ps.place, - priority: ps.priority, - ratio: ps.ratio, - memo: ps.memo, - imageUrl: ps.imageUrl, - expiresAt: new Date(ps.expiresAt), - }); -}); + const ad = await Ads.findOneBy({ id: ps.id }); + + if (ad == null) throw new ApiError(meta.errors.noSuchAd); + + await Ads.update(ad.id, { + url: ps.url, + place: ps.place, + priority: ps.priority, + ratio: ps.ratio, + memo: ps.memo, + imageUrl: ps.imageUrl, + expiresAt: new Date(ps.expiresAt), + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts index 44fd1fb8aac7..20a76f95e93c 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts @@ -59,18 +59,23 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const announcement = await Announcements.insert({ - id: genId(), - createdAt: new Date(), - updatedAt: null, - title: ps.title, - text: ps.text, - imageUrl: ps.imageUrl, - }).then(x => Announcements.findOneByOrFail(x.identifiers[0])); + const announcement = await Announcements.insert({ + id: genId(), + createdAt: new Date(), + updatedAt: null, + title: ps.title, + text: ps.text, + imageUrl: ps.imageUrl, + }).then(x => Announcements.findOneByOrFail(x.identifiers[0])); - return Object.assign({}, announcement, { createdAt: announcement.createdAt.toISOString(), updatedAt: null }); -}); + return Object.assign({}, announcement, { createdAt: announcement.createdAt.toISOString(), updatedAt: null }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts b/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts index 51b497006727..511f2094ba5c 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts @@ -30,13 +30,18 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const announcement = await Announcements.findOneBy({ id: ps.id }); + const announcement = await Announcements.findOneBy({ id: ps.id }); - if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement); + if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement); - await Announcements.delete(announcement.id); -}); + await Announcements.delete(announcement.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts index 92786fa3f179..df23f732a89d 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts @@ -1,6 +1,6 @@ -import { Announcements, AnnouncementReads } from '@/models/index.js'; -import { Announcement } from '@/models/entities/announcement.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Announcements, AnnouncementReads } from '@/models/index.js'; +import type { Announcement } from '@/models/entities/announcement.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../../../common/make-pagination-query.js'; @@ -68,29 +68,34 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(Announcements.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); + const query = makePaginationQuery(Announcements.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); - const announcements = await query.take(ps.limit).getMany(); + const announcements = await query.take(ps.limit).getMany(); - const reads = new Map(); + const reads = new Map(); - for (const announcement of announcements) { - reads.set(announcement, await AnnouncementReads.countBy({ - announcementId: announcement.id, - })); - } + for (const announcement of announcements) { + reads.set(announcement, await AnnouncementReads.countBy({ + announcementId: announcement.id, + })); + } - return announcements.map(announcement => ({ - id: announcement.id, - createdAt: announcement.createdAt.toISOString(), - updatedAt: announcement.updatedAt?.toISOString() ?? null, - title: announcement.title, - text: announcement.text, - imageUrl: announcement.imageUrl, - reads: reads.get(announcement)!, - })); -}); + return announcements.map(announcement => ({ + id: announcement.id, + createdAt: announcement.createdAt.toISOString(), + updatedAt: announcement.updatedAt?.toISOString() ?? null, + title: announcement.title, + text: announcement.text, + imageUrl: announcement.imageUrl, + reads: reads.get(announcement)!, + })); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts index 7b253418ffa7..dd23c97429bf 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts @@ -33,18 +33,23 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const announcement = await Announcements.findOneBy({ id: ps.id }); + const announcement = await Announcements.findOneBy({ id: ps.id }); - if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement); + if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement); - await Announcements.update(announcement.id, { - updatedAt: new Date(), - title: ps.title, - text: ps.text, - imageUrl: ps.imageUrl, - }); -}); + await Announcements.update(announcement.id, { + updatedAt: new Date(), + title: ps.title, + text: ps.text, + imageUrl: ps.imageUrl, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/delete-account.ts b/packages/backend/src/server/api/endpoints/admin/delete-account.ts index 25654c7dadc2..700015e7484c 100644 --- a/packages/backend/src/server/api/endpoints/admin/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/admin/delete-account.ts @@ -1,6 +1,6 @@ +import { Inject, Injectable } from '@nestjs/common'; import { Users } from '@/models/index.js'; import { deleteAccount } from '@/services/delete-account.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -25,14 +25,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const user = await Users.findOneByOrFail({ id: ps.userId }); - if (user.isDeleted) { - return; - } + const user = await Users.findOneByOrFail({ id: ps.userId }); + if (user.isDeleted) { + return; + } - await deleteAccount(user); -}); + await deleteAccount(user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts index bd6ab82a79af..8ef9fe500b2d 100644 --- a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts @@ -22,15 +22,20 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const files = await DriveFiles.findBy({ - userId: ps.userId, - }); + const files = await DriveFiles.findBy({ + userId: ps.userId, + }); - for (const file of files) { - deleteFile(file); + for (const file of files) { + deleteFile(file); + } + }); } -}); +} diff --git a/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts b/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts index 4914a51cf36c..78078091df89 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts @@ -23,32 +23,37 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const user = await Users.findOneBy({ id: ps.userId }); + const user = await Users.findOneBy({ id: ps.userId }); - if (user == null) { - throw new Error('user not found'); - } + if (user == null) { + throw new Error('user not found'); + } - if (!Users.isLocalUser(user)) { - throw new Error('user is not local user'); - } + if (!Users.isLocalUser(user)) { + throw new Error('user is not local user'); + } - /*if (user.isAdmin) { + /*if (user.isAdmin) { throw new Error('cannot suspend admin'); } if (user.isModerator) { throw new Error('cannot suspend moderator'); }*/ - await Users.update(user.id, { - driveCapacityOverrideMb: ps.overrideMb, - }); + await Users.update(user.id, { + driveCapacityOverrideMb: ps.overrideMb, + }); - insertModerationLog(me, 'change-drive-capacity-override', { - targetId: user.id, - }); -}); + insertModerationLog(me, 'change-drive-capacity-override', { + targetId: user.id, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts b/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts index 7749f7a88d5b..185765a97108 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts @@ -19,9 +19,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - createCleanRemoteFilesJob(); -}); + createCleanRemoteFilesJob(); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts b/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts index f02106efa008..113dd1444b65 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts @@ -21,15 +21,20 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const files = await DriveFiles.findBy({ - userId: IsNull(), - }); + const files = await DriveFiles.findBy({ + userId: IsNull(), + }); - for (const file of files) { - deleteFile(file); + for (const file of files) { + deleteFile(file); + } + }); } -}); +} diff --git a/packages/backend/src/server/api/endpoints/admin/drive/files.ts b/packages/backend/src/server/api/endpoints/admin/drive/files.ts index 06a49bab19f5..6fd0209c4073 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/files.ts @@ -1,5 +1,5 @@ -import { DriveFiles } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { DriveFiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../../../common/make-pagination-query.js'; @@ -43,35 +43,40 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId); + const query = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId); - if (ps.userId) { - query.andWhere('file.userId = :userId', { userId: ps.userId }); - } else { - if (ps.origin === 'local') { - query.andWhere('file.userHost IS NULL'); - } else if (ps.origin === 'remote') { - query.andWhere('file.userHost IS NOT NULL'); - } + if (ps.userId) { + query.andWhere('file.userId = :userId', { userId: ps.userId }); + } else { + if (ps.origin === 'local') { + query.andWhere('file.userHost IS NULL'); + } else if (ps.origin === 'remote') { + query.andWhere('file.userHost IS NOT NULL'); + } - if (ps.hostname) { - query.andWhere('file.userHost = :hostname', { hostname: ps.hostname }); - } - } + if (ps.hostname) { + query.andWhere('file.userHost = :hostname', { hostname: ps.hostname }); + } + } - if (ps.type) { - if (ps.type.endsWith('/*')) { - query.andWhere('file.type like :type', { type: ps.type.replace('/*', '/') + '%' }); - } else { - query.andWhere('file.type = :type', { type: ps.type }); - } - } + if (ps.type) { + if (ps.type.endsWith('/*')) { + query.andWhere('file.type like :type', { type: ps.type.replace('/*', '/') + '%' }); + } else { + query.andWhere('file.type = :type', { type: ps.type }); + } + } - const files = await query.take(ps.limit).getMany(); + const files = await query.take(ps.limit).getMany(); - return await DriveFiles.packMany(files, { detail: true, withUser: true, self: true }); -}); + return await DriveFiles.packMany(files, { detail: true, withUser: true, self: true }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts index aee8439ebdd3..8ba3137dd826 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts @@ -1,5 +1,5 @@ -import { DriveFiles } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { DriveFiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; @@ -173,28 +173,33 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const file = ps.fileId ? await DriveFiles.findOneBy({ id: ps.fileId }) : await DriveFiles.findOne({ - where: [{ - url: ps.url, - }, { - thumbnailUrl: ps.url, - }, { - webpublicUrl: ps.url, - }], - }); + const file = ps.fileId ? await DriveFiles.findOneBy({ id: ps.fileId }) : await DriveFiles.findOne({ + where: [{ + url: ps.url, + }, { + thumbnailUrl: ps.url, + }, { + webpublicUrl: ps.url, + }], + }); - if (file == null) { - throw new ApiError(meta.errors.noSuchFile); - } + if (file == null) { + throw new ApiError(meta.errors.noSuchFile); + } - if (!me.isAdmin) { - delete file.requestIp; - delete file.requestHeaders; - } + if (!me.isAdmin) { + delete file.requestIp; + delete file.requestHeaders; + } - return file; -}); + return file; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts index 263bd56c4b37..356430601279 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; +import { In } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; -import { In } from 'typeorm'; -import { ApiError } from '../../../error.js'; import { db } from '@/db/postgre.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['admin'], @@ -29,20 +29,25 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const emojis = await Emojis.findBy({ - id: In(ps.ids), - }); + const emojis = await Emojis.findBy({ + id: In(ps.ids), + }); + + for (const emoji of emojis) { + await Emojis.update(emoji.id, { + updatedAt: new Date(), + aliases: [...new Set(emoji.aliases.concat(ps.aliases))], + }); + } - for (const emoji of emojis) { - await Emojis.update(emoji.id, { - updatedAt: new Date(), - aliases: [...new Set(emoji.aliases.concat(ps.aliases))], + await db.queryResultCache!.remove(['meta_emojis']); }); } - - await db.queryResultCache!.remove(['meta_emojis']); -}); +} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index ed3521d8493b..f9823687fb82 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -1,12 +1,12 @@ import { Inject, Injectable } from '@nestjs/common'; +import rndstr from 'rndstr'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis, DriveFiles } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { insertModerationLog } from '@/services/insert-moderation-log.js'; -import { ApiError } from '../../../error.js'; -import rndstr from 'rndstr'; import { publishBroadcastStream } from '@/services/stream.js'; import { db } from '@/db/postgre.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['admin'], @@ -35,39 +35,44 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const file = await DriveFiles.findOneBy({ id: ps.fileId }); + const file = await DriveFiles.findOneBy({ id: ps.fileId }); - if (file == null) throw new ApiError(meta.errors.noSuchFile); + if (file == null) throw new ApiError(meta.errors.noSuchFile); - const name = file.name.split('.')[0].match(/^[a-z0-9_]+$/) ? file.name.split('.')[0] : `_${rndstr('a-z0-9', 8)}_`; + const name = file.name.split('.')[0].match(/^[a-z0-9_]+$/) ? file.name.split('.')[0] : `_${rndstr('a-z0-9', 8)}_`; - const emoji = await Emojis.insert({ - id: genId(), - updatedAt: new Date(), - name: name, - category: null, - host: null, - aliases: [], - originalUrl: file.url, - publicUrl: file.webpublicUrl ?? file.url, - type: file.webpublicType ?? file.type, - }).then(x => Emojis.findOneByOrFail(x.identifiers[0])); + const emoji = await Emojis.insert({ + id: genId(), + updatedAt: new Date(), + name: name, + category: null, + host: null, + aliases: [], + originalUrl: file.url, + publicUrl: file.webpublicUrl ?? file.url, + type: file.webpublicType ?? file.type, + }).then(x => Emojis.findOneByOrFail(x.identifiers[0])); - await db.queryResultCache!.remove(['meta_emojis']); + await db.queryResultCache!.remove(['meta_emojis']); - publishBroadcastStream('emojiAdded', { - emoji: await Emojis.pack(emoji.id), - }); + publishBroadcastStream('emojiAdded', { + emoji: await Emojis.pack(emoji.id), + }); - insertModerationLog(me, 'addEmoji', { - emojiId: emoji.id, - }); + insertModerationLog(me, 'addEmoji', { + emojiId: emoji.id, + }); - return { - id: emoji.id, - }; -}); + return { + id: emoji.id, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index 23183fd8151f..4c7fa295e859 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -2,11 +2,11 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; -import { ApiError } from '../../../error.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; +import type { DriveFile } from '@/models/entities/drive-file.js'; import { uploadFromUrl } from '@/services/drive/upload-from-url.js'; import { publishBroadcastStream } from '@/services/stream.js'; import { db } from '@/db/postgre.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['admin'], @@ -47,43 +47,48 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const emoji = await Emojis.findOneBy({ id: ps.emojiId }); + const emoji = await Emojis.findOneBy({ id: ps.emojiId }); - if (emoji == null) { - throw new ApiError(meta.errors.noSuchEmoji); - } + if (emoji == null) { + throw new ApiError(meta.errors.noSuchEmoji); + } - let driveFile: DriveFile; + let driveFile: DriveFile; - try { - // Create file - driveFile = await uploadFromUrl({ url: emoji.originalUrl, user: null, force: true }); - } catch (e) { - throw new ApiError(); - } + try { + // Create file + driveFile = await uploadFromUrl({ url: emoji.originalUrl, user: null, force: true }); + } catch (e) { + throw new ApiError(); + } - const copied = await Emojis.insert({ - id: genId(), - updatedAt: new Date(), - name: emoji.name, - host: null, - aliases: [], - originalUrl: driveFile.url, - publicUrl: driveFile.webpublicUrl ?? driveFile.url, - type: driveFile.webpublicType ?? driveFile.type, - }).then(x => Emojis.findOneByOrFail(x.identifiers[0])); + const copied = await Emojis.insert({ + id: genId(), + updatedAt: new Date(), + name: emoji.name, + host: null, + aliases: [], + originalUrl: driveFile.url, + publicUrl: driveFile.webpublicUrl ?? driveFile.url, + type: driveFile.webpublicType ?? driveFile.type, + }).then(x => Emojis.findOneByOrFail(x.identifiers[0])); - await db.queryResultCache!.remove(['meta_emojis']); + await db.queryResultCache!.remove(['meta_emojis']); - publishBroadcastStream('emojiAdded', { - emoji: await Emojis.pack(copied.id), - }); + publishBroadcastStream('emojiAdded', { + emoji: await Emojis.pack(copied.id), + }); - return { - id: copied.id, - }; -}); + return { + id: copied.id, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts index 552559820399..3182ed33bdd7 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts @@ -1,10 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; +import { In } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; -import { In } from 'typeorm'; import { insertModerationLog } from '@/services/insert-moderation-log.js'; -import { ApiError } from '../../../error.js'; import { db } from '@/db/postgre.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['admin'], @@ -27,21 +27,26 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const emojis = await Emojis.findBy({ - id: In(ps.ids), - }); + const emojis = await Emojis.findBy({ + id: In(ps.ids), + }); - for (const emoji of emojis) { - await Emojis.delete(emoji.id); + for (const emoji of emojis) { + await Emojis.delete(emoji.id); - await db.queryResultCache!.remove(['meta_emojis']); + await db.queryResultCache!.remove(['meta_emojis']); - insertModerationLog(me, 'deleteEmoji', { - emoji: emoji, + insertModerationLog(me, 'deleteEmoji', { + emoji: emoji, + }); + } }); } -}); +} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts index b82c2af401a5..eb2d35718395 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts @@ -2,8 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; import { insertModerationLog } from '@/services/insert-moderation-log.js'; -import { ApiError } from '../../../error.js'; import { db } from '@/db/postgre.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['admin'], @@ -32,19 +32,24 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const emoji = await Emojis.findOneBy({ id: ps.id }); + const emoji = await Emojis.findOneBy({ id: ps.id }); - if (emoji == null) throw new ApiError(meta.errors.noSuchEmoji); + if (emoji == null) throw new ApiError(meta.errors.noSuchEmoji); - await Emojis.delete(emoji.id); + await Emojis.delete(emoji.id); - await db.queryResultCache!.remove(['meta_emojis']); + await db.queryResultCache!.remove(['meta_emojis']); - insertModerationLog(me, 'deleteEmoji', { - emoji: emoji, - }); -}); + insertModerationLog(me, 'deleteEmoji', { + emoji: emoji, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts index 699876dff0f9..76e1f76c017c 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; +import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { createImportCustomEmojisJob } from '@/queue/index.js'; -import ms from 'ms'; export const meta = { secure: true, @@ -21,9 +21,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - createImportCustomEmojisJob(user, ps.fileId); -}); + createImportCustomEmojisJob(user, ps.fileId); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts index 29e731c676c9..4a044c888245 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts @@ -73,26 +73,31 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const q = makePaginationQuery(Emojis.createQueryBuilder('emoji'), ps.sinceId, ps.untilId); + const q = makePaginationQuery(Emojis.createQueryBuilder('emoji'), ps.sinceId, ps.untilId); - if (ps.host == null) { - q.andWhere(`emoji.host IS NOT NULL`); - } else { - q.andWhere(`emoji.host = :host`, { host: toPuny(ps.host) }); - } + if (ps.host == null) { + q.andWhere('emoji.host IS NOT NULL'); + } else { + q.andWhere('emoji.host = :host', { host: toPuny(ps.host) }); + } - if (ps.query) { - q.andWhere('emoji.name like :query', { query: '%' + ps.query + '%' }); - } + if (ps.query) { + q.andWhere('emoji.name like :query', { query: '%' + ps.query + '%' }); + } - const emojis = await q - .orderBy('emoji.id', 'DESC') - .take(ps.limit) - .getMany(); + const emojis = await q + .orderBy('emoji.id', 'DESC') + .take(ps.limit) + .getMany(); - return Emojis.packMany(emojis); -}); + return Emojis.packMany(emojis); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts index c3f5cfc67fc4..6c05cd4346f2 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; +import type { Emoji } from '@/models/entities/emoji.js'; import { makePaginationQuery } from '../../../common/make-pagination-query.js'; -import { Emoji } from '@/models/entities/emoji.js'; export const meta = { tags: ['admin'], @@ -67,30 +67,35 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const q = makePaginationQuery(Emojis.createQueryBuilder('emoji'), ps.sinceId, ps.untilId) - .andWhere(`emoji.host IS NULL`); + const q = makePaginationQuery(Emojis.createQueryBuilder('emoji'), ps.sinceId, ps.untilId) + .andWhere('emoji.host IS NULL'); - let emojis: Emoji[]; + let emojis: Emoji[]; - if (ps.query) { - //q.andWhere('emoji.name ILIKE :q', { q: `%${ps.query}%` }); - //const emojis = await q.take(ps.limit).getMany(); + if (ps.query) { + //q.andWhere('emoji.name ILIKE :q', { q: `%${ps.query}%` }); + //const emojis = await q.take(ps.limit).getMany(); - emojis = await q.getMany(); + emojis = await q.getMany(); - emojis = emojis.filter(emoji => - emoji.name.includes(ps.query!) || + emojis = emojis.filter(emoji => + emoji.name.includes(ps.query!) || emoji.aliases.some(a => a.includes(ps.query!)) || emoji.category?.includes(ps.query!)); - emojis.splice(ps.limit + 1); - } else { - emojis = await q.take(ps.limit).getMany(); - } + emojis.splice(ps.limit + 1); + } else { + emojis = await q.take(ps.limit).getMany(); + } - return Emojis.packMany(emojis); -}); + return Emojis.packMany(emojis); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts index 254add60a210..295292a6691c 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; +import { In } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; -import { In } from 'typeorm'; -import { ApiError } from '../../../error.js'; import { db } from '@/db/postgre.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['admin'], @@ -29,20 +29,25 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const emojis = await Emojis.findBy({ - id: In(ps.ids), - }); + const emojis = await Emojis.findBy({ + id: In(ps.ids), + }); + + for (const emoji of emojis) { + await Emojis.update(emoji.id, { + updatedAt: new Date(), + aliases: emoji.aliases.filter(x => !ps.aliases.includes(x)), + }); + } - for (const emoji of emojis) { - await Emojis.update(emoji.id, { - updatedAt: new Date(), - aliases: emoji.aliases.filter(x => !ps.aliases.includes(x)), + await db.queryResultCache!.remove(['meta_emojis']); }); } - - await db.queryResultCache!.remove(['meta_emojis']); -}); +} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts index 466444b61756..77538d666847 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; +import { In } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; -import { In } from 'typeorm'; -import { ApiError } from '../../../error.js'; import { db } from '@/db/postgre.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['admin'], @@ -29,16 +29,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - await Emojis.update({ - id: In(ps.ids), - }, { - updatedAt: new Date(), - aliases: ps.aliases, - }); + await Emojis.update({ + id: In(ps.ids), + }, { + updatedAt: new Date(), + aliases: ps.aliases, + }); - await db.queryResultCache!.remove(['meta_emojis']); -}); + await db.queryResultCache!.remove(['meta_emojis']); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts index cfdaf52c8999..209f91df642e 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; +import { In } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; -import { In } from 'typeorm'; -import { ApiError } from '../../../error.js'; import { db } from '@/db/postgre.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['admin'], @@ -31,16 +31,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - await Emojis.update({ - id: In(ps.ids), - }, { - updatedAt: new Date(), - category: ps.category, - }); + await Emojis.update({ + id: In(ps.ids), + }, { + updatedAt: new Date(), + category: ps.category, + }); - await db.queryResultCache!.remove(['meta_emojis']); -}); + await db.queryResultCache!.remove(['meta_emojis']); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index d322ca8e0bfb..78e063ddc05b 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; -import { ApiError } from '../../../error.js'; import { db } from '@/db/postgre.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['admin'], @@ -40,20 +40,25 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const emoji = await Emojis.findOneBy({ id: ps.id }); + const emoji = await Emojis.findOneBy({ id: ps.id }); - if (emoji == null) throw new ApiError(meta.errors.noSuchEmoji); + if (emoji == null) throw new ApiError(meta.errors.noSuchEmoji); - await Emojis.update(emoji.id, { - updatedAt: new Date(), - name: ps.name, - category: ps.category, - aliases: ps.aliases, - }); + await Emojis.update(emoji.id, { + updatedAt: new Date(), + name: ps.name, + category: ps.category, + aliases: ps.aliases, + }); - await db.queryResultCache!.remove(['meta_emojis']); -}); + await db.queryResultCache!.remove(['meta_emojis']); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts index e1bab7504c5c..16b775f1399d 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts @@ -22,15 +22,20 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const files = await DriveFiles.findBy({ - userHost: ps.host, - }); + const files = await DriveFiles.findBy({ + userHost: ps.host, + }); - for (const file of files) { - deleteFile(file); + for (const file of files) { + deleteFile(file); + } + }); } -}); +} diff --git a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts index ce169f6b1bdb..5bf49af92695 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts @@ -23,15 +23,20 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const instance = await Instances.findOneBy({ host: toPuny(ps.host) }); + const instance = await Instances.findOneBy({ host: toPuny(ps.host) }); - if (instance == null) { - throw new Error('instance not found'); - } + if (instance == null) { + throw new Error('instance not found'); + } - fetchInstanceMetadata(instance, true); -}); + fetchInstanceMetadata(instance, true); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts index 4c420f41e2dc..40821733df91 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts @@ -22,20 +22,25 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const followings = await Followings.findBy({ - followerHost: ps.host, - }); + const followings = await Followings.findBy({ + followerHost: ps.host, + }); - const pairs = await Promise.all(followings.map(f => Promise.all([ - Users.findOneByOrFail({ id: f.followerId }), - Users.findOneByOrFail({ id: f.followeeId }), - ]))); + const pairs = await Promise.all(followings.map(f => Promise.all([ + Users.findOneByOrFail({ id: f.followerId }), + Users.findOneByOrFail({ id: f.followeeId }), + ]))); - for (const pair of pairs) { - deleteFollowing(pair[0], pair[1]); + for (const pair of pairs) { + deleteFollowing(pair[0], pair[1]); + } + }); } -}); +} diff --git a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts index 828966fc7c55..21d59c3d6440 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts @@ -23,17 +23,22 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const instance = await Instances.findOneBy({ host: toPuny(ps.host) }); + const instance = await Instances.findOneBy({ host: toPuny(ps.host) }); - if (instance == null) { - throw new Error('instance not found'); - } + if (instance == null) { + throw new Error('instance not found'); + } - Instances.update({ host: toPuny(ps.host) }, { - isSuspended: ps.isSuspended, - }); -}); + Instances.update({ host: toPuny(ps.host) }, { + isSuspended: ps.isSuspended, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts index 9be4e6ac53f0..ef1515763220 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts @@ -16,14 +16,25 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async () => { - const stats = await db.query(`SELECT * FROM pg_indexes;`).then(recs => { - const res = [] as { tablename: string; indexname: string; }[]; - for (const rec of recs) { - res.push(rec); - } - return res; - }); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, - return stats; -}); + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async () => { + const stats = await db.query('SELECT * FROM pg_indexes;').then(recs => { + const res = [] as { tablename: string; indexname: string; }[]; + for (const rec of recs) { + res.push(rec); + } + return res; + }); + + return stats; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts index efbf39259595..57f46e540a74 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts @@ -1,5 +1,5 @@ -import { db } from '@/db/postgre.js'; import { Inject, Injectable } from '@nestjs/common'; +import { db } from '@/db/postgre.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -27,24 +27,35 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async () => { - const sizes = await - db.query(` +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async () => { + const sizes = await + db.query(` SELECT relname AS "table", reltuples as "count", pg_total_relation_size(C.oid) AS "size" FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) WHERE nspname NOT IN ('pg_catalog', 'information_schema') AND C.relkind <> 'i' AND nspname !~ '^pg_toast';`) - .then(recs => { - const res = {} as Record; - for (const rec of recs) { - res[rec.table] = { - count: parseInt(rec.count, 10), - size: parseInt(rec.size, 10), - }; - } - return res; - }); + .then(recs => { + const res = {} as Record; + for (const rec of recs) { + res[rec.table] = { + count: parseInt(rec.count, 10), + size: parseInt(rec.size, 10), + }; + } + return res; + }); - return sizes; -}); + return sizes; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts index 9195e8a87966..0a1d974b0dcf 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts @@ -1,5 +1,5 @@ -import { UserIps } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { UserIps } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -21,18 +21,23 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const ips = await UserIps.find({ - where: { userId: ps.userId }, - order: { createdAt: 'DESC' }, - take: 30, - }); + const ips = await UserIps.find({ + where: { userId: ps.userId }, + order: { createdAt: 'DESC' }, + take: 30, + }); - return ips.map(x => ({ - ip: x.ip, - createdAt: x.createdAt.toISOString(), - })); -}); + return ips.map(x => ({ + ip: x.ip, + createdAt: x.createdAt.toISOString(), + })); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/invite.ts b/packages/backend/src/server/api/endpoints/admin/invite.ts index 10dd44c857bd..49397b2e0b1b 100644 --- a/packages/backend/src/server/api/endpoints/admin/invite.ts +++ b/packages/backend/src/server/api/endpoints/admin/invite.ts @@ -32,19 +32,30 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async () => { - const code = rndstr({ - length: 8, - chars: '2-9A-HJ-NP-Z', // [0-9A-Z] w/o [01IO] (32 patterns) - }); - - await RegistrationTickets.insert({ - id: genId(), - createdAt: new Date(), - code, - }); - - return { - code, - }; -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async () => { + const code = rndstr({ + length: 8, + chars: '2-9A-HJ-NP-Z', // [0-9A-Z] w/o [01IO] (32 patterns) + }); + + await RegistrationTickets.insert({ + id: genId(), + createdAt: new Date(), + code, + }); + + return { + code, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 4a3b099eed6a..026a201a35d6 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -1,7 +1,7 @@ +import { Inject, Injectable } from '@nestjs/common'; import config from '@/config/index.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -344,94 +344,99 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const instance = await fetchMeta(true); + const instance = await fetchMeta(true); - return { - maintainerName: instance.maintainerName, - maintainerEmail: instance.maintainerEmail, - version: config.version, - name: instance.name, - uri: config.url, - description: instance.description, - langs: instance.langs, - tosUrl: instance.ToSUrl, - repositoryUrl: instance.repositoryUrl, - feedbackUrl: instance.feedbackUrl, - disableRegistration: instance.disableRegistration, - disableLocalTimeline: instance.disableLocalTimeline, - disableGlobalTimeline: instance.disableGlobalTimeline, - driveCapacityPerLocalUserMb: instance.localDriveCapacityMb, - driveCapacityPerRemoteUserMb: instance.remoteDriveCapacityMb, - emailRequiredForSignup: instance.emailRequiredForSignup, - enableHcaptcha: instance.enableHcaptcha, - hcaptchaSiteKey: instance.hcaptchaSiteKey, - enableRecaptcha: instance.enableRecaptcha, - recaptchaSiteKey: instance.recaptchaSiteKey, - swPublickey: instance.swPublicKey, - themeColor: instance.themeColor, - mascotImageUrl: instance.mascotImageUrl, - bannerUrl: instance.bannerUrl, - errorImageUrl: instance.errorImageUrl, - iconUrl: instance.iconUrl, - backgroundImageUrl: instance.backgroundImageUrl, - logoImageUrl: instance.logoImageUrl, - maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, // 後方互換性のため - defaultLightTheme: instance.defaultLightTheme, - defaultDarkTheme: instance.defaultDarkTheme, - enableEmail: instance.enableEmail, - enableTwitterIntegration: instance.enableTwitterIntegration, - enableGithubIntegration: instance.enableGithubIntegration, - enableDiscordIntegration: instance.enableDiscordIntegration, - enableServiceWorker: instance.enableServiceWorker, - translatorAvailable: instance.deeplAuthKey != null, - pinnedPages: instance.pinnedPages, - pinnedClipId: instance.pinnedClipId, - cacheRemoteFiles: instance.cacheRemoteFiles, - useStarForReactionFallback: instance.useStarForReactionFallback, - pinnedUsers: instance.pinnedUsers, - hiddenTags: instance.hiddenTags, - blockedHosts: instance.blockedHosts, - hcaptchaSecretKey: instance.hcaptchaSecretKey, - recaptchaSecretKey: instance.recaptchaSecretKey, - sensitiveMediaDetection: instance.sensitiveMediaDetection, - sensitiveMediaDetectionSensitivity: instance.sensitiveMediaDetectionSensitivity, - setSensitiveFlagAutomatically: instance.setSensitiveFlagAutomatically, - enableSensitiveMediaDetectionForVideos: instance.enableSensitiveMediaDetectionForVideos, - proxyAccountId: instance.proxyAccountId, - twitterConsumerKey: instance.twitterConsumerKey, - twitterConsumerSecret: instance.twitterConsumerSecret, - githubClientId: instance.githubClientId, - githubClientSecret: instance.githubClientSecret, - discordClientId: instance.discordClientId, - discordClientSecret: instance.discordClientSecret, - summalyProxy: instance.summalyProxy, - email: instance.email, - smtpSecure: instance.smtpSecure, - smtpHost: instance.smtpHost, - smtpPort: instance.smtpPort, - smtpUser: instance.smtpUser, - smtpPass: instance.smtpPass, - swPrivateKey: instance.swPrivateKey, - useObjectStorage: instance.useObjectStorage, - objectStorageBaseUrl: instance.objectStorageBaseUrl, - objectStorageBucket: instance.objectStorageBucket, - objectStoragePrefix: instance.objectStoragePrefix, - objectStorageEndpoint: instance.objectStorageEndpoint, - objectStorageRegion: instance.objectStorageRegion, - objectStoragePort: instance.objectStoragePort, - objectStorageAccessKey: instance.objectStorageAccessKey, - objectStorageSecretKey: instance.objectStorageSecretKey, - objectStorageUseSSL: instance.objectStorageUseSSL, - objectStorageUseProxy: instance.objectStorageUseProxy, - objectStorageSetPublicRead: instance.objectStorageSetPublicRead, - objectStorageS3ForcePathStyle: instance.objectStorageS3ForcePathStyle, - deeplAuthKey: instance.deeplAuthKey, - deeplIsPro: instance.deeplIsPro, - enableIpLogging: instance.enableIpLogging, - enableActiveEmailValidation: instance.enableActiveEmailValidation, - }; -}); + return { + maintainerName: instance.maintainerName, + maintainerEmail: instance.maintainerEmail, + version: config.version, + name: instance.name, + uri: config.url, + description: instance.description, + langs: instance.langs, + tosUrl: instance.ToSUrl, + repositoryUrl: instance.repositoryUrl, + feedbackUrl: instance.feedbackUrl, + disableRegistration: instance.disableRegistration, + disableLocalTimeline: instance.disableLocalTimeline, + disableGlobalTimeline: instance.disableGlobalTimeline, + driveCapacityPerLocalUserMb: instance.localDriveCapacityMb, + driveCapacityPerRemoteUserMb: instance.remoteDriveCapacityMb, + emailRequiredForSignup: instance.emailRequiredForSignup, + enableHcaptcha: instance.enableHcaptcha, + hcaptchaSiteKey: instance.hcaptchaSiteKey, + enableRecaptcha: instance.enableRecaptcha, + recaptchaSiteKey: instance.recaptchaSiteKey, + swPublickey: instance.swPublicKey, + themeColor: instance.themeColor, + mascotImageUrl: instance.mascotImageUrl, + bannerUrl: instance.bannerUrl, + errorImageUrl: instance.errorImageUrl, + iconUrl: instance.iconUrl, + backgroundImageUrl: instance.backgroundImageUrl, + logoImageUrl: instance.logoImageUrl, + maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, // 後方互換性のため + defaultLightTheme: instance.defaultLightTheme, + defaultDarkTheme: instance.defaultDarkTheme, + enableEmail: instance.enableEmail, + enableTwitterIntegration: instance.enableTwitterIntegration, + enableGithubIntegration: instance.enableGithubIntegration, + enableDiscordIntegration: instance.enableDiscordIntegration, + enableServiceWorker: instance.enableServiceWorker, + translatorAvailable: instance.deeplAuthKey != null, + pinnedPages: instance.pinnedPages, + pinnedClipId: instance.pinnedClipId, + cacheRemoteFiles: instance.cacheRemoteFiles, + useStarForReactionFallback: instance.useStarForReactionFallback, + pinnedUsers: instance.pinnedUsers, + hiddenTags: instance.hiddenTags, + blockedHosts: instance.blockedHosts, + hcaptchaSecretKey: instance.hcaptchaSecretKey, + recaptchaSecretKey: instance.recaptchaSecretKey, + sensitiveMediaDetection: instance.sensitiveMediaDetection, + sensitiveMediaDetectionSensitivity: instance.sensitiveMediaDetectionSensitivity, + setSensitiveFlagAutomatically: instance.setSensitiveFlagAutomatically, + enableSensitiveMediaDetectionForVideos: instance.enableSensitiveMediaDetectionForVideos, + proxyAccountId: instance.proxyAccountId, + twitterConsumerKey: instance.twitterConsumerKey, + twitterConsumerSecret: instance.twitterConsumerSecret, + githubClientId: instance.githubClientId, + githubClientSecret: instance.githubClientSecret, + discordClientId: instance.discordClientId, + discordClientSecret: instance.discordClientSecret, + summalyProxy: instance.summalyProxy, + email: instance.email, + smtpSecure: instance.smtpSecure, + smtpHost: instance.smtpHost, + smtpPort: instance.smtpPort, + smtpUser: instance.smtpUser, + smtpPass: instance.smtpPass, + swPrivateKey: instance.swPrivateKey, + useObjectStorage: instance.useObjectStorage, + objectStorageBaseUrl: instance.objectStorageBaseUrl, + objectStorageBucket: instance.objectStorageBucket, + objectStoragePrefix: instance.objectStoragePrefix, + objectStorageEndpoint: instance.objectStorageEndpoint, + objectStorageRegion: instance.objectStorageRegion, + objectStoragePort: instance.objectStoragePort, + objectStorageAccessKey: instance.objectStorageAccessKey, + objectStorageSecretKey: instance.objectStorageSecretKey, + objectStorageUseSSL: instance.objectStorageUseSSL, + objectStorageUseProxy: instance.objectStorageUseProxy, + objectStorageSetPublicRead: instance.objectStorageSetPublicRead, + objectStorageS3ForcePathStyle: instance.objectStorageS3ForcePathStyle, + deeplAuthKey: instance.deeplAuthKey, + deeplIsPro: instance.deeplIsPro, + enableIpLogging: instance.enableIpLogging, + enableActiveEmailValidation: instance.enableActiveEmailValidation, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/moderators/add.ts b/packages/backend/src/server/api/endpoints/admin/moderators/add.ts index d85d98c85805..773dc1e6df8e 100644 --- a/packages/backend/src/server/api/endpoints/admin/moderators/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/moderators/add.ts @@ -22,23 +22,28 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const user = await Users.findOneBy({ id: ps.userId }); + const user = await Users.findOneBy({ id: ps.userId }); - if (user == null) { - throw new Error('user not found'); - } + if (user == null) { + throw new Error('user not found'); + } - if (user.isAdmin) { - throw new Error('cannot mark as moderator if admin user'); - } + if (user.isAdmin) { + throw new Error('cannot mark as moderator if admin user'); + } - await Users.update(user.id, { - isModerator: true, - }); + await Users.update(user.id, { + isModerator: true, + }); - publishInternalEvent('userChangeModeratorState', { id: user.id, isModerator: true }); -}); + publishInternalEvent('userChangeModeratorState', { id: user.id, isModerator: true }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts b/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts index 4f61634ca001..b37474f98183 100644 --- a/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts +++ b/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts @@ -22,19 +22,24 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const user = await Users.findOneBy({ id: ps.userId }); + const user = await Users.findOneBy({ id: ps.userId }); - if (user == null) { - throw new Error('user not found'); - } + if (user == null) { + throw new Error('user not found'); + } - await Users.update(user.id, { - isModerator: false, - }); + await Users.update(user.id, { + isModerator: false, + }); - publishInternalEvent('userChangeModeratorState', { id: user.id, isModerator: false }); -}); + publishInternalEvent('userChangeModeratorState', { id: user.id, isModerator: false }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/promo/create.ts b/packages/backend/src/server/api/endpoints/admin/promo/create.ts index 52fdd411facf..cb9155dd033e 100644 --- a/packages/backend/src/server/api/endpoints/admin/promo/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/promo/create.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { PromoNotes } from '@/models/index.js'; import { ApiError } from '../../../error.js'; import { getNote } from '../../../common/getters.js'; -import { PromoNotes } from '@/models/index.js'; export const meta = { tags: ['admin'], @@ -38,24 +38,29 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); + const note = await getNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); - const exist = await PromoNotes.findOneBy({ noteId: note.id }); + const exist = await PromoNotes.findOneBy({ noteId: note.id }); - if (exist != null) { - throw new ApiError(meta.errors.alreadyPromoted); - } + if (exist != null) { + throw new ApiError(meta.errors.alreadyPromoted); + } - await PromoNotes.insert({ - noteId: note.id, - expiresAt: new Date(ps.expiresAt), - userId: note.userId, - }); -}); + await PromoNotes.insert({ + noteId: note.id, + expiresAt: new Date(ps.expiresAt), + userId: note.userId, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts index 311d8c3818fa..7fbb07611dab 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts @@ -20,11 +20,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - destroy(); + destroy(); - insertModerationLog(me, 'clearQueue'); -}); + insertModerationLog(me, 'clearQueue'); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts index b26c18a5ecbe..116ad33f9be5 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts @@ -1,6 +1,6 @@ -import { deliverQueue } from '@/queue/queues.js'; import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; +import { deliverQueue } from '@/queue/queues.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -43,24 +43,29 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const jobs = await deliverQueue.getJobs(['delayed']); + const jobs = await deliverQueue.getJobs(['delayed']); - const res = [] as [string, number][]; + const res = [] as [string, number][]; - for (const job of jobs) { - const host = new URL(job.data.to).host; - if (res.find(x => x[0] === host)) { + for (const job of jobs) { + const host = new URL(job.data.to).host; + if (res.find(x => x[0] === host)) { res.find(x => x[0] === host)![1]++; - } else { - res.push([host, 1]); - } - } + } else { + res.push([host, 1]); + } + } - res.sort((a, b) => b[1] - a[1]); + res.sort((a, b) => b[1] - a[1]); - return res; -}); + return res; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts index 68f335dbc4c4..b94559d93b2d 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts @@ -43,24 +43,29 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const jobs = await inboxQueue.getJobs(['delayed']); + const jobs = await inboxQueue.getJobs(['delayed']); - const res = [] as [string, number][]; + const res = [] as [string, number][]; - for (const job of jobs) { - const host = new URL(job.data.signature.keyId).host; - if (res.find(x => x[0] === host)) { + for (const job of jobs) { + const host = new URL(job.data.signature.keyId).host; + if (res.find(x => x[0] === host)) { res.find(x => x[0] === host)![1]++; - } else { - res.push([host, 1]); - } - } + } else { + res.push([host, 1]); + } + } - res.sort((a, b) => b[1] - a[1]); + res.sort((a, b) => b[1] - a[1]); - return res; -}); + return res; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts index 103bd08511d4..c6747cd262e9 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts @@ -1,5 +1,5 @@ -import { deliverQueue, inboxQueue, dbQueue, objectStorageQueue } from '@/queue/queues.js'; import { Inject, Injectable } from '@nestjs/common'; +import { deliverQueue, inboxQueue, dbQueue, objectStorageQueue } from '@/queue/queues.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -42,19 +42,24 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const deliverJobCounts = await deliverQueue.getJobCounts(); - const inboxJobCounts = await inboxQueue.getJobCounts(); - const dbJobCounts = await dbQueue.getJobCounts(); - const objectStorageJobCounts = await objectStorageQueue.getJobCounts(); + const deliverJobCounts = await deliverQueue.getJobCounts(); + const inboxJobCounts = await inboxQueue.getJobCounts(); + const dbJobCounts = await dbQueue.getJobCounts(); + const objectStorageJobCounts = await objectStorageQueue.getJobCounts(); - return { - deliver: deliverJobCounts, - inbox: inboxJobCounts, - db: dbJobCounts, - objectStorage: objectStorageJobCounts, - }; -}); + return { + deliver: deliverJobCounts, + inbox: inboxJobCounts, + db: dbJobCounts, + objectStorage: objectStorageJobCounts, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/relays/add.ts b/packages/backend/src/server/api/endpoints/admin/relays/add.ts index bf5ce2c1f283..23d914661fe2 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/add.ts @@ -58,15 +58,20 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - try { - if (new URL(ps.inbox).protocol !== 'https:') throw 'https only'; - } catch { - throw new ApiError(meta.errors.invalidUrl); - } + try { + if (new URL(ps.inbox).protocol !== 'https:') throw 'https only'; + } catch { + throw new ApiError(meta.errors.invalidUrl); + } - return await addRelay(ps.inbox); -}); + return await addRelay(ps.inbox); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/relays/list.ts b/packages/backend/src/server/api/endpoints/admin/relays/list.ts index ac0df8d8394a..9b383dc7fdc3 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/list.ts @@ -50,9 +50,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - return await listRelay(); -}); + return await listRelay(); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/relays/remove.ts b/packages/backend/src/server/api/endpoints/admin/relays/remove.ts index 43ed2f1d9fd9..1904d8183d5e 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/remove.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/remove.ts @@ -21,9 +21,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - return await removeRelay(ps.inbox); -}); + return await removeRelay(ps.inbox); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/reset-password.ts b/packages/backend/src/server/api/endpoints/admin/reset-password.ts index bee86d40a34e..5b9df8d1fe4e 100644 --- a/packages/backend/src/server/api/endpoints/admin/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/admin/reset-password.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; import bcrypt from 'bcryptjs'; import rndstr from 'rndstr'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { Users, UserProfiles } from '@/models/index.js'; export const meta = { @@ -36,32 +36,37 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const user = await Users.findOneBy({ id: ps.userId }); + const user = await Users.findOneBy({ id: ps.userId }); - if (user == null) { - throw new Error('user not found'); - } + if (user == null) { + throw new Error('user not found'); + } - if (user.isAdmin) { - throw new Error('cannot reset password of admin'); - } + if (user.isAdmin) { + throw new Error('cannot reset password of admin'); + } - const passwd = rndstr('a-zA-Z0-9', 8); + const passwd = rndstr('a-zA-Z0-9', 8); - // Generate hash of password - const hash = bcrypt.hashSync(passwd); + // Generate hash of password + const hash = bcrypt.hashSync(passwd); - await UserProfiles.update({ - userId: user.id, - }, { - password: hash, - }); + await UserProfiles.update({ + userId: user.id, + }, { + password: hash, + }); - return { - password: passwd, - }; -}); + return { + password: passwd, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts index ceff7cac5b0d..ab7b82360e5f 100644 --- a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts +++ b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts @@ -26,26 +26,31 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const report = await AbuseUserReports.findOneByOrFail({ id: ps.reportId }); + const report = await AbuseUserReports.findOneByOrFail({ id: ps.reportId }); - if (report == null) { - throw new Error('report not found'); - } + if (report == null) { + throw new Error('report not found'); + } - if (ps.forward && report.targetUserHost != null) { - const actor = await getInstanceActor(); - const targetUser = await Users.findOneByOrFail({ id: report.targetUserId }); + if (ps.forward && report.targetUserHost != null) { + const actor = await getInstanceActor(); + const targetUser = await Users.findOneByOrFail({ id: report.targetUserId }); - deliver(actor, renderActivity(renderFlag(actor, [targetUser.uri!], report.comment)), targetUser.inbox); - } + deliver(actor, renderActivity(renderFlag(actor, [targetUser.uri!], report.comment)), targetUser.inbox); + } - await AbuseUserReports.update(report.id, { - resolved: true, - assigneeId: me.id, - forwarded: ps.forward && report.targetUserHost != null, - }); -}); + await AbuseUserReports.update(report.id, { + resolved: true, + assigneeId: me.id, + forwarded: ps.forward && report.targetUserHost != null, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/send-email.ts b/packages/backend/src/server/api/endpoints/admin/send-email.ts index 2071117d4c1c..f9b8b7ee83fe 100644 --- a/packages/backend/src/server/api/endpoints/admin/send-email.ts +++ b/packages/backend/src/server/api/endpoints/admin/send-email.ts @@ -23,9 +23,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - await sendEmail(ps.to, ps.subject, ps.text, ps.text); -}); + await sendEmail(ps.to, ps.subject, ps.text, ps.text); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/server-info.ts b/packages/backend/src/server/api/endpoints/admin/server-info.ts index f15852b30504..fee612fe494e 100644 --- a/packages/backend/src/server/api/endpoints/admin/server-info.ts +++ b/packages/backend/src/server/api/endpoints/admin/server-info.ts @@ -2,8 +2,8 @@ import * as os from 'node:os'; import si from 'systeminformation'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { redisClient } from '../../../../db/redis.js'; import { db } from '@/db/postgre.js'; +import { redisClient } from '../../../../db/redis.js'; export const meta = { requireCredential: true, @@ -95,34 +95,45 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async () => { - const memStats = await si.mem(); - const fsStats = await si.fsSize(); - const netInterface = await si.networkInterfaceDefault(); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, - const redisServerInfo = await redisClient.info('Server'); - const m = redisServerInfo.match(new RegExp('^redis_version:(.*)', 'm')); - const redis_version = m?.[1]; + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async () => { + const memStats = await si.mem(); + const fsStats = await si.fsSize(); + const netInterface = await si.networkInterfaceDefault(); - return { - machine: os.hostname(), - os: os.platform(), - node: process.version, - psql: await db.query('SHOW server_version').then(x => x[0].server_version), - redis: redis_version, - cpu: { - model: os.cpus()[0].model, - cores: os.cpus().length, - }, - mem: { - total: memStats.total, - }, - fs: { - total: fsStats[0].size, - used: fsStats[0].used, - }, - net: { - interface: netInterface, - }, - }; -}); + const redisServerInfo = await redisClient.info('Server'); + const m = redisServerInfo.match(new RegExp('^redis_version:(.*)', 'm')); + const redis_version = m?.[1]; + + return { + machine: os.hostname(), + os: os.platform(), + node: process.version, + psql: await db.query('SHOW server_version').then(x => x[0].server_version), + redis: redis_version, + cpu: { + model: os.cpus()[0].model, + cores: os.cpus().length, + }, + mem: { + total: memStats.total, + }, + fs: { + total: fsStats[0].size, + used: fsStats[0].used, + }, + net: { + interface: netInterface, + }, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts index 53cdc14be114..db7efa2ae895 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts @@ -63,13 +63,18 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(ModerationLogs.createQueryBuilder('report'), ps.sinceId, ps.untilId); + const query = makePaginationQuery(ModerationLogs.createQueryBuilder('report'), ps.sinceId, ps.untilId); - const reports = await query.take(ps.limit).getMany(); + const reports = await query.take(ps.limit).getMany(); - return await ModerationLogs.packMany(reports); -}); + return await ModerationLogs.packMany(reports); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index 62c0e2e8df7a..4c6d65453eb5 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -1,5 +1,5 @@ -import { Signins, UserProfiles, Users } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Signins, UserProfiles, Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -26,58 +26,63 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const [user, profile] = await Promise.all([ - Users.findOneBy({ id: ps.userId }), - UserProfiles.findOneBy({ userId: ps.userId }), - ]); + const [user, profile] = await Promise.all([ + Users.findOneBy({ id: ps.userId }), + UserProfiles.findOneBy({ userId: ps.userId }), + ]); - if (user == null || profile == null) { - throw new Error('user not found'); - } + if (user == null || profile == null) { + throw new Error('user not found'); + } - const _me = await Users.findOneByOrFail({ id: me.id }); - if ((_me.isModerator && !_me.isAdmin) && user.isAdmin) { - throw new Error('cannot show info of admin'); - } + const _me = await Users.findOneByOrFail({ id: me.id }); + if ((_me.isModerator && !_me.isAdmin) && user.isAdmin) { + throw new Error('cannot show info of admin'); + } - if (!_me.isAdmin) { - return { - isModerator: user.isModerator, - isSilenced: user.isSilenced, - isSuspended: user.isSuspended, - }; - } + if (!_me.isAdmin) { + return { + isModerator: user.isModerator, + isSilenced: user.isSilenced, + isSuspended: user.isSuspended, + }; + } - const maskedKeys = ['accessToken', 'accessTokenSecret', 'refreshToken']; - Object.keys(profile.integrations).forEach(integration => { - maskedKeys.forEach(key => profile.integrations[integration][key] = ''); - }); + const maskedKeys = ['accessToken', 'accessTokenSecret', 'refreshToken']; + Object.keys(profile.integrations).forEach(integration => { + maskedKeys.forEach(key => profile.integrations[integration][key] = ''); + }); - const signins = await Signins.findBy({ userId: user.id }); + const signins = await Signins.findBy({ userId: user.id }); - return { - email: profile.email, - emailVerified: profile.emailVerified, - autoAcceptFollowed: profile.autoAcceptFollowed, - noCrawle: profile.noCrawle, - alwaysMarkNsfw: profile.alwaysMarkNsfw, - autoSensitive: profile.autoSensitive, - carefulBot: profile.carefulBot, - injectFeaturedNote: profile.injectFeaturedNote, - receiveAnnouncementEmail: profile.receiveAnnouncementEmail, - integrations: profile.integrations, - mutedWords: profile.mutedWords, - mutedInstances: profile.mutedInstances, - mutingNotificationTypes: profile.mutingNotificationTypes, - isModerator: user.isModerator, - isSilenced: user.isSilenced, - isSuspended: user.isSuspended, - lastActiveDate: user.lastActiveDate, - moderationNote: profile.moderationNote, - signins, - }; -}); + return { + email: profile.email, + emailVerified: profile.emailVerified, + autoAcceptFollowed: profile.autoAcceptFollowed, + noCrawle: profile.noCrawle, + alwaysMarkNsfw: profile.alwaysMarkNsfw, + autoSensitive: profile.autoSensitive, + carefulBot: profile.carefulBot, + injectFeaturedNote: profile.injectFeaturedNote, + receiveAnnouncementEmail: profile.receiveAnnouncementEmail, + integrations: profile.integrations, + mutedWords: profile.mutedWords, + mutedInstances: profile.mutedInstances, + mutingNotificationTypes: profile.mutingNotificationTypes, + isModerator: user.isModerator, + isSilenced: user.isSilenced, + isSuspended: user.isSuspended, + lastActiveDate: user.lastActiveDate, + moderationNote: profile.moderationNote, + signins, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/show-users.ts b/packages/backend/src/server/api/endpoints/admin/show-users.ts index 23b04a6ebada..b29f774ee976 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-users.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts @@ -1,5 +1,5 @@ -import { Users } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -42,49 +42,54 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const query = Users.createQueryBuilder('user'); + const query = Users.createQueryBuilder('user'); - switch (ps.state) { - case 'available': query.where('user.isSuspended = FALSE'); break; - case 'admin': query.where('user.isAdmin = TRUE'); break; - case 'moderator': query.where('user.isModerator = TRUE'); break; - case 'adminOrModerator': query.where('user.isAdmin = TRUE OR user.isModerator = TRUE'); break; - case 'alive': query.where('user.updatedAt > :date', { date: new Date(Date.now() - 1000 * 60 * 60 * 24 * 5) }); break; - case 'silenced': query.where('user.isSilenced = TRUE'); break; - case 'suspended': query.where('user.isSuspended = TRUE'); break; - } + switch (ps.state) { + case 'available': query.where('user.isSuspended = FALSE'); break; + case 'admin': query.where('user.isAdmin = TRUE'); break; + case 'moderator': query.where('user.isModerator = TRUE'); break; + case 'adminOrModerator': query.where('user.isAdmin = TRUE OR user.isModerator = TRUE'); break; + case 'alive': query.where('user.updatedAt > :date', { date: new Date(Date.now() - 1000 * 60 * 60 * 24 * 5) }); break; + case 'silenced': query.where('user.isSilenced = TRUE'); break; + case 'suspended': query.where('user.isSuspended = TRUE'); break; + } - switch (ps.origin) { - case 'local': query.andWhere('user.host IS NULL'); break; - case 'remote': query.andWhere('user.host IS NOT NULL'); break; - } + switch (ps.origin) { + case 'local': query.andWhere('user.host IS NULL'); break; + case 'remote': query.andWhere('user.host IS NOT NULL'); break; + } - if (ps.username) { - query.andWhere('user.usernameLower like :username', { username: ps.username.toLowerCase() + '%' }); - } + if (ps.username) { + query.andWhere('user.usernameLower like :username', { username: ps.username.toLowerCase() + '%' }); + } - if (ps.hostname) { - query.andWhere('user.host = :hostname', { hostname: ps.hostname.toLowerCase() }); - } + if (ps.hostname) { + query.andWhere('user.host = :hostname', { hostname: ps.hostname.toLowerCase() }); + } - switch (ps.sort) { - case '+follower': query.orderBy('user.followersCount', 'DESC'); break; - case '-follower': query.orderBy('user.followersCount', 'ASC'); break; - case '+createdAt': query.orderBy('user.createdAt', 'DESC'); break; - case '-createdAt': query.orderBy('user.createdAt', 'ASC'); break; - case '+updatedAt': query.orderBy('user.updatedAt', 'DESC', 'NULLS LAST'); break; - case '-updatedAt': query.orderBy('user.updatedAt', 'ASC', 'NULLS FIRST'); break; - default: query.orderBy('user.id', 'ASC'); break; - } + switch (ps.sort) { + case '+follower': query.orderBy('user.followersCount', 'DESC'); break; + case '-follower': query.orderBy('user.followersCount', 'ASC'); break; + case '+createdAt': query.orderBy('user.createdAt', 'DESC'); break; + case '-createdAt': query.orderBy('user.createdAt', 'ASC'); break; + case '+updatedAt': query.orderBy('user.updatedAt', 'DESC', 'NULLS LAST'); break; + case '-updatedAt': query.orderBy('user.updatedAt', 'ASC', 'NULLS FIRST'); break; + default: query.orderBy('user.id', 'ASC'); break; + } - query.take(ps.limit); - query.skip(ps.offset); + query.take(ps.limit); + query.skip(ps.offset); - const users = await query.getMany(); + const users = await query.getMany(); - return await Users.packMany(users, me, { detail: true }); -}); + return await Users.packMany(users, me, { detail: true }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/silence-user.ts b/packages/backend/src/server/api/endpoints/admin/silence-user.ts index 7347c631cd92..fb3d51da5dc3 100644 --- a/packages/backend/src/server/api/endpoints/admin/silence-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/silence-user.ts @@ -23,27 +23,32 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const user = await Users.findOneBy({ id: ps.userId }); + const user = await Users.findOneBy({ id: ps.userId }); - if (user == null) { - throw new Error('user not found'); - } + if (user == null) { + throw new Error('user not found'); + } - if (user.isAdmin) { - throw new Error('cannot silence admin'); - } + if (user.isAdmin) { + throw new Error('cannot silence admin'); + } - await Users.update(user.id, { - isSilenced: true, - }); + await Users.update(user.id, { + isSilenced: true, + }); - publishInternalEvent('userChangeSilencedState', { id: user.id, isSilenced: true }); + publishInternalEvent('userChangeSilencedState', { id: user.id, isSilenced: true }); - insertModerationLog(me, 'silence', { - targetId: user.id, - }); -}); + insertModerationLog(me, 'silence', { + targetId: user.id, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts index 07be0be6fd92..49dbb23c8104 100644 --- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import deleteFollowing from '@/services/following/delete.js'; import { Users, Followings, Notifications } from '@/models/index.js'; -import { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/user.js'; import { insertModerationLog } from '@/services/insert-moderation-log.js'; import { doPostSuspend } from '@/services/suspend-user.js'; import { publishUserEvent } from '@/services/stream.js'; @@ -26,43 +26,48 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const user = await Users.findOneBy({ id: ps.userId }); - - if (user == null) { - throw new Error('user not found'); - } - - if (user.isAdmin) { - throw new Error('cannot suspend admin'); - } - - if (user.isModerator) { - throw new Error('cannot suspend moderator'); - } - - await Users.update(user.id, { - isSuspended: true, - }); - - insertModerationLog(me, 'suspend', { - targetId: user.id, - }); - - // Terminate streaming - if (Users.isLocalUser(user)) { - publishUserEvent(user.id, 'terminate', {}); + const user = await Users.findOneBy({ id: ps.userId }); + + if (user == null) { + throw new Error('user not found'); + } + + if (user.isAdmin) { + throw new Error('cannot suspend admin'); + } + + if (user.isModerator) { + throw new Error('cannot suspend moderator'); + } + + await Users.update(user.id, { + isSuspended: true, + }); + + insertModerationLog(me, 'suspend', { + targetId: user.id, + }); + + // Terminate streaming + if (Users.isLocalUser(user)) { + publishUserEvent(user.id, 'terminate', {}); + } + + (async () => { + await doPostSuspend(user).catch(e => {}); + await unFollowAll(user).catch(e => {}); + await readAllNotify(user).catch(e => {}); + })(); + }); } - - (async () => { - await doPostSuspend(user).catch(e => {}); - await unFollowAll(user).catch(e => {}); - await readAllNotify(user).catch(e => {}); - })(); -}); +} async function unFollowAll(follower: User) { const followings = await Followings.findBy({ diff --git a/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts b/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts index 8eab0de57e28..52c06fbee43a 100644 --- a/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts @@ -23,23 +23,28 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const user = await Users.findOneBy({ id: ps.userId }); + const user = await Users.findOneBy({ id: ps.userId }); - if (user == null) { - throw new Error('user not found'); - } + if (user == null) { + throw new Error('user not found'); + } - await Users.update(user.id, { - isSilenced: false, - }); + await Users.update(user.id, { + isSilenced: false, + }); - publishInternalEvent('userChangeSilencedState', { id: user.id, isSilenced: false }); + publishInternalEvent('userChangeSilencedState', { id: user.id, isSilenced: false }); - insertModerationLog(me, 'unsilence', { - targetId: user.id, - }); -}); + insertModerationLog(me, 'unsilence', { + targetId: user.id, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts index cb97dc1dc34f..f1991fe181f2 100644 --- a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts @@ -23,23 +23,28 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const user = await Users.findOneBy({ id: ps.userId }); + const user = await Users.findOneBy({ id: ps.userId }); - if (user == null) { - throw new Error('user not found'); - } + if (user == null) { + throw new Error('user not found'); + } - await Users.update(user.id, { - isSuspended: false, - }); + await Users.update(user.id, { + isSuspended: false, + }); - insertModerationLog(me, 'unsuspend', { - targetId: user.id, - }); + insertModerationLog(me, 'unsuspend', { + targetId: user.id, + }); - doPostUnsuspend(user); -}); + doPostUnsuspend(user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 4620bff99c7b..82b739b956b4 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -1,8 +1,8 @@ +import { Inject, Injectable } from '@nestjs/common'; import { Meta } from '@/models/entities/meta.js'; import { insertModerationLog } from '@/services/insert-moderation-log.js'; import { DB_MAX_NOTE_TEXT_LENGTH } from '@/misc/hard-limits.js'; import { db } from '@/db/postgre.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -111,343 +111,348 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const set = {} as Partial; + const set = {} as Partial; - if (typeof ps.disableRegistration === 'boolean') { - set.disableRegistration = ps.disableRegistration; - } + if (typeof ps.disableRegistration === 'boolean') { + set.disableRegistration = ps.disableRegistration; + } - if (typeof ps.disableLocalTimeline === 'boolean') { - set.disableLocalTimeline = ps.disableLocalTimeline; - } + if (typeof ps.disableLocalTimeline === 'boolean') { + set.disableLocalTimeline = ps.disableLocalTimeline; + } - if (typeof ps.disableGlobalTimeline === 'boolean') { - set.disableGlobalTimeline = ps.disableGlobalTimeline; - } + if (typeof ps.disableGlobalTimeline === 'boolean') { + set.disableGlobalTimeline = ps.disableGlobalTimeline; + } - if (typeof ps.useStarForReactionFallback === 'boolean') { - set.useStarForReactionFallback = ps.useStarForReactionFallback; - } + if (typeof ps.useStarForReactionFallback === 'boolean') { + set.useStarForReactionFallback = ps.useStarForReactionFallback; + } - if (Array.isArray(ps.pinnedUsers)) { - set.pinnedUsers = ps.pinnedUsers.filter(Boolean); - } + if (Array.isArray(ps.pinnedUsers)) { + set.pinnedUsers = ps.pinnedUsers.filter(Boolean); + } - if (Array.isArray(ps.hiddenTags)) { - set.hiddenTags = ps.hiddenTags.filter(Boolean); - } + if (Array.isArray(ps.hiddenTags)) { + set.hiddenTags = ps.hiddenTags.filter(Boolean); + } - if (Array.isArray(ps.blockedHosts)) { - set.blockedHosts = ps.blockedHosts.filter(Boolean); - } + if (Array.isArray(ps.blockedHosts)) { + set.blockedHosts = ps.blockedHosts.filter(Boolean); + } - if (ps.themeColor !== undefined) { - set.themeColor = ps.themeColor; - } + if (ps.themeColor !== undefined) { + set.themeColor = ps.themeColor; + } - if (ps.mascotImageUrl !== undefined) { - set.mascotImageUrl = ps.mascotImageUrl; - } + if (ps.mascotImageUrl !== undefined) { + set.mascotImageUrl = ps.mascotImageUrl; + } - if (ps.bannerUrl !== undefined) { - set.bannerUrl = ps.bannerUrl; - } + if (ps.bannerUrl !== undefined) { + set.bannerUrl = ps.bannerUrl; + } - if (ps.iconUrl !== undefined) { - set.iconUrl = ps.iconUrl; - } + if (ps.iconUrl !== undefined) { + set.iconUrl = ps.iconUrl; + } - if (ps.backgroundImageUrl !== undefined) { - set.backgroundImageUrl = ps.backgroundImageUrl; - } + if (ps.backgroundImageUrl !== undefined) { + set.backgroundImageUrl = ps.backgroundImageUrl; + } - if (ps.logoImageUrl !== undefined) { - set.logoImageUrl = ps.logoImageUrl; - } + if (ps.logoImageUrl !== undefined) { + set.logoImageUrl = ps.logoImageUrl; + } - if (ps.name !== undefined) { - set.name = ps.name; - } + if (ps.name !== undefined) { + set.name = ps.name; + } - if (ps.description !== undefined) { - set.description = ps.description; - } + if (ps.description !== undefined) { + set.description = ps.description; + } - if (ps.defaultLightTheme !== undefined) { - set.defaultLightTheme = ps.defaultLightTheme; - } + if (ps.defaultLightTheme !== undefined) { + set.defaultLightTheme = ps.defaultLightTheme; + } - if (ps.defaultDarkTheme !== undefined) { - set.defaultDarkTheme = ps.defaultDarkTheme; - } + if (ps.defaultDarkTheme !== undefined) { + set.defaultDarkTheme = ps.defaultDarkTheme; + } - if (ps.localDriveCapacityMb !== undefined) { - set.localDriveCapacityMb = ps.localDriveCapacityMb; - } + if (ps.localDriveCapacityMb !== undefined) { + set.localDriveCapacityMb = ps.localDriveCapacityMb; + } - if (ps.remoteDriveCapacityMb !== undefined) { - set.remoteDriveCapacityMb = ps.remoteDriveCapacityMb; - } + if (ps.remoteDriveCapacityMb !== undefined) { + set.remoteDriveCapacityMb = ps.remoteDriveCapacityMb; + } - if (ps.cacheRemoteFiles !== undefined) { - set.cacheRemoteFiles = ps.cacheRemoteFiles; - } + if (ps.cacheRemoteFiles !== undefined) { + set.cacheRemoteFiles = ps.cacheRemoteFiles; + } - if (ps.emailRequiredForSignup !== undefined) { - set.emailRequiredForSignup = ps.emailRequiredForSignup; - } + if (ps.emailRequiredForSignup !== undefined) { + set.emailRequiredForSignup = ps.emailRequiredForSignup; + } - if (ps.enableHcaptcha !== undefined) { - set.enableHcaptcha = ps.enableHcaptcha; - } + if (ps.enableHcaptcha !== undefined) { + set.enableHcaptcha = ps.enableHcaptcha; + } - if (ps.hcaptchaSiteKey !== undefined) { - set.hcaptchaSiteKey = ps.hcaptchaSiteKey; - } + if (ps.hcaptchaSiteKey !== undefined) { + set.hcaptchaSiteKey = ps.hcaptchaSiteKey; + } - if (ps.hcaptchaSecretKey !== undefined) { - set.hcaptchaSecretKey = ps.hcaptchaSecretKey; - } + if (ps.hcaptchaSecretKey !== undefined) { + set.hcaptchaSecretKey = ps.hcaptchaSecretKey; + } - if (ps.enableRecaptcha !== undefined) { - set.enableRecaptcha = ps.enableRecaptcha; - } + if (ps.enableRecaptcha !== undefined) { + set.enableRecaptcha = ps.enableRecaptcha; + } - if (ps.recaptchaSiteKey !== undefined) { - set.recaptchaSiteKey = ps.recaptchaSiteKey; - } + if (ps.recaptchaSiteKey !== undefined) { + set.recaptchaSiteKey = ps.recaptchaSiteKey; + } - if (ps.recaptchaSecretKey !== undefined) { - set.recaptchaSecretKey = ps.recaptchaSecretKey; - } + if (ps.recaptchaSecretKey !== undefined) { + set.recaptchaSecretKey = ps.recaptchaSecretKey; + } - if (ps.sensitiveMediaDetection !== undefined) { - set.sensitiveMediaDetection = ps.sensitiveMediaDetection; - } + if (ps.sensitiveMediaDetection !== undefined) { + set.sensitiveMediaDetection = ps.sensitiveMediaDetection; + } - if (ps.sensitiveMediaDetectionSensitivity !== undefined) { - set.sensitiveMediaDetectionSensitivity = ps.sensitiveMediaDetectionSensitivity; - } + if (ps.sensitiveMediaDetectionSensitivity !== undefined) { + set.sensitiveMediaDetectionSensitivity = ps.sensitiveMediaDetectionSensitivity; + } - if (ps.setSensitiveFlagAutomatically !== undefined) { - set.setSensitiveFlagAutomatically = ps.setSensitiveFlagAutomatically; - } + if (ps.setSensitiveFlagAutomatically !== undefined) { + set.setSensitiveFlagAutomatically = ps.setSensitiveFlagAutomatically; + } - if (ps.enableSensitiveMediaDetectionForVideos !== undefined) { - set.enableSensitiveMediaDetectionForVideos = ps.enableSensitiveMediaDetectionForVideos; - } + if (ps.enableSensitiveMediaDetectionForVideos !== undefined) { + set.enableSensitiveMediaDetectionForVideos = ps.enableSensitiveMediaDetectionForVideos; + } - if (ps.proxyAccountId !== undefined) { - set.proxyAccountId = ps.proxyAccountId; - } + if (ps.proxyAccountId !== undefined) { + set.proxyAccountId = ps.proxyAccountId; + } - if (ps.maintainerName !== undefined) { - set.maintainerName = ps.maintainerName; - } + if (ps.maintainerName !== undefined) { + set.maintainerName = ps.maintainerName; + } - if (ps.maintainerEmail !== undefined) { - set.maintainerEmail = ps.maintainerEmail; - } + if (ps.maintainerEmail !== undefined) { + set.maintainerEmail = ps.maintainerEmail; + } - if (Array.isArray(ps.langs)) { - set.langs = ps.langs.filter(Boolean); - } + if (Array.isArray(ps.langs)) { + set.langs = ps.langs.filter(Boolean); + } - if (Array.isArray(ps.pinnedPages)) { - set.pinnedPages = ps.pinnedPages.filter(Boolean); - } + if (Array.isArray(ps.pinnedPages)) { + set.pinnedPages = ps.pinnedPages.filter(Boolean); + } - if (ps.pinnedClipId !== undefined) { - set.pinnedClipId = ps.pinnedClipId; - } + if (ps.pinnedClipId !== undefined) { + set.pinnedClipId = ps.pinnedClipId; + } - if (ps.summalyProxy !== undefined) { - set.summalyProxy = ps.summalyProxy; - } + if (ps.summalyProxy !== undefined) { + set.summalyProxy = ps.summalyProxy; + } - if (ps.enableTwitterIntegration !== undefined) { - set.enableTwitterIntegration = ps.enableTwitterIntegration; - } + if (ps.enableTwitterIntegration !== undefined) { + set.enableTwitterIntegration = ps.enableTwitterIntegration; + } - if (ps.twitterConsumerKey !== undefined) { - set.twitterConsumerKey = ps.twitterConsumerKey; - } + if (ps.twitterConsumerKey !== undefined) { + set.twitterConsumerKey = ps.twitterConsumerKey; + } - if (ps.twitterConsumerSecret !== undefined) { - set.twitterConsumerSecret = ps.twitterConsumerSecret; - } + if (ps.twitterConsumerSecret !== undefined) { + set.twitterConsumerSecret = ps.twitterConsumerSecret; + } - if (ps.enableGithubIntegration !== undefined) { - set.enableGithubIntegration = ps.enableGithubIntegration; - } + if (ps.enableGithubIntegration !== undefined) { + set.enableGithubIntegration = ps.enableGithubIntegration; + } - if (ps.githubClientId !== undefined) { - set.githubClientId = ps.githubClientId; - } + if (ps.githubClientId !== undefined) { + set.githubClientId = ps.githubClientId; + } - if (ps.githubClientSecret !== undefined) { - set.githubClientSecret = ps.githubClientSecret; - } + if (ps.githubClientSecret !== undefined) { + set.githubClientSecret = ps.githubClientSecret; + } - if (ps.enableDiscordIntegration !== undefined) { - set.enableDiscordIntegration = ps.enableDiscordIntegration; - } + if (ps.enableDiscordIntegration !== undefined) { + set.enableDiscordIntegration = ps.enableDiscordIntegration; + } - if (ps.discordClientId !== undefined) { - set.discordClientId = ps.discordClientId; - } + if (ps.discordClientId !== undefined) { + set.discordClientId = ps.discordClientId; + } - if (ps.discordClientSecret !== undefined) { - set.discordClientSecret = ps.discordClientSecret; - } + if (ps.discordClientSecret !== undefined) { + set.discordClientSecret = ps.discordClientSecret; + } - if (ps.enableEmail !== undefined) { - set.enableEmail = ps.enableEmail; - } + if (ps.enableEmail !== undefined) { + set.enableEmail = ps.enableEmail; + } - if (ps.email !== undefined) { - set.email = ps.email; - } + if (ps.email !== undefined) { + set.email = ps.email; + } - if (ps.smtpSecure !== undefined) { - set.smtpSecure = ps.smtpSecure; - } + if (ps.smtpSecure !== undefined) { + set.smtpSecure = ps.smtpSecure; + } - if (ps.smtpHost !== undefined) { - set.smtpHost = ps.smtpHost; - } + if (ps.smtpHost !== undefined) { + set.smtpHost = ps.smtpHost; + } - if (ps.smtpPort !== undefined) { - set.smtpPort = ps.smtpPort; - } + if (ps.smtpPort !== undefined) { + set.smtpPort = ps.smtpPort; + } - if (ps.smtpUser !== undefined) { - set.smtpUser = ps.smtpUser; - } - - if (ps.smtpPass !== undefined) { - set.smtpPass = ps.smtpPass; - } - - if (ps.errorImageUrl !== undefined) { - set.errorImageUrl = ps.errorImageUrl; - } - - if (ps.enableServiceWorker !== undefined) { - set.enableServiceWorker = ps.enableServiceWorker; - } - - if (ps.swPublicKey !== undefined) { - set.swPublicKey = ps.swPublicKey; - } - - if (ps.swPrivateKey !== undefined) { - set.swPrivateKey = ps.swPrivateKey; - } - - if (ps.tosUrl !== undefined) { - set.ToSUrl = ps.tosUrl; - } - - if (ps.repositoryUrl !== undefined) { - set.repositoryUrl = ps.repositoryUrl; - } - - if (ps.feedbackUrl !== undefined) { - set.feedbackUrl = ps.feedbackUrl; - } - - if (ps.useObjectStorage !== undefined) { - set.useObjectStorage = ps.useObjectStorage; - } - - if (ps.objectStorageBaseUrl !== undefined) { - set.objectStorageBaseUrl = ps.objectStorageBaseUrl; - } - - if (ps.objectStorageBucket !== undefined) { - set.objectStorageBucket = ps.objectStorageBucket; - } - - if (ps.objectStoragePrefix !== undefined) { - set.objectStoragePrefix = ps.objectStoragePrefix; - } - - if (ps.objectStorageEndpoint !== undefined) { - set.objectStorageEndpoint = ps.objectStorageEndpoint; - } + if (ps.smtpUser !== undefined) { + set.smtpUser = ps.smtpUser; + } - if (ps.objectStorageRegion !== undefined) { - set.objectStorageRegion = ps.objectStorageRegion; - } - - if (ps.objectStoragePort !== undefined) { - set.objectStoragePort = ps.objectStoragePort; - } - - if (ps.objectStorageAccessKey !== undefined) { - set.objectStorageAccessKey = ps.objectStorageAccessKey; - } - - if (ps.objectStorageSecretKey !== undefined) { - set.objectStorageSecretKey = ps.objectStorageSecretKey; - } + if (ps.smtpPass !== undefined) { + set.smtpPass = ps.smtpPass; + } - if (ps.objectStorageUseSSL !== undefined) { - set.objectStorageUseSSL = ps.objectStorageUseSSL; - } - - if (ps.objectStorageUseProxy !== undefined) { - set.objectStorageUseProxy = ps.objectStorageUseProxy; - } - - if (ps.objectStorageSetPublicRead !== undefined) { - set.objectStorageSetPublicRead = ps.objectStorageSetPublicRead; - } - - if (ps.objectStorageS3ForcePathStyle !== undefined) { - set.objectStorageS3ForcePathStyle = ps.objectStorageS3ForcePathStyle; - } - - if (ps.deeplAuthKey !== undefined) { - if (ps.deeplAuthKey === '') { - set.deeplAuthKey = null; - } else { - set.deeplAuthKey = ps.deeplAuthKey; - } - } - - if (ps.deeplIsPro !== undefined) { - set.deeplIsPro = ps.deeplIsPro; - } - - if (ps.enableIpLogging !== undefined) { - set.enableIpLogging = ps.enableIpLogging; - } - - if (ps.enableActiveEmailValidation !== undefined) { - set.enableActiveEmailValidation = ps.enableActiveEmailValidation; - } - - await db.transaction(async transactionalEntityManager => { - const metas = await transactionalEntityManager.find(Meta, { - order: { - id: 'DESC', - }, + if (ps.errorImageUrl !== undefined) { + set.errorImageUrl = ps.errorImageUrl; + } + + if (ps.enableServiceWorker !== undefined) { + set.enableServiceWorker = ps.enableServiceWorker; + } + + if (ps.swPublicKey !== undefined) { + set.swPublicKey = ps.swPublicKey; + } + + if (ps.swPrivateKey !== undefined) { + set.swPrivateKey = ps.swPrivateKey; + } + + if (ps.tosUrl !== undefined) { + set.ToSUrl = ps.tosUrl; + } + + if (ps.repositoryUrl !== undefined) { + set.repositoryUrl = ps.repositoryUrl; + } + + if (ps.feedbackUrl !== undefined) { + set.feedbackUrl = ps.feedbackUrl; + } + + if (ps.useObjectStorage !== undefined) { + set.useObjectStorage = ps.useObjectStorage; + } + + if (ps.objectStorageBaseUrl !== undefined) { + set.objectStorageBaseUrl = ps.objectStorageBaseUrl; + } + + if (ps.objectStorageBucket !== undefined) { + set.objectStorageBucket = ps.objectStorageBucket; + } + + if (ps.objectStoragePrefix !== undefined) { + set.objectStoragePrefix = ps.objectStoragePrefix; + } + + if (ps.objectStorageEndpoint !== undefined) { + set.objectStorageEndpoint = ps.objectStorageEndpoint; + } + + if (ps.objectStorageRegion !== undefined) { + set.objectStorageRegion = ps.objectStorageRegion; + } + + if (ps.objectStoragePort !== undefined) { + set.objectStoragePort = ps.objectStoragePort; + } + + if (ps.objectStorageAccessKey !== undefined) { + set.objectStorageAccessKey = ps.objectStorageAccessKey; + } + + if (ps.objectStorageSecretKey !== undefined) { + set.objectStorageSecretKey = ps.objectStorageSecretKey; + } + + if (ps.objectStorageUseSSL !== undefined) { + set.objectStorageUseSSL = ps.objectStorageUseSSL; + } + + if (ps.objectStorageUseProxy !== undefined) { + set.objectStorageUseProxy = ps.objectStorageUseProxy; + } + + if (ps.objectStorageSetPublicRead !== undefined) { + set.objectStorageSetPublicRead = ps.objectStorageSetPublicRead; + } + + if (ps.objectStorageS3ForcePathStyle !== undefined) { + set.objectStorageS3ForcePathStyle = ps.objectStorageS3ForcePathStyle; + } + + if (ps.deeplAuthKey !== undefined) { + if (ps.deeplAuthKey === '') { + set.deeplAuthKey = null; + } else { + set.deeplAuthKey = ps.deeplAuthKey; + } + } + + if (ps.deeplIsPro !== undefined) { + set.deeplIsPro = ps.deeplIsPro; + } + + if (ps.enableIpLogging !== undefined) { + set.enableIpLogging = ps.enableIpLogging; + } + + if (ps.enableActiveEmailValidation !== undefined) { + set.enableActiveEmailValidation = ps.enableActiveEmailValidation; + } + + await db.transaction(async transactionalEntityManager => { + const metas = await transactionalEntityManager.find(Meta, { + order: { + id: 'DESC', + }, + }); + + const meta = metas[0]; + + if (meta) { + await transactionalEntityManager.update(Meta, meta.id, set); + } else { + await transactionalEntityManager.save(Meta, set); + } + }); + + insertModerationLog(me, 'updateMeta'); }); - - const meta = metas[0]; - - if (meta) { - await transactionalEntityManager.update(Meta, meta.id, set); - } else { - await transactionalEntityManager.save(Meta, set); - } - }); - - insertModerationLog(me, 'updateMeta'); -}); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts index 069eba501a8f..3e55d5c66bed 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts @@ -1,5 +1,5 @@ -import { UserProfiles, Users } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { UserProfiles, Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -22,17 +22,22 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const user = await Users.findOneBy({ id: ps.userId }); + const user = await Users.findOneBy({ id: ps.userId }); - if (user == null) { - throw new Error('user not found'); - } + if (user == null) { + throw new Error('user not found'); + } - await UserProfiles.update({ userId: user.id }, { - moderationNote: ps.text, - }); -}); + await UserProfiles.update({ userId: user.id }, { + moderationNote: ps.text, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/vacuum.ts b/packages/backend/src/server/api/endpoints/admin/vacuum.ts index dc0d2ce3c9b5..243f57859430 100644 --- a/packages/backend/src/server/api/endpoints/admin/vacuum.ts +++ b/packages/backend/src/server/api/endpoints/admin/vacuum.ts @@ -23,21 +23,26 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const params: string[] = []; + const params: string[] = []; - if (ps.full) { - params.push('FULL'); - } + if (ps.full) { + params.push('FULL'); + } - if (ps.analyze) { - params.push('ANALYZE'); - } + if (ps.analyze) { + params.push('ANALYZE'); + } - db.query('VACUUM ' + params.join(' ')); + db.query('VACUUM ' + params.join(' ')); - insertModerationLog(me, 'vacuum', ps); -}); + insertModerationLog(me, 'vacuum', ps); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/announcements.ts b/packages/backend/src/server/api/endpoints/announcements.ts index 2a8d57476e19..783f3121d3f2 100644 --- a/packages/backend/src/server/api/endpoints/announcements.ts +++ b/packages/backend/src/server/api/endpoints/announcements.ts @@ -1,5 +1,5 @@ -import { Announcements, AnnouncementReads } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Announcements, AnnouncementReads } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../common/make-pagination-query.js'; @@ -67,27 +67,32 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(Announcements.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); + const query = makePaginationQuery(Announcements.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); - const announcements = await query.take(ps.limit).getMany(); + const announcements = await query.take(ps.limit).getMany(); - if (user) { - const reads = (await AnnouncementReads.findBy({ - userId: user.id, - })).map(x => x.announcementId); + if (user) { + const reads = (await AnnouncementReads.findBy({ + userId: user.id, + })).map(x => x.announcementId); - for (const announcement of announcements) { - (announcement as any).isRead = reads.includes(announcement.id); - } - } + for (const announcement of announcements) { + (announcement as any).isRead = reads.includes(announcement.id); + } + } - return (ps.withUnreads ? announcements.filter((a: any) => !a.isRead) : announcements).map((a) => ({ - ...a, - createdAt: a.createdAt.toISOString(), - updatedAt: a.updatedAt?.toISOString() ?? null, - })); -}); + return (ps.withUnreads ? announcements.filter((a: any) => !a.isRead) : announcements).map((a) => ({ + ...a, + createdAt: a.createdAt.toISOString(), + updatedAt: a.updatedAt?.toISOString() ?? null, + })); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index 63e8c6984a99..b29c80493664 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -65,6 +65,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/antennas/delete.ts b/packages/backend/src/server/api/endpoints/antennas/delete.ts index 5eaa4752ed84..1408ca50b3e9 100644 --- a/packages/backend/src/server/api/endpoints/antennas/delete.ts +++ b/packages/backend/src/server/api/endpoints/antennas/delete.ts @@ -32,6 +32,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/antennas/list.ts b/packages/backend/src/server/api/endpoints/antennas/list.ts index a19e8ebf00bc..f498365a47fc 100644 --- a/packages/backend/src/server/api/endpoints/antennas/list.ts +++ b/packages/backend/src/server/api/endpoints/antennas/list.ts @@ -30,6 +30,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index f26d7ef40500..0dfc82c11781 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -51,6 +51,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/antennas/show.ts b/packages/backend/src/server/api/endpoints/antennas/show.ts index 440e15e313f2..3890283178b3 100644 --- a/packages/backend/src/server/api/endpoints/antennas/show.ts +++ b/packages/backend/src/server/api/endpoints/antennas/show.ts @@ -37,6 +37,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts index 749d6b158717..5cce376d6d42 100644 --- a/packages/backend/src/server/api/endpoints/antennas/update.ts +++ b/packages/backend/src/server/api/endpoints/antennas/update.ts @@ -71,6 +71,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/ap/get.ts b/packages/backend/src/server/api/endpoints/ap/get.ts index 4e1173f5d641..2ff3bbe97b85 100644 --- a/packages/backend/src/server/api/endpoints/ap/get.ts +++ b/packages/backend/src/server/api/endpoints/ap/get.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; +import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import Resolver from '@/remote/activitypub/resolver.js'; import { ApiError } from '../../error.js'; -import ms from 'ms'; export const meta = { tags: ['federation'], @@ -35,11 +35,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const resolver = new Resolver(); - const object = await resolver.resolve(ps.uri); - return object; -}); + const resolver = new Resolver(); + const object = await resolver.resolve(ps.uri); + return object; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index 2b84aebef87e..00f56981702c 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -1,19 +1,19 @@ import { Inject, Injectable } from '@nestjs/common'; +import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import config from '@/config/index.js'; import { createPerson } from '@/remote/activitypub/models/person.js'; import { createNote } from '@/remote/activitypub/models/note.js'; import DbResolver from '@/remote/activitypub/db-resolver.js'; import Resolver from '@/remote/activitypub/resolver.js'; -import { ApiError } from '../../error.js'; import { extractDbHost } from '@/misc/convert-host.js'; import { Users, Notes } from '@/models/index.js'; -import { Note } from '@/models/entities/note.js'; -import { CacheableLocalUser, User } from '@/models/entities/user.js'; +import type { Note } from '@/models/entities/note.js'; +import type { CacheableLocalUser, User } from '@/models/entities/user.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { isActor, isPost, getApId } from '@/remote/activitypub/type.js'; -import ms from 'ms'; -import { SchemaType } from '@/misc/schema.js'; +import type { SchemaType } from '@/misc/schema.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['federation'], @@ -48,8 +48,8 @@ export const meta = { type: 'object', optional: false, nullable: false, ref: 'UserDetailedNotMe', - } - } + }, + }, }, { type: 'object', @@ -63,9 +63,9 @@ export const meta = { type: 'object', optional: false, nullable: false, ref: 'Note', - } - } - } + }, + }, + }, ], }, } as const; @@ -82,17 +82,22 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const object = await fetchAny(ps.uri, me); - if (object) { - return object; - } else { - throw new ApiError(meta.errors.noSuchObject); + const object = await fetchAny(ps.uri, me); + if (object) { + return object; + } else { + throw new ApiError(meta.errors.noSuchObject); + } + }); } -}); +} /*** * URIからUserかNoteを解決する diff --git a/packages/backend/src/server/api/endpoints/app/create.ts b/packages/backend/src/server/api/endpoints/app/create.ts index 133207d82150..34efe8963d6e 100644 --- a/packages/backend/src/server/api/endpoints/app/create.ts +++ b/packages/backend/src/server/api/endpoints/app/create.ts @@ -34,30 +34,35 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - // Generate secret - const secret = secureRndstr(32, true); - - // for backward compatibility - const permission = unique(ps.permission.map(v => v.replace(/^(.+)(\/|-)(read|write)$/, '$3:$1'))); - - // Create account - const app = await Apps.insert({ - id: genId(), - createdAt: new Date(), - userId: user ? user.id : null, - name: ps.name, - description: ps.description, - permission, - callbackUrl: ps.callbackUrl, - secret: secret, - }).then(x => Apps.findOneByOrFail(x.identifiers[0])); - - return await Apps.pack(app, null, { - detail: true, - includeSecret: true, - }); -}); + // Generate secret + const secret = secureRndstr(32, true); + + // for backward compatibility + const permission = unique(ps.permission.map(v => v.replace(/^(.+)(\/|-)(read|write)$/, '$3:$1'))); + + // Create account + const app = await Apps.insert({ + id: genId(), + createdAt: new Date(), + userId: user ? user.id : null, + name: ps.name, + description: ps.description, + permission, + callbackUrl: ps.callbackUrl, + secret: secret, + }).then(x => Apps.findOneByOrFail(x.identifiers[0])); + + return await Apps.pack(app, null, { + detail: true, + includeSecret: true, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/app/show.ts b/packages/backend/src/server/api/endpoints/app/show.ts index f8505c815ef2..43dcc1f9f729 100644 --- a/packages/backend/src/server/api/endpoints/app/show.ts +++ b/packages/backend/src/server/api/endpoints/app/show.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ApiError } from '../../error.js'; import { Apps } from '@/models/index.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['app'], @@ -30,18 +30,29 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user, token) => { - const isSecure = user != null && token == null; - - // Lookup app - const ap = await Apps.findOneBy({ id: ps.appId }); - - if (ap == null) { - throw new ApiError(meta.errors.noSuchApp); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user, token) => { + const isSecure = user != null && token == null; + + // Lookup app + const ap = await Apps.findOneBy({ id: ps.appId }); + + if (ap == null) { + throw new ApiError(meta.errors.noSuchApp); + } + + return await Apps.pack(ap, user, { + detail: true, + includeSecret: isSecure && (ap.userId === user!.id), + }); + }); } - - return await Apps.pack(ap, user, { - detail: true, - includeSecret: isSecure && (ap.userId === user!.id), - }); -}); +} diff --git a/packages/backend/src/server/api/endpoints/auth/accept.ts b/packages/backend/src/server/api/endpoints/auth/accept.ts index 6b2cdb13f3a1..594a16723cc3 100644 --- a/packages/backend/src/server/api/endpoints/auth/accept.ts +++ b/packages/backend/src/server/api/endpoints/auth/accept.ts @@ -1,10 +1,10 @@ import * as crypto from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ApiError } from '../../error.js'; import { AuthSessions, AccessTokens, Apps } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['auth'], @@ -34,52 +34,57 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - // Fetch token - const session = await AuthSessions - .findOneBy({ token: ps.token }); + // Fetch token + const session = await AuthSessions + .findOneBy({ token: ps.token }); - if (session == null) { - throw new ApiError(meta.errors.noSuchSession); - } + if (session == null) { + throw new ApiError(meta.errors.noSuchSession); + } + + // Generate access token + const accessToken = secureRndstr(32, true); - // Generate access token - const accessToken = secureRndstr(32, true); + // Fetch exist access token + const exist = await AccessTokens.findOneBy({ + appId: session.appId, + userId: user.id, + }); - // Fetch exist access token - const exist = await AccessTokens.findOneBy({ - appId: session.appId, - userId: user.id, - }); + if (exist == null) { + // Lookup app + const app = await Apps.findOneByOrFail({ id: session.appId }); - if (exist == null) { - // Lookup app - const app = await Apps.findOneByOrFail({ id: session.appId }); + // Generate Hash + const sha256 = crypto.createHash('sha256'); + sha256.update(accessToken + app.secret); + const hash = sha256.digest('hex'); - // Generate Hash - const sha256 = crypto.createHash('sha256'); - sha256.update(accessToken + app.secret); - const hash = sha256.digest('hex'); + const now = new Date(); - const now = new Date(); + // Insert access token doc + await AccessTokens.insert({ + id: genId(), + createdAt: now, + lastUsedAt: now, + appId: session.appId, + userId: user.id, + token: accessToken, + hash: hash, + }); + } - // Insert access token doc - await AccessTokens.insert({ - id: genId(), - createdAt: now, - lastUsedAt: now, - appId: session.appId, - userId: user.id, - token: accessToken, - hash: hash, + // Update session + await AuthSessions.update(session.id, { + userId: user.id, + }); }); } - - // Update session - await AuthSessions.update(session.id, { - userId: user.id, - }); -}); +} diff --git a/packages/backend/src/server/api/endpoints/auth/session/generate.ts b/packages/backend/src/server/api/endpoints/auth/session/generate.ts index a50f00e810bc..6342d5e086c0 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/generate.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/generate.ts @@ -1,10 +1,10 @@ import { v4 as uuid } from 'uuid'; -import config from '@/config/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import config from '@/config/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ApiError } from '../../../error.js'; import { Apps, AuthSessions } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['auth'], @@ -48,32 +48,37 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - // Lookup app - const app = await Apps.findOneBy({ - secret: ps.appSecret, - }); + // Lookup app + const app = await Apps.findOneBy({ + secret: ps.appSecret, + }); - if (app == null) { - throw new ApiError(meta.errors.noSuchApp); - } + if (app == null) { + throw new ApiError(meta.errors.noSuchApp); + } - // Generate token - const token = uuid(); + // Generate token + const token = uuid(); - // Create session token document - const doc = await AuthSessions.insert({ - id: genId(), - createdAt: new Date(), - appId: app.id, - token: token, - }).then(x => AuthSessions.findOneByOrFail(x.identifiers[0])); + // Create session token document + const doc = await AuthSessions.insert({ + id: genId(), + createdAt: new Date(), + appId: app.id, + token: token, + }).then(x => AuthSessions.findOneByOrFail(x.identifiers[0])); - return { - token: doc.token, - url: `${config.authUrl}/${doc.token}`, - }; -}); + return { + token: doc.token, + url: `${config.authUrl}/${doc.token}`, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/auth/session/show.ts b/packages/backend/src/server/api/endpoints/auth/session/show.ts index 3582a09339e1..8d6134901ca0 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/show.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/show.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ApiError } from '../../../error.js'; import { AuthSessions } from '@/models/index.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['auth'], @@ -50,18 +50,23 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - // Lookup session - const session = await AuthSessions.findOneBy({ - token: ps.token, - }); + // Lookup session + const session = await AuthSessions.findOneBy({ + token: ps.token, + }); - if (session == null) { - throw new ApiError(meta.errors.noSuchSession); - } + if (session == null) { + throw new ApiError(meta.errors.noSuchSession); + } - return await AuthSessions.pack(session, user); -}); + return await AuthSessions.pack(session, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts index 327eb7daab09..f4e8d0c0d8fb 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ApiError } from '../../../error.js'; import { Apps, AuthSessions, AccessTokens, Users } from '@/models/index.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['auth'], @@ -59,46 +59,51 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - // Lookup app - const app = await Apps.findOneBy({ - secret: ps.appSecret, - }); - - if (app == null) { - throw new ApiError(meta.errors.noSuchApp); - } - - // Fetch token - const session = await AuthSessions.findOneBy({ - token: ps.token, - appId: app.id, - }); - - if (session == null) { - throw new ApiError(meta.errors.noSuchSession); + // Lookup app + const app = await Apps.findOneBy({ + secret: ps.appSecret, + }); + + if (app == null) { + throw new ApiError(meta.errors.noSuchApp); + } + + // Fetch token + const session = await AuthSessions.findOneBy({ + token: ps.token, + appId: app.id, + }); + + if (session == null) { + throw new ApiError(meta.errors.noSuchSession); + } + + if (session.userId == null) { + throw new ApiError(meta.errors.pendingSession); + } + + // Lookup access token + const accessToken = await AccessTokens.findOneByOrFail({ + appId: app.id, + userId: session.userId, + }); + + // Delete session + AuthSessions.delete(session.id); + + return { + accessToken: accessToken.token, + user: await Users.pack(session.userId, null, { + detail: true, + }), + }; + }); } - - if (session.userId == null) { - throw new ApiError(meta.errors.pendingSession); - } - - // Lookup access token - const accessToken = await AccessTokens.findOneByOrFail({ - appId: app.id, - userId: session.userId, - }); - - // Delete session - AuthSessions.delete(session.id); - - return { - accessToken: accessToken.token, - user: await Users.pack(session.userId, null, { - detail: true, - }), - }; -}); +} diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts index eee748d8e3c0..e7b279ff4bdd 100644 --- a/packages/backend/src/server/api/endpoints/blocking/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking/create.ts @@ -57,6 +57,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts index cd776fcf8650..f110d4f6cb4f 100644 --- a/packages/backend/src/server/api/endpoints/blocking/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts @@ -57,6 +57,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/blocking/list.ts b/packages/backend/src/server/api/endpoints/blocking/list.ts index 19829173c8c4..996aafe63a42 100644 --- a/packages/backend/src/server/api/endpoints/blocking/list.ts +++ b/packages/backend/src/server/api/endpoints/blocking/list.ts @@ -35,6 +35,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/channels/create.ts b/packages/backend/src/server/api/endpoints/channels/create.ts index 8ed41f189c44..1c75c22d0eab 100644 --- a/packages/backend/src/server/api/endpoints/channels/create.ts +++ b/packages/backend/src/server/api/endpoints/channels/create.ts @@ -41,6 +41,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/channels/featured.ts b/packages/backend/src/server/api/endpoints/channels/featured.ts index cfaeef492e15..ada374f18fe1 100644 --- a/packages/backend/src/server/api/endpoints/channels/featured.ts +++ b/packages/backend/src/server/api/endpoints/channels/featured.ts @@ -28,6 +28,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/channels/follow.ts b/packages/backend/src/server/api/endpoints/channels/follow.ts index adb53347edc0..aa3df1f676a7 100644 --- a/packages/backend/src/server/api/endpoints/channels/follow.ts +++ b/packages/backend/src/server/api/endpoints/channels/follow.ts @@ -33,6 +33,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/channels/followed.ts b/packages/backend/src/server/api/endpoints/channels/followed.ts index 86c8a52bde9b..8b06409e63f4 100644 --- a/packages/backend/src/server/api/endpoints/channels/followed.ts +++ b/packages/backend/src/server/api/endpoints/channels/followed.ts @@ -35,6 +35,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/channels/owned.ts b/packages/backend/src/server/api/endpoints/channels/owned.ts index ef87a1521381..f33b1bd42bc3 100644 --- a/packages/backend/src/server/api/endpoints/channels/owned.ts +++ b/packages/backend/src/server/api/endpoints/channels/owned.ts @@ -35,6 +35,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/channels/show.ts b/packages/backend/src/server/api/endpoints/channels/show.ts index d0dd33f023d6..e31cc45c2d80 100644 --- a/packages/backend/src/server/api/endpoints/channels/show.ts +++ b/packages/backend/src/server/api/endpoints/channels/show.ts @@ -35,6 +35,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index 9f7f9c87ab26..915669663fc8 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -46,6 +46,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/channels/unfollow.ts b/packages/backend/src/server/api/endpoints/channels/unfollow.ts index 094bba578d09..119dac55fdb7 100644 --- a/packages/backend/src/server/api/endpoints/channels/unfollow.ts +++ b/packages/backend/src/server/api/endpoints/channels/unfollow.ts @@ -32,6 +32,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/channels/update.ts b/packages/backend/src/server/api/endpoints/channels/update.ts index d8d224eee4ee..12c843302d40 100644 --- a/packages/backend/src/server/api/endpoints/channels/update.ts +++ b/packages/backend/src/server/api/endpoints/channels/update.ts @@ -52,6 +52,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/active-users.ts b/packages/backend/src/server/api/endpoints/charts/active-users.ts index 1a1e3e878c62..16955e8b462b 100644 --- a/packages/backend/src/server/api/endpoints/charts/active-users.ts +++ b/packages/backend/src/server/api/endpoints/charts/active-users.ts @@ -26,6 +26,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/ap-request.ts b/packages/backend/src/server/api/endpoints/charts/ap-request.ts index 0a5dabea68e6..391d757d0037 100644 --- a/packages/backend/src/server/api/endpoints/charts/ap-request.ts +++ b/packages/backend/src/server/api/endpoints/charts/ap-request.ts @@ -26,6 +26,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/drive.ts b/packages/backend/src/server/api/endpoints/charts/drive.ts index 22e366d1dc89..99469791de0c 100644 --- a/packages/backend/src/server/api/endpoints/charts/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/drive.ts @@ -26,6 +26,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/federation.ts b/packages/backend/src/server/api/endpoints/charts/federation.ts index 2cc4456d59e6..ff765274a719 100644 --- a/packages/backend/src/server/api/endpoints/charts/federation.ts +++ b/packages/backend/src/server/api/endpoints/charts/federation.ts @@ -26,6 +26,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/hashtag.ts b/packages/backend/src/server/api/endpoints/charts/hashtag.ts index b147212714a9..0d8340cdb47c 100644 --- a/packages/backend/src/server/api/endpoints/charts/hashtag.ts +++ b/packages/backend/src/server/api/endpoints/charts/hashtag.ts @@ -27,6 +27,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/instance.ts b/packages/backend/src/server/api/endpoints/charts/instance.ts index 91a83249b24c..ecc8858d5cfb 100644 --- a/packages/backend/src/server/api/endpoints/charts/instance.ts +++ b/packages/backend/src/server/api/endpoints/charts/instance.ts @@ -27,6 +27,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/notes.ts b/packages/backend/src/server/api/endpoints/charts/notes.ts index 4150bf61bb1d..f850d227fd8b 100644 --- a/packages/backend/src/server/api/endpoints/charts/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/notes.ts @@ -26,6 +26,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/user/drive.ts b/packages/backend/src/server/api/endpoints/charts/user/drive.ts index a71602cd7352..6ebd1cf084b8 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/drive.ts @@ -27,6 +27,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/user/following.ts b/packages/backend/src/server/api/endpoints/charts/user/following.ts index b18d4fc744ac..1749285e0404 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/following.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/following.ts @@ -27,6 +27,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/user/notes.ts b/packages/backend/src/server/api/endpoints/charts/user/notes.ts index 2875c31f0e8c..9626c08c3122 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/notes.ts @@ -27,6 +27,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts index 2fada9336b83..6e37d01e9027 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts @@ -27,6 +27,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/charts/users.ts b/packages/backend/src/server/api/endpoints/charts/users.ts index 2d8197eaa8c3..80c8f348f4ff 100644 --- a/packages/backend/src/server/api/endpoints/charts/users.ts +++ b/packages/backend/src/server/api/endpoints/charts/users.ts @@ -26,6 +26,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/clips/add-note.ts b/packages/backend/src/server/api/endpoints/clips/add-note.ts index 1157f43c813b..c9031e14e282 100644 --- a/packages/backend/src/server/api/endpoints/clips/add-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/add-note.ts @@ -46,6 +46,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/clips/create.ts b/packages/backend/src/server/api/endpoints/clips/create.ts index 059bae8e646a..f3022adde615 100644 --- a/packages/backend/src/server/api/endpoints/clips/create.ts +++ b/packages/backend/src/server/api/endpoints/clips/create.ts @@ -31,6 +31,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/clips/delete.ts b/packages/backend/src/server/api/endpoints/clips/delete.ts index 1f469503a63f..7cfb56f9313c 100644 --- a/packages/backend/src/server/api/endpoints/clips/delete.ts +++ b/packages/backend/src/server/api/endpoints/clips/delete.ts @@ -31,6 +31,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/clips/list.ts b/packages/backend/src/server/api/endpoints/clips/list.ts index c26c598d2122..2b1d4ece6512 100644 --- a/packages/backend/src/server/api/endpoints/clips/list.ts +++ b/packages/backend/src/server/api/endpoints/clips/list.ts @@ -30,6 +30,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts index 3e6722c68c29..74b708f29ad0 100644 --- a/packages/backend/src/server/api/endpoints/clips/notes.ts +++ b/packages/backend/src/server/api/endpoints/clips/notes.ts @@ -48,6 +48,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/clips/remove-note.ts b/packages/backend/src/server/api/endpoints/clips/remove-note.ts index 710fbd4dfe8e..26909659810b 100644 --- a/packages/backend/src/server/api/endpoints/clips/remove-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/remove-note.ts @@ -39,6 +39,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/clips/show.ts b/packages/backend/src/server/api/endpoints/clips/show.ts index 64e59514fb19..a9ae8d055f3b 100644 --- a/packages/backend/src/server/api/endpoints/clips/show.ts +++ b/packages/backend/src/server/api/endpoints/clips/show.ts @@ -37,6 +37,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/clips/update.ts b/packages/backend/src/server/api/endpoints/clips/update.ts index cec36e48bcba..8f02ad3c618f 100644 --- a/packages/backend/src/server/api/endpoints/clips/update.ts +++ b/packages/backend/src/server/api/endpoints/clips/update.ts @@ -40,6 +40,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/drive.ts b/packages/backend/src/server/api/endpoints/drive.ts index a52b3a04ccf4..ca3238fa1597 100644 --- a/packages/backend/src/server/api/endpoints/drive.ts +++ b/packages/backend/src/server/api/endpoints/drive.ts @@ -1,6 +1,6 @@ +import { Inject, Injectable } from '@nestjs/common'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { DriveFiles } from '@/models/index.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -36,17 +36,22 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const instance = await fetchMeta(true); - - // Calculate drive usage - const usage = await DriveFiles.calcDriveUsageOf(user.id); - - return { - capacity: 1024 * 1024 * (user.driveCapacityOverrideMb || instance.localDriveCapacityMb), - usage: usage, - }; -}); + const instance = await fetchMeta(true); + + // Calculate drive usage + const usage = await DriveFiles.calcDriveUsageOf(user.id); + + return { + capacity: 1024 * 1024 * (user.driveCapacityOverrideMb || instance.localDriveCapacityMb), + usage: usage, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/drive/files.ts b/packages/backend/src/server/api/endpoints/drive/files.ts index 1ef358fb9243..2d89d925b2da 100644 --- a/packages/backend/src/server/api/endpoints/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/drive/files.ts @@ -37,6 +37,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts index d0ef15b84ab4..9d1888dd7860 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts @@ -43,6 +43,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts index 60dd0336be25..f4a26eccd0fa 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts @@ -29,6 +29,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/drive/files/delete.ts b/packages/backend/src/server/api/endpoints/drive/files/delete.ts index 085168f9a3c0..8f8eb85d9d0f 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/delete.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/delete.ts @@ -41,6 +41,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts index ffae36293d9f..2096e6d9884b 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts @@ -34,6 +34,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/drive/files/find.ts b/packages/backend/src/server/api/endpoints/drive/files/find.ts index ddb87a15228e..0294b337421c 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/find.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/find.ts @@ -36,6 +36,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/drive/files/show.ts b/packages/backend/src/server/api/endpoints/drive/files/show.ts index f2c39cc4e1cb..c99219cca363 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/show.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/show.ts @@ -56,6 +56,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/drive/files/update.ts b/packages/backend/src/server/api/endpoints/drive/files/update.ts index 57950ceffe65..89876bc37134 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts @@ -63,6 +63,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/drive/folders.ts b/packages/backend/src/server/api/endpoints/drive/folders.ts index 07fbb3e65293..9605ba61e894 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders.ts @@ -36,6 +36,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/drive/folders/create.ts b/packages/backend/src/server/api/endpoints/drive/folders/create.ts index b193555f2393..b01bbc5808cb 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/create.ts @@ -40,6 +40,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/drive/folders/delete.ts b/packages/backend/src/server/api/endpoints/drive/folders/delete.ts index 8ea7a378420e..3a5244bb55c7 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/delete.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/delete.ts @@ -38,6 +38,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/drive/folders/find.ts b/packages/backend/src/server/api/endpoints/drive/folders/find.ts index 3e3462be4e5f..f8305522938b 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/find.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/find.ts @@ -34,6 +34,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/drive/folders/show.ts b/packages/backend/src/server/api/endpoints/drive/folders/show.ts index 0d3e67cba2ae..9c492b0e5c31 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/show.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/show.ts @@ -37,6 +37,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/drive/folders/update.ts b/packages/backend/src/server/api/endpoints/drive/folders/update.ts index 4e3bd2e69211..0b95b33adf4c 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/update.ts @@ -52,6 +52,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/drive/stream.ts b/packages/backend/src/server/api/endpoints/drive/stream.ts index 35314bdc5293..51ae9bd7527b 100644 --- a/packages/backend/src/server/api/endpoints/drive/stream.ts +++ b/packages/backend/src/server/api/endpoints/drive/stream.ts @@ -36,6 +36,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/email-address/available.ts b/packages/backend/src/server/api/endpoints/email-address/available.ts index f10dc4abeef5..0189fd50d1af 100644 --- a/packages/backend/src/server/api/endpoints/email-address/available.ts +++ b/packages/backend/src/server/api/endpoints/email-address/available.ts @@ -35,6 +35,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/endpoint.ts b/packages/backend/src/server/api/endpoints/endpoint.ts index cc5115429aa5..fb1e4229abb4 100644 --- a/packages/backend/src/server/api/endpoints/endpoint.ts +++ b/packages/backend/src/server/api/endpoints/endpoint.ts @@ -20,16 +20,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const ep = endpoints.find(x => x.name === ps.endpoint); - if (ep == null) return null; - return { - params: Object.entries(ep.params.properties || {}).map(([k, v]) => ({ - name: k, - type: v.type.charAt(0).toUpperCase() + v.type.slice(1), - })), - }; -}); + const ep = endpoints.find(x => x.name === ps.endpoint); + if (ep == null) return null; + return { + params: Object.entries(ep.params.properties || {}).map(([k, v]) => ({ + name: k, + type: v.type.charAt(0).toUpperCase() + v.type.slice(1), + })), + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/endpoints.ts b/packages/backend/src/server/api/endpoints/endpoints.ts index b6ebf6bf769d..b42bf88bf239 100644 --- a/packages/backend/src/server/api/endpoints/endpoints.ts +++ b/packages/backend/src/server/api/endpoints/endpoints.ts @@ -30,6 +30,17 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async () => { - return endpoints.map(x => x.name); -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async () => { + return endpoints.map(x => x.name); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/export-custom-emojis.ts b/packages/backend/src/server/api/endpoints/export-custom-emojis.ts index 6cdcc4628af1..4da4b2d500f0 100644 --- a/packages/backend/src/server/api/endpoints/export-custom-emojis.ts +++ b/packages/backend/src/server/api/endpoints/export-custom-emojis.ts @@ -1,6 +1,6 @@ import ms from 'ms'; -import { createExportCustomEmojisJob } from '@/queue/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { createExportCustomEmojisJob } from '@/queue/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -22,9 +22,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - createExportCustomEmojisJob(user); -}); + createExportCustomEmojisJob(user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/federation/followers.ts b/packages/backend/src/server/api/endpoints/federation/followers.ts index 5d57d7602c6b..591e29089d4c 100644 --- a/packages/backend/src/server/api/endpoints/federation/followers.ts +++ b/packages/backend/src/server/api/endpoints/federation/followers.ts @@ -34,6 +34,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/federation/following.ts b/packages/backend/src/server/api/endpoints/federation/following.ts index 84499fdaf976..f7ffa38a62a9 100644 --- a/packages/backend/src/server/api/endpoints/federation/following.ts +++ b/packages/backend/src/server/api/endpoints/federation/following.ts @@ -34,6 +34,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts index 6d7b29b4e688..e7646d0c289d 100644 --- a/packages/backend/src/server/api/endpoints/federation/instances.ts +++ b/packages/backend/src/server/api/endpoints/federation/instances.ts @@ -41,6 +41,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/federation/show-instance.ts b/packages/backend/src/server/api/endpoints/federation/show-instance.ts index 297e2f243ece..792c7920dcf5 100644 --- a/packages/backend/src/server/api/endpoints/federation/show-instance.ts +++ b/packages/backend/src/server/api/endpoints/federation/show-instance.ts @@ -30,6 +30,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/federation/stats.ts b/packages/backend/src/server/api/endpoints/federation/stats.ts index b796805d2061..e6a4967f7951 100644 --- a/packages/backend/src/server/api/endpoints/federation/stats.ts +++ b/packages/backend/src/server/api/endpoints/federation/stats.ts @@ -25,6 +25,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts index ca494922ac56..824a4780ea6d 100644 --- a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts +++ b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts @@ -21,6 +21,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/federation/users.ts b/packages/backend/src/server/api/endpoints/federation/users.ts index 72021aa715d8..77c8533e2f92 100644 --- a/packages/backend/src/server/api/endpoints/federation/users.ts +++ b/packages/backend/src/server/api/endpoints/federation/users.ts @@ -34,6 +34,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/fetch-rss.ts b/packages/backend/src/server/api/endpoints/fetch-rss.ts index a3e6d46b3106..4d3687456127 100644 --- a/packages/backend/src/server/api/endpoints/fetch-rss.ts +++ b/packages/backend/src/server/api/endpoints/fetch-rss.ts @@ -1,7 +1,7 @@ import Parser from 'rss-parser'; +import { Inject, Injectable } from '@nestjs/common'; import { getResponse } from '@/misc/fetch.js'; import config from '@/config/index.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; const rssParser = new Parser(); @@ -26,21 +26,26 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const res = await getResponse({ - url: ps.url, - method: 'GET', - headers: Object.assign({ - 'User-Agent': config.userAgent, - Accept: 'application/rss+xml, */*', - }), - timeout: 5000, - }); - - const text = await res.text(); - - return rssParser.parseString(text); -}); + const res = await getResponse({ + url: ps.url, + method: 'GET', + headers: Object.assign({ + 'User-Agent': config.userAgent, + Accept: 'application/rss+xml, */*', + }), + timeout: 5000, + }); + + const text = await res.text(); + + return rssParser.parseString(text); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts index ba49262a587c..111de351cd1f 100644 --- a/packages/backend/src/server/api/endpoints/following/create.ts +++ b/packages/backend/src/server/api/endpoints/following/create.ts @@ -70,6 +70,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/following/delete.ts b/packages/backend/src/server/api/endpoints/following/delete.ts index 5d62f12b20a6..b51d2da8094d 100644 --- a/packages/backend/src/server/api/endpoints/following/delete.ts +++ b/packages/backend/src/server/api/endpoints/following/delete.ts @@ -57,6 +57,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/following/invalidate.ts b/packages/backend/src/server/api/endpoints/following/invalidate.ts index 3fdad31a7328..28c7ae1d4445 100644 --- a/packages/backend/src/server/api/endpoints/following/invalidate.ts +++ b/packages/backend/src/server/api/endpoints/following/invalidate.ts @@ -57,6 +57,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/following/requests/accept.ts b/packages/backend/src/server/api/endpoints/following/requests/accept.ts index 6cc7927535a4..a6c059599441 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/accept.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/accept.ts @@ -37,6 +37,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts index b558320271e4..12ed92b9c116 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts @@ -46,6 +46,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/following/requests/list.ts b/packages/backend/src/server/api/endpoints/following/requests/list.ts index d8c5fea47067..f446a70bbda5 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/list.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/list.ts @@ -46,6 +46,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/following/requests/reject.ts b/packages/backend/src/server/api/endpoints/following/requests/reject.ts index 26d9f7f02740..c2b8c384e6f8 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/reject.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/reject.ts @@ -32,6 +32,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/gallery/featured.ts b/packages/backend/src/server/api/endpoints/gallery/featured.ts index 1a5c2039dc06..509093b565d3 100644 --- a/packages/backend/src/server/api/endpoints/gallery/featured.ts +++ b/packages/backend/src/server/api/endpoints/gallery/featured.ts @@ -28,6 +28,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/gallery/popular.ts b/packages/backend/src/server/api/endpoints/gallery/popular.ts index dbf712f61911..80bb5a52a92a 100644 --- a/packages/backend/src/server/api/endpoints/gallery/popular.ts +++ b/packages/backend/src/server/api/endpoints/gallery/popular.ts @@ -28,6 +28,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/gallery/posts.ts b/packages/backend/src/server/api/endpoints/gallery/posts.ts index 18d764d73734..6c7a78de3641 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts.ts @@ -31,6 +31,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts index aee094a50c05..3fb9664b57b1 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts @@ -47,6 +47,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts b/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts index 34a0f48a51f8..4fd01c9572ef 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts @@ -31,6 +31,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts index f4740bdaa3f6..fcf1284b48c5 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts @@ -44,6 +44,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts b/packages/backend/src/server/api/endpoints/gallery/posts/show.ts index e69e7ce6998f..540576603b1c 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/show.ts @@ -35,6 +35,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts index b401d54dff88..de36c323b1c5 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts @@ -37,6 +37,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts index 9c008cc6c7f3..d8e79a2ce45f 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts @@ -47,6 +47,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/get-online-users-count.ts b/packages/backend/src/server/api/endpoints/get-online-users-count.ts index 4dff275046cd..82f87cd723bb 100644 --- a/packages/backend/src/server/api/endpoints/get-online-users-count.ts +++ b/packages/backend/src/server/api/endpoints/get-online-users-count.ts @@ -1,7 +1,7 @@ import { MoreThan } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; import { USER_ONLINE_THRESHOLD } from '@/const.js'; import { Users } from '@/models/index.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -17,12 +17,23 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async () => { - const count = await Users.countBy({ - lastActiveDate: MoreThan(new Date(Date.now() - USER_ONLINE_THRESHOLD)), - }); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async () => { + const count = await Users.countBy({ + lastActiveDate: MoreThan(new Date(Date.now() - USER_ONLINE_THRESHOLD)), + }); - return { - count, - }; -}); + return { + count, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/hashtags/list.ts b/packages/backend/src/server/api/endpoints/hashtags/list.ts index f30661546a56..c7c6137491bc 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/list.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/list.ts @@ -34,6 +34,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/hashtags/search.ts b/packages/backend/src/server/api/endpoints/hashtags/search.ts index d101c8607db6..7c3c50f678b5 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/search.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/search.ts @@ -31,6 +31,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/hashtags/show.ts b/packages/backend/src/server/api/endpoints/hashtags/show.ts index fbf815891381..768459229e43 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/show.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/show.ts @@ -36,6 +36,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/hashtags/trend.ts b/packages/backend/src/server/api/endpoints/hashtags/trend.ts index ee3a93657b58..0c132ca1d0e1 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/trend.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/trend.ts @@ -61,7 +61,16 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async () => { +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async () => { const instance = await fetchMeta(true); const hiddenTags = instance.hiddenTags.map(t => normalizeForSearch(t)); diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts index 130cb7ce3109..2969bd89f3eb 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/users.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts @@ -35,6 +35,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index 9d63089049ef..7415b4500dde 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -1,5 +1,5 @@ -import { Users } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -21,12 +21,23 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user, token) => { - const isSecure = token == null; +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user, token) => { + const isSecure = token == null; - // ここで渡ってきている user はキャッシュされていて古い可能性もあるので id だけ渡す - return await Users.pack(user.id, user, { - detail: true, - includeSecrets: isSecure, - }); -}); + // ここで渡ってきている user はキャッシュされていて古い可能性もあるので id だけ渡す + return await Users.pack(user.id, user, { + detail: true, + includeSecrets: isSecure, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/2fa/done.ts b/packages/backend/src/server/api/endpoints/i/2fa/done.ts index a676d0e65266..4a4840b1bccb 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/done.ts @@ -21,30 +21,35 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const token = ps.token.replace(/\s/g, ''); + const token = ps.token.replace(/\s/g, ''); - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - if (profile.twoFactorTempSecret == null) { - throw new Error('二段階認証の設定が開始されていません'); - } + if (profile.twoFactorTempSecret == null) { + throw new Error('二段階認証の設定が開始されていません'); + } - const verified = (speakeasy as any).totp.verify({ - secret: profile.twoFactorTempSecret, - encoding: 'base32', - token: token, - }); + const verified = (speakeasy as any).totp.verify({ + secret: profile.twoFactorTempSecret, + encoding: 'base32', + token: token, + }); - if (!verified) { - throw new Error('not verified'); - } + if (!verified) { + throw new Error('not verified'); + } - await UserProfiles.update(user.id, { - twoFactorSecret: profile.twoFactorTempSecret, - twoFactorEnabled: true, - }); -}); + await UserProfiles.update(user.id, { + twoFactorSecret: profile.twoFactorTempSecret, + twoFactorEnabled: true, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts index 9d88b450d957..1800860c7ea6 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts @@ -1,5 +1,5 @@ -import bcrypt from 'bcryptjs'; import { promisify } from 'node:util'; +import bcrypt from 'bcryptjs'; import * as cbor from 'cbor'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -10,8 +10,8 @@ import { Users, } from '@/models/index.js'; import config from '@/config/index.js'; -import { procedures, hash } from '../../../2fa.js'; import { publishMainStream } from '@/services/stream.js'; +import { procedures, hash } from '../../../2fa.js'; const cborDecodeFirst = promisify(cbor.decodeFirst) as any; const rpIdHashReal = hash(Buffer.from(config.hostname, 'utf-8')); @@ -38,113 +38,118 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); - - if (!same) { - throw new Error('incorrect password'); - } - - if (!profile.twoFactorEnabled) { - throw new Error('2fa not enabled'); - } - - const clientData = JSON.parse(ps.clientDataJSON); - - if (clientData.type !== 'webauthn.create') { - throw new Error('not a creation attestation'); - } - if (clientData.origin !== config.scheme + '://' + config.host) { - throw new Error('origin mismatch'); - } - - const clientDataJSONHash = hash(Buffer.from(ps.clientDataJSON, 'utf-8')); - - const attestation = await cborDecodeFirst(ps.attestationObject); - - const rpIdHash = attestation.authData.slice(0, 32); - if (!rpIdHashReal.equals(rpIdHash)) { - throw new Error('rpIdHash mismatch'); - } - - const flags = attestation.authData[32]; - - // eslint:disable-next-line:no-bitwise - if (!(flags & 1)) { - throw new Error('user not present'); - } - - const authData = Buffer.from(attestation.authData); - const credentialIdLength = authData.readUInt16BE(53); - const credentialId = authData.slice(55, 55 + credentialIdLength); - const publicKeyData = authData.slice(55 + credentialIdLength); - const publicKey: Map = await cborDecodeFirst(publicKeyData); - if (publicKey.get(3) !== -7) { - throw new Error('alg mismatch'); - } - - if (!(procedures as any)[attestation.fmt]) { - throw new Error('unsupported fmt'); - } - - const verificationData = (procedures as any)[attestation.fmt].verify({ - attStmt: attestation.attStmt, - authenticatorData: authData, - clientDataHash: clientDataJSONHash, - credentialId, - publicKey, - rpIdHash, - }); - if (!verificationData.valid) throw new Error('signature invalid'); - - const attestationChallenge = await AttestationChallenges.findOneBy({ - userId: user.id, - id: ps.challengeId, - registrationChallenge: true, - challenge: hash(clientData.challenge).toString('hex'), - }); - - if (!attestationChallenge) { - throw new Error('non-existent challenge'); - } - - await AttestationChallenges.delete({ - userId: user.id, - id: ps.challengeId, - }); - - // Expired challenge (> 5min old) - if ( - new Date().getTime() - attestationChallenge.createdAt.getTime() >= + const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + + // Compare password + const same = await bcrypt.compare(ps.password, profile.password!); + + if (!same) { + throw new Error('incorrect password'); + } + + if (!profile.twoFactorEnabled) { + throw new Error('2fa not enabled'); + } + + const clientData = JSON.parse(ps.clientDataJSON); + + if (clientData.type !== 'webauthn.create') { + throw new Error('not a creation attestation'); + } + if (clientData.origin !== config.scheme + '://' + config.host) { + throw new Error('origin mismatch'); + } + + const clientDataJSONHash = hash(Buffer.from(ps.clientDataJSON, 'utf-8')); + + const attestation = await cborDecodeFirst(ps.attestationObject); + + const rpIdHash = attestation.authData.slice(0, 32); + if (!rpIdHashReal.equals(rpIdHash)) { + throw new Error('rpIdHash mismatch'); + } + + const flags = attestation.authData[32]; + + // eslint:disable-next-line:no-bitwise + if (!(flags & 1)) { + throw new Error('user not present'); + } + + const authData = Buffer.from(attestation.authData); + const credentialIdLength = authData.readUInt16BE(53); + const credentialId = authData.slice(55, 55 + credentialIdLength); + const publicKeyData = authData.slice(55 + credentialIdLength); + const publicKey: Map = await cborDecodeFirst(publicKeyData); + if (publicKey.get(3) !== -7) { + throw new Error('alg mismatch'); + } + + if (!(procedures as any)[attestation.fmt]) { + throw new Error('unsupported fmt'); + } + + const verificationData = (procedures as any)[attestation.fmt].verify({ + attStmt: attestation.attStmt, + authenticatorData: authData, + clientDataHash: clientDataJSONHash, + credentialId, + publicKey, + rpIdHash, + }); + if (!verificationData.valid) throw new Error('signature invalid'); + + const attestationChallenge = await AttestationChallenges.findOneBy({ + userId: user.id, + id: ps.challengeId, + registrationChallenge: true, + challenge: hash(clientData.challenge).toString('hex'), + }); + + if (!attestationChallenge) { + throw new Error('non-existent challenge'); + } + + await AttestationChallenges.delete({ + userId: user.id, + id: ps.challengeId, + }); + + // Expired challenge (> 5min old) + if ( + new Date().getTime() - attestationChallenge.createdAt.getTime() >= 5 * 60 * 1000 - ) { - throw new Error('expired challenge'); + ) { + throw new Error('expired challenge'); + } + + const credentialIdString = credentialId.toString('hex'); + + await UserSecurityKeys.insert({ + userId: user.id, + id: credentialIdString, + lastUsed: new Date(), + name: ps.name, + publicKey: verificationData.publicKey.toString('hex'), + }); + + // Publish meUpdated event + publishMainStream(user.id, 'meUpdated', await Users.pack(user.id, user, { + detail: true, + includeSecrets: true, + })); + + return { + id: credentialIdString, + name: ps.name, + }; + }); } - - const credentialIdString = credentialId.toString('hex'); - - await UserSecurityKeys.insert({ - userId: user.id, - id: credentialIdString, - lastUsed: new Date(), - name: ps.name, - publicKey: verificationData.publicKey.toString('hex'), - }); - - // Publish meUpdated event - publishMainStream(user.id, 'meUpdated', await Users.pack(user.id, user, { - detail: true, - includeSecrets: true, - })); - - return { - id: credentialIdString, - name: ps.name, - }; -}); +} diff --git a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts index 9d5cccd14af9..492b87223156 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts @@ -20,11 +20,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - await UserProfiles.update(user.id, { - usePasswordLessLogin: ps.value, - }); -}); + await UserProfiles.update(user.id, { + usePasswordLessLogin: ps.value, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts index 8767603725bf..f33228a6a87a 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts @@ -1,9 +1,9 @@ +import { promisify } from 'node:util'; +import * as crypto from 'node:crypto'; import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserProfiles, AttestationChallenges } from '@/models/index.js'; -import { promisify } from 'node:util'; -import * as crypto from 'node:crypto'; import { genId } from '@/misc/gen-id.js'; import { hash } from '../../../2fa.js'; @@ -27,42 +27,47 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + // Compare password + const same = await bcrypt.compare(ps.password, profile.password!); - if (!same) { - throw new Error('incorrect password'); - } + if (!same) { + throw new Error('incorrect password'); + } - if (!profile.twoFactorEnabled) { - throw new Error('2fa not enabled'); - } + if (!profile.twoFactorEnabled) { + throw new Error('2fa not enabled'); + } - // 32 byte challenge - const entropy = await randomBytes(32); - const challenge = entropy.toString('base64') - .replace(/=/g, '') - .replace(/\+/g, '-') - .replace(/\//g, '_'); + // 32 byte challenge + const entropy = await randomBytes(32); + const challenge = entropy.toString('base64') + .replace(/=/g, '') + .replace(/\+/g, '-') + .replace(/\//g, '_'); - const challengeId = genId(); + const challengeId = genId(); - await AttestationChallenges.insert({ - userId: user.id, - id: challengeId, - challenge: hash(Buffer.from(challenge, 'utf-8')).toString('hex'), - createdAt: new Date(), - registrationChallenge: true, - }); + await AttestationChallenges.insert({ + userId: user.id, + id: challengeId, + challenge: hash(Buffer.from(challenge, 'utf-8')).toString('hex'), + createdAt: new Date(), + registrationChallenge: true, + }); - return { - challengeId, - challenge, - }; -}); + return { + challengeId, + challenge, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register.ts b/packages/backend/src/server/api/endpoints/i/2fa/register.ts index 6ebe67a56259..d6145295f188 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts @@ -1,9 +1,9 @@ import bcrypt from 'bcryptjs'; import * as speakeasy from 'speakeasy'; import * as QRCode from 'qrcode'; +import { Inject, Injectable } from '@nestjs/common'; import config from '@/config/index.js'; import { UserProfiles } from '@/models/index.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -24,42 +24,47 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + // Compare password + const same = await bcrypt.compare(ps.password, profile.password!); - if (!same) { - throw new Error('incorrect password'); - } + if (!same) { + throw new Error('incorrect password'); + } - // Generate user's secret key - const secret = speakeasy.generateSecret({ - length: 32, - }); + // Generate user's secret key + const secret = speakeasy.generateSecret({ + length: 32, + }); - await UserProfiles.update(user.id, { - twoFactorTempSecret: secret.base32, - }); + await UserProfiles.update(user.id, { + twoFactorTempSecret: secret.base32, + }); - // Get the data URL of the authenticator URL - const url = speakeasy.otpauthURL({ - secret: secret.base32, - encoding: 'base32', - label: user.username, - issuer: config.host, - }); - const dataUrl = await QRCode.toDataURL(url); + // Get the data URL of the authenticator URL + const url = speakeasy.otpauthURL({ + secret: secret.base32, + encoding: 'base32', + label: user.username, + issuer: config.host, + }); + const dataUrl = await QRCode.toDataURL(url); - return { - qr: dataUrl, - url, - secret: secret.base32, - label: user.username, - issuer: config.host, - }; -}); + return { + qr: dataUrl, + url, + secret: secret.base32, + label: user.username, + issuer: config.host, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts index 25eafaed2005..9e589b8b8694 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts @@ -23,30 +23,35 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + // Compare password + const same = await bcrypt.compare(ps.password, profile.password!); - if (!same) { - throw new Error('incorrect password'); - } + if (!same) { + throw new Error('incorrect password'); + } - // Make sure we only delete the user's own creds - await UserSecurityKeys.delete({ - userId: user.id, - id: ps.credentialId, - }); + // Make sure we only delete the user's own creds + await UserSecurityKeys.delete({ + userId: user.id, + id: ps.credentialId, + }); - // Publish meUpdated event - publishMainStream(user.id, 'meUpdated', await Users.pack(user.id, user, { - detail: true, - includeSecrets: true, - })); + // Publish meUpdated event + publishMainStream(user.id, 'meUpdated', await Users.pack(user.id, user, { + detail: true, + includeSecrets: true, + })); - return {}; -}); + return {}; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts index 75601c9d1c02..eda5e4e941d1 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts @@ -21,21 +21,26 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + // Compare password + const same = await bcrypt.compare(ps.password, profile.password!); - if (!same) { - throw new Error('incorrect password'); - } + if (!same) { + throw new Error('incorrect password'); + } - await UserProfiles.update(user.id, { - twoFactorSecret: null, - twoFactorEnabled: false, - }); -}); + await UserProfiles.update(user.id, { + twoFactorSecret: null, + twoFactorEnabled: false, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/apps.ts b/packages/backend/src/server/api/endpoints/i/apps.ts index de2f4ec3fc81..5b7a62217e54 100644 --- a/packages/backend/src/server/api/endpoints/i/apps.ts +++ b/packages/backend/src/server/api/endpoints/i/apps.ts @@ -20,28 +20,33 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = AccessTokens.createQueryBuilder('token') - .where('token.userId = :userId', { userId: user.id }); - - switch (ps.sort) { - case '+createdAt': query.orderBy('token.createdAt', 'DESC'); break; - case '-createdAt': query.orderBy('token.createdAt', 'ASC'); break; - case '+lastUsedAt': query.orderBy('token.lastUsedAt', 'DESC'); break; - case '-lastUsedAt': query.orderBy('token.lastUsedAt', 'ASC'); break; - default: query.orderBy('token.id', 'ASC'); break; - } + const query = AccessTokens.createQueryBuilder('token') + .where('token.userId = :userId', { userId: user.id }); - const tokens = await query.getMany(); + switch (ps.sort) { + case '+createdAt': query.orderBy('token.createdAt', 'DESC'); break; + case '-createdAt': query.orderBy('token.createdAt', 'ASC'); break; + case '+lastUsedAt': query.orderBy('token.lastUsedAt', 'DESC'); break; + case '-lastUsedAt': query.orderBy('token.lastUsedAt', 'ASC'); break; + default: query.orderBy('token.id', 'ASC'); break; + } - return await Promise.all(tokens.map(token => ({ - id: token.id, - name: token.name, - createdAt: token.createdAt, - lastUsedAt: token.lastUsedAt, - permission: token.permission, - }))); -}); + const tokens = await query.getMany(); + + return await Promise.all(tokens.map(token => ({ + id: token.id, + name: token.name, + createdAt: token.createdAt, + lastUsedAt: token.lastUsedAt, + permission: token.permission, + }))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts index 34db84f13c60..2177020a2ba0 100644 --- a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts +++ b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts @@ -13,7 +13,7 @@ export const paramDef = { properties: { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, offset: { type: 'integer', default: 0 }, - sort: { type: 'string', enum: ['desc', 'asc'], default: "desc" }, + sort: { type: 'string', enum: ['desc', 'asc'], default: 'desc' }, }, required: [], } as const; @@ -22,23 +22,28 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - // Get tokens - const tokens = await AccessTokens.find({ - where: { - userId: user.id, - }, - take: ps.limit, - skip: ps.offset, - order: { - id: ps.sort === 'asc' ? 1 : -1, - }, - }); + // Get tokens + const tokens = await AccessTokens.find({ + where: { + userId: user.id, + }, + take: ps.limit, + skip: ps.offset, + order: { + id: ps.sort === 'asc' ? 1 : -1, + }, + }); - return await Promise.all(tokens.map(token => Apps.pack(token.appId, user, { - detail: true, - }))); -}); + return await Promise.all(tokens.map(token => Apps.pack(token.appId, user, { + detail: true, + }))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/change-password.ts b/packages/backend/src/server/api/endpoints/i/change-password.ts index 1134cb7630f5..bc4e34d00256 100644 --- a/packages/backend/src/server/api/endpoints/i/change-password.ts +++ b/packages/backend/src/server/api/endpoints/i/change-password.ts @@ -22,24 +22,29 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - // Compare password - const same = await bcrypt.compare(ps.currentPassword, profile.password!); + // Compare password + const same = await bcrypt.compare(ps.currentPassword, profile.password!); - if (!same) { - throw new Error('incorrect password'); - } + if (!same) { + throw new Error('incorrect password'); + } - // Generate hash of password - const salt = await bcrypt.genSalt(8); - const hash = await bcrypt.hash(ps.newPassword, salt); + // Generate hash of password + const salt = await bcrypt.genSalt(8); + const hash = await bcrypt.hash(ps.newPassword, salt); - await UserProfiles.update(user.id, { - password: hash, - }); -}); + await UserProfiles.update(user.id, { + password: hash, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/delete-account.ts b/packages/backend/src/server/api/endpoints/i/delete-account.ts index b6cc3b8c21d2..1492630888ee 100644 --- a/packages/backend/src/server/api/endpoints/i/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts @@ -1,7 +1,7 @@ import bcrypt from 'bcryptjs'; +import { Inject, Injectable } from '@nestjs/common'; import { UserProfiles, Users } from '@/models/index.js'; import { deleteAccount } from '@/services/delete-account.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -22,22 +22,27 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - const userDetailed = await Users.findOneByOrFail({ id: user.id }); - if (userDetailed.isDeleted) { - return; - } + const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + const userDetailed = await Users.findOneByOrFail({ id: user.id }); + if (userDetailed.isDeleted) { + return; + } - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + // Compare password + const same = await bcrypt.compare(ps.password, profile.password!); - if (!same) { - throw new Error('incorrect password'); - } + if (!same) { + throw new Error('incorrect password'); + } - await deleteAccount(user); -}); + await deleteAccount(user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/export-blocking.ts b/packages/backend/src/server/api/endpoints/i/export-blocking.ts index 8c8a971586a6..ef445dcbb28d 100644 --- a/packages/backend/src/server/api/endpoints/i/export-blocking.ts +++ b/packages/backend/src/server/api/endpoints/i/export-blocking.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; +import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { createExportBlockingJob } from '@/queue/index.js'; -import ms from 'ms'; export const meta = { secure: true, @@ -22,9 +22,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - createExportBlockingJob(user); -}); + createExportBlockingJob(user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/export-following.ts b/packages/backend/src/server/api/endpoints/i/export-following.ts index a661e55c0db9..c75a14eb644f 100644 --- a/packages/backend/src/server/api/endpoints/i/export-following.ts +++ b/packages/backend/src/server/api/endpoints/i/export-following.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; +import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { createExportFollowingJob } from '@/queue/index.js'; -import ms from 'ms'; export const meta = { secure: true, @@ -25,9 +25,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - createExportFollowingJob(user, ps.excludeMuting, ps.excludeInactive); -}); + createExportFollowingJob(user, ps.excludeMuting, ps.excludeInactive); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/export-mute.ts b/packages/backend/src/server/api/endpoints/i/export-mute.ts index 811d7789f83d..27a57b37d98e 100644 --- a/packages/backend/src/server/api/endpoints/i/export-mute.ts +++ b/packages/backend/src/server/api/endpoints/i/export-mute.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; +import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { createExportMuteJob } from '@/queue/index.js'; -import ms from 'ms'; export const meta = { secure: true, @@ -22,9 +22,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - createExportMuteJob(user); -}); + createExportMuteJob(user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/export-notes.ts b/packages/backend/src/server/api/endpoints/i/export-notes.ts index 15ff13016213..def82fc9142c 100644 --- a/packages/backend/src/server/api/endpoints/i/export-notes.ts +++ b/packages/backend/src/server/api/endpoints/i/export-notes.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; +import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { createExportNotesJob } from '@/queue/index.js'; -import ms from 'ms'; export const meta = { secure: true, @@ -22,9 +22,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - createExportNotesJob(user); -}); + createExportNotesJob(user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/export-user-lists.ts b/packages/backend/src/server/api/endpoints/i/export-user-lists.ts index ac4ec9b3eb6f..39b857bffec7 100644 --- a/packages/backend/src/server/api/endpoints/i/export-user-lists.ts +++ b/packages/backend/src/server/api/endpoints/i/export-user-lists.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; +import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { createExportUserListsJob } from '@/queue/index.js'; -import ms from 'ms'; export const meta = { secure: true, @@ -22,9 +22,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - createExportUserListsJob(user); -}); + createExportUserListsJob(user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/favorites.ts b/packages/backend/src/server/api/endpoints/i/favorites.ts index 391660ad1710..3f3fede7c59e 100644 --- a/packages/backend/src/server/api/endpoints/i/favorites.ts +++ b/packages/backend/src/server/api/endpoints/i/favorites.ts @@ -35,17 +35,22 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(NoteFavorites.createQueryBuilder('favorite'), ps.sinceId, ps.untilId) - .andWhere(`favorite.userId = :meId`, { meId: user.id }) - .leftJoinAndSelect('favorite.note', 'note'); - - const favorites = await query - .take(ps.limit) - .getMany(); - - return await NoteFavorites.packMany(favorites, user); -}); + const query = makePaginationQuery(NoteFavorites.createQueryBuilder('favorite'), ps.sinceId, ps.untilId) + .andWhere('favorite.userId = :meId', { meId: user.id }) + .leftJoinAndSelect('favorite.note', 'note'); + + const favorites = await query + .take(ps.limit) + .getMany(); + + return await NoteFavorites.packMany(favorites, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts index 45c94469466c..b03a5e4e3ea8 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts @@ -28,7 +28,7 @@ export const meta = { ref: 'GalleryPost', }, }, - } + }, }, } as const; @@ -46,17 +46,22 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(GalleryLikes.createQueryBuilder('like'), ps.sinceId, ps.untilId) - .andWhere(`like.userId = :meId`, { meId: user.id }) - .leftJoinAndSelect('like.post', 'post'); + const query = makePaginationQuery(GalleryLikes.createQueryBuilder('like'), ps.sinceId, ps.untilId) + .andWhere('like.userId = :meId', { meId: user.id }) + .leftJoinAndSelect('like.post', 'post'); - const likes = await query - .take(ps.limit) - .getMany(); + const likes = await query + .take(ps.limit) + .getMany(); - return await GalleryLikes.packMany(likes, user); -}); + return await GalleryLikes.packMany(likes, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts index 8aec20f793ec..c6358ead7f13 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts @@ -35,16 +35,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) - .andWhere(`post.userId = :meId`, { meId: user.id }); + const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) + .andWhere('post.userId = :meId', { meId: user.id }); - const posts = await query - .take(ps.limit) - .getMany(); + const posts = await query + .take(ps.limit) + .getMany(); - return await GalleryPosts.packMany(posts, user); -}); + return await GalleryPosts.packMany(posts, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts b/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts index a29e204ead49..52324041890f 100644 --- a/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts +++ b/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts @@ -31,14 +31,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - return { - count: await MutedNotes.countBy({ - userId: user.id, - reason: 'word', - }), - }; -}); + return { + count: await MutedNotes.countBy({ + userId: user.id, + reason: 'word', + }), + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/import-blocking.ts b/packages/backend/src/server/api/endpoints/i/import-blocking.ts index c165efed89ad..4fa9e72dd171 100644 --- a/packages/backend/src/server/api/endpoints/i/import-blocking.ts +++ b/packages/backend/src/server/api/endpoints/i/import-blocking.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; +import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { createImportBlockingJob } from '@/queue/index.js'; -import ms from 'ms'; -import { ApiError } from '../../error.js'; import { DriveFiles } from '@/models/index.js'; +import { ApiError } from '../../error.js'; export const meta = { secure: true, @@ -53,16 +53,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const file = await DriveFiles.findOneBy({ id: ps.fileId }); + const file = await DriveFiles.findOneBy({ id: ps.fileId }); - if (file == null) throw new ApiError(meta.errors.noSuchFile); - //if (!file.type.endsWith('/csv')) throw new ApiError(meta.errors.unexpectedFileType); - if (file.size > 50000) throw new ApiError(meta.errors.tooBigFile); - if (file.size === 0) throw new ApiError(meta.errors.emptyFile); + if (file == null) throw new ApiError(meta.errors.noSuchFile); + //if (!file.type.endsWith('/csv')) throw new ApiError(meta.errors.unexpectedFileType); + if (file.size > 50000) throw new ApiError(meta.errors.tooBigFile); + if (file.size === 0) throw new ApiError(meta.errors.emptyFile); - createImportBlockingJob(user, file.id); -}); + createImportBlockingJob(user, file.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/import-following.ts b/packages/backend/src/server/api/endpoints/i/import-following.ts index 485a84e8d1b7..1965c380d853 100644 --- a/packages/backend/src/server/api/endpoints/i/import-following.ts +++ b/packages/backend/src/server/api/endpoints/i/import-following.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; +import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { createImportFollowingJob } from '@/queue/index.js'; -import ms from 'ms'; -import { ApiError } from '../../error.js'; import { DriveFiles } from '@/models/index.js'; +import { ApiError } from '../../error.js'; export const meta = { secure: true, @@ -52,16 +52,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const file = await DriveFiles.findOneBy({ id: ps.fileId }); + const file = await DriveFiles.findOneBy({ id: ps.fileId }); - if (file == null) throw new ApiError(meta.errors.noSuchFile); - //if (!file.type.endsWith('/csv')) throw new ApiError(meta.errors.unexpectedFileType); - if (file.size > 50000) throw new ApiError(meta.errors.tooBigFile); - if (file.size === 0) throw new ApiError(meta.errors.emptyFile); + if (file == null) throw new ApiError(meta.errors.noSuchFile); + //if (!file.type.endsWith('/csv')) throw new ApiError(meta.errors.unexpectedFileType); + if (file.size > 50000) throw new ApiError(meta.errors.tooBigFile); + if (file.size === 0) throw new ApiError(meta.errors.emptyFile); - createImportFollowingJob(user, file.id); -}); + createImportFollowingJob(user, file.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/import-muting.ts b/packages/backend/src/server/api/endpoints/i/import-muting.ts index 9ae28e97d20b..b54933802a8d 100644 --- a/packages/backend/src/server/api/endpoints/i/import-muting.ts +++ b/packages/backend/src/server/api/endpoints/i/import-muting.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; +import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { createImportMutingJob } from '@/queue/index.js'; -import ms from 'ms'; -import { ApiError } from '../../error.js'; import { DriveFiles } from '@/models/index.js'; +import { ApiError } from '../../error.js'; export const meta = { secure: true, @@ -53,16 +53,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const file = await DriveFiles.findOneBy({ id: ps.fileId }); + const file = await DriveFiles.findOneBy({ id: ps.fileId }); - if (file == null) throw new ApiError(meta.errors.noSuchFile); - //if (!file.type.endsWith('/csv')) throw new ApiError(meta.errors.unexpectedFileType); - if (file.size > 50000) throw new ApiError(meta.errors.tooBigFile); - if (file.size === 0) throw new ApiError(meta.errors.emptyFile); + if (file == null) throw new ApiError(meta.errors.noSuchFile); + //if (!file.type.endsWith('/csv')) throw new ApiError(meta.errors.unexpectedFileType); + if (file.size > 50000) throw new ApiError(meta.errors.tooBigFile); + if (file.size === 0) throw new ApiError(meta.errors.emptyFile); - createImportMutingJob(user, file.id); -}); + createImportMutingJob(user, file.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/import-user-lists.ts b/packages/backend/src/server/api/endpoints/i/import-user-lists.ts index acbcbaa364e2..8e1b23fd7bd0 100644 --- a/packages/backend/src/server/api/endpoints/i/import-user-lists.ts +++ b/packages/backend/src/server/api/endpoints/i/import-user-lists.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; +import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { createImportUserListsJob } from '@/queue/index.js'; -import ms from 'ms'; -import { ApiError } from '../../error.js'; import { DriveFiles } from '@/models/index.js'; +import { ApiError } from '../../error.js'; export const meta = { secure: true, @@ -52,16 +52,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const file = await DriveFiles.findOneBy({ id: ps.fileId }); + const file = await DriveFiles.findOneBy({ id: ps.fileId }); - if (file == null) throw new ApiError(meta.errors.noSuchFile); - //if (!file.type.endsWith('/csv')) throw new ApiError(meta.errors.unexpectedFileType); - if (file.size > 30000) throw new ApiError(meta.errors.tooBigFile); - if (file.size === 0) throw new ApiError(meta.errors.emptyFile); + if (file == null) throw new ApiError(meta.errors.noSuchFile); + //if (!file.type.endsWith('/csv')) throw new ApiError(meta.errors.unexpectedFileType); + if (file.size > 30000) throw new ApiError(meta.errors.tooBigFile); + if (file.size === 0) throw new ApiError(meta.errors.emptyFile); - createImportUserListsJob(user, file.id); -}); + createImportUserListsJob(user, file.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index 27cdc8ee0ab8..849eda135d8e 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -1,10 +1,10 @@ import { Brackets } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; import { Notifications, Followings, Mutings, Users, UserProfiles } from '@/models/index.js'; import { notificationTypes } from '@/types.js'; import read from '@/services/note/read.js'; -import { readNotification } from '../../common/read-notification.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { readNotification } from '../../common/read-notification.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; export const meta = { @@ -53,99 +53,104 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - // includeTypes が空の場合はクエリしない - if (ps.includeTypes && ps.includeTypes.length === 0) { - return []; - } - // excludeTypes に全指定されている場合はクエリしない - if (notificationTypes.every(type => ps.excludeTypes?.includes(type))) { - return []; - } - const followingQuery = Followings.createQueryBuilder('following') - .select('following.followeeId') - .where('following.followerId = :followerId', { followerId: user.id }); - - const mutingQuery = Mutings.createQueryBuilder('muting') - .select('muting.muteeId') - .where('muting.muterId = :muterId', { muterId: user.id }); - - const mutingInstanceQuery = UserProfiles.createQueryBuilder('user_profile') - .select('user_profile.mutedInstances') - .where('user_profile.userId = :muterId', { muterId: user.id }); - - const suspendedQuery = Users.createQueryBuilder('users') - .select('users.id') - .where('users.isSuspended = TRUE'); - - const query = makePaginationQuery(Notifications.createQueryBuilder('notification'), ps.sinceId, ps.untilId) - .andWhere('notification.notifieeId = :meId', { meId: user.id }) - .leftJoinAndSelect('notification.notifier', 'notifier') - .leftJoinAndSelect('notification.note', 'note') - .leftJoinAndSelect('notifier.avatar', 'notifierAvatar') - .leftJoinAndSelect('notifier.banner', 'notifierBanner') - .leftJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('user.avatar', 'avatar') - .leftJoinAndSelect('user.banner', 'banner') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') - .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') - .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - - // muted users - query.andWhere(new Brackets(qb => { qb - .where(`notification.notifierId NOT IN (${ mutingQuery.getQuery() })`) - .orWhere('notification.notifierId IS NULL'); - })); - query.setParameters(mutingQuery.getParameters()); - - // muted instances - query.andWhere(new Brackets(qb => { qb - .andWhere('notifier.host IS NULL') - .orWhere(`NOT (( ${mutingInstanceQuery.getQuery()} )::jsonb ? notifier.host)`); - })); - query.setParameters(mutingInstanceQuery.getParameters()); - - // suspended users - query.andWhere(new Brackets(qb => { qb - .where(`notification.notifierId NOT IN (${ suspendedQuery.getQuery() })`) - .orWhere('notification.notifierId IS NULL'); - })); - - if (ps.following) { - query.andWhere(`((notification.notifierId IN (${ followingQuery.getQuery() })) OR (notification.notifierId = :meId))`, { meId: user.id }); - query.setParameters(followingQuery.getParameters()); - } - - if (ps.includeTypes && ps.includeTypes.length > 0) { - query.andWhere('notification.type IN (:...includeTypes)', { includeTypes: ps.includeTypes }); - } else if (ps.excludeTypes && ps.excludeTypes.length > 0) { - query.andWhere('notification.type NOT IN (:...excludeTypes)', { excludeTypes: ps.excludeTypes }); + // includeTypes が空の場合はクエリしない + if (ps.includeTypes && ps.includeTypes.length === 0) { + return []; + } + // excludeTypes に全指定されている場合はクエリしない + if (notificationTypes.every(type => ps.excludeTypes?.includes(type))) { + return []; + } + const followingQuery = Followings.createQueryBuilder('following') + .select('following.followeeId') + .where('following.followerId = :followerId', { followerId: user.id }); + + const mutingQuery = Mutings.createQueryBuilder('muting') + .select('muting.muteeId') + .where('muting.muterId = :muterId', { muterId: user.id }); + + const mutingInstanceQuery = UserProfiles.createQueryBuilder('user_profile') + .select('user_profile.mutedInstances') + .where('user_profile.userId = :muterId', { muterId: user.id }); + + const suspendedQuery = Users.createQueryBuilder('users') + .select('users.id') + .where('users.isSuspended = TRUE'); + + const query = makePaginationQuery(Notifications.createQueryBuilder('notification'), ps.sinceId, ps.untilId) + .andWhere('notification.notifieeId = :meId', { meId: user.id }) + .leftJoinAndSelect('notification.notifier', 'notifier') + .leftJoinAndSelect('notification.note', 'note') + .leftJoinAndSelect('notifier.avatar', 'notifierAvatar') + .leftJoinAndSelect('notifier.banner', 'notifierBanner') + .leftJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('user.avatar', 'avatar') + .leftJoinAndSelect('user.banner', 'banner') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); + + // muted users + query.andWhere(new Brackets(qb => { qb + .where(`notification.notifierId NOT IN (${ mutingQuery.getQuery() })`) + .orWhere('notification.notifierId IS NULL'); + })); + query.setParameters(mutingQuery.getParameters()); + + // muted instances + query.andWhere(new Brackets(qb => { qb + .andWhere('notifier.host IS NULL') + .orWhere(`NOT (( ${mutingInstanceQuery.getQuery()} )::jsonb ? notifier.host)`); + })); + query.setParameters(mutingInstanceQuery.getParameters()); + + // suspended users + query.andWhere(new Brackets(qb => { qb + .where(`notification.notifierId NOT IN (${ suspendedQuery.getQuery() })`) + .orWhere('notification.notifierId IS NULL'); + })); + + if (ps.following) { + query.andWhere(`((notification.notifierId IN (${ followingQuery.getQuery() })) OR (notification.notifierId = :meId))`, { meId: user.id }); + query.setParameters(followingQuery.getParameters()); + } + + if (ps.includeTypes && ps.includeTypes.length > 0) { + query.andWhere('notification.type IN (:...includeTypes)', { includeTypes: ps.includeTypes }); + } else if (ps.excludeTypes && ps.excludeTypes.length > 0) { + query.andWhere('notification.type NOT IN (:...excludeTypes)', { excludeTypes: ps.excludeTypes }); + } + + if (ps.unreadOnly) { + query.andWhere('notification.isRead = false'); + } + + const notifications = await query.take(ps.limit).getMany(); + + // Mark all as read + if (notifications.length > 0 && ps.markAsRead) { + readNotification(user.id, notifications.map(x => x.id)); + } + + const notes = notifications.filter(notification => ['mention', 'reply', 'quote'].includes(notification.type)).map(notification => notification.note!); + + if (notes.length > 0) { + read(user.id, notes); + } + + return await Notifications.packMany(notifications, user.id); + }); } - - if (ps.unreadOnly) { - query.andWhere('notification.isRead = false'); - } - - const notifications = await query.take(ps.limit).getMany(); - - // Mark all as read - if (notifications.length > 0 && ps.markAsRead) { - readNotification(user.id, notifications.map(x => x.id)); - } - - const notes = notifications.filter(notification => ['mention', 'reply', 'quote'].includes(notification.type)).map(notification => notification.note!); - - if (notes.length > 0) { - read(user.id, notes); - } - - return await Notifications.packMany(notifications, user.id); -}); +} diff --git a/packages/backend/src/server/api/endpoints/i/page-likes.ts b/packages/backend/src/server/api/endpoints/i/page-likes.ts index 57b12d76b217..abd2a2e8a581 100644 --- a/packages/backend/src/server/api/endpoints/i/page-likes.ts +++ b/packages/backend/src/server/api/endpoints/i/page-likes.ts @@ -27,7 +27,7 @@ export const meta = { ref: 'Page', }, }, - } + }, }, } as const; @@ -45,17 +45,22 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(PageLikes.createQueryBuilder('like'), ps.sinceId, ps.untilId) - .andWhere(`like.userId = :meId`, { meId: user.id }) - .leftJoinAndSelect('like.page', 'page'); + const query = makePaginationQuery(PageLikes.createQueryBuilder('like'), ps.sinceId, ps.untilId) + .andWhere('like.userId = :meId', { meId: user.id }) + .leftJoinAndSelect('like.page', 'page'); - const likes = await query - .take(ps.limit) - .getMany(); + const likes = await query + .take(ps.limit) + .getMany(); - return PageLikes.packMany(likes, user); -}); + return PageLikes.packMany(likes, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/pages.ts b/packages/backend/src/server/api/endpoints/i/pages.ts index 2434e49d089c..c3662a29f7ad 100644 --- a/packages/backend/src/server/api/endpoints/i/pages.ts +++ b/packages/backend/src/server/api/endpoints/i/pages.ts @@ -35,16 +35,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(Pages.createQueryBuilder('page'), ps.sinceId, ps.untilId) - .andWhere(`page.userId = :meId`, { meId: user.id }); + const query = makePaginationQuery(Pages.createQueryBuilder('page'), ps.sinceId, ps.untilId) + .andWhere('page.userId = :meId', { meId: user.id }); - const pages = await query - .take(ps.limit) - .getMany(); + const pages = await query + .take(ps.limit) + .getMany(); - return await Pages.packMany(pages); -}); + return await Pages.packMany(pages); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/pin.ts b/packages/backend/src/server/api/endpoints/i/pin.ts index 5a7a7b2cfd9d..8a4de033edec 100644 --- a/packages/backend/src/server/api/endpoints/i/pin.ts +++ b/packages/backend/src/server/api/endpoints/i/pin.ts @@ -1,8 +1,8 @@ -import { addPinned } from '@/services/i/pin.js'; import { Inject, Injectable } from '@nestjs/common'; +import { addPinned } from '@/services/i/pin.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ApiError } from '../../error.js'; import { Users } from '@/models/index.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['account', 'notes'], @@ -50,18 +50,23 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - await addPinned(user, ps.noteId).catch(e => { - if (e.id === '70c4e51f-5bea-449c-a030-53bee3cce202') throw new ApiError(meta.errors.noSuchNote); - if (e.id === '15a018eb-58e5-4da1-93be-330fcc5e4e1a') throw new ApiError(meta.errors.pinLimitExceeded); - if (e.id === '23f0cf4e-59a3-4276-a91d-61a5891c1514') throw new ApiError(meta.errors.alreadyPinned); - throw e; - }); + await addPinned(user, ps.noteId).catch(e => { + if (e.id === '70c4e51f-5bea-449c-a030-53bee3cce202') throw new ApiError(meta.errors.noSuchNote); + if (e.id === '15a018eb-58e5-4da1-93be-330fcc5e4e1a') throw new ApiError(meta.errors.pinLimitExceeded); + if (e.id === '23f0cf4e-59a3-4276-a91d-61a5891c1514') throw new ApiError(meta.errors.alreadyPinned); + throw e; + }); - return await Users.pack(user.id, user, { - detail: true, - }); -}); + return await Users.pack(user.id, user, { + detail: true, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts b/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts index 69ece1a1c0ec..3ce07b9dc0ff 100644 --- a/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts +++ b/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts @@ -1,5 +1,5 @@ -import { publishMainStream } from '@/services/stream.js'; import { Inject, Injectable } from '@nestjs/common'; +import { publishMainStream } from '@/services/stream.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { MessagingMessages, UserGroupJoinings } from '@/models/index.js'; @@ -21,28 +21,33 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - // Update documents - await MessagingMessages.update({ - recipientId: user.id, - isRead: false, - }, { - isRead: true, - }); - - const joinings = await UserGroupJoinings.findBy({ userId: user.id }); - - await Promise.all(joinings.map(j => MessagingMessages.createQueryBuilder().update() - .set({ - reads: (() => `array_append("reads", '${user.id}')`) as any, - }) - .where(`groupId = :groupId`, { groupId: j.userGroupId }) - .andWhere('userId != :userId', { userId: user.id }) - .andWhere('NOT (:userId = ANY(reads))', { userId: user.id }) - .execute())); - - publishMainStream(user.id, 'readAllMessagingMessages'); -}); + // Update documents + await MessagingMessages.update({ + recipientId: user.id, + isRead: false, + }, { + isRead: true, + }); + + const joinings = await UserGroupJoinings.findBy({ userId: user.id }); + + await Promise.all(joinings.map(j => MessagingMessages.createQueryBuilder().update() + .set({ + reads: (() => `array_append("reads", '${user.id}')`) as any, + }) + .where('groupId = :groupId', { groupId: j.userGroupId }) + .andWhere('userId != :userId', { userId: user.id }) + .andWhere('NOT (:userId = ANY(reads))', { userId: user.id }) + .execute())); + + publishMainStream(user.id, 'readAllMessagingMessages'); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts b/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts index d7dc91b1b08d..021fa5d98698 100644 --- a/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts +++ b/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts @@ -1,5 +1,5 @@ -import { publishMainStream } from '@/services/stream.js'; import { Inject, Injectable } from '@nestjs/common'; +import { publishMainStream } from '@/services/stream.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteUnreads } from '@/models/index.js'; @@ -21,16 +21,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - // Remove documents - await NoteUnreads.delete({ - userId: user.id, - }); + // Remove documents + await NoteUnreads.delete({ + userId: user.id, + }); - // 全て既読になったイベントを発行 - publishMainStream(user.id, 'readAllUnreadMentions'); - publishMainStream(user.id, 'readAllUnreadSpecifiedNotes'); -}); + // 全て既読になったイベントを発行 + publishMainStream(user.id, 'readAllUnreadMentions'); + publishMainStream(user.id, 'readAllUnreadSpecifiedNotes'); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/read-announcement.ts b/packages/backend/src/server/api/endpoints/i/read-announcement.ts index f808def1de1b..355cb88f2ddc 100644 --- a/packages/backend/src/server/api/endpoints/i/read-announcement.ts +++ b/packages/backend/src/server/api/endpoints/i/read-announcement.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ApiError } from '../../error.js'; import { genId } from '@/misc/gen-id.js'; import { AnnouncementReads, Announcements, Users } from '@/models/index.js'; import { publishMainStream } from '@/services/stream.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['account'], @@ -33,36 +33,41 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - // Check if announcement exists - const announcement = await Announcements.findOneBy({ id: ps.announcementId }); + // Check if announcement exists + const announcement = await Announcements.findOneBy({ id: ps.announcementId }); - if (announcement == null) { - throw new ApiError(meta.errors.noSuchAnnouncement); - } + if (announcement == null) { + throw new ApiError(meta.errors.noSuchAnnouncement); + } - // Check if already read - const read = await AnnouncementReads.findOneBy({ - announcementId: ps.announcementId, - userId: user.id, - }); + // Check if already read + const read = await AnnouncementReads.findOneBy({ + announcementId: ps.announcementId, + userId: user.id, + }); - if (read != null) { - return; - } + if (read != null) { + return; + } - // Create read - await AnnouncementReads.insert({ - id: genId(), - createdAt: new Date(), - announcementId: ps.announcementId, - userId: user.id, - }); + // Create read + await AnnouncementReads.insert({ + id: genId(), + createdAt: new Date(), + announcementId: ps.announcementId, + userId: user.id, + }); - if (!await Users.getHasUnreadAnnouncement(user.id)) { - publishMainStream(user.id, 'readAllAnnouncements'); + if (!await Users.getHasUnreadAnnouncement(user.id)) { + publishMainStream(user.id, 'readAllAnnouncements'); + } + }); } -}); +} diff --git a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts index 1756cad156b7..9efc78a51240 100644 --- a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts +++ b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts @@ -1,9 +1,9 @@ import bcrypt from 'bcryptjs'; -import { publishInternalEvent, publishMainStream, publishUserEvent } from '@/services/stream.js'; -import generateUserToken from '../../common/generate-native-user-token.js'; import { Inject, Injectable } from '@nestjs/common'; +import { publishInternalEvent, publishMainStream, publishUserEvent } from '@/services/stream.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Users, UserProfiles } from '@/models/index.js'; +import generateUserToken from '../../common/generate-native-user-token.js'; export const meta = { requireCredential: true, @@ -23,34 +23,39 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const freshUser = await Users.findOneByOrFail({ id: user.id }); - const oldToken = freshUser.token; + const freshUser = await Users.findOneByOrFail({ id: user.id }); + const oldToken = freshUser.token; - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + // Compare password + const same = await bcrypt.compare(ps.password, profile.password!); - if (!same) { - throw new Error('incorrect password'); - } + if (!same) { + throw new Error('incorrect password'); + } - const newToken = generateUserToken(); + const newToken = generateUserToken(); - await Users.update(user.id, { - token: newToken, - }); + await Users.update(user.id, { + token: newToken, + }); - // Publish event - publishInternalEvent('userTokenRegenerated', { id: user.id, oldToken, newToken }); - publishMainStream(user.id, 'myTokenRegenerated'); + // Publish event + publishInternalEvent('userTokenRegenerated', { id: user.id, oldToken, newToken }); + publishMainStream(user.id, 'myTokenRegenerated'); - // Terminate streaming - setTimeout(() => { - publishUserEvent(user.id, 'terminate', {}); - }, 5000); -}); + // Terminate streaming + setTimeout(() => { + publishUserEvent(user.id, 'terminate', {}); + }, 5000); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts index 15ccdd32ed28..c6de3d02e654 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts @@ -22,22 +22,27 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = RegistryItems.createQueryBuilder('item') - .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }) - .andWhere('item.scope = :scope', { scope: ps.scope }); + const query = RegistryItems.createQueryBuilder('item') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: user.id }) + .andWhere('item.scope = :scope', { scope: ps.scope }); - const items = await query.getMany(); + const items = await query.getMany(); - const res = {} as Record; + const res = {} as Record; - for (const item of items) { - res[item.key] = item.value; - } + for (const item of items) { + res[item.key] = item.value; + } - return res; -}); + return res; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts index fbf6fbee905a..3390f0f2a22a 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts @@ -32,24 +32,29 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = RegistryItems.createQueryBuilder('item') - .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }) - .andWhere('item.key = :key', { key: ps.key }) - .andWhere('item.scope = :scope', { scope: ps.scope }); - - const item = await query.getOne(); - - if (item == null) { - throw new ApiError(meta.errors.noSuchKey); + const query = RegistryItems.createQueryBuilder('item') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: user.id }) + .andWhere('item.key = :key', { key: ps.key }) + .andWhere('item.scope = :scope', { scope: ps.scope }); + + const item = await query.getOne(); + + if (item == null) { + throw new ApiError(meta.errors.noSuchKey); + } + + return { + updatedAt: item.updatedAt, + value: item.value, + }; + }); } - - return { - updatedAt: item.updatedAt, - value: item.value, - }; -}); +} diff --git a/packages/backend/src/server/api/endpoints/i/registry/get.ts b/packages/backend/src/server/api/endpoints/i/registry/get.ts index bc03f42538cf..3cf9b387e797 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get.ts @@ -32,21 +32,26 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = RegistryItems.createQueryBuilder('item') - .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }) - .andWhere('item.key = :key', { key: ps.key }) - .andWhere('item.scope = :scope', { scope: ps.scope }); + const query = RegistryItems.createQueryBuilder('item') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: user.id }) + .andWhere('item.key = :key', { key: ps.key }) + .andWhere('item.scope = :scope', { scope: ps.scope }); - const item = await query.getOne(); + const item = await query.getOne(); - if (item == null) { - throw new ApiError(meta.errors.noSuchKey); - } + if (item == null) { + throw new ApiError(meta.errors.noSuchKey); + } - return item.value; -}); + return item.value; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts index 27a49e1d4e36..d980b752ec1c 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts @@ -22,22 +22,25 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = RegistryItems.createQueryBuilder('item') - .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }) - .andWhere('item.scope = :scope', { scope: ps.scope }); + const query = RegistryItems.createQueryBuilder('item') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: user.id }) + .andWhere('item.scope = :scope', { scope: ps.scope }); - const items = await query.getMany(); + const items = await query.getMany(); - const res = {} as Record; + const res = {} as Record; - for (const item of items) { - const type = typeof item.value; - res[item.key] = + for (const item of items) { + const type = typeof item.value; + res[item.key] = item.value === null ? 'null' : Array.isArray(item.value) ? 'array' : type === 'number' ? 'number' : @@ -45,7 +48,9 @@ export default class extends Endpoint { type === 'boolean' ? 'boolean' : type === 'object' ? 'object' : null as never; - } + } - return res; -}); + return res; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys.ts b/packages/backend/src/server/api/endpoints/i/registry/keys.ts index 8d1d3e67ec8f..d21ff885a631 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/keys.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/keys.ts @@ -22,17 +22,22 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = RegistryItems.createQueryBuilder('item') - .select('item.key') - .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }) - .andWhere('item.scope = :scope', { scope: ps.scope }); + const query = RegistryItems.createQueryBuilder('item') + .select('item.key') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: user.id }) + .andWhere('item.scope = :scope', { scope: ps.scope }); - const items = await query.getMany(); + const items = await query.getMany(); - return items.map(x => x.key); -}); + return items.map(x => x.key); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/registry/remove.ts b/packages/backend/src/server/api/endpoints/i/registry/remove.ts index 9af1b75f6bba..063dc54046b9 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/remove.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/remove.ts @@ -32,21 +32,26 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = RegistryItems.createQueryBuilder('item') - .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }) - .andWhere('item.key = :key', { key: ps.key }) - .andWhere('item.scope = :scope', { scope: ps.scope }); + const query = RegistryItems.createQueryBuilder('item') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: user.id }) + .andWhere('item.key = :key', { key: ps.key }) + .andWhere('item.scope = :scope', { scope: ps.scope }); - const item = await query.getOne(); + const item = await query.getOne(); - if (item == null) { - throw new ApiError(meta.errors.noSuchKey); - } + if (item == null) { + throw new ApiError(meta.errors.noSuchKey); + } - await RegistryItems.remove(item); -}); + await RegistryItems.remove(item); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/registry/scopes.ts b/packages/backend/src/server/api/endpoints/i/registry/scopes.ts index 3905cbbd2a00..65dd6073f547 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/scopes.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/scopes.ts @@ -18,23 +18,28 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = RegistryItems.createQueryBuilder('item') - .select('item.scope') - .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }); + const query = RegistryItems.createQueryBuilder('item') + .select('item.scope') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: user.id }); - const items = await query.getMany(); + const items = await query.getMany(); - const res = [] as string[][]; + const res = [] as string[][]; - for (const item of items) { - if (res.some(scope => scope.join('.') === item.scope.join('.'))) continue; - res.push(item.scope); - } + for (const item of items) { + if (res.some(scope => scope.join('.') === item.scope.join('.'))) continue; + res.push(item.scope); + } - return res; -}); + return res; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/registry/set.ts b/packages/backend/src/server/api/endpoints/i/registry/set.ts index 60120b3701c8..b7e540f5dc72 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/set.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/set.ts @@ -1,5 +1,5 @@ -import { publishMainStream } from '@/services/stream.js'; import { Inject, Injectable } from '@nestjs/common'; +import { publishMainStream } from '@/services/stream.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { RegistryItems } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; @@ -26,40 +26,45 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = RegistryItems.createQueryBuilder('item') - .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }) - .andWhere('item.key = :key', { key: ps.key }) - .andWhere('item.scope = :scope', { scope: ps.scope }); + const query = RegistryItems.createQueryBuilder('item') + .where('item.domain IS NULL') + .andWhere('item.userId = :userId', { userId: user.id }) + .andWhere('item.key = :key', { key: ps.key }) + .andWhere('item.scope = :scope', { scope: ps.scope }); - const existingItem = await query.getOne(); + const existingItem = await query.getOne(); - if (existingItem) { - await RegistryItems.update(existingItem.id, { - updatedAt: new Date(), - value: ps.value, - }); - } else { - await RegistryItems.insert({ - id: genId(), - createdAt: new Date(), - updatedAt: new Date(), - userId: user.id, - domain: null, - scope: ps.scope, - key: ps.key, - value: ps.value, + if (existingItem) { + await RegistryItems.update(existingItem.id, { + updatedAt: new Date(), + value: ps.value, + }); + } else { + await RegistryItems.insert({ + id: genId(), + createdAt: new Date(), + updatedAt: new Date(), + userId: user.id, + domain: null, + scope: ps.scope, + key: ps.key, + value: ps.value, + }); + } + + // TODO: サードパーティアプリが傍受出来てしまうのでどうにかする + publishMainStream(user.id, 'registryUpdated', { + scope: ps.scope, + key: ps.key, + value: ps.value, + }); }); } - - // TODO: サードパーティアプリが傍受出来てしまうのでどうにかする - publishMainStream(user.id, 'registryUpdated', { - scope: ps.scope, - key: ps.key, - value: ps.value, - }); -}); +} diff --git a/packages/backend/src/server/api/endpoints/i/revoke-token.ts b/packages/backend/src/server/api/endpoints/i/revoke-token.ts index adce9a773182..90e5c6155f1f 100644 --- a/packages/backend/src/server/api/endpoints/i/revoke-token.ts +++ b/packages/backend/src/server/api/endpoints/i/revoke-token.ts @@ -21,19 +21,24 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const token = await AccessTokens.findOneBy({ id: ps.tokenId }); + const token = await AccessTokens.findOneBy({ id: ps.tokenId }); - if (token) { - await AccessTokens.delete({ - id: ps.tokenId, - userId: user.id, - }); + if (token) { + await AccessTokens.delete({ + id: ps.tokenId, + userId: user.id, + }); - // Terminate streaming - publishUserEvent(user.id, 'terminate'); + // Terminate streaming + publishUserEvent(user.id, 'terminate'); + } + }); } -}); +} diff --git a/packages/backend/src/server/api/endpoints/i/signin-history.ts b/packages/backend/src/server/api/endpoints/i/signin-history.ts index 4d73fe7c49e6..3a87d7546cd6 100644 --- a/packages/backend/src/server/api/endpoints/i/signin-history.ts +++ b/packages/backend/src/server/api/endpoints/i/signin-history.ts @@ -23,14 +23,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(Signins.createQueryBuilder('signin'), ps.sinceId, ps.untilId) - .andWhere(`signin.userId = :meId`, { meId: user.id }); + const query = makePaginationQuery(Signins.createQueryBuilder('signin'), ps.sinceId, ps.untilId) + .andWhere('signin.userId = :meId', { meId: user.id }); - const history = await query.take(ps.limit).getMany(); + const history = await query.take(ps.limit).getMany(); - return await Promise.all(history.map(record => Signins.pack(record))); -}); + return await Promise.all(history.map(record => Signins.pack(record))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/unpin.ts b/packages/backend/src/server/api/endpoints/i/unpin.ts index dcc662469f15..57d71637e4d4 100644 --- a/packages/backend/src/server/api/endpoints/i/unpin.ts +++ b/packages/backend/src/server/api/endpoints/i/unpin.ts @@ -1,8 +1,8 @@ -import { removePinned } from '@/services/i/pin.js'; import { Inject, Injectable } from '@nestjs/common'; +import { removePinned } from '@/services/i/pin.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ApiError } from '../../error.js'; import { Users } from '@/models/index.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['account', 'notes'], @@ -38,16 +38,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - await removePinned(user, ps.noteId).catch(e => { - if (e.id === 'b302d4cf-c050-400a-bbb3-be208681f40c') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); - - return await Users.pack(user.id, user, { - detail: true, - }); -}); + await removePinned(user, ps.noteId).catch(e => { + if (e.id === 'b302d4cf-c050-400a-bbb3-be208681f40c') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); + + return await Users.pack(user.id, user, { + detail: true, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts index cd432e373053..fff105f36cae 100644 --- a/packages/backend/src/server/api/endpoints/i/update-email.ts +++ b/packages/backend/src/server/api/endpoints/i/update-email.ts @@ -1,14 +1,14 @@ -import { publishMainStream } from '@/services/stream.js'; import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; import rndstr from 'rndstr'; -import config from '@/config/index.js'; import ms from 'ms'; import bcrypt from 'bcryptjs'; +import config from '@/config/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { publishMainStream } from '@/services/stream.js'; import { Users, UserProfiles } from '@/models/index.js'; import { sendEmail } from '@/services/send-email.js'; -import { ApiError } from '../../error.js'; import { validateEmailForAccount } from '@/services/validate-email-for-account.js'; +import { ApiError } from '../../error.js'; export const meta = { requireCredential: true, @@ -48,53 +48,58 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + // Compare password + const same = await bcrypt.compare(ps.password, profile.password!); - if (!same) { - throw new ApiError(meta.errors.incorrectPassword); - } + if (!same) { + throw new ApiError(meta.errors.incorrectPassword); + } - if (ps.email != null) { - const available = await validateEmailForAccount(ps.email); - if (!available) { - throw new ApiError(meta.errors.unavailable); - } - } + if (ps.email != null) { + const available = await validateEmailForAccount(ps.email); + if (!available) { + throw new ApiError(meta.errors.unavailable); + } + } - await UserProfiles.update(user.id, { - email: ps.email, - emailVerified: false, - emailVerifyCode: null, - }); + await UserProfiles.update(user.id, { + email: ps.email, + emailVerified: false, + emailVerifyCode: null, + }); - const iObj = await Users.pack(user.id, user, { - detail: true, - includeSecrets: true, - }); + const iObj = await Users.pack(user.id, user, { + detail: true, + includeSecrets: true, + }); - // Publish meUpdated event - publishMainStream(user.id, 'meUpdated', iObj); + // Publish meUpdated event + publishMainStream(user.id, 'meUpdated', iObj); - if (ps.email != null) { - const code = rndstr('a-z0-9', 16); + if (ps.email != null) { + const code = rndstr('a-z0-9', 16); - await UserProfiles.update(user.id, { - emailVerifyCode: code, - }); + await UserProfiles.update(user.id, { + emailVerifyCode: code, + }); - const link = `${config.url}/verify-email/${code}`; + const link = `${config.url}/verify-email/${code}`; - sendEmail(ps.email, 'Email verification', - `To verify email, please click this link:
${link}`, - `To verify email, please click this link: ${link}`); - } + sendEmail(ps.email, 'Email verification', + `To verify email, please click this link:
${link}`, + `To verify email, please click this link: ${link}`); + } - return iObj; -}); + return iObj; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index bc3928b5c685..2ba40b869da1 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -1,5 +1,6 @@ import RE2 from 're2'; import * as mfm from 'mfm-js'; +import { Inject, Injectable } from '@nestjs/common'; import { publishMainStream, publishUserEvent } from '@/services/stream.js'; import acceptAllFollowRequests from '@/services/following/requests/accept-all.js'; import { publishToFollowers } from '@/services/i/update.js'; @@ -7,14 +8,13 @@ import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mf import { extractHashtags } from '@/misc/extract-hashtags.js'; import { updateUsertags } from '@/services/update-hashtag.js'; import { Users, DriveFiles, UserProfiles, Pages } from '@/models/index.js'; -import { User } from '@/models/entities/user.js'; -import { UserProfile } from '@/models/entities/user-profile.js'; +import type { User } from '@/models/entities/user.js'; +import type { UserProfile } from '@/models/entities/user-profile.js'; import { notificationTypes } from '@/types.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { langmap } from '@/misc/langmap.js'; -import { ApiError } from '../../error.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['account'], @@ -123,134 +123,145 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, _user, token) => { - const user = await Users.findOneByOrFail({ id: _user.id }); - const isSecure = token == null; - - const updates = {} as Partial; - const profileUpdates = {} as Partial; - - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - if (ps.name !== undefined) updates.name = ps.name; - if (ps.description !== undefined) profileUpdates.description = ps.description; - if (ps.lang !== undefined) profileUpdates.lang = ps.lang; - if (ps.location !== undefined) profileUpdates.location = ps.location; - if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday; - if (ps.ffVisibility !== undefined) profileUpdates.ffVisibility = ps.ffVisibility; - if (ps.avatarId !== undefined) updates.avatarId = ps.avatarId; - if (ps.bannerId !== undefined) updates.bannerId = ps.bannerId; - if (ps.mutedWords !== undefined) { - // validate regular expression syntax - ps.mutedWords.filter(x => !Array.isArray(x)).forEach(x => { - const regexp = x.match(/^\/(.+)\/(.*)$/); - if (!regexp) throw new ApiError(meta.errors.invalidRegexp); - - try { - new RE2(regexp[1], regexp[2]); - } catch (err) { - throw new ApiError(meta.errors.invalidRegexp); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, _user, token) => { + const user = await Users.findOneByOrFail({ id: _user.id }); + const isSecure = token == null; + + const updates = {} as Partial; + const profileUpdates = {} as Partial; + + const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + + if (ps.name !== undefined) updates.name = ps.name; + if (ps.description !== undefined) profileUpdates.description = ps.description; + if (ps.lang !== undefined) profileUpdates.lang = ps.lang; + if (ps.location !== undefined) profileUpdates.location = ps.location; + if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday; + if (ps.ffVisibility !== undefined) profileUpdates.ffVisibility = ps.ffVisibility; + if (ps.avatarId !== undefined) updates.avatarId = ps.avatarId; + if (ps.bannerId !== undefined) updates.bannerId = ps.bannerId; + if (ps.mutedWords !== undefined) { + // validate regular expression syntax + ps.mutedWords.filter(x => !Array.isArray(x)).forEach(x => { + const regexp = x.match(/^\/(.+)\/(.*)$/); + if (!regexp) throw new ApiError(meta.errors.invalidRegexp); + + try { + new RE2(regexp[1], regexp[2]); + } catch (err) { + throw new ApiError(meta.errors.invalidRegexp); + } + }); + + profileUpdates.mutedWords = ps.mutedWords; + profileUpdates.enableWordMute = ps.mutedWords.length > 0; + } + if (ps.mutedInstances !== undefined) profileUpdates.mutedInstances = ps.mutedInstances; + if (ps.mutingNotificationTypes !== undefined) profileUpdates.mutingNotificationTypes = ps.mutingNotificationTypes as typeof notificationTypes[number][]; + if (typeof ps.isLocked === 'boolean') updates.isLocked = ps.isLocked; + if (typeof ps.isExplorable === 'boolean') updates.isExplorable = ps.isExplorable; + if (typeof ps.hideOnlineStatus === 'boolean') updates.hideOnlineStatus = ps.hideOnlineStatus; + if (typeof ps.publicReactions === 'boolean') profileUpdates.publicReactions = ps.publicReactions; + if (typeof ps.isBot === 'boolean') updates.isBot = ps.isBot; + if (typeof ps.showTimelineReplies === 'boolean') updates.showTimelineReplies = ps.showTimelineReplies; + if (typeof ps.carefulBot === 'boolean') profileUpdates.carefulBot = ps.carefulBot; + if (typeof ps.autoAcceptFollowed === 'boolean') profileUpdates.autoAcceptFollowed = ps.autoAcceptFollowed; + if (typeof ps.noCrawle === 'boolean') profileUpdates.noCrawle = ps.noCrawle; + if (typeof ps.isCat === 'boolean') updates.isCat = ps.isCat; + if (typeof ps.injectFeaturedNote === 'boolean') profileUpdates.injectFeaturedNote = ps.injectFeaturedNote; + if (typeof ps.receiveAnnouncementEmail === 'boolean') profileUpdates.receiveAnnouncementEmail = ps.receiveAnnouncementEmail; + if (typeof ps.alwaysMarkNsfw === 'boolean') profileUpdates.alwaysMarkNsfw = ps.alwaysMarkNsfw; + if (typeof ps.autoSensitive === 'boolean') profileUpdates.autoSensitive = ps.autoSensitive; + if (ps.emailNotificationTypes !== undefined) profileUpdates.emailNotificationTypes = ps.emailNotificationTypes; + + if (ps.avatarId) { + const avatar = await DriveFiles.findOneBy({ id: ps.avatarId }); + + if (avatar == null || avatar.userId !== user.id) throw new ApiError(meta.errors.noSuchAvatar); + if (!avatar.type.startsWith('image/')) throw new ApiError(meta.errors.avatarNotAnImage); } - }); - - profileUpdates.mutedWords = ps.mutedWords; - profileUpdates.enableWordMute = ps.mutedWords.length > 0; - } - if (ps.mutedInstances !== undefined) profileUpdates.mutedInstances = ps.mutedInstances; - if (ps.mutingNotificationTypes !== undefined) profileUpdates.mutingNotificationTypes = ps.mutingNotificationTypes as typeof notificationTypes[number][]; - if (typeof ps.isLocked === 'boolean') updates.isLocked = ps.isLocked; - if (typeof ps.isExplorable === 'boolean') updates.isExplorable = ps.isExplorable; - if (typeof ps.hideOnlineStatus === 'boolean') updates.hideOnlineStatus = ps.hideOnlineStatus; - if (typeof ps.publicReactions === 'boolean') profileUpdates.publicReactions = ps.publicReactions; - if (typeof ps.isBot === 'boolean') updates.isBot = ps.isBot; - if (typeof ps.showTimelineReplies === 'boolean') updates.showTimelineReplies = ps.showTimelineReplies; - if (typeof ps.carefulBot === 'boolean') profileUpdates.carefulBot = ps.carefulBot; - if (typeof ps.autoAcceptFollowed === 'boolean') profileUpdates.autoAcceptFollowed = ps.autoAcceptFollowed; - if (typeof ps.noCrawle === 'boolean') profileUpdates.noCrawle = ps.noCrawle; - if (typeof ps.isCat === 'boolean') updates.isCat = ps.isCat; - if (typeof ps.injectFeaturedNote === 'boolean') profileUpdates.injectFeaturedNote = ps.injectFeaturedNote; - if (typeof ps.receiveAnnouncementEmail === 'boolean') profileUpdates.receiveAnnouncementEmail = ps.receiveAnnouncementEmail; - if (typeof ps.alwaysMarkNsfw === 'boolean') profileUpdates.alwaysMarkNsfw = ps.alwaysMarkNsfw; - if (typeof ps.autoSensitive === 'boolean') profileUpdates.autoSensitive = ps.autoSensitive; - if (ps.emailNotificationTypes !== undefined) profileUpdates.emailNotificationTypes = ps.emailNotificationTypes; - - if (ps.avatarId) { - const avatar = await DriveFiles.findOneBy({ id: ps.avatarId }); - - if (avatar == null || avatar.userId !== user.id) throw new ApiError(meta.errors.noSuchAvatar); - if (!avatar.type.startsWith('image/')) throw new ApiError(meta.errors.avatarNotAnImage); - } - if (ps.bannerId) { - const banner = await DriveFiles.findOneBy({ id: ps.bannerId }); + if (ps.bannerId) { + const banner = await DriveFiles.findOneBy({ id: ps.bannerId }); - if (banner == null || banner.userId !== user.id) throw new ApiError(meta.errors.noSuchBanner); - if (!banner.type.startsWith('image/')) throw new ApiError(meta.errors.bannerNotAnImage); - } + if (banner == null || banner.userId !== user.id) throw new ApiError(meta.errors.noSuchBanner); + if (!banner.type.startsWith('image/')) throw new ApiError(meta.errors.bannerNotAnImage); + } - if (ps.pinnedPageId) { - const page = await Pages.findOneBy({ id: ps.pinnedPageId }); + if (ps.pinnedPageId) { + const page = await Pages.findOneBy({ id: ps.pinnedPageId }); - if (page == null || page.userId !== user.id) throw new ApiError(meta.errors.noSuchPage); + if (page == null || page.userId !== user.id) throw new ApiError(meta.errors.noSuchPage); - profileUpdates.pinnedPageId = page.id; - } else if (ps.pinnedPageId === null) { - profileUpdates.pinnedPageId = null; - } + profileUpdates.pinnedPageId = page.id; + } else if (ps.pinnedPageId === null) { + profileUpdates.pinnedPageId = null; + } - if (ps.fields) { - profileUpdates.fields = ps.fields - .filter(x => typeof x.name === 'string' && x.name !== '' && typeof x.value === 'string' && x.value !== '') - .map(x => { - return { name: x.name, value: x.value }; - }); - } + if (ps.fields) { + profileUpdates.fields = ps.fields + .filter(x => typeof x.name === 'string' && x.name !== '' && typeof x.value === 'string' && x.value !== '') + .map(x => { + return { name: x.name, value: x.value }; + }); + } - //#region emojis/tags + //#region emojis/tags - let emojis = [] as string[]; - let tags = [] as string[]; + let emojis = [] as string[]; + let tags = [] as string[]; - const newName = updates.name === undefined ? user.name : updates.name; - const newDescription = profileUpdates.description === undefined ? profile.description : profileUpdates.description; + const newName = updates.name === undefined ? user.name : updates.name; + const newDescription = profileUpdates.description === undefined ? profile.description : profileUpdates.description; - if (newName != null) { - const tokens = mfm.parseSimple(newName); - emojis = emojis.concat(extractCustomEmojisFromMfm(tokens!)); - } + if (newName != null) { + const tokens = mfm.parseSimple(newName); + emojis = emojis.concat(extractCustomEmojisFromMfm(tokens!)); + } - if (newDescription != null) { - const tokens = mfm.parse(newDescription); - emojis = emojis.concat(extractCustomEmojisFromMfm(tokens!)); - tags = extractHashtags(tokens!).map(tag => normalizeForSearch(tag)).splice(0, 32); - } + if (newDescription != null) { + const tokens = mfm.parse(newDescription); + emojis = emojis.concat(extractCustomEmojisFromMfm(tokens!)); + tags = extractHashtags(tokens!).map(tag => normalizeForSearch(tag)).splice(0, 32); + } - updates.emojis = emojis; - updates.tags = tags; + updates.emojis = emojis; + updates.tags = tags; - // ハッシュタグ更新 - updateUsertags(user, tags); - //#endregion + // ハッシュタグ更新 + updateUsertags(user, tags); + //#endregion - if (Object.keys(updates).length > 0) await Users.update(user.id, updates); - if (Object.keys(profileUpdates).length > 0) await UserProfiles.update(user.id, profileUpdates); + if (Object.keys(updates).length > 0) await Users.update(user.id, updates); + if (Object.keys(profileUpdates).length > 0) await UserProfiles.update(user.id, profileUpdates); - const iObj = await Users.pack(user.id, user, { - detail: true, - includeSecrets: isSecure, - }); + const iObj = await Users.pack(user.id, user, { + detail: true, + includeSecrets: isSecure, + }); - // Publish meUpdated event - publishMainStream(user.id, 'meUpdated', iObj); - publishUserEvent(user.id, 'updateUserProfile', await UserProfiles.findOneBy({ userId: user.id })); + // Publish meUpdated event + publishMainStream(user.id, 'meUpdated', iObj); + publishUserEvent(user.id, 'updateUserProfile', await UserProfiles.findOneBy({ userId: user.id })); - // 鍵垢を解除したとき、溜まっていたフォローリクエストがあるならすべて承認 - if (user.isLocked && ps.isLocked === false) { - acceptAllFollowRequests(user); - } + // 鍵垢を解除したとき、溜まっていたフォローリクエストがあるならすべて承認 + if (user.isLocked && ps.isLocked === false) { + acceptAllFollowRequests(user); + } - // フォロワーにUpdateを配信 - publishToFollowers(user.id); + // フォロワーにUpdateを配信 + publishToFollowers(user.id); - return iObj; -}); + return iObj; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/user-group-invites.ts b/packages/backend/src/server/api/endpoints/i/user-group-invites.ts index e530a065f1f9..6f1e59abe94c 100644 --- a/packages/backend/src/server/api/endpoints/i/user-group-invites.ts +++ b/packages/backend/src/server/api/endpoints/i/user-group-invites.ts @@ -46,17 +46,22 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(UserGroupInvitations.createQueryBuilder('invitation'), ps.sinceId, ps.untilId) - .andWhere(`invitation.userId = :meId`, { meId: user.id }) - .leftJoinAndSelect('invitation.userGroup', 'user_group'); + const query = makePaginationQuery(UserGroupInvitations.createQueryBuilder('invitation'), ps.sinceId, ps.untilId) + .andWhere('invitation.userId = :meId', { meId: user.id }) + .leftJoinAndSelect('invitation.userGroup', 'user_group'); - const invitations = await query - .take(ps.limit) - .getMany(); + const invitations = await query + .take(ps.limit) + .getMany(); - return await UserGroupInvitations.packMany(invitations); -}); + return await UserGroupInvitations.packMany(invitations); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts index aabe267369f7..96b6475c2f35 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts @@ -30,21 +30,26 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const webhook = await Webhooks.insert({ - id: genId(), - createdAt: new Date(), - userId: user.id, - name: ps.name, - url: ps.url, - secret: ps.secret, - on: ps.on, - }).then(x => Webhooks.findOneByOrFail(x.identifiers[0])); - - publishInternalEvent('webhookCreated', webhook); - - return webhook; -}); + const webhook = await Webhooks.insert({ + id: genId(), + createdAt: new Date(), + userId: user.id, + name: ps.name, + url: ps.url, + secret: ps.secret, + on: ps.on, + }).then(x => Webhooks.findOneByOrFail(x.identifiers[0])); + + publishInternalEvent('webhookCreated', webhook); + + return webhook; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts b/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts index 6373f3992c7f..199061738284 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ApiError } from '../../../error.js'; import { Webhooks } from '@/models/index.js'; import { publishInternalEvent } from '@/services/stream.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['webhooks'], @@ -32,20 +32,25 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const webhook = await Webhooks.findOneBy({ - id: ps.webhookId, - userId: user.id, - }); + const webhook = await Webhooks.findOneBy({ + id: ps.webhookId, + userId: user.id, + }); - if (webhook == null) { - throw new ApiError(meta.errors.noSuchWebhook); - } + if (webhook == null) { + throw new ApiError(meta.errors.noSuchWebhook); + } - await Webhooks.delete(webhook.id); + await Webhooks.delete(webhook.id); - publishInternalEvent('webhookDeleted', webhook); -}); + publishInternalEvent('webhookDeleted', webhook); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/list.ts b/packages/backend/src/server/api/endpoints/i/webhooks/list.ts index 6ec37cb2a1c4..2735def6865e 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/list.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/list.ts @@ -20,13 +20,18 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const webhooks = await Webhooks.findBy({ - userId: me.id, - }); + const webhooks = await Webhooks.findBy({ + userId: me.id, + }); - return webhooks; -}); + return webhooks; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/show.ts b/packages/backend/src/server/api/endpoints/i/webhooks/show.ts index e544e215e1ae..72007b7c910e 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/show.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/show.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ApiError } from '../../../error.js'; import { Webhooks } from '@/models/index.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['webhooks'], @@ -31,18 +31,23 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const webhook = await Webhooks.findOneBy({ - id: ps.webhookId, - userId: user.id, - }); + const webhook = await Webhooks.findOneBy({ + id: ps.webhookId, + userId: user.id, + }); - if (webhook == null) { - throw new ApiError(meta.errors.noSuchWebhook); - } + if (webhook == null) { + throw new ApiError(meta.errors.noSuchWebhook); + } - return webhook; -}); + return webhook; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/update.ts b/packages/backend/src/server/api/endpoints/i/webhooks/update.ts index b2e84cab2dcb..bac3f9c1efc5 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/update.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/update.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ApiError } from '../../../error.js'; import { Webhooks } from '@/models/index.js'; import { publishInternalEvent } from '@/services/stream.js'; import { webhookEventTypes } from '@/models/entities/webhook.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['webhooks'], @@ -41,26 +41,31 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const webhook = await Webhooks.findOneBy({ - id: ps.webhookId, - userId: user.id, - }); + const webhook = await Webhooks.findOneBy({ + id: ps.webhookId, + userId: user.id, + }); - if (webhook == null) { - throw new ApiError(meta.errors.noSuchWebhook); - } + if (webhook == null) { + throw new ApiError(meta.errors.noSuchWebhook); + } - await Webhooks.update(webhook.id, { - name: ps.name, - url: ps.url, - secret: ps.secret, - on: ps.on, - active: ps.active, - }); + await Webhooks.update(webhook.id, { + name: ps.name, + url: ps.url, + secret: ps.secret, + on: ps.on, + active: ps.active, + }); - publishInternalEvent('webhookUpdated', webhook); -}); + publishInternalEvent('webhookUpdated', webhook); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/messaging/history.ts b/packages/backend/src/server/api/endpoints/messaging/history.ts index 3104a2342d4b..b1df658288ba 100644 --- a/packages/backend/src/server/api/endpoints/messaging/history.ts +++ b/packages/backend/src/server/api/endpoints/messaging/history.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; +import { Brackets } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { MessagingMessage } from '@/models/entities/messaging-message.js'; +import type { MessagingMessage } from '@/models/entities/messaging-message.js'; import { MessagingMessages, Mutings, UserGroupJoinings } from '@/models/index.js'; -import { Brackets } from 'typeorm'; export const meta = { tags: ['messaging'], @@ -35,64 +35,69 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const mute = await Mutings.findBy({ - muterId: user.id, - }); - - const groups = ps.group ? await UserGroupJoinings.findBy({ - userId: user.id, - }).then(xs => xs.map(x => x.userGroupId)) : []; - - if (ps.group && groups.length === 0) { - return []; - } + const mute = await Mutings.findBy({ + muterId: user.id, + }); - const history: MessagingMessage[] = []; + const groups = ps.group ? await UserGroupJoinings.findBy({ + userId: user.id, + }).then(xs => xs.map(x => x.userGroupId)) : []; - for (let i = 0; i < ps.limit; i++) { - const found = ps.group - ? history.map(m => m.groupId!) - : history.map(m => (m.userId === user.id) ? m.recipientId! : m.userId!); - - const query = MessagingMessages.createQueryBuilder('message') - .orderBy('message.createdAt', 'DESC'); - - if (ps.group) { - query.where(`message.groupId IN (:...groups)`, { groups: groups }); - - if (found.length > 0) { - query.andWhere(`message.groupId NOT IN (:...found)`, { found: found }); - } - } else { - query.where(new Brackets(qb => { qb - .where(`message.userId = :userId`, { userId: user.id }) - .orWhere(`message.recipientId = :userId`, { userId: user.id }); - })); - query.andWhere(`message.groupId IS NULL`); - - if (found.length > 0) { - query.andWhere(`message.userId NOT IN (:...found)`, { found: found }); - query.andWhere(`message.recipientId NOT IN (:...found)`, { found: found }); + if (ps.group && groups.length === 0) { + return []; } - if (mute.length > 0) { - query.andWhere(`message.userId NOT IN (:...mute)`, { mute: mute.map(m => m.muteeId) }); - query.andWhere(`message.recipientId NOT IN (:...mute)`, { mute: mute.map(m => m.muteeId) }); + const history: MessagingMessage[] = []; + + for (let i = 0; i < ps.limit; i++) { + const found = ps.group + ? history.map(m => m.groupId!) + : history.map(m => (m.userId === user.id) ? m.recipientId! : m.userId!); + + const query = MessagingMessages.createQueryBuilder('message') + .orderBy('message.createdAt', 'DESC'); + + if (ps.group) { + query.where('message.groupId IN (:...groups)', { groups: groups }); + + if (found.length > 0) { + query.andWhere('message.groupId NOT IN (:...found)', { found: found }); + } + } else { + query.where(new Brackets(qb => { qb + .where('message.userId = :userId', { userId: user.id }) + .orWhere('message.recipientId = :userId', { userId: user.id }); + })); + query.andWhere('message.groupId IS NULL'); + + if (found.length > 0) { + query.andWhere('message.userId NOT IN (:...found)', { found: found }); + query.andWhere('message.recipientId NOT IN (:...found)', { found: found }); + } + + if (mute.length > 0) { + query.andWhere('message.userId NOT IN (:...mute)', { mute: mute.map(m => m.muteeId) }); + query.andWhere('message.recipientId NOT IN (:...mute)', { mute: mute.map(m => m.muteeId) }); + } + } + + const message = await query.getOne(); + + if (message) { + history.push(message); + } else { + break; + } } - } - - const message = await query.getOne(); - if (message) { - history.push(message); - } else { - break; - } + return await Promise.all(history.map(h => MessagingMessages.pack(h.id, user))); + }); } - - return await Promise.all(history.map(h => MessagingMessages.pack(h.id, user))); -}); +} diff --git a/packages/backend/src/server/api/endpoints/messaging/messages.ts b/packages/backend/src/server/api/endpoints/messaging/messages.ts index 5d882942b6db..78164fd48437 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages.ts @@ -1,10 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; +import { Brackets } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { MessagingMessages, UserGroups, UserGroupJoinings, Users } from '@/models/index.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; -import { MessagingMessages, UserGroups, UserGroupJoinings, Users } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { Brackets } from 'typeorm'; import { readUserMessagingMessage, readGroupMessagingMessage, deliverReadActivity } from '../../common/read-messaging-message.js'; export const meta = { @@ -73,76 +73,81 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - if (ps.userId != null) { - // Fetch recipient (user) - const recipient = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; - }); - - const query = makePaginationQuery(MessagingMessages.createQueryBuilder('message'), ps.sinceId, ps.untilId) - .andWhere(new Brackets(qb => { qb - .where(new Brackets(qb => { qb - .where('message.userId = :meId') - .andWhere('message.recipientId = :recipientId'); - })) - .orWhere(new Brackets(qb => { qb - .where('message.userId = :recipientId') - .andWhere('message.recipientId = :meId'); - })); - })) - .setParameter('meId', user.id) - .setParameter('recipientId', recipient.id); - - const messages = await query.take(ps.limit).getMany(); - - // Mark all as read - if (ps.markAsRead) { - readUserMessagingMessage(user.id, recipient.id, messages.filter(m => m.recipientId === user.id).map(x => x.id)); - - // リモートユーザーとのメッセージだったら既読配信 - if (Users.isLocalUser(user) && Users.isRemoteUser(recipient)) { - deliverReadActivity(user, recipient, messages); + if (ps.userId != null) { + // Fetch recipient (user) + const recipient = await getUser(ps.userId).catch(e => { + if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw e; + }); + + const query = makePaginationQuery(MessagingMessages.createQueryBuilder('message'), ps.sinceId, ps.untilId) + .andWhere(new Brackets(qb => { qb + .where(new Brackets(qb => { qb + .where('message.userId = :meId') + .andWhere('message.recipientId = :recipientId'); + })) + .orWhere(new Brackets(qb => { qb + .where('message.userId = :recipientId') + .andWhere('message.recipientId = :meId'); + })); + })) + .setParameter('meId', user.id) + .setParameter('recipientId', recipient.id); + + const messages = await query.take(ps.limit).getMany(); + + // Mark all as read + if (ps.markAsRead) { + readUserMessagingMessage(user.id, recipient.id, messages.filter(m => m.recipientId === user.id).map(x => x.id)); + + // リモートユーザーとのメッセージだったら既読配信 + if (Users.isLocalUser(user) && Users.isRemoteUser(recipient)) { + deliverReadActivity(user, recipient, messages); + } + } + + return await Promise.all(messages.map(message => MessagingMessages.pack(message, user, { + populateRecipient: false, + }))); + } else if (ps.groupId != null) { + // Fetch recipient (group) + const recipientGroup = await UserGroups.findOneBy({ id: ps.groupId }); + + if (recipientGroup == null) { + throw new ApiError(meta.errors.noSuchGroup); + } + + // check joined + const joining = await UserGroupJoinings.findOneBy({ + userId: user.id, + userGroupId: recipientGroup.id, + }); + + if (joining == null) { + throw new ApiError(meta.errors.groupAccessDenied); + } + + const query = makePaginationQuery(MessagingMessages.createQueryBuilder('message'), ps.sinceId, ps.untilId) + .andWhere('message.groupId = :groupId', { groupId: recipientGroup.id }); + + const messages = await query.take(ps.limit).getMany(); + + // Mark all as read + if (ps.markAsRead) { + readGroupMessagingMessage(user.id, recipientGroup.id, messages.map(x => x.id)); + } + + return await Promise.all(messages.map(message => MessagingMessages.pack(message, user, { + populateGroup: false, + }))); } - } - - return await Promise.all(messages.map(message => MessagingMessages.pack(message, user, { - populateRecipient: false, - }))); - } else if (ps.groupId != null) { - // Fetch recipient (group) - const recipientGroup = await UserGroups.findOneBy({ id: ps.groupId }); - - if (recipientGroup == null) { - throw new ApiError(meta.errors.noSuchGroup); - } - - // check joined - const joining = await UserGroupJoinings.findOneBy({ - userId: user.id, - userGroupId: recipientGroup.id, }); - - if (joining == null) { - throw new ApiError(meta.errors.groupAccessDenied); - } - - const query = makePaginationQuery(MessagingMessages.createQueryBuilder('message'), ps.sinceId, ps.untilId) - .andWhere(`message.groupId = :groupId`, { groupId: recipientGroup.id }); - - const messages = await query.take(ps.limit).getMany(); - - // Mark all as read - if (ps.markAsRead) { - readGroupMessagingMessage(user.id, recipientGroup.id, messages.map(x => x.id)); - } - - return await Promise.all(messages.map(message => MessagingMessages.pack(message, user, { - populateGroup: false, - }))); } -}); +} diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts index d7e2248ab147..fc3a8b4f0969 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts @@ -1,11 +1,11 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ApiError } from '../../../error.js'; -import { getUser } from '../../../common/getters.js'; import { MessagingMessages, DriveFiles, UserGroups, UserGroupJoinings, Blockings } from '@/models/index.js'; -import { User } from '@/models/entities/user.js'; -import { UserGroup } from '@/models/entities/user-group.js'; +import type { User } from '@/models/entities/user.js'; +import type { UserGroup } from '@/models/entities/user-group.js'; import { createMessage } from '@/services/messages/create.js'; +import { getUser } from '../../../common/getters.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['messaging'], @@ -91,68 +91,73 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - let recipientUser: User | null; - let recipientGroup: UserGroup | null; - - if (ps.userId != null) { - // Myself - if (ps.userId === user.id) { - throw new ApiError(meta.errors.recipientIsYourself); - } - - // Fetch recipient (user) - recipientUser = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; - }); - - // Check blocking - const block = await Blockings.findOneBy({ - blockerId: recipientUser.id, - blockeeId: user.id, - }); - if (block) { - throw new ApiError(meta.errors.youHaveBeenBlocked); - } - } else if (ps.groupId != null) { - // Fetch recipient (group) - recipientGroup = await UserGroups.findOneBy({ id: ps.groupId! }); - - if (recipientGroup == null) { - throw new ApiError(meta.errors.noSuchGroup); - } - - // check joined - const joining = await UserGroupJoinings.findOneBy({ - userId: user.id, - userGroupId: recipientGroup.id, + let recipientUser: User | null; + let recipientGroup: UserGroup | null; + + if (ps.userId != null) { + // Myself + if (ps.userId === user.id) { + throw new ApiError(meta.errors.recipientIsYourself); + } + + // Fetch recipient (user) + recipientUser = await getUser(ps.userId).catch(e => { + if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw e; + }); + + // Check blocking + const block = await Blockings.findOneBy({ + blockerId: recipientUser.id, + blockeeId: user.id, + }); + if (block) { + throw new ApiError(meta.errors.youHaveBeenBlocked); + } + } else if (ps.groupId != null) { + // Fetch recipient (group) + recipientGroup = await UserGroups.findOneBy({ id: ps.groupId! }); + + if (recipientGroup == null) { + throw new ApiError(meta.errors.noSuchGroup); + } + + // check joined + const joining = await UserGroupJoinings.findOneBy({ + userId: user.id, + userGroupId: recipientGroup.id, + }); + + if (joining == null) { + throw new ApiError(meta.errors.groupAccessDenied); + } + } + + let file = null; + if (ps.fileId != null) { + file = await DriveFiles.findOneBy({ + id: ps.fileId, + userId: user.id, + }); + + if (file == null) { + throw new ApiError(meta.errors.noSuchFile); + } + } + + // テキストが無いかつ添付ファイルも無かったらエラー + if (ps.text == null && file == null) { + throw new ApiError(meta.errors.contentRequired); + } + + return await createMessage(user, recipientUser, recipientGroup, ps.text, file); }); - - if (joining == null) { - throw new ApiError(meta.errors.groupAccessDenied); - } - } - - let file = null; - if (ps.fileId != null) { - file = await DriveFiles.findOneBy({ - id: ps.fileId, - userId: user.id, - }); - - if (file == null) { - throw new ApiError(meta.errors.noSuchFile); - } - } - - // テキストが無いかつ添付ファイルも無かったらエラー - if (ps.text == null && file == null) { - throw new ApiError(meta.errors.contentRequired); } - - return await createMessage(user, recipientUser, recipientGroup, ps.text, file); -}); +} diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts b/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts index 3eb6dcaccbba..34d13966b634 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; import ms from 'ms'; -import { ApiError } from '../../../error.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { MessagingMessages } from '@/models/index.js'; import { deleteMessage } from '@/services/messages/delete.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['messaging'], @@ -39,18 +39,23 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const message = await MessagingMessages.findOneBy({ - id: ps.messageId, - userId: user.id, - }); + const message = await MessagingMessages.findOneBy({ + id: ps.messageId, + userId: user.id, + }); - if (message == null) { - throw new ApiError(meta.errors.noSuchMessage); - } + if (message == null) { + throw new ApiError(meta.errors.noSuchMessage); + } - await deleteMessage(message); -}); + await deleteMessage(message); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/read.ts b/packages/backend/src/server/api/endpoints/messaging/messages/read.ts index ef33d8e453df..85044b3d1fc0 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/read.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/read.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ApiError } from '../../../error.js'; import { MessagingMessages } from '@/models/index.js'; +import { ApiError } from '../../../error.js'; import { readUserMessagingMessage, readGroupMessagingMessage } from '../../../common/read-messaging-message.js'; export const meta = { @@ -32,25 +32,30 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const message = await MessagingMessages.findOneBy({ id: ps.messageId }); - - if (message == null) { - throw new ApiError(meta.errors.noSuchMessage); - } - - if (message.recipientId) { - await readUserMessagingMessage(user.id, message.userId, [message.id]).catch(e => { - if (e.id === 'e140a4bf-49ce-4fb6-b67c-b78dadf6b52f') throw new ApiError(meta.errors.noSuchMessage); - throw e; - }); - } else if (message.groupId) { - await readGroupMessagingMessage(user.id, message.groupId, [message.id]).catch(e => { - if (e.id === '930a270c-714a-46b2-b776-ad27276dc569') throw new ApiError(meta.errors.noSuchMessage); - throw e; + const message = await MessagingMessages.findOneBy({ id: ps.messageId }); + + if (message == null) { + throw new ApiError(meta.errors.noSuchMessage); + } + + if (message.recipientId) { + await readUserMessagingMessage(user.id, message.userId, [message.id]).catch(e => { + if (e.id === 'e140a4bf-49ce-4fb6-b67c-b78dadf6b52f') throw new ApiError(meta.errors.noSuchMessage); + throw e; + }); + } else if (message.groupId) { + await readGroupMessagingMessage(user.id, message.groupId, [message.id]).catch(e => { + if (e.id === '930a270c-714a-46b2-b776-ad27276dc569') throw new ApiError(meta.errors.noSuchMessage); + throw e; + }); + } }); } -}); +} diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index f2938307217c..e082d9e9afc6 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -1,10 +1,10 @@ import { IsNull, MoreThan } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; import config from '@/config/index.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { Ads, Emojis, Users } from '@/models/index.js'; import { DB_MAX_NOTE_TEXT_LENGTH } from '@/misc/hard-limits.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -308,114 +308,119 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const instance = await fetchMeta(true); + const instance = await fetchMeta(true); - const emojis = await Emojis.find({ - where: { - host: IsNull(), - }, - order: { - category: 'ASC', - name: 'ASC', - }, - cache: { - id: 'meta_emojis', - milliseconds: 3600000, // 1 hour - }, - }); + const emojis = await Emojis.find({ + where: { + host: IsNull(), + }, + order: { + category: 'ASC', + name: 'ASC', + }, + cache: { + id: 'meta_emojis', + milliseconds: 3600000, // 1 hour + }, + }); - const ads = await Ads.find({ - where: { - expiresAt: MoreThan(new Date()), - }, - }); + const ads = await Ads.find({ + where: { + expiresAt: MoreThan(new Date()), + }, + }); - const response: any = { - maintainerName: instance.maintainerName, - maintainerEmail: instance.maintainerEmail, + const response: any = { + maintainerName: instance.maintainerName, + maintainerEmail: instance.maintainerEmail, - version: config.version, + version: config.version, - name: instance.name, - uri: config.url, - description: instance.description, - langs: instance.langs, - tosUrl: instance.ToSUrl, - repositoryUrl: instance.repositoryUrl, - feedbackUrl: instance.feedbackUrl, - disableRegistration: instance.disableRegistration, - disableLocalTimeline: instance.disableLocalTimeline, - disableGlobalTimeline: instance.disableGlobalTimeline, - driveCapacityPerLocalUserMb: instance.localDriveCapacityMb, - driveCapacityPerRemoteUserMb: instance.remoteDriveCapacityMb, - emailRequiredForSignup: instance.emailRequiredForSignup, - enableHcaptcha: instance.enableHcaptcha, - hcaptchaSiteKey: instance.hcaptchaSiteKey, - enableRecaptcha: instance.enableRecaptcha, - recaptchaSiteKey: instance.recaptchaSiteKey, - swPublickey: instance.swPublicKey, - themeColor: instance.themeColor, - mascotImageUrl: instance.mascotImageUrl, - bannerUrl: instance.bannerUrl, - errorImageUrl: instance.errorImageUrl, - iconUrl: instance.iconUrl, - backgroundImageUrl: instance.backgroundImageUrl, - logoImageUrl: instance.logoImageUrl, - maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, // 後方互換性のため - emojis: await Emojis.packMany(emojis), - defaultLightTheme: instance.defaultLightTheme, - defaultDarkTheme: instance.defaultDarkTheme, - ads: ads.map(ad => ({ - id: ad.id, - url: ad.url, - place: ad.place, - ratio: ad.ratio, - imageUrl: ad.imageUrl, - })), - enableEmail: instance.enableEmail, + name: instance.name, + uri: config.url, + description: instance.description, + langs: instance.langs, + tosUrl: instance.ToSUrl, + repositoryUrl: instance.repositoryUrl, + feedbackUrl: instance.feedbackUrl, + disableRegistration: instance.disableRegistration, + disableLocalTimeline: instance.disableLocalTimeline, + disableGlobalTimeline: instance.disableGlobalTimeline, + driveCapacityPerLocalUserMb: instance.localDriveCapacityMb, + driveCapacityPerRemoteUserMb: instance.remoteDriveCapacityMb, + emailRequiredForSignup: instance.emailRequiredForSignup, + enableHcaptcha: instance.enableHcaptcha, + hcaptchaSiteKey: instance.hcaptchaSiteKey, + enableRecaptcha: instance.enableRecaptcha, + recaptchaSiteKey: instance.recaptchaSiteKey, + swPublickey: instance.swPublicKey, + themeColor: instance.themeColor, + mascotImageUrl: instance.mascotImageUrl, + bannerUrl: instance.bannerUrl, + errorImageUrl: instance.errorImageUrl, + iconUrl: instance.iconUrl, + backgroundImageUrl: instance.backgroundImageUrl, + logoImageUrl: instance.logoImageUrl, + maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, // 後方互換性のため + emojis: await Emojis.packMany(emojis), + defaultLightTheme: instance.defaultLightTheme, + defaultDarkTheme: instance.defaultDarkTheme, + ads: ads.map(ad => ({ + id: ad.id, + url: ad.url, + place: ad.place, + ratio: ad.ratio, + imageUrl: ad.imageUrl, + })), + enableEmail: instance.enableEmail, - enableTwitterIntegration: instance.enableTwitterIntegration, - enableGithubIntegration: instance.enableGithubIntegration, - enableDiscordIntegration: instance.enableDiscordIntegration, + enableTwitterIntegration: instance.enableTwitterIntegration, + enableGithubIntegration: instance.enableGithubIntegration, + enableDiscordIntegration: instance.enableDiscordIntegration, - enableServiceWorker: instance.enableServiceWorker, + enableServiceWorker: instance.enableServiceWorker, - translatorAvailable: instance.deeplAuthKey != null, + translatorAvailable: instance.deeplAuthKey != null, - ...(ps.detail ? { - pinnedPages: instance.pinnedPages, - pinnedClipId: instance.pinnedClipId, - cacheRemoteFiles: instance.cacheRemoteFiles, - requireSetup: (await Users.countBy({ - host: IsNull(), - })) === 0, - } : {}), - }; + ...(ps.detail ? { + pinnedPages: instance.pinnedPages, + pinnedClipId: instance.pinnedClipId, + cacheRemoteFiles: instance.cacheRemoteFiles, + requireSetup: (await Users.countBy({ + host: IsNull(), + })) === 0, + } : {}), + }; - if (ps.detail) { - const proxyAccount = instance.proxyAccountId ? await Users.pack(instance.proxyAccountId).catch(() => null) : null; + if (ps.detail) { + const proxyAccount = instance.proxyAccountId ? await Users.pack(instance.proxyAccountId).catch(() => null) : null; - response.proxyAccountName = proxyAccount ? proxyAccount.username : null; - response.features = { - registration: !instance.disableRegistration, - localTimeLine: !instance.disableLocalTimeline, - globalTimeLine: !instance.disableGlobalTimeline, - emailRequiredForSignup: instance.emailRequiredForSignup, - elasticsearch: config.elasticsearch ? true : false, - hcaptcha: instance.enableHcaptcha, - recaptcha: instance.enableRecaptcha, - objectStorage: instance.useObjectStorage, - twitter: instance.enableTwitterIntegration, - github: instance.enableGithubIntegration, - discord: instance.enableDiscordIntegration, - serviceWorker: instance.enableServiceWorker, - miauth: true, - }; - } + response.proxyAccountName = proxyAccount ? proxyAccount.username : null; + response.features = { + registration: !instance.disableRegistration, + localTimeLine: !instance.disableLocalTimeline, + globalTimeLine: !instance.disableGlobalTimeline, + emailRequiredForSignup: instance.emailRequiredForSignup, + elasticsearch: config.elasticsearch ? true : false, + hcaptcha: instance.enableHcaptcha, + recaptcha: instance.enableRecaptcha, + objectStorage: instance.useObjectStorage, + twitter: instance.enableTwitterIntegration, + github: instance.enableGithubIntegration, + discord: instance.enableDiscordIntegration, + serviceWorker: instance.enableServiceWorker, + miauth: true, + }; + } - return response; -}); + return response; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts index 62a1f919b2e0..268f90da6670 100644 --- a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts +++ b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts @@ -41,31 +41,36 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - // Generate access token - const accessToken = secureRndstr(32, true); + // Generate access token + const accessToken = secureRndstr(32, true); - const now = new Date(); + const now = new Date(); - // Insert access token doc - await AccessTokens.insert({ - id: genId(), - createdAt: now, - lastUsedAt: now, - session: ps.session, - userId: user.id, - token: accessToken, - hash: accessToken, - name: ps.name, - description: ps.description, - iconUrl: ps.iconUrl, - permission: ps.permission, - }); + // Insert access token doc + await AccessTokens.insert({ + id: genId(), + createdAt: now, + lastUsedAt: now, + session: ps.session, + userId: user.id, + token: accessToken, + hash: accessToken, + name: ps.name, + description: ps.description, + iconUrl: ps.iconUrl, + permission: ps.permission, + }); - return { - token: accessToken, - }; -}); + return { + token: accessToken, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/mute/create.ts b/packages/backend/src/server/api/endpoints/mute/create.ts index 57f14db559dd..8bbc3e95f487 100644 --- a/packages/backend/src/server/api/endpoints/mute/create.ts +++ b/packages/backend/src/server/api/endpoints/mute/create.ts @@ -1,11 +1,11 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ApiError } from '../../error.js'; -import { getUser } from '../../common/getters.js'; import { genId } from '@/misc/gen-id.js'; import { Mutings, NoteWatchings } from '@/models/index.js'; -import { Muting } from '@/models/entities/muting.js'; +import type { Muting } from '@/models/entities/muting.js'; import { publishUserEvent } from '@/services/stream.js'; +import { getUser } from '../../common/getters.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['account'], @@ -52,50 +52,55 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const muter = user; - - // 自分自身 - if (user.id === ps.userId) { - throw new ApiError(meta.errors.muteeIsYourself); + const muter = user; + + // 自分自身 + if (user.id === ps.userId) { + throw new ApiError(meta.errors.muteeIsYourself); + } + + // Get mutee + const mutee = await getUser(ps.userId).catch(e => { + if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw e; + }); + + // Check if already muting + const exist = await Mutings.findOneBy({ + muterId: muter.id, + muteeId: mutee.id, + }); + + if (exist != null) { + throw new ApiError(meta.errors.alreadyMuting); + } + + if (ps.expiresAt && ps.expiresAt <= Date.now()) { + return; + } + + // Create mute + await Mutings.insert({ + id: genId(), + createdAt: new Date(), + expiresAt: ps.expiresAt ? new Date(ps.expiresAt) : null, + muterId: muter.id, + muteeId: mutee.id, + } as Muting); + + publishUserEvent(user.id, 'mute', mutee); + + NoteWatchings.delete({ + userId: muter.id, + noteUserId: mutee.id, + }); + }); } - - // Get mutee - const mutee = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; - }); - - // Check if already muting - const exist = await Mutings.findOneBy({ - muterId: muter.id, - muteeId: mutee.id, - }); - - if (exist != null) { - throw new ApiError(meta.errors.alreadyMuting); - } - - if (ps.expiresAt && ps.expiresAt <= Date.now()) { - return; - } - - // Create mute - await Mutings.insert({ - id: genId(), - createdAt: new Date(), - expiresAt: ps.expiresAt ? new Date(ps.expiresAt) : null, - muterId: muter.id, - muteeId: mutee.id, - } as Muting); - - publishUserEvent(user.id, 'mute', mutee); - - NoteWatchings.delete({ - userId: muter.id, - noteUserId: mutee.id, - }); -}); +} diff --git a/packages/backend/src/server/api/endpoints/mute/delete.ts b/packages/backend/src/server/api/endpoints/mute/delete.ts index 11e4607a22a2..911e6aab054d 100644 --- a/packages/backend/src/server/api/endpoints/mute/delete.ts +++ b/packages/backend/src/server/api/endpoints/mute/delete.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ApiError } from '../../error.js'; -import { getUser } from '../../common/getters.js'; import { Mutings } from '@/models/index.js'; import { publishUserEvent } from '@/services/stream.js'; +import { ApiError } from '../../error.js'; +import { getUser } from '../../common/getters.js'; export const meta = { tags: ['account'], @@ -45,37 +45,42 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const muter = user; + const muter = user; - // Check if the mutee is yourself - if (user.id === ps.userId) { - throw new ApiError(meta.errors.muteeIsYourself); - } + // Check if the mutee is yourself + if (user.id === ps.userId) { + throw new ApiError(meta.errors.muteeIsYourself); + } - // Get mutee - const mutee = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; - }); + // Get mutee + const mutee = await getUser(ps.userId).catch(e => { + if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw e; + }); - // Check not muting - const exist = await Mutings.findOneBy({ - muterId: muter.id, - muteeId: mutee.id, - }); + // Check not muting + const exist = await Mutings.findOneBy({ + muterId: muter.id, + muteeId: mutee.id, + }); - if (exist == null) { - throw new ApiError(meta.errors.notMuting); - } + if (exist == null) { + throw new ApiError(meta.errors.notMuting); + } - // Delete mute - await Mutings.delete({ - id: exist.id, - }); + // Delete mute + await Mutings.delete({ + id: exist.id, + }); - publishUserEvent(user.id, 'unmute', mutee); -}); + publishUserEvent(user.id, 'unmute', mutee); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/mute/list.ts b/packages/backend/src/server/api/endpoints/mute/list.ts index ed1321605425..8e7a21f30d2a 100644 --- a/packages/backend/src/server/api/endpoints/mute/list.ts +++ b/packages/backend/src/server/api/endpoints/mute/list.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { Mutings } from '@/models/index.js'; +import { makePaginationQuery } from '../../common/make-pagination-query.js'; export const meta = { tags: ['account'], @@ -35,16 +35,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Mutings.createQueryBuilder('muting'), ps.sinceId, ps.untilId) - .andWhere(`muting.muterId = :meId`, { meId: me.id }); + const query = makePaginationQuery(Mutings.createQueryBuilder('muting'), ps.sinceId, ps.untilId) + .andWhere('muting.muterId = :meId', { meId: me.id }); - const mutings = await query - .take(ps.limit) - .getMany(); + const mutings = await query + .take(ps.limit) + .getMany(); - return await Mutings.packMany(mutings, me); -}); + return await Mutings.packMany(mutings, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/my/apps.ts b/packages/backend/src/server/api/endpoints/my/apps.ts index 46498cc5f332..f2ee083f7d0f 100644 --- a/packages/backend/src/server/api/endpoints/my/apps.ts +++ b/packages/backend/src/server/api/endpoints/my/apps.ts @@ -31,21 +31,26 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = { - userId: user.id, - }; - - const apps = await Apps.find({ - where: query, - take: ps.limit, - skip: ps.offset, - }); - - return await Promise.all(apps.map(app => Apps.pack(app, user, { - detail: true, - }))); -}); + const query = { + userId: user.id, + }; + + const apps = await Apps.find({ + where: query, + take: ps.limit, + skip: ps.offset, + }); + + return await Promise.all(apps.map(app => Apps.pack(app, user, { + detail: true, + }))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes.ts b/packages/backend/src/server/api/endpoints/notes.ts index 40b148ce5021..e82826bda32b 100644 --- a/packages/backend/src/server/api/endpoints/notes.ts +++ b/packages/backend/src/server/api/endpoints/notes.ts @@ -36,6 +36,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index f6838843d419..27d75a6faa10 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -1,6 +1,6 @@ import { Brackets } from 'typeorm'; -import { Notes } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; @@ -38,41 +38,46 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .andWhere(new Brackets(qb => { qb - .where('note.replyId = :noteId', { noteId: ps.noteId }) - .orWhere(new Brackets(qb => { qb - .where('note.renoteId = :noteId', { noteId: ps.noteId }) + const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(new Brackets(qb => { qb - .where('note.text IS NOT NULL') - .orWhere('note.fileIds != \'{}\'') - .orWhere('note.hasPoll = TRUE'); - })); - })); - })) - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('user.avatar', 'avatar') - .leftJoinAndSelect('user.banner', 'banner') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') - .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') - .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); + .where('note.replyId = :noteId', { noteId: ps.noteId }) + .orWhere(new Brackets(qb => { qb + .where('note.renoteId = :noteId', { noteId: ps.noteId }) + .andWhere(new Brackets(qb => { qb + .where('note.text IS NOT NULL') + .orWhere('note.fileIds != \'{}\'') + .orWhere('note.hasPoll = TRUE'); + })); + })); + })) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('user.avatar', 'avatar') + .leftJoinAndSelect('user.banner', 'banner') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateVisibilityQuery(query, user); - if (user) { - generateMutedUserQuery(query, user); - generateBlockedUserQuery(query, user); - } + generateVisibilityQuery(query, user); + if (user) { + generateMutedUserQuery(query, user); + generateBlockedUserQuery(query, user); + } - const notes = await query.take(ps.limit).getMany(); + const notes = await query.take(ps.limit).getMany(); - return await Notes.packMany(notes, user); -}); + return await Notes.packMany(notes, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/clips.ts b/packages/backend/src/server/api/endpoints/notes/clips.ts index 63800271040d..9a66d3a56dbe 100644 --- a/packages/backend/src/server/api/endpoints/notes/clips.ts +++ b/packages/backend/src/server/api/endpoints/notes/clips.ts @@ -1,6 +1,6 @@ import { In } from 'typeorm'; -import { ClipNotes, Clips } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { ClipNotes, Clips } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../common/getters.js'; import { ApiError } from '../../error.js'; @@ -41,23 +41,28 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); - - const clipNotes = await ClipNotes.findBy({ - noteId: note.id, - }); - - const clips = await Clips.findBy({ - id: In(clipNotes.map(x => x.clipId)), - isPublic: true, - }); - - return await Promise.all(clips.map(x => Clips.pack(x))); -}); + const note = await getNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); + + const clipNotes = await ClipNotes.findBy({ + noteId: note.id, + }); + + const clips = await Clips.findBy({ + id: In(clipNotes.map(x => x.clipId)), + isPublic: true, + }); + + return await Promise.all(clips.map(x => Clips.pack(x))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/conversation.ts b/packages/backend/src/server/api/endpoints/notes/conversation.ts index de868832fc03..f558ae4d3399 100644 --- a/packages/backend/src/server/api/endpoints/notes/conversation.ts +++ b/packages/backend/src/server/api/endpoints/notes/conversation.ts @@ -1,6 +1,6 @@ -import { Note } from '@/models/entities/note.js'; -import { Notes } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import type { Note } from '@/models/entities/note.js'; +import { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { getNote } from '../../common/getters.js'; @@ -43,39 +43,44 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); + const note = await getNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); - const conversation: Note[] = []; - let i = 0; + const conversation: Note[] = []; + let i = 0; - async function get(id: any) { - i++; - const p = await Notes.findOneBy({ id }); - if (p == null) return; + async function get(id: any) { + i++; + const p = await Notes.findOneBy({ id }); + if (p == null) return; - if (i > ps.offset!) { - conversation.push(p); - } + if (i > ps.offset!) { + conversation.push(p); + } - if (conversation.length === ps.limit) { - return; - } + if (conversation.length === ps.limit) { + return; + } - if (p.replyId) { - await get(p.replyId); - } - } + if (p.replyId) { + await get(p.replyId); + } + } - if (note.replyId) { - await get(note.replyId); - } + if (note.replyId) { + await get(note.replyId); + } - return await Notes.packMany(conversation, user); -}); + return await Notes.packMany(conversation, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index fbd1fa0e9773..ffa81b1a742d 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -1,16 +1,16 @@ import ms from 'ms'; import { In } from 'typeorm'; -import { User } from '@/models/entities/user.js'; +import { Inject, Injectable } from '@nestjs/common'; +import type { User } from '@/models/entities/user.js'; import { Users, DriveFiles, Notes, Channels, Blockings } from '@/models/index.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; -import { Note } from '@/models/entities/note.js'; -import { Channel } from '@/models/entities/channel.js'; +import type { DriveFile } from '@/models/entities/drive-file.js'; +import type { Note } from '@/models/entities/note.js'; +import type { Channel } from '@/models/entities/channel.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { noteService } from '@/services/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { noteVisibilities } from '../../../../types.js'; import { ApiError } from '../../error.js'; -import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['notes'], @@ -165,118 +165,123 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - let visibleUsers: User[] = []; - if (ps.visibleUserIds) { - visibleUsers = await Users.findBy({ - id: In(ps.visibleUserIds), - }); - } + let visibleUsers: User[] = []; + if (ps.visibleUserIds) { + visibleUsers = await Users.findBy({ + id: In(ps.visibleUserIds), + }); + } - let files: DriveFile[] = []; - const fileIds = ps.fileIds != null ? ps.fileIds : ps.mediaIds != null ? ps.mediaIds : null; - if (fileIds != null) { - files = await DriveFiles.createQueryBuilder('file') - .where('file.userId = :userId AND file.id IN (:...fileIds)', { - userId: user.id, - fileIds, - }) - .orderBy('array_position(ARRAY[:...fileIds], "id"::text)') - .setParameters({ fileIds }) - .getMany(); - } + let files: DriveFile[] = []; + const fileIds = ps.fileIds != null ? ps.fileIds : ps.mediaIds != null ? ps.mediaIds : null; + if (fileIds != null) { + files = await DriveFiles.createQueryBuilder('file') + .where('file.userId = :userId AND file.id IN (:...fileIds)', { + userId: user.id, + fileIds, + }) + .orderBy('array_position(ARRAY[:...fileIds], "id"::text)') + .setParameters({ fileIds }) + .getMany(); + } - let renote: Note | null = null; - if (ps.renoteId != null) { - // Fetch renote to note - renote = await Notes.findOneBy({ id: ps.renoteId }); + let renote: Note | null = null; + if (ps.renoteId != null) { + // Fetch renote to note + renote = await Notes.findOneBy({ id: ps.renoteId }); - if (renote == null) { - throw new ApiError(meta.errors.noSuchRenoteTarget); - } else if (renote.renoteId && !renote.text && !renote.fileIds && !renote.hasPoll) { - throw new ApiError(meta.errors.cannotReRenote); - } + if (renote == null) { + throw new ApiError(meta.errors.noSuchRenoteTarget); + } else if (renote.renoteId && !renote.text && !renote.fileIds && !renote.hasPoll) { + throw new ApiError(meta.errors.cannotReRenote); + } - // Check blocking - if (renote.userId !== user.id) { - const block = await Blockings.findOneBy({ - blockerId: renote.userId, - blockeeId: user.id, - }); - if (block) { - throw new ApiError(meta.errors.youHaveBeenBlocked); + // Check blocking + if (renote.userId !== user.id) { + const block = await Blockings.findOneBy({ + blockerId: renote.userId, + blockeeId: user.id, + }); + if (block) { + throw new ApiError(meta.errors.youHaveBeenBlocked); + } + } } - } - } - let reply: Note | null = null; - if (ps.replyId != null) { - // Fetch reply - reply = await Notes.findOneBy({ id: ps.replyId }); + let reply: Note | null = null; + if (ps.replyId != null) { + // Fetch reply + reply = await Notes.findOneBy({ id: ps.replyId }); - if (reply == null) { - throw new ApiError(meta.errors.noSuchReplyTarget); - } else if (reply.renoteId && !reply.text && !reply.fileIds && !reply.hasPoll) { - throw new ApiError(meta.errors.cannotReplyToPureRenote); - } + if (reply == null) { + throw new ApiError(meta.errors.noSuchReplyTarget); + } else if (reply.renoteId && !reply.text && !reply.fileIds && !reply.hasPoll) { + throw new ApiError(meta.errors.cannotReplyToPureRenote); + } - // Check blocking - if (reply.userId !== user.id) { - const block = await Blockings.findOneBy({ - blockerId: reply.userId, - blockeeId: user.id, - }); - if (block) { - throw new ApiError(meta.errors.youHaveBeenBlocked); + // Check blocking + if (reply.userId !== user.id) { + const block = await Blockings.findOneBy({ + blockerId: reply.userId, + blockeeId: user.id, + }); + if (block) { + throw new ApiError(meta.errors.youHaveBeenBlocked); + } + } } - } - } - if (ps.poll) { - if (typeof ps.poll.expiresAt === 'number') { - if (ps.poll.expiresAt < Date.now()) { - throw new ApiError(meta.errors.cannotCreateAlreadyExpiredPoll); + if (ps.poll) { + if (typeof ps.poll.expiresAt === 'number') { + if (ps.poll.expiresAt < Date.now()) { + throw new ApiError(meta.errors.cannotCreateAlreadyExpiredPoll); + } + } else if (typeof ps.poll.expiredAfter === 'number') { + ps.poll.expiresAt = Date.now() + ps.poll.expiredAfter; + } } - } else if (typeof ps.poll.expiredAfter === 'number') { - ps.poll.expiresAt = Date.now() + ps.poll.expiredAfter; - } - } - let channel: Channel | null = null; - if (ps.channelId != null) { - channel = await Channels.findOneBy({ id: ps.channelId }); + let channel: Channel | null = null; + if (ps.channelId != null) { + channel = await Channels.findOneBy({ id: ps.channelId }); - if (channel == null) { - throw new ApiError(meta.errors.noSuchChannel); - } - } + if (channel == null) { + throw new ApiError(meta.errors.noSuchChannel); + } + } - // 投稿を作成 - const note = await noteService.create(user, { - createdAt: new Date(), - files: files, - poll: ps.poll ? { - choices: ps.poll.choices, - multiple: ps.poll.multiple || false, - expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null, - } : undefined, - text: ps.text || undefined, - reply, - renote, - cw: ps.cw, - localOnly: ps.localOnly, - visibility: ps.visibility, - visibleUsers, - channel, - apMentions: ps.noExtractMentions ? [] : undefined, - apHashtags: ps.noExtractHashtags ? [] : undefined, - apEmojis: ps.noExtractEmojis ? [] : undefined, - }); + // 投稿を作成 + const note = await noteService.create(user, { + createdAt: new Date(), + files: files, + poll: ps.poll ? { + choices: ps.poll.choices, + multiple: ps.poll.multiple || false, + expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null, + } : undefined, + text: ps.text || undefined, + reply, + renote, + cw: ps.cw, + localOnly: ps.localOnly, + visibility: ps.visibility, + visibleUsers, + channel, + apMentions: ps.noExtractMentions ? [] : undefined, + apHashtags: ps.noExtractHashtags ? [] : undefined, + apEmojis: ps.noExtractEmojis ? [] : undefined, + }); - return { - createdNote: await Notes.pack(note, user), - }; -}); + return { + createdNote: await Notes.pack(note, user), + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/delete.ts b/packages/backend/src/server/api/endpoints/notes/delete.ts index 06ba6f77645f..3bc6dd1716e9 100644 --- a/packages/backend/src/server/api/endpoints/notes/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/delete.ts @@ -1,7 +1,7 @@ import ms from 'ms'; +import { Inject, Injectable } from '@nestjs/common'; import deleteNote from '@/services/note/delete.js'; import { Users } from '@/models/index.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../common/getters.js'; import { ApiError } from '../../error.js'; @@ -46,19 +46,24 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); + const note = await getNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); - if ((!user.isAdmin && !user.isModerator) && (note.userId !== user.id)) { - throw new ApiError(meta.errors.accessDenied); - } + if ((!user.isAdmin && !user.isModerator) && (note.userId !== user.id)) { + throw new ApiError(meta.errors.accessDenied); + } - // この操作を行うのが投稿者とは限らない(例えばモデレーター)ため - await deleteNote(await Users.findOneByOrFail({ id: note.userId }), note); -}); + // この操作を行うのが投稿者とは限らない(例えばモデレーター)ため + await deleteNote(await Users.findOneByOrFail({ id: note.userId }), note); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts index 2fadcd19c62d..876d278eda60 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts @@ -1,6 +1,6 @@ +import { Inject, Injectable } from '@nestjs/common'; import { NoteFavorites } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { getNote } from '../../../common/getters.js'; @@ -39,31 +39,36 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - // Get favoritee - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); + // Get favoritee + const note = await getNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); - // if already favorited - const exist = await NoteFavorites.findOneBy({ - noteId: note.id, - userId: user.id, - }); + // if already favorited + const exist = await NoteFavorites.findOneBy({ + noteId: note.id, + userId: user.id, + }); - if (exist != null) { - throw new ApiError(meta.errors.alreadyFavorited); - } + if (exist != null) { + throw new ApiError(meta.errors.alreadyFavorited); + } - // Create favorite - await NoteFavorites.insert({ - id: genId(), - createdAt: new Date(), - noteId: note.id, - userId: user.id, - }); -}); + // Create favorite + await NoteFavorites.insert({ + id: genId(), + createdAt: new Date(), + noteId: note.id, + userId: user.id, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts index 1d5744b44a93..0e844a38db7e 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts @@ -1,5 +1,5 @@ -import { NoteFavorites } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { NoteFavorites } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { getNote } from '../../../common/getters.js'; @@ -38,26 +38,31 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - // Get favoritee - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); - - // if already favorited - const exist = await NoteFavorites.findOneBy({ - noteId: note.id, - userId: user.id, - }); - - if (exist == null) { - throw new ApiError(meta.errors.notFavorited); - } + // Get favoritee + const note = await getNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); - // Delete favorite - await NoteFavorites.delete(exist.id); -}); + // if already favorited + const exist = await NoteFavorites.findOneBy({ + noteId: note.id, + userId: user.id, + }); + + if (exist == null) { + throw new ApiError(meta.errors.notFavorited); + } + + // Delete favorite + await NoteFavorites.delete(exist.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index d6c4447f0814..2b5f9b86379e 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -1,5 +1,5 @@ -import { Notes } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; @@ -33,42 +33,47 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const max = 30; - const day = 1000 * 60 * 60 * 24 * 3; // 3日前まで + const max = 30; + const day = 1000 * 60 * 60 * 24 * 3; // 3日前まで - const query = Notes.createQueryBuilder('note') - .addSelect('note.score') - .where('note.userHost IS NULL') - .andWhere('note.score > 0') - .andWhere('note.createdAt > :date', { date: new Date(Date.now() - day) }) - .andWhere('note.visibility = \'public\'') - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('user.avatar', 'avatar') - .leftJoinAndSelect('user.banner', 'banner') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') - .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') - .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); + const query = Notes.createQueryBuilder('note') + .addSelect('note.score') + .where('note.userHost IS NULL') + .andWhere('note.score > 0') + .andWhere('note.createdAt > :date', { date: new Date(Date.now() - day) }) + .andWhere('note.visibility = \'public\'') + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('user.avatar', 'avatar') + .leftJoinAndSelect('user.banner', 'banner') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - if (user) generateMutedUserQuery(query, user); - if (user) generateBlockedUserQuery(query, user); + if (user) generateMutedUserQuery(query, user); + if (user) generateBlockedUserQuery(query, user); - let notes = await query - .orderBy('note.score', 'DESC') - .take(max) - .getMany(); + let notes = await query + .orderBy('note.score', 'DESC') + .take(max) + .getMany(); - notes.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); + notes.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); - notes = notes.slice(ps.offset, ps.offset + ps.limit); + notes = notes.slice(ps.offset, ps.offset + ps.limit); - return await Notes.packMany(notes, user); -}); + return await Notes.packMany(notes, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index 5bab0bcfa822..78dfa1320a53 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -1,7 +1,7 @@ +import { Inject, Injectable } from '@nestjs/common'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { Notes } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -53,53 +53,58 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const m = await fetchMeta(); - if (m.disableGlobalTimeline) { - if (user == null || (!user.isAdmin && !user.isModerator)) { - throw new ApiError(meta.errors.gtlDisabled); - } - } + const m = await fetchMeta(); + if (m.disableGlobalTimeline) { + if (user == null || (!user.isAdmin && !user.isModerator)) { + throw new ApiError(meta.errors.gtlDisabled); + } + } - //#region Construct query - const query = makePaginationQuery(Notes.createQueryBuilder('note'), - ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere('note.visibility = \'public\'') - .andWhere('note.channelId IS NULL') - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('user.avatar', 'avatar') - .leftJoinAndSelect('user.banner', 'banner') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') - .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') - .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); + //#region Construct query + const query = makePaginationQuery(Notes.createQueryBuilder('note'), + ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + .andWhere('note.visibility = \'public\'') + .andWhere('note.channelId IS NULL') + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('user.avatar', 'avatar') + .leftJoinAndSelect('user.banner', 'banner') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateRepliesQuery(query, user); - if (user) { - generateMutedUserQuery(query, user); - generateMutedNoteQuery(query, user); - generateBlockedUserQuery(query, user); - } + generateRepliesQuery(query, user); + if (user) { + generateMutedUserQuery(query, user); + generateMutedNoteQuery(query, user); + generateBlockedUserQuery(query, user); + } - if (ps.withFiles) { - query.andWhere('note.fileIds != \'{}\''); - } - //#endregion + if (ps.withFiles) { + query.andWhere('note.fileIds != \'{}\''); + } + //#endregion - const timeline = await query.take(ps.limit).getMany(); + const timeline = await query.take(ps.limit).getMany(); - process.nextTick(() => { - if (user) { - activeUsersChart.read(user); - } - }); + process.nextTick(() => { + if (user) { + activeUsersChart.read(user); + } + }); - return await Notes.packMany(timeline, user); -}); + return await Notes.packMany(timeline, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index c5d457dccdbe..8689ee19b91e 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -1,8 +1,8 @@ import { Brackets } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { Followings, Notes } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -61,86 +61,91 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const m = await fetchMeta(); - if (m.disableLocalTimeline && (!user.isAdmin && !user.isModerator)) { - throw new ApiError(meta.errors.stlDisabled); - } - - //#region Construct query - const followingQuery = Followings.createQueryBuilder('following') - .select('following.followeeId') - .where('following.followerId = :followerId', { followerId: user.id }); - - const query = makePaginationQuery(Notes.createQueryBuilder('note'), - ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere(new Brackets(qb => { - qb.where(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: user.id }) - .orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); - })) - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('user.avatar', 'avatar') - .leftJoinAndSelect('user.banner', 'banner') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') - .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') - .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') - .setParameters(followingQuery.getParameters()); - - generateChannelQuery(query, user); - generateRepliesQuery(query, user); - generateVisibilityQuery(query, user); - generateMutedUserQuery(query, user); - generateMutedNoteQuery(query, user); - generateBlockedUserQuery(query, user); - - if (ps.includeMyRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.userId != :meId', { meId: user.id }); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } - - if (ps.includeRenotedMyNotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteUserId != :meId', { meId: user.id }); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); + const m = await fetchMeta(); + if (m.disableLocalTimeline && (!user.isAdmin && !user.isModerator)) { + throw new ApiError(meta.errors.stlDisabled); + } + + //#region Construct query + const followingQuery = Followings.createQueryBuilder('following') + .select('following.followeeId') + .where('following.followerId = :followerId', { followerId: user.id }); + + const query = makePaginationQuery(Notes.createQueryBuilder('note'), + ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + .andWhere(new Brackets(qb => { + qb.where(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: user.id }) + .orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); + })) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('user.avatar', 'avatar') + .leftJoinAndSelect('user.banner', 'banner') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') + .setParameters(followingQuery.getParameters()); + + generateChannelQuery(query, user); + generateRepliesQuery(query, user); + generateVisibilityQuery(query, user); + generateMutedUserQuery(query, user); + generateMutedNoteQuery(query, user); + generateBlockedUserQuery(query, user); + + if (ps.includeMyRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.userId != :meId', { meId: user.id }); + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); + })); + } + + if (ps.includeRenotedMyNotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteUserId != :meId', { meId: user.id }); + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); + })); + } + + if (ps.includeLocalRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteUserHost IS NOT NULL'); + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); + })); + } + + if (ps.withFiles) { + query.andWhere('note.fileIds != \'{}\''); + } + //#endregion + + const timeline = await query.take(ps.limit).getMany(); + + process.nextTick(() => { + activeUsersChart.read(user); + }); + + return await Notes.packMany(timeline, user); + }); } - - if (ps.includeLocalRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteUserHost IS NOT NULL'); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } - - if (ps.withFiles) { - query.andWhere('note.fileIds != \'{}\''); - } - //#endregion - - const timeline = await query.take(ps.limit).getMany(); - - process.nextTick(() => { - activeUsersChart.read(user); - }); - - return await Notes.packMany(timeline, user); -}); +} diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 66f44a2f1509..7f980273d921 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -1,8 +1,9 @@ import { Brackets } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; import { fetchMeta } from '@/misc/fetch-meta.js'; -import { Notes, Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { Notes } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; @@ -60,67 +61,72 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const m = await fetchMeta(); - if (m.disableLocalTimeline) { - if (user == null || (!user.isAdmin && !user.isModerator)) { - throw new ApiError(meta.errors.ltlDisabled); - } - } + const m = await fetchMeta(); + if (m.disableLocalTimeline) { + if (user == null || (!user.isAdmin && !user.isModerator)) { + throw new ApiError(meta.errors.ltlDisabled); + } + } - //#region Construct query - const query = makePaginationQuery(Notes.createQueryBuilder('note'), - ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)') - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('user.avatar', 'avatar') - .leftJoinAndSelect('user.banner', 'banner') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') - .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') - .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); + //#region Construct query + const query = makePaginationQuery(Notes.createQueryBuilder('note'), + ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + .andWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)') + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('user.avatar', 'avatar') + .leftJoinAndSelect('user.banner', 'banner') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateChannelQuery(query, user); - generateRepliesQuery(query, user); - generateVisibilityQuery(query, user); - if (user) generateMutedUserQuery(query, user); - if (user) generateMutedNoteQuery(query, user); - if (user) generateBlockedUserQuery(query, user); + generateChannelQuery(query, user); + generateRepliesQuery(query, user); + generateVisibilityQuery(query, user); + if (user) generateMutedUserQuery(query, user); + if (user) generateMutedNoteQuery(query, user); + if (user) generateBlockedUserQuery(query, user); - if (ps.withFiles) { - query.andWhere('note.fileIds != \'{}\''); - } - - if (ps.fileType != null) { - query.andWhere('note.fileIds != \'{}\''); - query.andWhere(new Brackets(qb => { - for (const type of ps.fileType!) { - const i = ps.fileType!.indexOf(type); - qb.orWhere(`:type${i} = ANY(note.attachedFileTypes)`, { [`type${i}`]: type }); + if (ps.withFiles) { + query.andWhere('note.fileIds != \'{}\''); } - })); - if (ps.excludeNsfw) { - query.andWhere('note.cw IS NULL'); - query.andWhere('0 = (SELECT COUNT(*) FROM drive_file df WHERE df.id = ANY(note."fileIds") AND df."isSensitive" = TRUE)'); - } - } - //#endregion + if (ps.fileType != null) { + query.andWhere('note.fileIds != \'{}\''); + query.andWhere(new Brackets(qb => { + for (const type of ps.fileType!) { + const i = ps.fileType!.indexOf(type); + qb.orWhere(`:type${i} = ANY(note.attachedFileTypes)`, { [`type${i}`]: type }); + } + })); - const timeline = await query.take(ps.limit).getMany(); + if (ps.excludeNsfw) { + query.andWhere('note.cw IS NULL'); + query.andWhere('0 = (SELECT COUNT(*) FROM drive_file df WHERE df.id = ANY(note."fileIds") AND df."isSensitive" = TRUE)'); + } + } + //#endregion - process.nextTick(() => { - if (user) { - activeUsersChart.read(user); - } - }); + const timeline = await query.take(ps.limit).getMany(); - return await Notes.packMany(timeline, user); -}); + process.nextTick(() => { + if (user) { + activeUsersChart.read(user); + } + }); + + return await Notes.packMany(timeline, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index b8b2a3d5a1b6..02df7f07eca0 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -1,7 +1,7 @@ import { Brackets } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; import read from '@/services/note/read.js'; import { Notes, Followings } from '@/models/index.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; @@ -41,48 +41,53 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const followingQuery = Followings.createQueryBuilder('following') - .select('following.followeeId') - .where('following.followerId = :followerId', { followerId: user.id }); + const followingQuery = Followings.createQueryBuilder('following') + .select('following.followeeId') + .where('following.followerId = :followerId', { followerId: user.id }); - const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .andWhere(new Brackets(qb => { qb - .where(`'{"${user.id}"}' <@ note.mentions`) - .orWhere(`'{"${user.id}"}' <@ note.visibleUserIds`); - })) - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('user.avatar', 'avatar') - .leftJoinAndSelect('user.banner', 'banner') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') - .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') - .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); + const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) + .andWhere(new Brackets(qb => { qb + .where(`'{"${user.id}"}' <@ note.mentions`) + .orWhere(`'{"${user.id}"}' <@ note.visibleUserIds`); + })) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('user.avatar', 'avatar') + .leftJoinAndSelect('user.banner', 'banner') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateVisibilityQuery(query, user); - generateMutedUserQuery(query, user); - generateMutedNoteThreadQuery(query, user); - generateBlockedUserQuery(query, user); + generateVisibilityQuery(query, user); + generateMutedUserQuery(query, user); + generateMutedNoteThreadQuery(query, user); + generateBlockedUserQuery(query, user); - if (ps.visibility) { - query.andWhere('note.visibility = :visibility', { visibility: ps.visibility }); - } + if (ps.visibility) { + query.andWhere('note.visibility = :visibility', { visibility: ps.visibility }); + } - if (ps.following) { - query.andWhere(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: user.id }); - query.setParameters(followingQuery.getParameters()); - } + if (ps.following) { + query.andWhere(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: user.id }); + query.setParameters(followingQuery.getParameters()); + } - const mentions = await query.take(ps.limit).getMany(); + const mentions = await query.take(ps.limit).getMany(); - read(user.id, mentions); + read(user.id, mentions); - return await Notes.packMany(mentions, user); -}); + return await Notes.packMany(mentions, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts index c3eda14ec751..d145a7bbd79d 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts @@ -1,6 +1,6 @@ import { Brackets, In } from 'typeorm'; -import { Polls, Mutings, Notes, PollVotes } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Polls, Mutings, Notes, PollVotes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -32,59 +32,64 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = Polls.createQueryBuilder('poll') - .where('poll.userHost IS NULL') - .andWhere('poll.userId != :meId', { meId: user.id }) - .andWhere('poll.noteVisibility = \'public\'') - .andWhere(new Brackets(qb => { qb - .where('poll.expiresAt IS NULL') - .orWhere('poll.expiresAt > :now', { now: new Date() }); - })); - - //#region exclude arleady voted polls - const votedQuery = PollVotes.createQueryBuilder('vote') - .select('vote.noteId') - .where('vote.userId = :meId', { meId: user.id }); - - query - .andWhere(`poll.noteId NOT IN (${ votedQuery.getQuery() })`); - - query.setParameters(votedQuery.getParameters()); - //#endregion - - //#region mute - const mutingQuery = Mutings.createQueryBuilder('muting') - .select('muting.muteeId') - .where('muting.muterId = :muterId', { muterId: user.id }); - - query - .andWhere(`poll.userId NOT IN (${ mutingQuery.getQuery() })`); - - query.setParameters(mutingQuery.getParameters()); - //#endregion - - const polls = await query - .orderBy('poll.noteId', 'DESC') - .take(ps.limit) - .skip(ps.offset) - .getMany(); - - if (polls.length === 0) return []; - - const notes = await Notes.find({ - where: { - id: In(polls.map(poll => poll.noteId)), - }, - order: { - createdAt: 'DESC', - }, - }); + const query = Polls.createQueryBuilder('poll') + .where('poll.userHost IS NULL') + .andWhere('poll.userId != :meId', { meId: user.id }) + .andWhere('poll.noteVisibility = \'public\'') + .andWhere(new Brackets(qb => { qb + .where('poll.expiresAt IS NULL') + .orWhere('poll.expiresAt > :now', { now: new Date() }); + })); + + //#region exclude arleady voted polls + const votedQuery = PollVotes.createQueryBuilder('vote') + .select('vote.noteId') + .where('vote.userId = :meId', { meId: user.id }); + + query + .andWhere(`poll.noteId NOT IN (${ votedQuery.getQuery() })`); + + query.setParameters(votedQuery.getParameters()); + //#endregion + + //#region mute + const mutingQuery = Mutings.createQueryBuilder('muting') + .select('muting.muteeId') + .where('muting.muterId = :muterId', { muterId: user.id }); + + query + .andWhere(`poll.userId NOT IN (${ mutingQuery.getQuery() })`); + + query.setParameters(mutingQuery.getParameters()); + //#endregion + + const polls = await query + .orderBy('poll.noteId', 'DESC') + .take(ps.limit) + .skip(ps.offset) + .getMany(); + + if (polls.length === 0) return []; + + const notes = await Notes.find({ + where: { + id: In(polls.map(poll => poll.noteId)), + }, + order: { + createdAt: 'DESC', + }, + }); - return await Notes.packMany(notes, user, { - detail: true, - }); -}); + return await Notes.packMany(notes, user, { + detail: true, + }); + }); + } +} 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 4824e6befd73..812feca72587 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -1,4 +1,5 @@ import { Not } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; import { publishNoteStream } from '@/services/stream.js'; import { createNotification } from '@/services/create-notification.js'; import { deliver } from '@/queue/index.js'; @@ -6,12 +7,11 @@ import { renderActivity } from '@/remote/activitypub/renderer/index.js'; import renderVote from '@/remote/activitypub/renderer/vote.js'; import { deliverQuestionUpdate } from '@/services/note/polls/update.js'; import { PollVotes, NoteWatchings, Users, Polls, Blockings } from '@/models/index.js'; -import { IRemoteUser } from '@/models/entities/user.js'; +import type { IRemoteUser } from '@/models/entities/user.js'; import { genId } from '@/misc/gen-id.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; -import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['notes'], @@ -72,105 +72,110 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const createdAt = new Date(); + const createdAt = new Date(); - // Get votee - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); + // Get votee + const note = await getNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); - if (!note.hasPoll) { - throw new ApiError(meta.errors.noPoll); - } + if (!note.hasPoll) { + throw new ApiError(meta.errors.noPoll); + } - // Check blocking - if (note.userId !== user.id) { - const block = await Blockings.findOneBy({ - blockerId: note.userId, - blockeeId: user.id, - }); - if (block) { - throw new ApiError(meta.errors.youHaveBeenBlocked); - } - } + // Check blocking + if (note.userId !== user.id) { + const block = await Blockings.findOneBy({ + blockerId: note.userId, + blockeeId: user.id, + }); + if (block) { + throw new ApiError(meta.errors.youHaveBeenBlocked); + } + } - const poll = await Polls.findOneByOrFail({ noteId: note.id }); + const poll = await Polls.findOneByOrFail({ noteId: note.id }); - if (poll.expiresAt && poll.expiresAt < createdAt) { - throw new ApiError(meta.errors.alreadyExpired); - } + if (poll.expiresAt && poll.expiresAt < createdAt) { + throw new ApiError(meta.errors.alreadyExpired); + } - if (poll.choices[ps.choice] == null) { - throw new ApiError(meta.errors.invalidChoice); - } + if (poll.choices[ps.choice] == null) { + throw new ApiError(meta.errors.invalidChoice); + } - // if already voted - const exist = await PollVotes.findBy({ - noteId: note.id, - userId: user.id, - }); + // if already voted + const exist = await PollVotes.findBy({ + noteId: note.id, + userId: user.id, + }); - if (exist.length) { - if (poll.multiple) { - if (exist.some(x => x.choice === ps.choice)) { - throw new ApiError(meta.errors.alreadyVoted); + if (exist.length) { + if (poll.multiple) { + if (exist.some(x => x.choice === ps.choice)) { + throw new ApiError(meta.errors.alreadyVoted); + } + } else { + throw new ApiError(meta.errors.alreadyVoted); + } } - } else { - throw new ApiError(meta.errors.alreadyVoted); - } - } - // Create vote - const vote = await PollVotes.insert({ - id: genId(), - createdAt, - noteId: note.id, - userId: user.id, - choice: ps.choice, - }).then(x => PollVotes.findOneByOrFail(x.identifiers[0])); - - // Increment votes count - const index = ps.choice + 1; // In SQL, array index is 1 based - await Polls.query(`UPDATE poll SET votes[${index}] = votes[${index}] + 1 WHERE "noteId" = '${poll.noteId}'`); - - publishNoteStream(note.id, 'pollVoted', { - choice: ps.choice, - userId: user.id, - }); - - // Notify - createNotification(note.userId, 'pollVote', { - notifierId: user.id, - noteId: note.id, - choice: ps.choice, - }); - - // Fetch watchers - NoteWatchings.findBy({ - noteId: note.id, - userId: Not(user.id), - }).then(watchers => { - for (const watcher of watchers) { - createNotification(watcher.userId, 'pollVote', { + // Create vote + const vote = await PollVotes.insert({ + id: genId(), + createdAt, + noteId: note.id, + userId: user.id, + choice: ps.choice, + }).then(x => PollVotes.findOneByOrFail(x.identifiers[0])); + + // Increment votes count + const index = ps.choice + 1; // In SQL, array index is 1 based + await Polls.query(`UPDATE poll SET votes[${index}] = votes[${index}] + 1 WHERE "noteId" = '${poll.noteId}'`); + + publishNoteStream(note.id, 'pollVoted', { + choice: ps.choice, + userId: user.id, + }); + + // Notify + createNotification(note.userId, 'pollVote', { notifierId: user.id, noteId: note.id, choice: ps.choice, }); - } - }); - // リモート投票の場合リプライ送信 - if (note.userHost != null) { - const pollOwner = await Users.findOneByOrFail({ id: note.userId }) as IRemoteUser; + // Fetch watchers + NoteWatchings.findBy({ + noteId: note.id, + userId: Not(user.id), + }).then(watchers => { + for (const watcher of watchers) { + createNotification(watcher.userId, 'pollVote', { + notifierId: user.id, + noteId: note.id, + choice: ps.choice, + }); + } + }); - deliver(user, renderActivity(await renderVote(user, vote, note, poll, pollOwner)), pollOwner.inbox); - } + // リモート投票の場合リプライ送信 + if (note.userHost != null) { + const pollOwner = await Users.findOneByOrFail({ id: note.userId }) as IRemoteUser; - // リモートフォロワーにUpdate配信 - deliverQuestionUpdate(note.id); -}); + deliver(user, renderActivity(await renderVote(user, vote, note, poll, pollOwner)), pollOwner.inbox); + } + + // リモートフォロワーにUpdate配信 + deliverQuestionUpdate(note.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts index 3256aa750698..6e1aa9b056f7 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts @@ -1,9 +1,10 @@ -import { DeepPartial, FindOptionsWhere } from 'typeorm'; -import { NoteReactions } from '@/models/index.js'; -import { NoteReaction } from '@/models/entities/note-reaction.js'; +import { DeepPartial } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; +import { NoteReactions } from '@/models/index.js'; +import type { NoteReaction } from '@/models/entities/note-reaction.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; +import type { FindOptionsWhere } from 'typeorm'; export const meta = { tags: ['notes', 'reactions'], @@ -49,31 +50,36 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = { - noteId: ps.noteId, - } as FindOptionsWhere; + const query = { + noteId: ps.noteId, + } as FindOptionsWhere; - if (ps.type) { - // ローカルリアクションはホスト名が . とされているが - // DB 上ではそうではないので、必要に応じて変換 - const suffix = '@.:'; - const type = ps.type.endsWith(suffix) ? ps.type.slice(0, ps.type.length - suffix.length) + ':' : ps.type; - query.reaction = type; - } + if (ps.type) { + // ローカルリアクションはホスト名が . とされているが + // DB 上ではそうではないので、必要に応じて変換 + const suffix = '@.:'; + const type = ps.type.endsWith(suffix) ? ps.type.slice(0, ps.type.length - suffix.length) + ':' : ps.type; + query.reaction = type; + } - const reactions = await NoteReactions.find({ - where: query, - take: ps.limit, - skip: ps.offset, - order: { - id: -1, - }, - relations: ['user', 'user.avatar', 'user.banner', 'note'], - }); + const reactions = await NoteReactions.find({ + where: query, + take: ps.limit, + skip: ps.offset, + order: { + id: -1, + }, + relations: ['user', 'user.avatar', 'user.banner', 'note'], + }); - return await Promise.all(reactions.map(reaction => NoteReactions.pack(reaction, user))); -}); + return await Promise.all(reactions.map(reaction => NoteReactions.pack(reaction, user))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts index 88f9faa31cfb..6b6c9d8e701f 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts @@ -1,5 +1,5 @@ -import createReaction from '@/services/note/reaction/create.js'; import { Inject, Injectable } from '@nestjs/common'; +import createReaction from '@/services/note/reaction/create.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; @@ -45,18 +45,23 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); - await createReaction(user, note, ps.reaction).catch(e => { - if (e.id === '51c42bb4-931a-456b-bff7-e5a8a70dd298') throw new ApiError(meta.errors.alreadyReacted); - if (e.id === 'e70412a4-7197-4726-8e74-f3e0deb92aa7') throw new ApiError(meta.errors.youHaveBeenBlocked); - throw e; - }); - return; -}); + const note = await getNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); + await createReaction(user, note, ps.reaction).catch(e => { + if (e.id === '51c42bb4-931a-456b-bff7-e5a8a70dd298') throw new ApiError(meta.errors.alreadyReacted); + if (e.id === 'e70412a4-7197-4726-8e74-f3e0deb92aa7') throw new ApiError(meta.errors.youHaveBeenBlocked); + throw e; + }); + return; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts index 607db7c23383..c7f2bad0d96a 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts @@ -1,6 +1,6 @@ import ms from 'ms'; -import deleteReaction from '@/services/note/reaction/delete.js'; import { Inject, Injectable } from '@nestjs/common'; +import deleteReaction from '@/services/note/reaction/delete.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; @@ -45,16 +45,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); - await deleteReaction(user, note).catch(e => { - if (e.id === '60527ec9-b4cb-4a88-a6bd-32d3ad26817d') throw new ApiError(meta.errors.notReacted); - throw e; - }); -}); + const note = await getNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); + await deleteReaction(user, note).catch(e => { + if (e.id === '60527ec9-b4cb-4a88-a6bd-32d3ad26817d') throw new ApiError(meta.errors.notReacted); + throw e; + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index 031f510b5741..91909ddc9aa2 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -1,5 +1,5 @@ -import { Notes } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../common/getters.js'; import { ApiError } from '../../error.js'; @@ -47,34 +47,39 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); + const note = await getNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); - const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .andWhere('note.renoteId = :renoteId', { renoteId: note.id }) - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('user.avatar', 'avatar') - .leftJoinAndSelect('user.banner', 'banner') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') - .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') - .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); + const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) + .andWhere('note.renoteId = :renoteId', { renoteId: note.id }) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('user.avatar', 'avatar') + .leftJoinAndSelect('user.banner', 'banner') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateVisibilityQuery(query, user); - if (user) generateMutedUserQuery(query, user); - if (user) generateBlockedUserQuery(query, user); + generateVisibilityQuery(query, user); + if (user) generateMutedUserQuery(query, user); + if (user) generateBlockedUserQuery(query, user); - const renotes = await query.take(ps.limit).getMany(); + const renotes = await query.take(ps.limit).getMany(); - return await Notes.packMany(renotes, user); -}); + return await Notes.packMany(renotes, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index 07f9c80c3afe..1d5065dde57f 100644 --- a/packages/backend/src/server/api/endpoints/notes/replies.ts +++ b/packages/backend/src/server/api/endpoints/notes/replies.ts @@ -1,5 +1,5 @@ -import { Notes } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; @@ -37,29 +37,34 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .andWhere('note.replyId = :replyId', { replyId: ps.noteId }) - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('user.avatar', 'avatar') - .leftJoinAndSelect('user.banner', 'banner') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') - .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') - .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); + const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) + .andWhere('note.replyId = :replyId', { replyId: ps.noteId }) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('user.avatar', 'avatar') + .leftJoinAndSelect('user.banner', 'banner') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateVisibilityQuery(query, user); - if (user) generateMutedUserQuery(query, user); - if (user) generateBlockedUserQuery(query, user); + generateVisibilityQuery(query, user); + if (user) generateMutedUserQuery(query, user); + if (user) generateBlockedUserQuery(query, user); - const timeline = await query.take(ps.limit).getMany(); + const timeline = await query.take(ps.limit).getMany(); - return await Notes.packMany(timeline, user); -}); + return await Notes.packMany(timeline, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts index d51d36cdf9e3..78657ca65545 100644 --- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts +++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts @@ -1,8 +1,8 @@ import { Brackets } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; import { Notes } from '@/models/index.js'; import { safeForSql } from '@/misc/safe-for-sql.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; @@ -70,78 +70,83 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('user.avatar', 'avatar') - .leftJoinAndSelect('user.banner', 'banner') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') - .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') - .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); + const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('user.avatar', 'avatar') + .leftJoinAndSelect('user.banner', 'banner') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateVisibilityQuery(query, me); - if (me) generateMutedUserQuery(query, me); - if (me) generateBlockedUserQuery(query, me); + generateVisibilityQuery(query, me); + if (me) generateMutedUserQuery(query, me); + if (me) generateBlockedUserQuery(query, me); - try { - if (ps.tag) { - if (!safeForSql(ps.tag)) throw 'Injection'; - query.andWhere(`'{"${normalizeForSearch(ps.tag)}"}' <@ note.tags`); - } else { - query.andWhere(new Brackets(qb => { - for (const tags of ps.query!) { - qb.orWhere(new Brackets(qb => { - for (const tag of tags) { - if (!safeForSql(tag)) throw 'Injection'; - qb.andWhere(`'{"${normalizeForSearch(tag)}"}' <@ note.tags`); + try { + if (ps.tag) { + if (!safeForSql(ps.tag)) throw 'Injection'; + query.andWhere(`'{"${normalizeForSearch(ps.tag)}"}' <@ note.tags`); + } else { + query.andWhere(new Brackets(qb => { + for (const tags of ps.query!) { + qb.orWhere(new Brackets(qb => { + for (const tag of tags) { + if (!safeForSql(tag)) throw 'Injection'; + qb.andWhere(`'{"${normalizeForSearch(tag)}"}' <@ note.tags`); + } + })); } })); } - })); - } - } catch (e) { - if (e === 'Injection') return []; - throw e; - } + } catch (e) { + if (e === 'Injection') return []; + throw e; + } - if (ps.reply != null) { - if (ps.reply) { - query.andWhere('note.replyId IS NOT NULL'); - } else { - query.andWhere('note.replyId IS NULL'); - } - } + if (ps.reply != null) { + if (ps.reply) { + query.andWhere('note.replyId IS NOT NULL'); + } else { + query.andWhere('note.replyId IS NULL'); + } + } - if (ps.renote != null) { - if (ps.renote) { - query.andWhere('note.renoteId IS NOT NULL'); - } else { - query.andWhere('note.renoteId IS NULL'); - } - } + if (ps.renote != null) { + if (ps.renote) { + query.andWhere('note.renoteId IS NOT NULL'); + } else { + query.andWhere('note.renoteId IS NULL'); + } + } - if (ps.withFiles) { - query.andWhere('note.fileIds != \'{}\''); - } + if (ps.withFiles) { + query.andWhere('note.fileIds != \'{}\''); + } - if (ps.poll != null) { - if (ps.poll) { - query.andWhere('note.hasPoll = TRUE'); - } else { - query.andWhere('note.hasPoll = FALSE'); - } - } + if (ps.poll != null) { + if (ps.poll) { + query.andWhere('note.hasPoll = TRUE'); + } else { + query.andWhere('note.hasPoll = FALSE'); + } + } - // Search notes - const notes = await query.take(ps.limit).getMany(); + // Search notes + const notes = await query.take(ps.limit).getMany(); - return await Notes.packMany(notes, me); -}); + return await Notes.packMany(notes, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index 226b5bce6d5e..5f53594c0f88 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -1,9 +1,9 @@ import { In } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; import { Notes } from '@/models/index.js'; import config from '@/config/index.js'; -import es from '../../../../db/elasticsearch.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import es from '../../../../db/elasticsearch.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; @@ -51,99 +51,104 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - if (es == null) { - const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId); - - if (ps.userId) { - query.andWhere('note.userId = :userId', { userId: ps.userId }); - } else if (ps.channelId) { - query.andWhere('note.channelId = :channelId', { channelId: ps.channelId }); - } - - query - .andWhere('note.text ILIKE :q', { q: `%${ps.query}%` }) - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('user.avatar', 'avatar') - .leftJoinAndSelect('user.banner', 'banner') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') - .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') - .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - - generateVisibilityQuery(query, me); - if (me) generateMutedUserQuery(query, me); - if (me) generateBlockedUserQuery(query, me); - - const notes = await query.take(ps.limit).getMany(); - - return await Notes.packMany(notes, me); - } else { - const userQuery = ps.userId != null ? [{ - term: { - userId: ps.userId, - }, - }] : []; - - const hostQuery = ps.userId == null ? - ps.host === null ? [{ - bool: { - must_not: { - exists: { - field: 'userHost', - }, + if (es == null) { + const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId); + + if (ps.userId) { + query.andWhere('note.userId = :userId', { userId: ps.userId }); + } else if (ps.channelId) { + query.andWhere('note.channelId = :channelId', { channelId: ps.channelId }); + } + + query + .andWhere('note.text ILIKE :q', { q: `%${ps.query}%` }) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('user.avatar', 'avatar') + .leftJoinAndSelect('user.banner', 'banner') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); + + generateVisibilityQuery(query, me); + if (me) generateMutedUserQuery(query, me); + if (me) generateBlockedUserQuery(query, me); + + const notes = await query.take(ps.limit).getMany(); + + return await Notes.packMany(notes, me); + } else { + const userQuery = ps.userId != null ? [{ + term: { + userId: ps.userId, }, - }, - }] : ps.host !== undefined ? [{ - term: { - userHost: ps.host, - }, - }] : [] - : []; - - const result = await es.search({ - index: config.elasticsearch.index || 'misskey_note', - body: { - size: ps.limit, - from: ps.offset, - query: { - bool: { - must: [{ - simple_query_string: { - fields: ['text'], - query: ps.query.toLowerCase(), - default_operator: 'and', + }] : []; + + const hostQuery = ps.userId == null ? + ps.host === null ? [{ + bool: { + must_not: { + exists: { + field: 'userHost', + }, }, - }, ...hostQuery, ...userQuery], + }, + }] : ps.host !== undefined ? [{ + term: { + userHost: ps.host, + }, + }] : [] + : []; + + const result = await es.search({ + index: config.elasticsearch.index || 'misskey_note', + body: { + size: ps.limit, + from: ps.offset, + query: { + bool: { + must: [{ + simple_query_string: { + fields: ['text'], + query: ps.query.toLowerCase(), + default_operator: 'and', + }, + }, ...hostQuery, ...userQuery], + }, + }, + sort: [{ + _doc: 'desc', + }], }, - }, - sort: [{ - _doc: 'desc', - }], - }, - }); + }); - const hits = result.body.hits.hits.map((hit: any) => hit._id); + const hits = result.body.hits.hits.map((hit: any) => hit._id); - if (hits.length === 0) return []; + if (hits.length === 0) return []; - // Fetch found notes - const notes = await Notes.find({ - where: { - id: In(hits), - }, - order: { - id: -1, - }, - }); + // Fetch found notes + const notes = await Notes.find({ + where: { + id: In(hits), + }, + order: { + id: -1, + }, + }); - return await Notes.packMany(notes, me); + return await Notes.packMany(notes, me); + } + }); } -}); +} diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts index 114ef929ff78..e51979ea5987 100644 --- a/packages/backend/src/server/api/endpoints/notes/show.ts +++ b/packages/backend/src/server/api/endpoints/notes/show.ts @@ -1,5 +1,5 @@ -import { Notes } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../common/getters.js'; import { ApiError } from '../../error.js'; @@ -36,16 +36,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); - - return await Notes.pack(note, user, { - detail: true, - }); -}); + const note = await getNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); + + return await Notes.pack(note, user, { + detail: true, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/state.ts b/packages/backend/src/server/api/endpoints/notes/state.ts index c08fbb0713b9..7a38e26ed546 100644 --- a/packages/backend/src/server/api/endpoints/notes/state.ts +++ b/packages/backend/src/server/api/endpoints/notes/state.ts @@ -1,5 +1,5 @@ -import { NoteFavorites, Notes, NoteThreadMutings, NoteWatchings } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { NoteFavorites, Notes, NoteThreadMutings, NoteWatchings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -39,39 +39,44 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const note = await Notes.findOneByOrFail({ id: ps.noteId }); + const note = await Notes.findOneByOrFail({ id: ps.noteId }); - const [favorite, watching, threadMuting] = await Promise.all([ - NoteFavorites.count({ - where: { - userId: user.id, - noteId: note.id, - }, - take: 1, - }), - NoteWatchings.count({ - where: { - userId: user.id, - noteId: note.id, - }, - take: 1, - }), - NoteThreadMutings.count({ - where: { - userId: user.id, - threadId: note.threadId || note.id, - }, - take: 1, - }), - ]); + const [favorite, watching, threadMuting] = await Promise.all([ + NoteFavorites.count({ + where: { + userId: user.id, + noteId: note.id, + }, + take: 1, + }), + NoteWatchings.count({ + where: { + userId: user.id, + noteId: note.id, + }, + take: 1, + }), + NoteThreadMutings.count({ + where: { + userId: user.id, + threadId: note.threadId || note.id, + }, + take: 1, + }), + ]); - return { - isFavorited: favorite !== 0, - isWatching: watching !== 0, - isMutedThread: threadMuting !== 0, - }; -}); + return { + isFavorited: favorite !== 0, + isWatching: watching !== 0, + isMutedThread: threadMuting !== 0, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts index 1721ae3a4b86..8add08da618f 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts @@ -1,7 +1,7 @@ +import { Inject, Injectable } from '@nestjs/common'; import { Notes, NoteThreadMutings } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import readNote from '@/services/note/read.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; @@ -34,29 +34,34 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); - - const mutedNotes = await Notes.find({ - where: [{ - id: note.threadId || note.id, - }, { - threadId: note.threadId || note.id, - }], - }); - - await readNote(user.id, mutedNotes); - - await NoteThreadMutings.insert({ - id: genId(), - createdAt: new Date(), - threadId: note.threadId || note.id, - userId: user.id, - }); -}); + const note = await getNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); + + const mutedNotes = await Notes.find({ + where: [{ + id: note.threadId || note.id, + }, { + threadId: note.threadId || note.id, + }], + }); + + await readNote(user.id, mutedNotes); + + await NoteThreadMutings.insert({ + id: genId(), + createdAt: new Date(), + threadId: note.threadId || note.id, + userId: user.id, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts index fed00a8169eb..3f7dbc3fa793 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts @@ -1,5 +1,5 @@ -import { NoteThreadMutings } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { NoteThreadMutings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; @@ -32,17 +32,22 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); - - await NoteThreadMutings.delete({ - threadId: note.threadId || note.id, - userId: user.id, - }); -}); + const note = await getNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); + + await NoteThreadMutings.delete({ + threadId: note.threadId || note.id, + userId: user.id, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index d1dd09366f8b..e42b97fca7af 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -1,7 +1,7 @@ import { Brackets } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; import { Notes, Followings } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; @@ -51,88 +51,93 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const hasFollowing = (await Followings.count({ - where: { - followerId: user.id, - }, - take: 1, - })) !== 0; - - //#region Construct query - const followingQuery = Followings.createQueryBuilder('following') - .select('following.followeeId') - .where('following.followerId = :followerId', { followerId: user.id }); - - const query = makePaginationQuery(Notes.createQueryBuilder('note'), - ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere(new Brackets(qb => { qb - .where('note.userId = :meId', { meId: user.id }); - if (hasFollowing) qb.orWhere(`note.userId IN (${ followingQuery.getQuery() })`); - })) - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('user.avatar', 'avatar') - .leftJoinAndSelect('user.banner', 'banner') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') - .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') - .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') - .setParameters(followingQuery.getParameters()); - - generateChannelQuery(query, user); - generateRepliesQuery(query, user); - generateVisibilityQuery(query, user); - generateMutedUserQuery(query, user); - generateMutedNoteQuery(query, user); - generateBlockedUserQuery(query, user); - - if (ps.includeMyRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.userId != :meId', { meId: user.id }); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } + const hasFollowing = (await Followings.count({ + where: { + followerId: user.id, + }, + take: 1, + })) !== 0; - if (ps.includeRenotedMyNotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteUserId != :meId', { meId: user.id }); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } + //#region Construct query + const followingQuery = Followings.createQueryBuilder('following') + .select('following.followeeId') + .where('following.followerId = :followerId', { followerId: user.id }); - if (ps.includeLocalRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteUserHost IS NOT NULL'); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } + const query = makePaginationQuery(Notes.createQueryBuilder('note'), + ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + .andWhere(new Brackets(qb => { qb + .where('note.userId = :meId', { meId: user.id }); + if (hasFollowing) qb.orWhere(`note.userId IN (${ followingQuery.getQuery() })`); + })) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('user.avatar', 'avatar') + .leftJoinAndSelect('user.banner', 'banner') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') + .setParameters(followingQuery.getParameters()); - if (ps.withFiles) { - query.andWhere('note.fileIds != \'{}\''); - } - //#endregion + generateChannelQuery(query, user); + generateRepliesQuery(query, user); + generateVisibilityQuery(query, user); + generateMutedUserQuery(query, user); + generateMutedNoteQuery(query, user); + generateBlockedUserQuery(query, user); + + if (ps.includeMyRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.userId != :meId', { meId: user.id }); + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); + })); + } + + if (ps.includeRenotedMyNotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteUserId != :meId', { meId: user.id }); + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); + })); + } - const timeline = await query.take(ps.limit).getMany(); + if (ps.includeLocalRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteUserHost IS NOT NULL'); + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); + })); + } - process.nextTick(() => { - activeUsersChart.read(user); - }); + if (ps.withFiles) { + query.andWhere('note.fileIds != \'{}\''); + } + //#endregion - return await Notes.packMany(timeline, user); -}); + const timeline = await query.take(ps.limit).getMany(); + + process.nextTick(() => { + activeUsersChart.read(user); + }); + + return await Notes.packMany(timeline, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts index d980fae068b6..54b0d6210833 100644 --- a/packages/backend/src/server/api/endpoints/notes/translate.ts +++ b/packages/backend/src/server/api/endpoints/notes/translate.ts @@ -1,13 +1,13 @@ import { URLSearchParams } from 'node:url'; import fetch from 'node-fetch'; +import { Inject, Injectable } from '@nestjs/common'; import config from '@/config/index.js'; import { getAgentByUrl } from '@/misc/fetch.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { Notes } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { getNote } from '../../common/getters.js'; -import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['notes'], @@ -41,61 +41,66 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); - - if (!(await Notes.isVisibleForMe(note, user ? user.id : null))) { - return 204; // TODO: 良い感じのエラー返す - } - - if (note.text == null) { - return 204; - } - - const instance = await fetchMeta(); - - if (instance.deeplAuthKey == null) { - return 204; // TODO: 良い感じのエラー返す - } - - let targetLang = ps.targetLang; - if (targetLang.includes('-')) targetLang = targetLang.split('-')[0]; - - const params = new URLSearchParams(); - params.append('auth_key', instance.deeplAuthKey); - params.append('text', note.text); - params.append('target_lang', targetLang); - - const endpoint = instance.deeplIsPro ? 'https://api.deepl.com/v2/translate' : 'https://api-free.deepl.com/v2/translate'; - - const res = await fetch(endpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'User-Agent': config.userAgent, - Accept: 'application/json, */*', - }, - body: params, - // TODO - //timeout: 10000, - agent: getAgentByUrl, - }); - - const json = (await res.json()) as { + const note = await getNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); + + if (!(await Notes.isVisibleForMe(note, user ? user.id : null))) { + return 204; // TODO: 良い感じのエラー返す + } + + if (note.text == null) { + return 204; + } + + const instance = await fetchMeta(); + + if (instance.deeplAuthKey == null) { + return 204; // TODO: 良い感じのエラー返す + } + + let targetLang = ps.targetLang; + if (targetLang.includes('-')) targetLang = targetLang.split('-')[0]; + + const params = new URLSearchParams(); + params.append('auth_key', instance.deeplAuthKey); + params.append('text', note.text); + params.append('target_lang', targetLang); + + const endpoint = instance.deeplIsPro ? 'https://api.deepl.com/v2/translate' : 'https://api-free.deepl.com/v2/translate'; + + const res = await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'User-Agent': config.userAgent, + Accept: 'application/json, */*', + }, + body: params, + // TODO + //timeout: 10000, + agent: getAgentByUrl, + }); + + const json = (await res.json()) as { translations: { detected_source_language: string; text: string; }[]; }; - return { - sourceLang: json.translations[0].detected_source_language, - text: json.translations[0].text, - }; -}); + return { + sourceLang: json.translations[0].detected_source_language, + text: json.translations[0].text, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/unrenote.ts b/packages/backend/src/server/api/endpoints/notes/unrenote.ts index 0b20ce3a658f..3c67eb618816 100644 --- a/packages/backend/src/server/api/endpoints/notes/unrenote.ts +++ b/packages/backend/src/server/api/endpoints/notes/unrenote.ts @@ -1,7 +1,7 @@ import ms from 'ms'; +import { Inject, Injectable } from '@nestjs/common'; import deleteNote from '@/services/note/delete.js'; import { Notes, Users } from '@/models/index.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../common/getters.js'; import { ApiError } from '../../error.js'; @@ -40,21 +40,26 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); - - const renotes = await Notes.findBy({ - userId: user.id, - renoteId: note.id, - }); - - for (const note of renotes) { - deleteNote(await Users.findOneByOrFail({ id: user.id }), note); + const note = await getNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); + + const renotes = await Notes.findBy({ + userId: user.id, + renoteId: note.id, + }); + + for (const note of renotes) { + deleteNote(await Users.findOneByOrFail({ id: user.id }), note); + } + }); } -}); +} diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 99033ac88c2f..0a89c3b97ba0 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -1,7 +1,7 @@ import { Brackets } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; import { UserLists, UserListJoinings, Notes } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -56,75 +56,80 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const list = await UserLists.findOneBy({ - id: ps.listId, - userId: user.id, - }); - - if (list == null) { - throw new ApiError(meta.errors.noSuchList); - } - - //#region Construct query - const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .innerJoin(UserListJoinings.metadata.targetName, 'userListJoining', 'userListJoining.userId = note.userId') - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('user.avatar', 'avatar') - .leftJoinAndSelect('user.banner', 'banner') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') - .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') - .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') - .andWhere('userListJoining.userListId = :userListId', { userListId: list.id }); - - generateVisibilityQuery(query, user); - - if (ps.includeMyRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.userId != :meId', { meId: user.id }); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } - - if (ps.includeRenotedMyNotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteUserId != :meId', { meId: user.id }); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); + const list = await UserLists.findOneBy({ + id: ps.listId, + userId: user.id, + }); + + if (list == null) { + throw new ApiError(meta.errors.noSuchList); + } + + //#region Construct query + const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) + .innerJoin(UserListJoinings.metadata.targetName, 'userListJoining', 'userListJoining.userId = note.userId') + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('user.avatar', 'avatar') + .leftJoinAndSelect('user.banner', 'banner') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') + .andWhere('userListJoining.userListId = :userListId', { userListId: list.id }); + + generateVisibilityQuery(query, user); + + if (ps.includeMyRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.userId != :meId', { meId: user.id }); + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); + })); + } + + if (ps.includeRenotedMyNotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteUserId != :meId', { meId: user.id }); + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); + })); + } + + if (ps.includeLocalRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteUserHost IS NOT NULL'); + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); + })); + } + + if (ps.withFiles) { + query.andWhere('note.fileIds != \'{}\''); + } + //#endregion + + const timeline = await query.take(ps.limit).getMany(); + + activeUsersChart.read(user); + + return await Notes.packMany(timeline, user); + }); } - - if (ps.includeLocalRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteUserHost IS NOT NULL'); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } - - if (ps.withFiles) { - query.andWhere('note.fileIds != \'{}\''); - } - //#endregion - - const timeline = await query.take(ps.limit).getMany(); - - activeUsersChart.read(user); - - return await Notes.packMany(timeline, user); -}); +} diff --git a/packages/backend/src/server/api/endpoints/notes/watching/create.ts b/packages/backend/src/server/api/endpoints/notes/watching/create.ts index f33961c9fee7..9e1b96fea547 100644 --- a/packages/backend/src/server/api/endpoints/notes/watching/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/watching/create.ts @@ -1,5 +1,5 @@ -import watch from '@/services/note/watch.js'; import { Inject, Injectable } from '@nestjs/common'; +import watch from '@/services/note/watch.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; @@ -32,14 +32,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); + const note = await getNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); - await watch(user.id, note); -}); + await watch(user.id, note); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/watching/delete.ts b/packages/backend/src/server/api/endpoints/notes/watching/delete.ts index 9488016b3f86..6cb88c0bd232 100644 --- a/packages/backend/src/server/api/endpoints/notes/watching/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/watching/delete.ts @@ -1,5 +1,5 @@ -import unwatch from '@/services/note/unwatch.js'; import { Inject, Injectable } from '@nestjs/common'; +import unwatch from '@/services/note/unwatch.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; @@ -32,14 +32,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); + const note = await getNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); - await unwatch(user.id, note); -}); + await unwatch(user.id, note); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notifications/create.ts b/packages/backend/src/server/api/endpoints/notifications/create.ts index 2afbc2347fc1..25fd4349e626 100644 --- a/packages/backend/src/server/api/endpoints/notifications/create.ts +++ b/packages/backend/src/server/api/endpoints/notifications/create.ts @@ -1,5 +1,5 @@ -import { createNotification } from '@/services/create-notification.js'; import { Inject, Injectable } from '@nestjs/common'; +import { createNotification } from '@/services/create-notification.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -24,11 +24,22 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user, token) => { - createNotification(user.id, 'app', { - appAccessTokenId: token ? token.id : null, - customBody: ps.body, - customHeader: ps.header, - customIcon: ps.icon, - }); -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async (ps, user, token) => { + createNotification(user.id, 'app', { + appAccessTokenId: token ? token.id : null, + customBody: ps.body, + customHeader: ps.header, + customIcon: ps.icon, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts index 2e17bd6155af..c74dc696affc 100644 --- a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts @@ -1,7 +1,7 @@ +import { Inject, Injectable } from '@nestjs/common'; import { publishMainStream } from '@/services/stream.js'; import { pushNotification } from '@/services/push-notification.js'; import { Notifications } from '@/models/index.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -22,19 +22,24 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - // Update documents - await Notifications.update({ - notifieeId: user.id, - isRead: false, - }, { - isRead: true, - }); + // Update documents + await Notifications.update({ + notifieeId: user.id, + isRead: false, + }, { + isRead: true, + }); - // 全ての通知を読みましたよというイベントを発行 - publishMainStream(user.id, 'readAllNotifications'); - pushNotification(user.id, 'readAllNotifications', undefined); -}); + // 全ての通知を読みましたよというイベントを発行 + publishMainStream(user.id, 'readAllNotifications'); + pushNotification(user.id, 'readAllNotifications', undefined); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notifications/read.ts b/packages/backend/src/server/api/endpoints/notifications/read.ts index de48cee31cbd..569d277a7839 100644 --- a/packages/backend/src/server/api/endpoints/notifications/read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/read.ts @@ -47,10 +47,15 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - if ('notificationId' in ps) return readNotification(user.id, [ps.notificationId]); - return readNotification(user.id, ps.notificationIds); -}); + if ('notificationId' in ps) return readNotification(user.id, [ps.notificationId]); + return readNotification(user.id, ps.notificationIds); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/page-push.ts b/packages/backend/src/server/api/endpoints/page-push.ts index d7a9b347c3bd..7e51b3c1ce31 100644 --- a/packages/backend/src/server/api/endpoints/page-push.ts +++ b/packages/backend/src/server/api/endpoints/page-push.ts @@ -1,6 +1,6 @@ +import { Inject, Injectable } from '@nestjs/common'; import { publishMainStream } from '@/services/stream.js'; import { Users, Pages } from '@/models/index.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../error.js'; @@ -31,22 +31,27 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const page = await Pages.findOneBy({ id: ps.pageId }); - if (page == null) { - throw new ApiError(meta.errors.noSuchPage); - } + const page = await Pages.findOneBy({ id: ps.pageId }); + if (page == null) { + throw new ApiError(meta.errors.noSuchPage); + } - publishMainStream(page.userId, 'pageEvent', { - pageId: ps.pageId, - event: ps.event, - var: ps.var, - userId: user.id, - user: await Users.pack(user.id, { id: page.userId }, { - detail: true, - }), - }); -}); + publishMainStream(page.userId, 'pageEvent', { + pageId: ps.pageId, + event: ps.event, + var: ps.var, + userId: user.id, + user: await Users.pack(user.id, { id: page.userId }, { + detail: true, + }), + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/pages/create.ts b/packages/backend/src/server/api/endpoints/pages/create.ts index 969714511734..b1d15acee4fa 100644 --- a/packages/backend/src/server/api/endpoints/pages/create.ts +++ b/packages/backend/src/server/api/endpoints/pages/create.ts @@ -1,8 +1,8 @@ import ms from 'ms'; +import { Inject, Injectable } from '@nestjs/common'; import { Pages, DriveFiles } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { Page } from '@/models/entities/page.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; @@ -63,48 +63,53 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - let eyeCatchingImage = null; - if (ps.eyeCatchingImageId != null) { - eyeCatchingImage = await DriveFiles.findOneBy({ - id: ps.eyeCatchingImageId, - userId: user.id, - }); + let eyeCatchingImage = null; + if (ps.eyeCatchingImageId != null) { + eyeCatchingImage = await DriveFiles.findOneBy({ + id: ps.eyeCatchingImageId, + userId: user.id, + }); - if (eyeCatchingImage == null) { - throw new ApiError(meta.errors.noSuchFile); - } - } + if (eyeCatchingImage == null) { + throw new ApiError(meta.errors.noSuchFile); + } + } - await Pages.findBy({ - userId: user.id, - name: ps.name, - }).then(result => { - if (result.length > 0) { - throw new ApiError(meta.errors.nameAlreadyExists); - } - }); + await Pages.findBy({ + userId: user.id, + name: ps.name, + }).then(result => { + if (result.length > 0) { + throw new ApiError(meta.errors.nameAlreadyExists); + } + }); - const page = await Pages.insert(new Page({ - id: genId(), - createdAt: new Date(), - updatedAt: new Date(), - title: ps.title, - name: ps.name, - summary: ps.summary, - content: ps.content, - variables: ps.variables, - script: ps.script, - eyeCatchingImageId: eyeCatchingImage ? eyeCatchingImage.id : null, - userId: user.id, - visibility: 'public', - alignCenter: ps.alignCenter, - hideTitleWhenPinned: ps.hideTitleWhenPinned, - font: ps.font, - })).then(x => Pages.findOneByOrFail(x.identifiers[0])); + const page = await Pages.insert(new Page({ + id: genId(), + createdAt: new Date(), + updatedAt: new Date(), + title: ps.title, + name: ps.name, + summary: ps.summary, + content: ps.content, + variables: ps.variables, + script: ps.script, + eyeCatchingImageId: eyeCatchingImage ? eyeCatchingImage.id : null, + userId: user.id, + visibility: 'public', + alignCenter: ps.alignCenter, + hideTitleWhenPinned: ps.hideTitleWhenPinned, + font: ps.font, + })).then(x => Pages.findOneByOrFail(x.identifiers[0])); - return await Pages.pack(page); -}); + return await Pages.pack(page); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/pages/delete.ts b/packages/backend/src/server/api/endpoints/pages/delete.ts index e70585f87812..059b980553bd 100644 --- a/packages/backend/src/server/api/endpoints/pages/delete.ts +++ b/packages/backend/src/server/api/endpoints/pages/delete.ts @@ -1,5 +1,5 @@ -import { Pages } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Pages } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; @@ -37,17 +37,22 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const page = await Pages.findOneBy({ id: ps.pageId }); - if (page == null) { - throw new ApiError(meta.errors.noSuchPage); + const page = await Pages.findOneBy({ id: ps.pageId }); + if (page == null) { + throw new ApiError(meta.errors.noSuchPage); + } + if (page.userId !== user.id) { + throw new ApiError(meta.errors.accessDenied); + } + + await Pages.delete(page.id); + }); } - if (page.userId !== user.id) { - throw new ApiError(meta.errors.accessDenied); - } - - await Pages.delete(page.id); -}); +} diff --git a/packages/backend/src/server/api/endpoints/pages/featured.ts b/packages/backend/src/server/api/endpoints/pages/featured.ts index b6c632c3150a..209ca7d520b4 100644 --- a/packages/backend/src/server/api/endpoints/pages/featured.ts +++ b/packages/backend/src/server/api/endpoints/pages/featured.ts @@ -1,5 +1,5 @@ -import { Pages } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Pages } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -28,16 +28,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const query = Pages.createQueryBuilder('page') - .where('page.visibility = \'public\'') - .andWhere('page.likedCount > 0') - .orderBy('page.likedCount', 'DESC'); + const query = Pages.createQueryBuilder('page') + .where('page.visibility = \'public\'') + .andWhere('page.likedCount > 0') + .orderBy('page.likedCount', 'DESC'); - const pages = await query.take(10).getMany(); + const pages = await query.take(10).getMany(); - return await Pages.packMany(pages, me); -}); + return await Pages.packMany(pages, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/pages/like.ts b/packages/backend/src/server/api/endpoints/pages/like.ts index 763836f6edbf..3362c771843c 100644 --- a/packages/backend/src/server/api/endpoints/pages/like.ts +++ b/packages/backend/src/server/api/endpoints/pages/like.ts @@ -1,6 +1,6 @@ +import { Inject, Injectable } from '@nestjs/common'; import { Pages, PageLikes } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; @@ -44,36 +44,41 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const page = await Pages.findOneBy({ id: ps.pageId }); - if (page == null) { - throw new ApiError(meta.errors.noSuchPage); - } + const page = await Pages.findOneBy({ id: ps.pageId }); + if (page == null) { + throw new ApiError(meta.errors.noSuchPage); + } - if (page.userId === user.id) { - throw new ApiError(meta.errors.yourPage); - } + if (page.userId === user.id) { + throw new ApiError(meta.errors.yourPage); + } - // if already liked - const exist = await PageLikes.findOneBy({ - pageId: page.id, - userId: user.id, - }); + // if already liked + const exist = await PageLikes.findOneBy({ + pageId: page.id, + userId: user.id, + }); - if (exist != null) { - throw new ApiError(meta.errors.alreadyLiked); - } + if (exist != null) { + throw new ApiError(meta.errors.alreadyLiked); + } - // Create like - await PageLikes.insert({ - id: genId(), - createdAt: new Date(), - pageId: page.id, - userId: user.id, - }); + // Create like + await PageLikes.insert({ + id: genId(), + createdAt: new Date(), + pageId: page.id, + userId: user.id, + }); - Pages.increment({ id: page.id }, 'likedCount', 1); -}); + Pages.increment({ id: page.id }, 'likedCount', 1); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/pages/show.ts b/packages/backend/src/server/api/endpoints/pages/show.ts index a978f4ddcd00..0cf0e7e0ba4b 100644 --- a/packages/backend/src/server/api/endpoints/pages/show.ts +++ b/packages/backend/src/server/api/endpoints/pages/show.ts @@ -1,7 +1,7 @@ import { IsNull } from 'typeorm'; -import { Pages, Users } from '@/models/index.js'; -import { Page } from '@/models/entities/page.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Pages, Users } from '@/models/index.js'; +import type { Page } from '@/models/entities/page.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; @@ -48,30 +48,35 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - let page: Page | null = null; + let page: Page | null = null; - if (ps.pageId) { - page = await Pages.findOneBy({ id: ps.pageId }); - } else if (ps.name && ps.username) { - const author = await Users.findOneBy({ - host: IsNull(), - usernameLower: ps.username.toLowerCase(), - }); - if (author) { - page = await Pages.findOneBy({ - name: ps.name, - userId: author.id, - }); - } - } + if (ps.pageId) { + page = await Pages.findOneBy({ id: ps.pageId }); + } else if (ps.name && ps.username) { + const author = await Users.findOneBy({ + host: IsNull(), + usernameLower: ps.username.toLowerCase(), + }); + if (author) { + page = await Pages.findOneBy({ + name: ps.name, + userId: author.id, + }); + } + } - if (page == null) { - throw new ApiError(meta.errors.noSuchPage); - } + if (page == null) { + throw new ApiError(meta.errors.noSuchPage); + } - return await Pages.pack(page, user); -}); + return await Pages.pack(page, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/pages/unlike.ts b/packages/backend/src/server/api/endpoints/pages/unlike.ts index f7f6a3aa39a8..569efe38f275 100644 --- a/packages/backend/src/server/api/endpoints/pages/unlike.ts +++ b/packages/backend/src/server/api/endpoints/pages/unlike.ts @@ -1,5 +1,5 @@ -import { Pages, PageLikes } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Pages, PageLikes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; @@ -37,26 +37,31 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const page = await Pages.findOneBy({ id: ps.pageId }); - if (page == null) { - throw new ApiError(meta.errors.noSuchPage); - } + const page = await Pages.findOneBy({ id: ps.pageId }); + if (page == null) { + throw new ApiError(meta.errors.noSuchPage); + } - const exist = await PageLikes.findOneBy({ - pageId: page.id, - userId: user.id, - }); + const exist = await PageLikes.findOneBy({ + pageId: page.id, + userId: user.id, + }); - if (exist == null) { - throw new ApiError(meta.errors.notLiked); - } + if (exist == null) { + throw new ApiError(meta.errors.notLiked); + } - // Delete like - await PageLikes.delete(exist.id); + // Delete like + await PageLikes.delete(exist.id); - Pages.decrement({ id: page.id }, 'likedCount', 1); -}); + Pages.decrement({ id: page.id }, 'likedCount', 1); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/pages/update.ts b/packages/backend/src/server/api/endpoints/pages/update.ts index 36066d965a16..4391854d7e89 100644 --- a/packages/backend/src/server/api/endpoints/pages/update.ts +++ b/packages/backend/src/server/api/endpoints/pages/update.ts @@ -1,7 +1,7 @@ import ms from 'ms'; import { Not } from 'typeorm'; -import { Pages, DriveFiles } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Pages, DriveFiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; @@ -69,55 +69,60 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const page = await Pages.findOneBy({ id: ps.pageId }); - if (page == null) { - throw new ApiError(meta.errors.noSuchPage); - } - if (page.userId !== user.id) { - throw new ApiError(meta.errors.accessDenied); - } + const page = await Pages.findOneBy({ id: ps.pageId }); + if (page == null) { + throw new ApiError(meta.errors.noSuchPage); + } + if (page.userId !== user.id) { + throw new ApiError(meta.errors.accessDenied); + } - let eyeCatchingImage = null; - if (ps.eyeCatchingImageId != null) { - eyeCatchingImage = await DriveFiles.findOneBy({ - id: ps.eyeCatchingImageId, - userId: user.id, - }); + let eyeCatchingImage = null; + if (ps.eyeCatchingImageId != null) { + eyeCatchingImage = await DriveFiles.findOneBy({ + id: ps.eyeCatchingImageId, + userId: user.id, + }); - if (eyeCatchingImage == null) { - throw new ApiError(meta.errors.noSuchFile); - } - } + if (eyeCatchingImage == null) { + throw new ApiError(meta.errors.noSuchFile); + } + } - await Pages.findBy({ - id: Not(ps.pageId), - userId: user.id, - name: ps.name, - }).then(result => { - if (result.length > 0) { - throw new ApiError(meta.errors.nameAlreadyExists); - } - }); + await Pages.findBy({ + id: Not(ps.pageId), + userId: user.id, + name: ps.name, + }).then(result => { + if (result.length > 0) { + throw new ApiError(meta.errors.nameAlreadyExists); + } + }); - await Pages.update(page.id, { - updatedAt: new Date(), - title: ps.title, - name: ps.name === undefined ? page.name : ps.name, - summary: ps.name === undefined ? page.summary : ps.summary, - content: ps.content, - variables: ps.variables, - script: ps.script, - alignCenter: ps.alignCenter === undefined ? page.alignCenter : ps.alignCenter, - hideTitleWhenPinned: ps.hideTitleWhenPinned === undefined ? page.hideTitleWhenPinned : ps.hideTitleWhenPinned, - font: ps.font === undefined ? page.font : ps.font, - eyeCatchingImageId: ps.eyeCatchingImageId === null - ? null - : ps.eyeCatchingImageId === undefined - ? page.eyeCatchingImageId - : eyeCatchingImage!.id, - }); -}); + await Pages.update(page.id, { + updatedAt: new Date(), + title: ps.title, + name: ps.name === undefined ? page.name : ps.name, + summary: ps.name === undefined ? page.summary : ps.summary, + content: ps.content, + variables: ps.variables, + script: ps.script, + alignCenter: ps.alignCenter === undefined ? page.alignCenter : ps.alignCenter, + hideTitleWhenPinned: ps.hideTitleWhenPinned === undefined ? page.hideTitleWhenPinned : ps.hideTitleWhenPinned, + font: ps.font === undefined ? page.font : ps.font, + eyeCatchingImageId: ps.eyeCatchingImageId === null + ? null + : ps.eyeCatchingImageId === undefined + ? page.eyeCatchingImageId + : eyeCatchingImage!.id, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/ping.ts b/packages/backend/src/server/api/endpoints/ping.ts index a9b6bdbbaa91..1dfd8dce4640 100644 --- a/packages/backend/src/server/api/endpoints/ping.ts +++ b/packages/backend/src/server/api/endpoints/ping.ts @@ -25,8 +25,19 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async () => { - return { - pong: Date.now(), - }; -}); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async () => { + return { + pong: Date.now(), + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/pinned-users.ts b/packages/backend/src/server/api/endpoints/pinned-users.ts index a72db47201ae..0d9ddbfd30e8 100644 --- a/packages/backend/src/server/api/endpoints/pinned-users.ts +++ b/packages/backend/src/server/api/endpoints/pinned-users.ts @@ -1,9 +1,9 @@ import { IsNull } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; import { Users } from '@/models/index.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import * as Acct from '@/misc/acct.js'; -import { User } from '@/models/entities/user.js'; -import { Inject, Injectable } from '@nestjs/common'; +import type { User } from '@/models/entities/user.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -32,16 +32,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const meta = await fetchMeta(); + const meta = await fetchMeta(); - const users = await Promise.all(meta.pinnedUsers.map(acct => Acct.parse(acct)).map(acct => Users.findOneBy({ - usernameLower: acct.username.toLowerCase(), - host: acct.host ?? IsNull(), - }))); + const users = await Promise.all(meta.pinnedUsers.map(acct => Acct.parse(acct)).map(acct => Users.findOneBy({ + usernameLower: acct.username.toLowerCase(), + host: acct.host ?? IsNull(), + }))); - return await Users.packMany(users.filter(x => x !== undefined) as User[], me, { detail: true }); -}); + return await Users.packMany(users.filter(x => x !== undefined) as User[], me, { detail: true }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/promo/read.ts b/packages/backend/src/server/api/endpoints/promo/read.ts index 0a405ba02de0..020ddeba5338 100644 --- a/packages/backend/src/server/api/endpoints/promo/read.ts +++ b/packages/backend/src/server/api/endpoints/promo/read.ts @@ -1,6 +1,6 @@ +import { Inject, Injectable } from '@nestjs/common'; import { PromoReads } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { getNote } from '../../common/getters.js'; @@ -31,28 +31,33 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); - - const exist = await PromoReads.findOneBy({ - noteId: note.id, - userId: user.id, - }); - - if (exist != null) { - return; + const note = await getNote(ps.noteId).catch(e => { + if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw e; + }); + + const exist = await PromoReads.findOneBy({ + noteId: note.id, + userId: user.id, + }); + + if (exist != null) { + return; + } + + await PromoReads.insert({ + id: genId(), + createdAt: new Date(), + noteId: note.id, + userId: user.id, + }); + }); } - - await PromoReads.insert({ - id: genId(), - createdAt: new Date(), - noteId: note.id, - userId: user.id, - }); -}); +} diff --git a/packages/backend/src/server/api/endpoints/request-reset-password.ts b/packages/backend/src/server/api/endpoints/request-reset-password.ts index 9eb819fa1a98..37a04b6686d6 100644 --- a/packages/backend/src/server/api/endpoints/request-reset-password.ts +++ b/packages/backend/src/server/api/endpoints/request-reset-password.ts @@ -1,14 +1,14 @@ import rndstr from 'rndstr'; import ms from 'ms'; import { IsNull } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; import { publishMainStream } from '@/services/stream.js'; import config from '@/config/index.js'; import { Users, UserProfiles, PasswordResetRequests } from '@/models/index.js'; import { sendEmail } from '@/services/send-email.js'; import { genId } from '@/misc/gen-id.js'; -import { ApiError } from '../error.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { ApiError } from '../error.js'; export const meta = { tags: ['reset password'], @@ -40,44 +40,49 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const user = await Users.findOneBy({ - usernameLower: ps.username.toLowerCase(), - host: IsNull(), - }); - - // 合致するユーザーが登録されていなかったら無視 - if (user == null) { - return; - } - - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - // 合致するメアドが登録されていなかったら無視 - if (profile.email !== ps.email) { - return; - } - - // メアドが認証されていなかったら無視 - if (!profile.emailVerified) { - return; + const user = await Users.findOneBy({ + usernameLower: ps.username.toLowerCase(), + host: IsNull(), + }); + + // 合致するユーザーが登録されていなかったら無視 + if (user == null) { + return; + } + + const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + + // 合致するメアドが登録されていなかったら無視 + if (profile.email !== ps.email) { + return; + } + + // メアドが認証されていなかったら無視 + if (!profile.emailVerified) { + return; + } + + const token = rndstr('a-z0-9', 64); + + await PasswordResetRequests.insert({ + id: genId(), + createdAt: new Date(), + userId: profile.userId, + token, + }); + + const link = `${config.url}/reset-password/${token}`; + + sendEmail(ps.email, 'Password reset requested', + `To reset password, please click this link:
${link}`, + `To reset password, please click this link: ${link}`); + }); } - - const token = rndstr('a-z0-9', 64); - - await PasswordResetRequests.insert({ - id: genId(), - createdAt: new Date(), - userId: profile.userId, - token, - }); - - const link = `${config.url}/reset-password/${token}`; - - sendEmail(ps.email, 'Password reset requested', - `To reset password, please click this link:
${link}`, - `To reset password, please click this link: ${link}`); -}); +} diff --git a/packages/backend/src/server/api/endpoints/reset-db.ts b/packages/backend/src/server/api/endpoints/reset-db.ts index 89fb74017d37..d8cedc37bd8f 100644 --- a/packages/backend/src/server/api/endpoints/reset-db.ts +++ b/packages/backend/src/server/api/endpoints/reset-db.ts @@ -1,5 +1,5 @@ -import { resetDb } from '@/db/postgre.js'; import { Inject, Injectable } from '@nestjs/common'; +import { resetDb } from '@/db/postgre.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../error.js'; @@ -25,13 +25,18 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - if (process.env.NODE_ENV !== 'test') throw 'NODE_ENV is not a test'; + if (process.env.NODE_ENV !== 'test') throw 'NODE_ENV is not a test'; - await resetDb(); + await resetDb(); - await new Promise(resolve => setTimeout(resolve, 1000)); -}); + await new Promise(resolve => setTimeout(resolve, 1000)); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/reset-password.ts b/packages/backend/src/server/api/endpoints/reset-password.ts index da5ab929039e..ed5c43375f87 100644 --- a/packages/backend/src/server/api/endpoints/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/reset-password.ts @@ -1,7 +1,8 @@ import bcrypt from 'bcryptjs'; -import { publishMainStream } from '@/services/stream.js'; -import { Users, UserProfiles, PasswordResetRequests } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { publishMainStream } from '@/services/stream.js'; +import type { Users } from '@/models/index.js'; +import { UserProfiles, PasswordResetRequests } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../error.js'; @@ -30,26 +31,31 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const req = await PasswordResetRequests.findOneByOrFail({ - token: ps.token, - }); + const req = await PasswordResetRequests.findOneByOrFail({ + token: ps.token, + }); - // 発行してから30分以上経過していたら無効 - if (Date.now() - req.createdAt.getTime() > 1000 * 60 * 30) { - throw new Error(); // TODO - } + // 発行してから30分以上経過していたら無効 + if (Date.now() - req.createdAt.getTime() > 1000 * 60 * 30) { + throw new Error(); // TODO + } - // Generate hash of password - const salt = await bcrypt.genSalt(8); - const hash = await bcrypt.hash(ps.password, salt); + // Generate hash of password + const salt = await bcrypt.genSalt(8); + const hash = await bcrypt.hash(ps.password, salt); - await UserProfiles.update(req.userId, { - password: hash, - }); + await UserProfiles.update(req.userId, { + password: hash, + }); - PasswordResetRequests.delete(req.id); -}); + PasswordResetRequests.delete(req.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/server-info.ts b/packages/backend/src/server/api/endpoints/server-info.ts index 2b2a1c2650e0..114f3ac7642e 100644 --- a/packages/backend/src/server/api/endpoints/server-info.ts +++ b/packages/backend/src/server/api/endpoints/server-info.ts @@ -16,22 +16,33 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async () => { - const memStats = await si.mem(); - const fsStats = await si.fsSize(); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, - return { - machine: os.hostname(), - cpu: { - model: os.cpus()[0].model, - cores: os.cpus().length, - }, - mem: { - total: memStats.total, - }, - fs: { - total: fsStats[0].size, - used: fsStats[0].used, - }, - }; -}); + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async () => { + const memStats = await si.mem(); + const fsStats = await si.fsSize(); + + return { + machine: os.hostname(), + cpu: { + model: os.cpus()[0].model, + cores: os.cpus().length, + }, + mem: { + total: memStats.total, + }, + fs: { + total: fsStats[0].size, + used: fsStats[0].used, + }, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/stats.ts b/packages/backend/src/server/api/endpoints/stats.ts index 4a35631d8123..82bf1237d114 100644 --- a/packages/backend/src/server/api/endpoints/stats.ts +++ b/packages/backend/src/server/api/endpoints/stats.ts @@ -1,5 +1,5 @@ -import { Instances, NoteReactions, Notes, Users } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Instances, NoteReactions, Notes, Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { } from '@/services/chart/index.js'; import { IsNull } from 'typeorm'; @@ -52,34 +52,45 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async () => { - const [ - notesCount, - originalNotesCount, - usersCount, - originalUsersCount, - reactionsCount, - //originalReactionsCount, - instances, - ] = await Promise.all([ - Notes.count({ cache: 3600000 }), // 1 hour - Notes.count({ where: { userHost: IsNull() }, cache: 3600000 }), - Users.count({ cache: 3600000 }), - Users.count({ where: { host: IsNull() }, cache: 3600000 }), - NoteReactions.count({ cache: 3600000 }), // 1 hour - //NoteReactions.count({ where: { userHost: IsNull() }, cache: 3600000 }), - Instances.count({ cache: 3600000 }), - ]); +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + super(meta, paramDef, async () => { + const [ + notesCount, + originalNotesCount, + usersCount, + originalUsersCount, + reactionsCount, + //originalReactionsCount, + instances, + ] = await Promise.all([ + Notes.count({ cache: 3600000 }), // 1 hour + Notes.count({ where: { userHost: IsNull() }, cache: 3600000 }), + Users.count({ cache: 3600000 }), + Users.count({ where: { host: IsNull() }, cache: 3600000 }), + NoteReactions.count({ cache: 3600000 }), // 1 hour + //NoteReactions.count({ where: { userHost: IsNull() }, cache: 3600000 }), + Instances.count({ cache: 3600000 }), + ]); - return { - notesCount, - originalNotesCount, - usersCount, - originalUsersCount, - reactionsCount, - //originalReactionsCount, - instances, - driveUsageLocal: 0, - driveUsageRemote: 0, - }; -}); + return { + notesCount, + originalNotesCount, + usersCount, + originalUsersCount, + reactionsCount, + //originalReactionsCount, + instances, + driveUsageLocal: 0, + driveUsageRemote: 0, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/sw/register.ts b/packages/backend/src/server/api/endpoints/sw/register.ts index 8c030d90e5ee..db52b7601f6f 100644 --- a/packages/backend/src/server/api/endpoints/sw/register.ts +++ b/packages/backend/src/server/api/endpoints/sw/register.ts @@ -1,7 +1,7 @@ +import { Inject, Injectable } from '@nestjs/common'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { genId } from '@/misc/gen-id.js'; import { SwSubscriptions } from '@/models/index.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -42,38 +42,43 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - // if already subscribed - const exist = await SwSubscriptions.findOneBy({ - userId: user.id, - endpoint: ps.endpoint, - auth: ps.auth, - publickey: ps.publickey, - }); + // if already subscribed + const exist = await SwSubscriptions.findOneBy({ + userId: user.id, + endpoint: ps.endpoint, + auth: ps.auth, + publickey: ps.publickey, + }); - const instance = await fetchMeta(true); + const instance = await fetchMeta(true); - if (exist != null) { - return { - state: 'already-subscribed' as const, - key: instance.swPublicKey, - }; - } + if (exist != null) { + return { + state: 'already-subscribed' as const, + key: instance.swPublicKey, + }; + } - await SwSubscriptions.insert({ - id: genId(), - createdAt: new Date(), - userId: user.id, - endpoint: ps.endpoint, - auth: ps.auth, - publickey: ps.publickey, - }); + await SwSubscriptions.insert({ + id: genId(), + createdAt: new Date(), + userId: user.id, + endpoint: ps.endpoint, + auth: ps.auth, + publickey: ps.publickey, + }); - return { - state: 'subscribed' as const, - key: instance.swPublicKey, - }; -}); + return { + state: 'subscribed' as const, + key: instance.swPublicKey, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/sw/unregister.ts b/packages/backend/src/server/api/endpoints/sw/unregister.ts index 5666b13ef92a..dbf5e23e049b 100644 --- a/packages/backend/src/server/api/endpoints/sw/unregister.ts +++ b/packages/backend/src/server/api/endpoints/sw/unregister.ts @@ -1,5 +1,5 @@ -import { SwSubscriptions } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { SwSubscriptions } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -22,12 +22,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - await SwSubscriptions.delete({ - userId: user.id, - endpoint: ps.endpoint, - }); -}); + await SwSubscriptions.delete({ + userId: user.id, + endpoint: ps.endpoint, + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/test.ts b/packages/backend/src/server/api/endpoints/test.ts index d8a240859931..bd47e647dcb4 100644 --- a/packages/backend/src/server/api/endpoints/test.ts +++ b/packages/backend/src/server/api/endpoints/test.ts @@ -25,9 +25,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - return ps; -}); + return ps; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/username/available.ts b/packages/backend/src/server/api/endpoints/username/available.ts index 0e9ebfe5fdbb..94fc1384a807 100644 --- a/packages/backend/src/server/api/endpoints/username/available.ts +++ b/packages/backend/src/server/api/endpoints/username/available.ts @@ -1,6 +1,6 @@ import { IsNull } from 'typeorm'; -import { Users, UsedUsernames } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Users, UsedUsernames } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -32,19 +32,24 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - // Get exist - const exist = await Users.countBy({ - host: IsNull(), - usernameLower: ps.username.toLowerCase(), - }); - - const exist2 = await UsedUsernames.countBy({ username: ps.username.toLowerCase() }); - - return { - available: exist === 0 && exist2 === 0, - }; -}); + // Get exist + const exist = await Users.countBy({ + host: IsNull(), + usernameLower: ps.username.toLowerCase(), + }); + + const exist2 = await UsedUsernames.countBy({ username: ps.username.toLowerCase() }); + + return { + available: exist === 0 && exist2 === 0, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users.ts b/packages/backend/src/server/api/endpoints/users.ts index e1c71a8214a2..48f826e7e6a6 100644 --- a/packages/backend/src/server/api/endpoints/users.ts +++ b/packages/backend/src/server/api/endpoints/users.ts @@ -1,5 +1,5 @@ -import { Users } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { generateMutedUserQueryForUsers } from '../common/generate-muted-user-query.js'; import { generateBlockQueryForUsers } from '../common/generate-block-query.js'; @@ -42,46 +42,51 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const query = Users.createQueryBuilder('user'); - query.where('user.isExplorable = TRUE'); + const query = Users.createQueryBuilder('user'); + query.where('user.isExplorable = TRUE'); - switch (ps.state) { - case 'admin': query.andWhere('user.isAdmin = TRUE'); break; - case 'moderator': query.andWhere('user.isModerator = TRUE'); break; - case 'adminOrModerator': query.andWhere('user.isAdmin = TRUE OR user.isModerator = TRUE'); break; - case 'alive': query.andWhere('user.updatedAt > :date', { date: new Date(Date.now() - 1000 * 60 * 60 * 24 * 5) }); break; - } + switch (ps.state) { + case 'admin': query.andWhere('user.isAdmin = TRUE'); break; + case 'moderator': query.andWhere('user.isModerator = TRUE'); break; + case 'adminOrModerator': query.andWhere('user.isAdmin = TRUE OR user.isModerator = TRUE'); break; + case 'alive': query.andWhere('user.updatedAt > :date', { date: new Date(Date.now() - 1000 * 60 * 60 * 24 * 5) }); break; + } - switch (ps.origin) { - case 'local': query.andWhere('user.host IS NULL'); break; - case 'remote': query.andWhere('user.host IS NOT NULL'); break; - } + switch (ps.origin) { + case 'local': query.andWhere('user.host IS NULL'); break; + case 'remote': query.andWhere('user.host IS NOT NULL'); break; + } - if (ps.hostname) { - query.andWhere('user.host = :hostname', { hostname: ps.hostname.toLowerCase() }); - } + if (ps.hostname) { + query.andWhere('user.host = :hostname', { hostname: ps.hostname.toLowerCase() }); + } - switch (ps.sort) { - case '+follower': query.orderBy('user.followersCount', 'DESC'); break; - case '-follower': query.orderBy('user.followersCount', 'ASC'); break; - case '+createdAt': query.orderBy('user.createdAt', 'DESC'); break; - case '-createdAt': query.orderBy('user.createdAt', 'ASC'); break; - case '+updatedAt': query.andWhere('user.updatedAt IS NOT NULL').orderBy('user.updatedAt', 'DESC'); break; - case '-updatedAt': query.andWhere('user.updatedAt IS NOT NULL').orderBy('user.updatedAt', 'ASC'); break; - default: query.orderBy('user.id', 'ASC'); break; - } + switch (ps.sort) { + case '+follower': query.orderBy('user.followersCount', 'DESC'); break; + case '-follower': query.orderBy('user.followersCount', 'ASC'); break; + case '+createdAt': query.orderBy('user.createdAt', 'DESC'); break; + case '-createdAt': query.orderBy('user.createdAt', 'ASC'); break; + case '+updatedAt': query.andWhere('user.updatedAt IS NOT NULL').orderBy('user.updatedAt', 'DESC'); break; + case '-updatedAt': query.andWhere('user.updatedAt IS NOT NULL').orderBy('user.updatedAt', 'ASC'); break; + default: query.orderBy('user.id', 'ASC'); break; + } - if (me) generateMutedUserQueryForUsers(query, me); - if (me) generateBlockQueryForUsers(query, me); + if (me) generateMutedUserQueryForUsers(query, me); + if (me) generateBlockQueryForUsers(query, me); - query.take(ps.limit); - query.skip(ps.offset); + query.take(ps.limit); + query.skip(ps.offset); - const users = await query.getMany(); + const users = await query.getMany(); - return await Users.packMany(users, me, { detail: true }); -}); + return await Users.packMany(users, me, { detail: true }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/clips.ts b/packages/backend/src/server/api/endpoints/users/clips.ts index 1b34ce8ceb21..b44bc8f7f29b 100644 --- a/packages/backend/src/server/api/endpoints/users/clips.ts +++ b/packages/backend/src/server/api/endpoints/users/clips.ts @@ -1,5 +1,5 @@ -import { Clips } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Clips } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -34,17 +34,22 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(Clips.createQueryBuilder('clip'), ps.sinceId, ps.untilId) - .andWhere('clip.userId = :userId', { userId: ps.userId }) - .andWhere('clip.isPublic = true'); + const query = makePaginationQuery(Clips.createQueryBuilder('clip'), ps.sinceId, ps.untilId) + .andWhere('clip.userId = :userId', { userId: ps.userId }) + .andWhere('clip.isPublic = true'); - const clips = await query - .take(ps.limit) - .getMany(); + const clips = await query + .take(ps.limit) + .getMany(); - return await Clips.packMany(clips); -}); + return await Clips.packMany(clips); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index a9191b4d07dc..c8ba6ae6e0b8 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -1,7 +1,7 @@ import { IsNull } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; import { Users, Followings, UserProfiles } from '@/models/index.js'; import { toPunyNullable } from '@/misc/convert-host.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -70,45 +70,50 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const user = await Users.findOneBy(ps.userId != null - ? { id: ps.userId } - : { usernameLower: ps.username!.toLowerCase(), host: toPunyNullable(ps.host) ?? IsNull() }); + const user = await Users.findOneBy(ps.userId != null + ? { id: ps.userId } + : { usernameLower: ps.username!.toLowerCase(), host: toPunyNullable(ps.host) ?? IsNull() }); - if (user == null) { - throw new ApiError(meta.errors.noSuchUser); - } + if (user == null) { + throw new ApiError(meta.errors.noSuchUser); + } - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - if (profile.ffVisibility === 'private') { - if (me == null || (me.id !== user.id)) { - throw new ApiError(meta.errors.forbidden); - } - } else if (profile.ffVisibility === 'followers') { - if (me == null) { - throw new ApiError(meta.errors.forbidden); - } else if (me.id !== user.id) { - const following = await Followings.findOneBy({ - followeeId: user.id, - followerId: me.id, - }); - if (following == null) { - throw new ApiError(meta.errors.forbidden); + if (profile.ffVisibility === 'private') { + if (me == null || (me.id !== user.id)) { + throw new ApiError(meta.errors.forbidden); + } + } else if (profile.ffVisibility === 'followers') { + if (me == null) { + throw new ApiError(meta.errors.forbidden); + } else if (me.id !== user.id) { + const following = await Followings.findOneBy({ + followeeId: user.id, + followerId: me.id, + }); + if (following == null) { + throw new ApiError(meta.errors.forbidden); + } + } } - } - } - const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) - .andWhere('following.followeeId = :userId', { userId: user.id }) - .innerJoinAndSelect('following.follower', 'follower'); + const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) + .andWhere('following.followeeId = :userId', { userId: user.id }) + .innerJoinAndSelect('following.follower', 'follower'); - const followings = await query - .take(ps.limit) - .getMany(); + const followings = await query + .take(ps.limit) + .getMany(); - return await Followings.packMany(followings, me, { populateFollower: true }); -}); + return await Followings.packMany(followings, me, { populateFollower: true }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index e5cea984341b..b8fd71fd1dcc 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -1,7 +1,7 @@ import { IsNull } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; import { Users, Followings, UserProfiles } from '@/models/index.js'; import { toPunyNullable } from '@/misc/convert-host.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -70,45 +70,50 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const user = await Users.findOneBy(ps.userId != null - ? { id: ps.userId } - : { usernameLower: ps.username!.toLowerCase(), host: toPunyNullable(ps.host) ?? IsNull() }); + const user = await Users.findOneBy(ps.userId != null + ? { id: ps.userId } + : { usernameLower: ps.username!.toLowerCase(), host: toPunyNullable(ps.host) ?? IsNull() }); - if (user == null) { - throw new ApiError(meta.errors.noSuchUser); - } + if (user == null) { + throw new ApiError(meta.errors.noSuchUser); + } - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - if (profile.ffVisibility === 'private') { - if (me == null || (me.id !== user.id)) { - throw new ApiError(meta.errors.forbidden); - } - } else if (profile.ffVisibility === 'followers') { - if (me == null) { - throw new ApiError(meta.errors.forbidden); - } else if (me.id !== user.id) { - const following = await Followings.findOneBy({ - followeeId: user.id, - followerId: me.id, - }); - if (following == null) { - throw new ApiError(meta.errors.forbidden); + if (profile.ffVisibility === 'private') { + if (me == null || (me.id !== user.id)) { + throw new ApiError(meta.errors.forbidden); + } + } else if (profile.ffVisibility === 'followers') { + if (me == null) { + throw new ApiError(meta.errors.forbidden); + } else if (me.id !== user.id) { + const following = await Followings.findOneBy({ + followeeId: user.id, + followerId: me.id, + }); + if (following == null) { + throw new ApiError(meta.errors.forbidden); + } + } } - } - } - const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) - .andWhere('following.followerId = :userId', { userId: user.id }) - .innerJoinAndSelect('following.followee', 'followee'); + const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) + .andWhere('following.followerId = :userId', { userId: user.id }) + .innerJoinAndSelect('following.followee', 'followee'); - const followings = await query - .take(ps.limit) - .getMany(); + const followings = await query + .take(ps.limit) + .getMany(); - return await Followings.packMany(followings, me, { populateFollowee: true }); -}); + return await Followings.packMany(followings, me, { populateFollowee: true }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts index da61ac211e98..053ac6928ca1 100644 --- a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts @@ -34,16 +34,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) - .andWhere(`post.userId = :userId`, { userId: ps.userId }); + const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) + .andWhere('post.userId = :userId', { userId: ps.userId }); - const posts = await query - .take(ps.limit) - .getMany(); + const posts = await query + .take(ps.limit) + .getMany(); - return await GalleryPosts.packMany(posts, user); -}); + return await GalleryPosts.packMany(posts, user); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts index 376427da7cb6..f1366037be62 100644 --- a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts +++ b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts @@ -1,7 +1,7 @@ import { Not, In, IsNull } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; import { maximum } from '@/prelude/array.js'; import { Notes, Users } from '@/models/index.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; @@ -55,67 +55,72 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - // Lookup user - const user = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; - }); - - // Fetch recent notes - const recentNotes = await Notes.find({ - where: { - userId: user.id, - replyId: Not(IsNull()), - }, - order: { - id: -1, - }, - take: 1000, - select: ['replyId'], - }); + // Lookup user + const user = await getUser(ps.userId).catch(e => { + if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw e; + }); + + // Fetch recent notes + const recentNotes = await Notes.find({ + where: { + userId: user.id, + replyId: Not(IsNull()), + }, + order: { + id: -1, + }, + take: 1000, + select: ['replyId'], + }); + + // 投稿が少なかったら中断 + if (recentNotes.length === 0) { + return []; + } + + // TODO ミュートを考慮 + const replyTargetNotes = await Notes.find({ + where: { + id: In(recentNotes.map(p => p.replyId)), + }, + select: ['userId'], + }); - // 投稿が少なかったら中断 - if (recentNotes.length === 0) { - return []; - } + const repliedUsers: any = {}; - // TODO ミュートを考慮 - const replyTargetNotes = await Notes.find({ - where: { - id: In(recentNotes.map(p => p.replyId)), - }, - select: ['userId'], - }); - - const repliedUsers: any = {}; - - // Extract replies from recent notes - for (const userId of replyTargetNotes.map(x => x.userId.toString())) { - if (repliedUsers[userId]) { - repliedUsers[userId]++; - } else { - repliedUsers[userId] = 1; - } - } + // Extract replies from recent notes + for (const userId of replyTargetNotes.map(x => x.userId.toString())) { + if (repliedUsers[userId]) { + repliedUsers[userId]++; + } else { + repliedUsers[userId] = 1; + } + } - // Calc peak - const peak = maximum(Object.values(repliedUsers)); + // Calc peak + const peak = maximum(Object.values(repliedUsers)); - // Sort replies by frequency - const repliedUsersSorted = Object.keys(repliedUsers).sort((a, b) => repliedUsers[b] - repliedUsers[a]); + // Sort replies by frequency + const repliedUsersSorted = Object.keys(repliedUsers).sort((a, b) => repliedUsers[b] - repliedUsers[a]); - // Extract top replied users - const topRepliedUsers = repliedUsersSorted.slice(0, ps.limit); + // Extract top replied users + const topRepliedUsers = repliedUsersSorted.slice(0, ps.limit); - // Make replies object (includes weights) - const repliesObj = await Promise.all(topRepliedUsers.map(async (user) => ({ - user: await Users.pack(user, me, { detail: true }), - weight: repliedUsers[user] / peak, - }))); + // Make replies object (includes weights) + const repliesObj = await Promise.all(topRepliedUsers.map(async (user) => ({ + user: await Users.pack(user, me, { detail: true }), + weight: repliedUsers[user] / peak, + }))); - return repliesObj; -}); + return repliesObj; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/groups/create.ts b/packages/backend/src/server/api/endpoints/users/groups/create.ts index 8845359ea8b8..4f7ce904ec33 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/create.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/create.ts @@ -1,8 +1,8 @@ +import { Inject, Injectable } from '@nestjs/common'; import { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; -import { UserGroup } from '@/models/entities/user-group.js'; -import { UserGroupJoining } from '@/models/entities/user-group-joining.js'; -import { Inject, Injectable } from '@nestjs/common'; +import type { UserGroup } from '@/models/entities/user-group.js'; +import type { UserGroupJoining } from '@/models/entities/user-group-joining.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -33,24 +33,29 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const userGroup = await UserGroups.insert({ - id: genId(), - createdAt: new Date(), - userId: user.id, - name: ps.name, - } as UserGroup).then(x => UserGroups.findOneByOrFail(x.identifiers[0])); - - // Push the owner - await UserGroupJoinings.insert({ - id: genId(), - createdAt: new Date(), - userId: user.id, - userGroupId: userGroup.id, - } as UserGroupJoining); - - return await UserGroups.pack(userGroup); -}); + const userGroup = await UserGroups.insert({ + id: genId(), + createdAt: new Date(), + userId: user.id, + name: ps.name, + } as UserGroup).then(x => UserGroups.findOneByOrFail(x.identifiers[0])); + + // Push the owner + await UserGroupJoinings.insert({ + id: genId(), + createdAt: new Date(), + userId: user.id, + userGroupId: userGroup.id, + } as UserGroupJoining); + + return await UserGroups.pack(userGroup); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/groups/delete.ts b/packages/backend/src/server/api/endpoints/users/groups/delete.ts index eff72d061a4c..5492946cb14c 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/delete.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/delete.ts @@ -1,5 +1,5 @@ -import { UserGroups } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { UserGroups } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; @@ -33,18 +33,23 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const userGroup = await UserGroups.findOneBy({ - id: ps.groupId, - userId: user.id, - }); + const userGroup = await UserGroups.findOneBy({ + id: ps.groupId, + userId: user.id, + }); - if (userGroup == null) { - throw new ApiError(meta.errors.noSuchGroup); - } + if (userGroup == null) { + throw new ApiError(meta.errors.noSuchGroup); + } - await UserGroups.delete(userGroup.id); -}); + await UserGroups.delete(userGroup.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts b/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts index 178b94531a67..e0250b92f327 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts @@ -1,9 +1,9 @@ +import { Inject, Injectable } from '@nestjs/common'; import { UserGroupJoinings, UserGroupInvitations } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; -import { UserGroupJoining } from '@/models/entities/user-group-joining.js'; -import { ApiError } from '../../../../error.js'; -import { Inject, Injectable } from '@nestjs/common'; +import type { UserGroupJoining } from '@/models/entities/user-group-joining.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { ApiError } from '../../../../error.js'; export const meta = { tags: ['groups', 'users'], @@ -35,30 +35,35 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - // Fetch the invitation - const invitation = await UserGroupInvitations.findOneBy({ - id: ps.invitationId, - }); + // Fetch the invitation + const invitation = await UserGroupInvitations.findOneBy({ + id: ps.invitationId, + }); - if (invitation == null) { - throw new ApiError(meta.errors.noSuchInvitation); - } + if (invitation == null) { + throw new ApiError(meta.errors.noSuchInvitation); + } - if (invitation.userId !== user.id) { - throw new ApiError(meta.errors.noSuchInvitation); - } + if (invitation.userId !== user.id) { + throw new ApiError(meta.errors.noSuchInvitation); + } - // Push the user - await UserGroupJoinings.insert({ - id: genId(), - createdAt: new Date(), - userId: user.id, - userGroupId: invitation.userGroupId, - } as UserGroupJoining); + // Push the user + await UserGroupJoinings.insert({ + id: genId(), + createdAt: new Date(), + userId: user.id, + userGroupId: invitation.userGroupId, + } as UserGroupJoining); - UserGroupInvitations.delete(invitation.id); -}); + UserGroupInvitations.delete(invitation.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts b/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts index 73a359e1f5fa..ffd11e9ece64 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts @@ -1,5 +1,5 @@ -import { UserGroupInvitations } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { UserGroupInvitations } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../../error.js'; @@ -33,22 +33,27 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - // Fetch the invitation - const invitation = await UserGroupInvitations.findOneBy({ - id: ps.invitationId, - }); + // Fetch the invitation + const invitation = await UserGroupInvitations.findOneBy({ + id: ps.invitationId, + }); - if (invitation == null) { - throw new ApiError(meta.errors.noSuchInvitation); - } + if (invitation == null) { + throw new ApiError(meta.errors.noSuchInvitation); + } - if (invitation.userId !== user.id) { - throw new ApiError(meta.errors.noSuchInvitation); - } + if (invitation.userId !== user.id) { + throw new ApiError(meta.errors.noSuchInvitation); + } - await UserGroupInvitations.delete(invitation.id); -}); + await UserGroupInvitations.delete(invitation.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/groups/invite.ts b/packages/backend/src/server/api/endpoints/users/groups/invite.ts index 78c922c6f215..bf8f10050e64 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invite.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invite.ts @@ -1,11 +1,11 @@ +import { Inject, Injectable } from '@nestjs/common'; import { UserGroups, UserGroupJoinings, UserGroupInvitations } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; -import { UserGroupInvitation } from '@/models/entities/user-group-invitation.js'; +import type { UserGroupInvitation } from '@/models/entities/user-group-invitation.js'; import { createNotification } from '@/services/create-notification.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { getUser } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; -import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['groups', 'users'], @@ -56,54 +56,59 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - // Fetch the group - const userGroup = await UserGroups.findOneBy({ - id: ps.groupId, - userId: me.id, - }); - - if (userGroup == null) { - throw new ApiError(meta.errors.noSuchGroup); + // Fetch the group + const userGroup = await UserGroups.findOneBy({ + id: ps.groupId, + userId: me.id, + }); + + if (userGroup == null) { + throw new ApiError(meta.errors.noSuchGroup); + } + + // Fetch the user + const user = await getUser(ps.userId).catch(e => { + if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw e; + }); + + const joining = await UserGroupJoinings.findOneBy({ + userGroupId: userGroup.id, + userId: user.id, + }); + + if (joining) { + throw new ApiError(meta.errors.alreadyAdded); + } + + const existInvitation = await UserGroupInvitations.findOneBy({ + userGroupId: userGroup.id, + userId: user.id, + }); + + if (existInvitation) { + throw new ApiError(meta.errors.alreadyInvited); + } + + const invitation = await UserGroupInvitations.insert({ + id: genId(), + createdAt: new Date(), + userId: user.id, + userGroupId: userGroup.id, + } as UserGroupInvitation).then(x => UserGroupInvitations.findOneByOrFail(x.identifiers[0])); + + // 通知を作成 + createNotification(user.id, 'groupInvited', { + notifierId: me.id, + userGroupInvitationId: invitation.id, + }); + }); } - - // Fetch the user - const user = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; - }); - - const joining = await UserGroupJoinings.findOneBy({ - userGroupId: userGroup.id, - userId: user.id, - }); - - if (joining) { - throw new ApiError(meta.errors.alreadyAdded); - } - - const existInvitation = await UserGroupInvitations.findOneBy({ - userGroupId: userGroup.id, - userId: user.id, - }); - - if (existInvitation) { - throw new ApiError(meta.errors.alreadyInvited); - } - - const invitation = await UserGroupInvitations.insert({ - id: genId(), - createdAt: new Date(), - userId: user.id, - userGroupId: userGroup.id, - } as UserGroupInvitation).then(x => UserGroupInvitations.findOneByOrFail(x.identifiers[0])); - - // 通知を作成 - createNotification(user.id, 'groupInvited', { - notifierId: me.id, - userGroupInvitationId: invitation.id, - }); -}); +} diff --git a/packages/backend/src/server/api/endpoints/users/groups/joined.ts b/packages/backend/src/server/api/endpoints/users/groups/joined.ts index fddec53fe58f..9bf60c42dd3b 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/joined.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/joined.ts @@ -1,6 +1,6 @@ import { Not, In } from 'typeorm'; -import { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -33,20 +33,25 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const ownedGroups = await UserGroups.findBy({ - userId: me.id, - }); - - const joinings = await UserGroupJoinings.findBy({ - userId: me.id, - ...(ownedGroups.length > 0 ? { - userGroupId: Not(In(ownedGroups.map(x => x.id))), - } : {}), - }); - - return await Promise.all(joinings.map(x => UserGroups.pack(x.userGroupId))); -}); + const ownedGroups = await UserGroups.findBy({ + userId: me.id, + }); + + const joinings = await UserGroupJoinings.findBy({ + userId: me.id, + ...(ownedGroups.length > 0 ? { + userGroupId: Not(In(ownedGroups.map(x => x.id))), + } : {}), + }); + + return await Promise.all(joinings.map(x => UserGroups.pack(x.userGroupId))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/groups/leave.ts b/packages/backend/src/server/api/endpoints/users/groups/leave.ts index bb0ad57fe794..1c5be433639d 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/leave.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/leave.ts @@ -1,5 +1,5 @@ -import { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; @@ -39,22 +39,27 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - // Fetch the group - const userGroup = await UserGroups.findOneBy({ - id: ps.groupId, - }); + // Fetch the group + const userGroup = await UserGroups.findOneBy({ + id: ps.groupId, + }); - if (userGroup == null) { - throw new ApiError(meta.errors.noSuchGroup); - } + if (userGroup == null) { + throw new ApiError(meta.errors.noSuchGroup); + } - if (me.id === userGroup.userId) { - throw new ApiError(meta.errors.youAreOwner); - } + if (me.id === userGroup.userId) { + throw new ApiError(meta.errors.youAreOwner); + } - await UserGroupJoinings.delete({ userGroupId: userGroup.id, userId: me.id }); -}); + await UserGroupJoinings.delete({ userGroupId: userGroup.id, userId: me.id }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/groups/owned.ts b/packages/backend/src/server/api/endpoints/users/groups/owned.ts index cde934d8e459..e8cee2943d35 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/owned.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/owned.ts @@ -1,5 +1,5 @@ -import { UserGroups } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { UserGroups } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -32,13 +32,18 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const userGroups = await UserGroups.findBy({ - userId: me.id, - }); - - return await Promise.all(userGroups.map(x => UserGroups.pack(x))); -}); + const userGroups = await UserGroups.findBy({ + userId: me.id, + }); + + return await Promise.all(userGroups.map(x => UserGroups.pack(x))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/groups/pull.ts b/packages/backend/src/server/api/endpoints/users/groups/pull.ts index 77a0321b6d27..07c5afbcd364 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/pull.ts @@ -1,5 +1,5 @@ -import { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { getUser } from '../../../common/getters.js'; @@ -47,30 +47,35 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - // Fetch the group - const userGroup = await UserGroups.findOneBy({ - id: ps.groupId, - userId: me.id, - }); + // Fetch the group + const userGroup = await UserGroups.findOneBy({ + id: ps.groupId, + userId: me.id, + }); - if (userGroup == null) { - throw new ApiError(meta.errors.noSuchGroup); - } + if (userGroup == null) { + throw new ApiError(meta.errors.noSuchGroup); + } - // Fetch the user - const user = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; - }); + // Fetch the user + const user = await getUser(ps.userId).catch(e => { + if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw e; + }); - if (user.id === userGroup.userId) { - throw new ApiError(meta.errors.isOwner); - } + if (user.id === userGroup.userId) { + throw new ApiError(meta.errors.isOwner); + } - // Pull the user - await UserGroupJoinings.delete({ userGroupId: userGroup.id, userId: user.id }); -}); + // Pull the user + await UserGroupJoinings.delete({ userGroupId: userGroup.id, userId: user.id }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/groups/show.ts b/packages/backend/src/server/api/endpoints/users/groups/show.ts index 39fb982c9a73..2da2502d633c 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/show.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/show.ts @@ -1,5 +1,5 @@ -import { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; @@ -39,27 +39,32 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - // Fetch the group - const userGroup = await UserGroups.findOneBy({ - id: ps.groupId, - }); + // Fetch the group + const userGroup = await UserGroups.findOneBy({ + id: ps.groupId, + }); - if (userGroup == null) { - throw new ApiError(meta.errors.noSuchGroup); - } + if (userGroup == null) { + throw new ApiError(meta.errors.noSuchGroup); + } - const joining = await UserGroupJoinings.findOneBy({ - userId: me.id, - userGroupId: userGroup.id, - }); + const joining = await UserGroupJoinings.findOneBy({ + userId: me.id, + userGroupId: userGroup.id, + }); - if (joining == null && userGroup.userId !== me.id) { - throw new ApiError(meta.errors.noSuchGroup); - } + if (joining == null && userGroup.userId !== me.id) { + throw new ApiError(meta.errors.noSuchGroup); + } - return await UserGroups.pack(userGroup); -}); + return await UserGroups.pack(userGroup); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/groups/transfer.ts b/packages/backend/src/server/api/endpoints/users/groups/transfer.ts index 56ae222706d8..f8755500bba5 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/transfer.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/transfer.ts @@ -1,5 +1,5 @@ -import { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { getUser } from '../../../common/getters.js'; @@ -53,38 +53,43 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - // Fetch the group - const userGroup = await UserGroups.findOneBy({ - id: ps.groupId, - userId: me.id, - }); - - if (userGroup == null) { - throw new ApiError(meta.errors.noSuchGroup); + // Fetch the group + const userGroup = await UserGroups.findOneBy({ + id: ps.groupId, + userId: me.id, + }); + + if (userGroup == null) { + throw new ApiError(meta.errors.noSuchGroup); + } + + // Fetch the user + const user = await getUser(ps.userId).catch(e => { + if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw e; + }); + + const joining = await UserGroupJoinings.findOneBy({ + userGroupId: userGroup.id, + userId: user.id, + }); + + if (joining == null) { + throw new ApiError(meta.errors.noSuchGroupMember); + } + + await UserGroups.update(userGroup.id, { + userId: ps.userId, + }); + + return await UserGroups.pack(userGroup.id); + }); } - - // Fetch the user - const user = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; - }); - - const joining = await UserGroupJoinings.findOneBy({ - userGroupId: userGroup.id, - userId: user.id, - }); - - if (joining == null) { - throw new ApiError(meta.errors.noSuchGroupMember); - } - - await UserGroups.update(userGroup.id, { - userId: ps.userId, - }); - - return await UserGroups.pack(userGroup.id); -}); +} diff --git a/packages/backend/src/server/api/endpoints/users/groups/update.ts b/packages/backend/src/server/api/endpoints/users/groups/update.ts index c4587bf7f9be..8d3b3d347bc6 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/update.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/update.ts @@ -1,5 +1,5 @@ -import { UserGroups } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { UserGroups } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; @@ -40,23 +40,28 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - // Fetch the group - const userGroup = await UserGroups.findOneBy({ - id: ps.groupId, - userId: me.id, - }); - - if (userGroup == null) { - throw new ApiError(meta.errors.noSuchGroup); - } + // Fetch the group + const userGroup = await UserGroups.findOneBy({ + id: ps.groupId, + userId: me.id, + }); + + if (userGroup == null) { + throw new ApiError(meta.errors.noSuchGroup); + } - await UserGroups.update(userGroup.id, { - name: ps.name, - }); + await UserGroups.update(userGroup.id, { + name: ps.name, + }); - return await UserGroups.pack(userGroup.id); -}); + return await UserGroups.pack(userGroup.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/lists/create.ts b/packages/backend/src/server/api/endpoints/users/lists/create.ts index 8fa710a4860f..7d4f31f83c34 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts @@ -1,7 +1,7 @@ +import { Inject, Injectable } from '@nestjs/common'; import { UserLists } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; -import { UserList } from '@/models/entities/user-list.js'; -import { Inject, Injectable } from '@nestjs/common'; +import type { UserList } from '@/models/entities/user-list.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -32,16 +32,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const userList = await UserLists.insert({ - id: genId(), - createdAt: new Date(), - userId: user.id, - name: ps.name, - } as UserList).then(x => UserLists.findOneByOrFail(x.identifiers[0])); - - return await UserLists.pack(userList); -}); + const userList = await UserLists.insert({ + id: genId(), + createdAt: new Date(), + userId: user.id, + name: ps.name, + } as UserList).then(x => UserLists.findOneByOrFail(x.identifiers[0])); + + return await UserLists.pack(userList); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/lists/delete.ts b/packages/backend/src/server/api/endpoints/users/lists/delete.ts index e7df202ae74e..08862f95a665 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/delete.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/delete.ts @@ -1,5 +1,5 @@ -import { UserLists } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { UserLists } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; @@ -33,18 +33,23 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const userList = await UserLists.findOneBy({ - id: ps.listId, - userId: user.id, - }); + const userList = await UserLists.findOneBy({ + id: ps.listId, + userId: user.id, + }); - if (userList == null) { - throw new ApiError(meta.errors.noSuchList); - } + if (userList == null) { + throw new ApiError(meta.errors.noSuchList); + } - await UserLists.delete(userList.id); -}); + await UserLists.delete(userList.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/lists/list.ts b/packages/backend/src/server/api/endpoints/users/lists/list.ts index 4dad0524150d..3bed98e615ba 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/list.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/list.ts @@ -1,5 +1,5 @@ -import { UserLists } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { UserLists } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -32,13 +32,18 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const userLists = await UserLists.findBy({ - userId: me.id, - }); - - return await Promise.all(userLists.map(x => UserLists.pack(x))); -}); + const userLists = await UserLists.findBy({ + userId: me.id, + }); + + return await Promise.all(userLists.map(x => UserLists.pack(x))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/lists/pull.ts b/packages/backend/src/server/api/endpoints/users/lists/pull.ts index f4d0ea30aeb6..b7f231f2ee8b 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/pull.ts @@ -1,6 +1,6 @@ +import { Inject, Injectable } from '@nestjs/common'; import { publishUserListStream } from '@/services/stream.js'; import { UserLists, UserListJoinings, Users } from '@/models/index.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { getUser } from '../../../common/getters.js'; @@ -42,28 +42,33 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - // Fetch the list - const userList = await UserLists.findOneBy({ - id: ps.listId, - userId: me.id, - }); + // Fetch the list + const userList = await UserLists.findOneBy({ + id: ps.listId, + userId: me.id, + }); - if (userList == null) { - throw new ApiError(meta.errors.noSuchList); - } + if (userList == null) { + throw new ApiError(meta.errors.noSuchList); + } - // Fetch the user - const user = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; - }); + // Fetch the user + const user = await getUser(ps.userId).catch(e => { + if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw e; + }); - // Pull the user - await UserListJoinings.delete({ userListId: userList.id, userId: user.id }); + // Pull the user + await UserListJoinings.delete({ userListId: userList.id, userId: user.id }); - publishUserListStream(userList.id, 'userRemoved', await Users.pack(user)); -}); + publishUserListStream(userList.id, 'userRemoved', await Users.pack(user)); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/lists/push.ts b/packages/backend/src/server/api/endpoints/users/lists/push.ts index b6e37686441e..dd7d813e2f52 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/push.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/push.ts @@ -1,6 +1,6 @@ +import { Inject, Injectable } from '@nestjs/common'; import { pushUserToUserList } from '@/services/user-list/push.js'; import { UserLists, UserListJoinings, Blockings } from '@/models/index.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { getUser } from '../../../common/getters.js'; @@ -54,46 +54,51 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - // Fetch the list - const userList = await UserLists.findOneBy({ - id: ps.listId, - userId: me.id, - }); - - if (userList == null) { - throw new ApiError(meta.errors.noSuchList); - } - - // Fetch the user - const user = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; - }); - - // Check blocking - if (user.id !== me.id) { - const block = await Blockings.findOneBy({ - blockerId: user.id, - blockeeId: me.id, + // Fetch the list + const userList = await UserLists.findOneBy({ + id: ps.listId, + userId: me.id, + }); + + if (userList == null) { + throw new ApiError(meta.errors.noSuchList); + } + + // Fetch the user + const user = await getUser(ps.userId).catch(e => { + if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw e; + }); + + // Check blocking + if (user.id !== me.id) { + const block = await Blockings.findOneBy({ + blockerId: user.id, + blockeeId: me.id, + }); + if (block) { + throw new ApiError(meta.errors.youHaveBeenBlocked); + } + } + + const exist = await UserListJoinings.findOneBy({ + userListId: userList.id, + userId: user.id, + }); + + if (exist) { + throw new ApiError(meta.errors.alreadyAdded); + } + + // Push the user + await pushUserToUserList(user, userList); }); - if (block) { - throw new ApiError(meta.errors.youHaveBeenBlocked); - } } - - const exist = await UserListJoinings.findOneBy({ - userListId: userList.id, - userId: user.id, - }); - - if (exist) { - throw new ApiError(meta.errors.alreadyAdded); - } - - // Push the user - await pushUserToUserList(user, userList); -}); +} diff --git a/packages/backend/src/server/api/endpoints/users/lists/show.ts b/packages/backend/src/server/api/endpoints/users/lists/show.ts index 40823ce72a81..fc13a50ada68 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/show.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/show.ts @@ -1,5 +1,5 @@ -import { UserLists } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { UserLists } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; @@ -39,19 +39,24 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - // Fetch the list - const userList = await UserLists.findOneBy({ - id: ps.listId, - userId: me.id, - }); - - if (userList == null) { - throw new ApiError(meta.errors.noSuchList); + // Fetch the list + const userList = await UserLists.findOneBy({ + id: ps.listId, + userId: me.id, + }); + + if (userList == null) { + throw new ApiError(meta.errors.noSuchList); + } + + return await UserLists.pack(userList); + }); } - - return await UserLists.pack(userList); -}); +} diff --git a/packages/backend/src/server/api/endpoints/users/lists/update.ts b/packages/backend/src/server/api/endpoints/users/lists/update.ts index b783c8b574f6..31b435bfc585 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/update.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/update.ts @@ -1,5 +1,5 @@ -import { UserLists } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { UserLists } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; @@ -40,23 +40,28 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - // Fetch the list - const userList = await UserLists.findOneBy({ - id: ps.listId, - userId: user.id, - }); - - if (userList == null) { - throw new ApiError(meta.errors.noSuchList); - } + // Fetch the list + const userList = await UserLists.findOneBy({ + id: ps.listId, + userId: user.id, + }); + + if (userList == null) { + throw new ApiError(meta.errors.noSuchList); + } - await UserLists.update(userList.id, { - name: ps.name, - }); + await UserLists.update(userList.id, { + name: ps.name, + }); - return await UserLists.pack(userList.id); -}); + return await UserLists.pack(userList.id); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 28f071445277..14e8a4712624 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -1,6 +1,6 @@ import { Brackets } from 'typeorm'; -import { Notes } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; @@ -57,73 +57,78 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - // Lookup user - const user = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; - }); - - //#region Construct query - const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere('note.userId = :userId', { userId: user.id }) - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('user.avatar', 'avatar') - .leftJoinAndSelect('user.banner', 'banner') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') - .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') - .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - - generateVisibilityQuery(query, me); - if (me) { - generateMutedUserQuery(query, me, user); - generateBlockedUserQuery(query, me); - } - - if (ps.withFiles) { - query.andWhere('note.fileIds != \'{}\''); - } + // Lookup user + const user = await getUser(ps.userId).catch(e => { + if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw e; + }); + + //#region Construct query + const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + .andWhere('note.userId = :userId', { userId: user.id }) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('user.avatar', 'avatar') + .leftJoinAndSelect('user.banner', 'banner') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); + + generateVisibilityQuery(query, me); + if (me) { + generateMutedUserQuery(query, me, user); + generateBlockedUserQuery(query, me); + } - if (ps.fileType != null) { - query.andWhere('note.fileIds != \'{}\''); - query.andWhere(new Brackets(qb => { - for (const type of ps.fileType!) { - const i = ps.fileType!.indexOf(type); - qb.orWhere(`:type${i} = ANY(note.attachedFileTypes)`, { [`type${i}`]: type }); + if (ps.withFiles) { + query.andWhere('note.fileIds != \'{}\''); } - })); - if (ps.excludeNsfw) { - query.andWhere('note.cw IS NULL'); - query.andWhere('0 = (SELECT COUNT(*) FROM drive_file df WHERE df.id = ANY(note."fileIds") AND df."isSensitive" = TRUE)'); - } - } + if (ps.fileType != null) { + query.andWhere('note.fileIds != \'{}\''); + query.andWhere(new Brackets(qb => { + for (const type of ps.fileType!) { + const i = ps.fileType!.indexOf(type); + qb.orWhere(`:type${i} = ANY(note.attachedFileTypes)`, { [`type${i}`]: type }); + } + })); + + if (ps.excludeNsfw) { + query.andWhere('note.cw IS NULL'); + query.andWhere('0 = (SELECT COUNT(*) FROM drive_file df WHERE df.id = ANY(note."fileIds") AND df."isSensitive" = TRUE)'); + } + } - if (!ps.includeReplies) { - query.andWhere('note.replyId IS NULL'); - } + if (!ps.includeReplies) { + query.andWhere('note.replyId IS NULL'); + } - if (ps.includeMyRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.userId != :userId', { userId: user.id }); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } + if (ps.includeMyRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.userId != :userId', { userId: user.id }); + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); + })); + } - //#endregion + //#endregion - const timeline = await query.take(ps.limit).getMany(); + const timeline = await query.take(ps.limit).getMany(); - return await Notes.packMany(timeline, me); -}); + return await Notes.packMany(timeline, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/pages.ts b/packages/backend/src/server/api/endpoints/users/pages.ts index 9c86851f9f51..b3c716cad77b 100644 --- a/packages/backend/src/server/api/endpoints/users/pages.ts +++ b/packages/backend/src/server/api/endpoints/users/pages.ts @@ -1,5 +1,5 @@ -import { Pages } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Pages } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -34,17 +34,22 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const query = makePaginationQuery(Pages.createQueryBuilder('page'), ps.sinceId, ps.untilId) - .andWhere('page.userId = :userId', { userId: ps.userId }) - .andWhere('page.visibility = \'public\''); + const query = makePaginationQuery(Pages.createQueryBuilder('page'), ps.sinceId, ps.untilId) + .andWhere('page.userId = :userId', { userId: ps.userId }) + .andWhere('page.visibility = \'public\''); - const pages = await query - .take(ps.limit) - .getMany(); + const pages = await query + .take(ps.limit) + .getMany(); - return await Pages.packMany(pages); -}); + return await Pages.packMany(pages); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts index d5a1b383adf8..58cddba551c8 100644 --- a/packages/backend/src/server/api/endpoints/users/reactions.ts +++ b/packages/backend/src/server/api/endpoints/users/reactions.ts @@ -1,5 +1,5 @@ -import { NoteReactions, UserProfiles } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { NoteReactions, UserProfiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; @@ -48,26 +48,31 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const profile = await UserProfiles.findOneByOrFail({ userId: ps.userId }); + const profile = await UserProfiles.findOneByOrFail({ userId: ps.userId }); - if (me == null || (me.id !== ps.userId && !profile.publicReactions)) { - throw new ApiError(meta.errors.reactionsNotPublic); - } + if (me == null || (me.id !== ps.userId && !profile.publicReactions)) { + throw new ApiError(meta.errors.reactionsNotPublic); + } - const query = makePaginationQuery(NoteReactions.createQueryBuilder('reaction'), - ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere('reaction.userId = :userId', { userId: ps.userId }) - .leftJoinAndSelect('reaction.note', 'note'); + const query = makePaginationQuery(NoteReactions.createQueryBuilder('reaction'), + ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + .andWhere('reaction.userId = :userId', { userId: ps.userId }) + .leftJoinAndSelect('reaction.note', 'note'); - generateVisibilityQuery(query, me); + generateVisibilityQuery(query, me); - const reactions = await query - .take(ps.limit) - .getMany(); + const reactions = await query + .take(ps.limit) + .getMany(); - return await Promise.all(reactions.map(reaction => NoteReactions.pack(reaction, me, { withNote: true }))); -}); + return await Promise.all(reactions.map(reaction => NoteReactions.pack(reaction, me, { withNote: true }))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/recommendation.ts b/packages/backend/src/server/api/endpoints/users/recommendation.ts index 7f7a83b1a8d7..fb1678db9794 100644 --- a/packages/backend/src/server/api/endpoints/users/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/users/recommendation.ts @@ -1,6 +1,6 @@ import ms from 'ms'; -import { Users, Followings } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Users, Followings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { generateMutedUserQueryForUsers } from '../../common/generate-muted-user-query.js'; import { generateBlockedUserQuery, generateBlockQueryForUsers } from '../../common/generate-block-query.js'; @@ -38,32 +38,37 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const query = Users.createQueryBuilder('user') - .where('user.isLocked = FALSE') - .andWhere('user.isExplorable = TRUE') - .andWhere('user.host IS NULL') - .andWhere('user.updatedAt >= :date', { date: new Date(Date.now() - ms('7days')) }) - .andWhere('user.id != :meId', { meId: me.id }) - .orderBy('user.followersCount', 'DESC'); + const query = Users.createQueryBuilder('user') + .where('user.isLocked = FALSE') + .andWhere('user.isExplorable = TRUE') + .andWhere('user.host IS NULL') + .andWhere('user.updatedAt >= :date', { date: new Date(Date.now() - ms('7days')) }) + .andWhere('user.id != :meId', { meId: me.id }) + .orderBy('user.followersCount', 'DESC'); - generateMutedUserQueryForUsers(query, me); - generateBlockQueryForUsers(query, me); - generateBlockedUserQuery(query, me); + generateMutedUserQueryForUsers(query, me); + generateBlockQueryForUsers(query, me); + generateBlockedUserQuery(query, me); - const followingQuery = Followings.createQueryBuilder('following') - .select('following.followeeId') - .where('following.followerId = :followerId', { followerId: me.id }); + const followingQuery = Followings.createQueryBuilder('following') + .select('following.followeeId') + .where('following.followerId = :followerId', { followerId: me.id }); - query - .andWhere(`user.id NOT IN (${ followingQuery.getQuery() })`); + query + .andWhere(`user.id NOT IN (${ followingQuery.getQuery() })`); - query.setParameters(followingQuery.getParameters()); + query.setParameters(followingQuery.getParameters()); - const users = await query.take(ps.limit).skip(ps.offset).getMany(); + const users = await query.take(ps.limit).skip(ps.offset).getMany(); - return await Users.packMany(users, me, { detail: true }); -}); + return await Users.packMany(users, me, { detail: true }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/relation.ts b/packages/backend/src/server/api/endpoints/users/relation.ts index 374368d566bd..f087825e8369 100644 --- a/packages/backend/src/server/api/endpoints/users/relation.ts +++ b/packages/backend/src/server/api/endpoints/users/relation.ts @@ -1,5 +1,5 @@ -import { Users } from '@/models/index.js'; import { Inject, Injectable } from '@nestjs/common'; +import { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -116,13 +116,18 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const ids = Array.isArray(ps.userId) ? ps.userId : [ps.userId]; + const ids = Array.isArray(ps.userId) ? ps.userId : [ps.userId]; - const relations = await Promise.all(ids.map(id => Users.getRelation(me.id, id))); + const relations = await Promise.all(ids.map(id => Users.getRelation(me.id, id))); - return Array.isArray(ps.userId) ? relations : relations[0]; -}); + return Array.isArray(ps.userId) ? relations : relations[0]; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts index f7b87bfc850b..a305d94c2147 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -1,13 +1,13 @@ import * as sanitizeHtml from 'sanitize-html'; +import { Inject, Injectable } from '@nestjs/common'; import { publishAdminStream } from '@/services/stream.js'; import { AbuseUserReports, Users } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { sendEmail } from '@/services/send-email.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; import { getUser } from '../../common/getters.js'; import { ApiError } from '../../error.js'; -import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { tags: ['users'], @@ -50,58 +50,63 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - // Lookup user - const user = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; - }); + // Lookup user + const user = await getUser(ps.userId).catch(e => { + if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw e; + }); - if (user.id === me.id) { - throw new ApiError(meta.errors.cannotReportYourself); - } + if (user.id === me.id) { + throw new ApiError(meta.errors.cannotReportYourself); + } - if (user.isAdmin) { - throw new ApiError(meta.errors.cannotReportAdmin); - } + if (user.isAdmin) { + throw new ApiError(meta.errors.cannotReportAdmin); + } - const report = await AbuseUserReports.insert({ - id: genId(), - createdAt: new Date(), - targetUserId: user.id, - targetUserHost: user.host, - reporterId: me.id, - reporterHost: null, - comment: ps.comment, - }).then(x => AbuseUserReports.findOneByOrFail(x.identifiers[0])); + const report = await AbuseUserReports.insert({ + id: genId(), + createdAt: new Date(), + targetUserId: user.id, + targetUserHost: user.host, + reporterId: me.id, + reporterHost: null, + comment: ps.comment, + }).then(x => AbuseUserReports.findOneByOrFail(x.identifiers[0])); - // Publish event to moderators - setImmediate(async () => { - const moderators = await Users.find({ - where: [{ - isAdmin: true, - }, { - isModerator: true, - }], - }); + // Publish event to moderators + setImmediate(async () => { + const moderators = await Users.find({ + where: [{ + isAdmin: true, + }, { + isModerator: true, + }], + }); - for (const moderator of moderators) { - publishAdminStream(moderator.id, 'newAbuseUserReport', { - id: report.id, - targetUserId: report.targetUserId, - reporterId: report.reporterId, - comment: report.comment, - }); - } + for (const moderator of moderators) { + publishAdminStream(moderator.id, 'newAbuseUserReport', { + id: report.id, + targetUserId: report.targetUserId, + reporterId: report.reporterId, + comment: report.comment, + }); + } - const meta = await fetchMeta(); - if (meta.email) { - sendEmail(meta.email, 'New abuse report', - sanitizeHtml(ps.comment), - sanitizeHtml(ps.comment)); - } - }); -}); + const meta = await fetchMeta(); + if (meta.email) { + sendEmail(meta.email, 'New abuse report', + sanitizeHtml(ps.comment), + sanitizeHtml(ps.comment)); + } + }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index d2c67ef24fb0..96f7448cf5ef 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -1,8 +1,8 @@ import { Brackets } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; import { Followings, Users } from '@/models/index.js'; import { USER_ACTIVE_THRESHOLD } from '@/const.js'; -import { User } from '@/models/entities/user.js'; -import { Inject, Injectable } from '@nestjs/common'; +import type { User } from '@/models/entities/user.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -43,81 +43,86 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日 - - if (ps.host) { - const q = Users.createQueryBuilder('user') - .where('user.isSuspended = FALSE') - .andWhere('user.host LIKE :host', { host: ps.host.toLowerCase() + '%' }); - - if (ps.username) { - q.andWhere('user.usernameLower LIKE :username', { username: ps.username.toLowerCase() + '%' }); - } - - q.andWhere('user.updatedAt IS NOT NULL'); - q.orderBy('user.updatedAt', 'DESC'); - - const users = await q.take(ps.limit).getMany(); - - return await Users.packMany(users, me, { detail: ps.detail }); - } else if (ps.username) { - let users: User[] = []; - - if (me) { - const followingQuery = Followings.createQueryBuilder('following') - .select('following.followeeId') - .where('following.followerId = :followerId', { followerId: me.id }); - - const query = Users.createQueryBuilder('user') - .where(`user.id IN (${ followingQuery.getQuery() })`) - .andWhere('user.id != :meId', { meId: me.id }) - .andWhere('user.isSuspended = FALSE') - .andWhere('user.usernameLower LIKE :username', { username: ps.username.toLowerCase() + '%' }) - .andWhere(new Brackets(qb => { qb - .where('user.updatedAt IS NULL') - .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); - })); - - query.setParameters(followingQuery.getParameters()); - - users = await query - .orderBy('user.usernameLower', 'ASC') - .take(ps.limit) - .getMany(); - - if (users.length < ps.limit) { - const otherQuery = await Users.createQueryBuilder('user') - .where(`user.id NOT IN (${ followingQuery.getQuery() })`) - .andWhere('user.id != :meId', { meId: me.id }) - .andWhere('user.isSuspended = FALSE') - .andWhere('user.usernameLower LIKE :username', { username: ps.username.toLowerCase() + '%' }) - .andWhere('user.updatedAt IS NOT NULL'); - - otherQuery.setParameters(followingQuery.getParameters()); - - const otherUsers = await otherQuery - .orderBy('user.updatedAt', 'DESC') - .take(ps.limit - users.length) - .getMany(); - - users = users.concat(otherUsers); + const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日 + + if (ps.host) { + const q = Users.createQueryBuilder('user') + .where('user.isSuspended = FALSE') + .andWhere('user.host LIKE :host', { host: ps.host.toLowerCase() + '%' }); + + if (ps.username) { + q.andWhere('user.usernameLower LIKE :username', { username: ps.username.toLowerCase() + '%' }); + } + + q.andWhere('user.updatedAt IS NOT NULL'); + q.orderBy('user.updatedAt', 'DESC'); + + const users = await q.take(ps.limit).getMany(); + + return await Users.packMany(users, me, { detail: ps.detail }); + } else if (ps.username) { + let users: User[] = []; + + if (me) { + const followingQuery = Followings.createQueryBuilder('following') + .select('following.followeeId') + .where('following.followerId = :followerId', { followerId: me.id }); + + const query = Users.createQueryBuilder('user') + .where(`user.id IN (${ followingQuery.getQuery() })`) + .andWhere('user.id != :meId', { meId: me.id }) + .andWhere('user.isSuspended = FALSE') + .andWhere('user.usernameLower LIKE :username', { username: ps.username.toLowerCase() + '%' }) + .andWhere(new Brackets(qb => { qb + .where('user.updatedAt IS NULL') + .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); + })); + + query.setParameters(followingQuery.getParameters()); + + users = await query + .orderBy('user.usernameLower', 'ASC') + .take(ps.limit) + .getMany(); + + if (users.length < ps.limit) { + const otherQuery = await Users.createQueryBuilder('user') + .where(`user.id NOT IN (${ followingQuery.getQuery() })`) + .andWhere('user.id != :meId', { meId: me.id }) + .andWhere('user.isSuspended = FALSE') + .andWhere('user.usernameLower LIKE :username', { username: ps.username.toLowerCase() + '%' }) + .andWhere('user.updatedAt IS NOT NULL'); + + otherQuery.setParameters(followingQuery.getParameters()); + + const otherUsers = await otherQuery + .orderBy('user.updatedAt', 'DESC') + .take(ps.limit - users.length) + .getMany(); + + users = users.concat(otherUsers); + } + } else { + users = await Users.createQueryBuilder('user') + .where('user.isSuspended = FALSE') + .andWhere('user.usernameLower LIKE :username', { username: ps.username.toLowerCase() + '%' }) + .andWhere('user.updatedAt IS NOT NULL') + .orderBy('user.updatedAt', 'DESC') + .take(ps.limit - users.length) + .getMany(); + } + + return await Users.packMany(users, me, { detail: !!ps.detail }); } - } else { - users = await Users.createQueryBuilder('user') - .where('user.isSuspended = FALSE') - .andWhere('user.usernameLower LIKE :username', { username: ps.username.toLowerCase() + '%' }) - .andWhere('user.updatedAt IS NOT NULL') - .orderBy('user.updatedAt', 'DESC') - .take(ps.limit - users.length) - .getMany(); - } - - return await Users.packMany(users, me, { detail: !!ps.detail }); - } - return []; -}); + return []; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index 7ec144af504d..c37cf29e1a49 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -1,7 +1,7 @@ import { Brackets } from 'typeorm'; -import { UserProfiles, Users } from '@/models/index.js'; -import { User } from '@/models/entities/user.js'; import { Inject, Injectable } from '@nestjs/common'; +import { UserProfiles, Users } from '@/models/index.js'; +import type { User } from '@/models/entities/user.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -38,92 +38,97 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日 - - const isUsername = ps.query.startsWith('@'); - - let users: User[] = []; - - if (isUsername) { - const usernameQuery = Users.createQueryBuilder('user') - .where('user.usernameLower LIKE :username', { username: ps.query.replace('@', '').toLowerCase() + '%' }) - .andWhere(new Brackets(qb => { qb - .where('user.updatedAt IS NULL') - .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); - })) - .andWhere('user.isSuspended = FALSE'); - - if (ps.origin === 'local') { - usernameQuery.andWhere('user.host IS NULL'); - } else if (ps.origin === 'remote') { - usernameQuery.andWhere('user.host IS NOT NULL'); - } - - users = await usernameQuery - .orderBy('user.updatedAt', 'DESC', 'NULLS LAST') - .take(ps.limit) - .skip(ps.offset) - .getMany(); - } else { - const nameQuery = Users.createQueryBuilder('user') - .where(new Brackets(qb => { - qb.where('user.name ILIKE :query', { query: '%' + ps.query + '%' }); - - // Also search username if it qualifies as username - if (Users.validateLocalUsername(ps.query)) { - qb.orWhere('user.usernameLower LIKE :username', { username: '%' + ps.query.toLowerCase() + '%' }); + const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日 + + const isUsername = ps.query.startsWith('@'); + + let users: User[] = []; + + if (isUsername) { + const usernameQuery = Users.createQueryBuilder('user') + .where('user.usernameLower LIKE :username', { username: ps.query.replace('@', '').toLowerCase() + '%' }) + .andWhere(new Brackets(qb => { qb + .where('user.updatedAt IS NULL') + .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); + })) + .andWhere('user.isSuspended = FALSE'); + + if (ps.origin === 'local') { + usernameQuery.andWhere('user.host IS NULL'); + } else if (ps.origin === 'remote') { + usernameQuery.andWhere('user.host IS NOT NULL'); + } + + users = await usernameQuery + .orderBy('user.updatedAt', 'DESC', 'NULLS LAST') + .take(ps.limit) + .skip(ps.offset) + .getMany(); + } else { + const nameQuery = Users.createQueryBuilder('user') + .where(new Brackets(qb => { + qb.where('user.name ILIKE :query', { query: '%' + ps.query + '%' }); + + // Also search username if it qualifies as username + if (Users.validateLocalUsername(ps.query)) { + qb.orWhere('user.usernameLower LIKE :username', { username: '%' + ps.query.toLowerCase() + '%' }); + } + })) + .andWhere(new Brackets(qb => { qb + .where('user.updatedAt IS NULL') + .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); + })) + .andWhere('user.isSuspended = FALSE'); + + if (ps.origin === 'local') { + nameQuery.andWhere('user.host IS NULL'); + } else if (ps.origin === 'remote') { + nameQuery.andWhere('user.host IS NOT NULL'); + } + + users = await nameQuery + .orderBy('user.updatedAt', 'DESC', 'NULLS LAST') + .take(ps.limit) + .skip(ps.offset) + .getMany(); + + if (users.length < ps.limit) { + const profQuery = UserProfiles.createQueryBuilder('prof') + .select('prof.userId') + .where('prof.description ILIKE :query', { query: '%' + ps.query + '%' }); + + if (ps.origin === 'local') { + profQuery.andWhere('prof.userHost IS NULL'); + } else if (ps.origin === 'remote') { + profQuery.andWhere('prof.userHost IS NOT NULL'); + } + + const query = Users.createQueryBuilder('user') + .where(`user.id IN (${ profQuery.getQuery() })`) + .andWhere(new Brackets(qb => { qb + .where('user.updatedAt IS NULL') + .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); + })) + .andWhere('user.isSuspended = FALSE') + .setParameters(profQuery.getParameters()); + + users = users.concat(await query + .orderBy('user.updatedAt', 'DESC', 'NULLS LAST') + .take(ps.limit) + .skip(ps.offset) + .getMany(), + ); } - })) - .andWhere(new Brackets(qb => { qb - .where('user.updatedAt IS NULL') - .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); - })) - .andWhere('user.isSuspended = FALSE'); - - if (ps.origin === 'local') { - nameQuery.andWhere('user.host IS NULL'); - } else if (ps.origin === 'remote') { - nameQuery.andWhere('user.host IS NOT NULL'); - } - - users = await nameQuery - .orderBy('user.updatedAt', 'DESC', 'NULLS LAST') - .take(ps.limit) - .skip(ps.offset) - .getMany(); - - if (users.length < ps.limit) { - const profQuery = UserProfiles.createQueryBuilder('prof') - .select('prof.userId') - .where('prof.description ILIKE :query', { query: '%' + ps.query + '%' }); - - if (ps.origin === 'local') { - profQuery.andWhere('prof.userHost IS NULL'); - } else if (ps.origin === 'remote') { - profQuery.andWhere('prof.userHost IS NOT NULL'); } - const query = Users.createQueryBuilder('user') - .where(`user.id IN (${ profQuery.getQuery() })`) - .andWhere(new Brackets(qb => { qb - .where('user.updatedAt IS NULL') - .orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold }); - })) - .andWhere('user.isSuspended = FALSE') - .setParameters(profQuery.getParameters()); - - users = users.concat(await query - .orderBy('user.updatedAt', 'DESC', 'NULLS LAST') - .take(ps.limit) - .skip(ps.offset) - .getMany(), - ); - } + return await Users.packMany(users, me, { detail: ps.detail }); + }); } - - return await Users.packMany(users, me, { detail: ps.detail }); -}); +} diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts index 2be5404ba085..7ba8ec1f35b6 100644 --- a/packages/backend/src/server/api/endpoints/users/show.ts +++ b/packages/backend/src/server/api/endpoints/users/show.ts @@ -1,11 +1,12 @@ -import { FindOptionsWhere, In, IsNull } from 'typeorm'; +import { In, IsNull } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; import { resolveUser } from '@/remote/resolve-user.js'; import { Users } from '@/models/index.js'; -import { User } from '@/models/entities/user.js'; -import { Inject, Injectable } from '@nestjs/common'; +import type { User } from '@/models/entities/user.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { apiLogger } from '../../logger.js'; import { ApiError } from '../../error.js'; +import type { FindOptionsWhere } from 'typeorm'; export const meta = { tags: ['users'], @@ -82,56 +83,61 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - let user; - - const isAdminOrModerator = me && (me.isAdmin || me.isModerator); - - if (ps.userIds) { - if (ps.userIds.length === 0) { - return []; - } - - const users = await Users.findBy(isAdminOrModerator ? { - id: In(ps.userIds), - } : { - id: In(ps.userIds), - isSuspended: false, - }); - - // リクエストされた通りに並べ替え - const _users: User[] = []; - for (const id of ps.userIds) { - _users.push(users.find(x => x.id === id)!); - } - - return await Promise.all(_users.map(u => Users.pack(u, me, { - detail: true, - }))); - } else { - // Lookup user - if (typeof ps.host === 'string' && typeof ps.username === 'string') { - user = await resolveUser(ps.username, ps.host).catch(e => { - apiLogger.warn(`failed to resolve remote user: ${e}`); - throw new ApiError(meta.errors.failedToResolveRemoteUser); - }); - } else { - const q: FindOptionsWhere = ps.userId != null - ? { id: ps.userId } - : { usernameLower: ps.username!.toLowerCase(), host: IsNull() }; - - user = await Users.findOneBy(q); - } - - if (user == null || (!isAdminOrModerator && user.isSuspended)) { - throw new ApiError(meta.errors.noSuchUser); - } - - return await Users.pack(user, me, { - detail: true, + let user; + + const isAdminOrModerator = me && (me.isAdmin || me.isModerator); + + if (ps.userIds) { + if (ps.userIds.length === 0) { + return []; + } + + const users = await Users.findBy(isAdminOrModerator ? { + id: In(ps.userIds), + } : { + id: In(ps.userIds), + isSuspended: false, + }); + + // リクエストされた通りに並べ替え + const _users: User[] = []; + for (const id of ps.userIds) { + _users.push(users.find(x => x.id === id)!); + } + + return await Promise.all(_users.map(u => Users.pack(u, me, { + detail: true, + }))); + } else { + // Lookup user + if (typeof ps.host === 'string' && typeof ps.username === 'string') { + user = await resolveUser(ps.username, ps.host).catch(e => { + apiLogger.warn(`failed to resolve remote user: ${e}`); + throw new ApiError(meta.errors.failedToResolveRemoteUser); + }); + } else { + const q: FindOptionsWhere = ps.userId != null + ? { id: ps.userId } + : { usernameLower: ps.username!.toLowerCase(), host: IsNull() }; + + user = await Users.findOneBy(q); + } + + if (user == null || (!isAdminOrModerator && user.isSuspended)) { + throw new ApiError(meta.errors.noSuchUser); + } + + return await Users.pack(user, me, { + detail: true, + }); + } }); } -}); +} diff --git a/packages/backend/src/server/api/endpoints/users/stats.ts b/packages/backend/src/server/api/endpoints/users/stats.ts index 5952460a3228..d46aeb691c7f 100644 --- a/packages/backend/src/server/api/endpoints/users/stats.ts +++ b/packages/backend/src/server/api/endpoints/users/stats.ts @@ -1,6 +1,6 @@ +import { Inject, Injectable } from '@nestjs/common'; import { DriveFiles, Followings, NoteFavorites, NoteReactions, Notes, PageLikes, PollVotes, Users } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; @@ -120,81 +120,86 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const user = await Users.findOneBy({ id: ps.userId }); - if (user == null) { - throw new ApiError(meta.errors.noSuchUser); - } + const user = await Users.findOneBy({ id: ps.userId }); + if (user == null) { + throw new ApiError(meta.errors.noSuchUser); + } - const result = await awaitAll({ - notesCount: Notes.createQueryBuilder('note') - .where('note.userId = :userId', { userId: user.id }) - .getCount(), - repliesCount: Notes.createQueryBuilder('note') - .where('note.userId = :userId', { userId: user.id }) - .andWhere('note.replyId IS NOT NULL') - .getCount(), - renotesCount: Notes.createQueryBuilder('note') - .where('note.userId = :userId', { userId: user.id }) - .andWhere('note.renoteId IS NOT NULL') - .getCount(), - repliedCount: Notes.createQueryBuilder('note') - .where('note.replyUserId = :userId', { userId: user.id }) - .getCount(), - renotedCount: Notes.createQueryBuilder('note') - .where('note.renoteUserId = :userId', { userId: user.id }) - .getCount(), - pollVotesCount: PollVotes.createQueryBuilder('vote') - .where('vote.userId = :userId', { userId: user.id }) - .getCount(), - pollVotedCount: PollVotes.createQueryBuilder('vote') - .innerJoin('vote.note', 'note') - .where('note.userId = :userId', { userId: user.id }) - .getCount(), - localFollowingCount: Followings.createQueryBuilder('following') - .where('following.followerId = :userId', { userId: user.id }) - .andWhere('following.followeeHost IS NULL') - .getCount(), - remoteFollowingCount: Followings.createQueryBuilder('following') - .where('following.followerId = :userId', { userId: user.id }) - .andWhere('following.followeeHost IS NOT NULL') - .getCount(), - localFollowersCount: Followings.createQueryBuilder('following') - .where('following.followeeId = :userId', { userId: user.id }) - .andWhere('following.followerHost IS NULL') - .getCount(), - remoteFollowersCount: Followings.createQueryBuilder('following') - .where('following.followeeId = :userId', { userId: user.id }) - .andWhere('following.followerHost IS NOT NULL') - .getCount(), - sentReactionsCount: NoteReactions.createQueryBuilder('reaction') - .where('reaction.userId = :userId', { userId: user.id }) - .getCount(), - receivedReactionsCount: NoteReactions.createQueryBuilder('reaction') - .innerJoin('reaction.note', 'note') - .where('note.userId = :userId', { userId: user.id }) - .getCount(), - noteFavoritesCount: NoteFavorites.createQueryBuilder('favorite') - .where('favorite.userId = :userId', { userId: user.id }) - .getCount(), - pageLikesCount: PageLikes.createQueryBuilder('like') - .where('like.userId = :userId', { userId: user.id }) - .getCount(), - pageLikedCount: PageLikes.createQueryBuilder('like') - .innerJoin('like.page', 'page') - .where('page.userId = :userId', { userId: user.id }) - .getCount(), - driveFilesCount: DriveFiles.createQueryBuilder('file') - .where('file.userId = :userId', { userId: user.id }) - .getCount(), - driveUsage: DriveFiles.calcDriveUsageOf(user), - }); + const result = await awaitAll({ + notesCount: Notes.createQueryBuilder('note') + .where('note.userId = :userId', { userId: user.id }) + .getCount(), + repliesCount: Notes.createQueryBuilder('note') + .where('note.userId = :userId', { userId: user.id }) + .andWhere('note.replyId IS NOT NULL') + .getCount(), + renotesCount: Notes.createQueryBuilder('note') + .where('note.userId = :userId', { userId: user.id }) + .andWhere('note.renoteId IS NOT NULL') + .getCount(), + repliedCount: Notes.createQueryBuilder('note') + .where('note.replyUserId = :userId', { userId: user.id }) + .getCount(), + renotedCount: Notes.createQueryBuilder('note') + .where('note.renoteUserId = :userId', { userId: user.id }) + .getCount(), + pollVotesCount: PollVotes.createQueryBuilder('vote') + .where('vote.userId = :userId', { userId: user.id }) + .getCount(), + pollVotedCount: PollVotes.createQueryBuilder('vote') + .innerJoin('vote.note', 'note') + .where('note.userId = :userId', { userId: user.id }) + .getCount(), + localFollowingCount: Followings.createQueryBuilder('following') + .where('following.followerId = :userId', { userId: user.id }) + .andWhere('following.followeeHost IS NULL') + .getCount(), + remoteFollowingCount: Followings.createQueryBuilder('following') + .where('following.followerId = :userId', { userId: user.id }) + .andWhere('following.followeeHost IS NOT NULL') + .getCount(), + localFollowersCount: Followings.createQueryBuilder('following') + .where('following.followeeId = :userId', { userId: user.id }) + .andWhere('following.followerHost IS NULL') + .getCount(), + remoteFollowersCount: Followings.createQueryBuilder('following') + .where('following.followeeId = :userId', { userId: user.id }) + .andWhere('following.followerHost IS NOT NULL') + .getCount(), + sentReactionsCount: NoteReactions.createQueryBuilder('reaction') + .where('reaction.userId = :userId', { userId: user.id }) + .getCount(), + receivedReactionsCount: NoteReactions.createQueryBuilder('reaction') + .innerJoin('reaction.note', 'note') + .where('note.userId = :userId', { userId: user.id }) + .getCount(), + noteFavoritesCount: NoteFavorites.createQueryBuilder('favorite') + .where('favorite.userId = :userId', { userId: user.id }) + .getCount(), + pageLikesCount: PageLikes.createQueryBuilder('like') + .where('like.userId = :userId', { userId: user.id }) + .getCount(), + pageLikedCount: PageLikes.createQueryBuilder('like') + .innerJoin('like.page', 'page') + .where('page.userId = :userId', { userId: user.id }) + .getCount(), + driveFilesCount: DriveFiles.createQueryBuilder('file') + .where('file.userId = :userId', { userId: user.id }) + .getCount(), + driveUsage: DriveFiles.calcDriveUsageOf(user), + }); - result.followingCount = result.localFollowingCount + result.remoteFollowingCount; - result.followersCount = result.localFollowersCount + result.remoteFollowersCount; + result.followingCount = result.localFollowingCount + result.remoteFollowingCount; + result.followersCount = result.localFollowersCount + result.remoteFollowersCount; - return result; -}); + return result; + }); + } +} From 2fd506c9781090fbdb649164646509f4293ba470 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 11 Sep 2022 16:53:47 +0900 Subject: [PATCH 010/180] wip --- packages/backend/src/app.module.ts | 2 + .../backend/src/server/api/api-handler.ts | 10 +- packages/backend/src/server/api/call.ts | 23 +- .../src/server/api/endpoints.module.ts | 640 ++++++++++++++++++ packages/backend/src/server/api/endpoints.ts | 4 +- packages/backend/src/server/api/index.ts | 167 ++--- packages/backend/src/server/index.ts | 7 +- 7 files changed, 746 insertions(+), 107 deletions(-) create mode 100644 packages/backend/src/server/api/endpoints.module.ts diff --git a/packages/backend/src/app.module.ts b/packages/backend/src/app.module.ts index ee0cef6d9c41..15c5cb44d140 100644 --- a/packages/backend/src/app.module.ts +++ b/packages/backend/src/app.module.ts @@ -1,8 +1,10 @@ import { Module } from '@nestjs/common'; import { Notes } from '@/models/index.js'; +import { EndpointsModule } from '@/server/api/endpoints.module.js'; @Module({ imports: [ + EndpointsModule, ], providers: [{ provide: 'notesRepository', diff --git a/packages/backend/src/server/api/api-handler.ts b/packages/backend/src/server/api/api-handler.ts index ec71ddd2c007..354ec01681ad 100644 --- a/packages/backend/src/server/api/api-handler.ts +++ b/packages/backend/src/server/api/api-handler.ts @@ -1,12 +1,12 @@ -import Koa from 'koa'; -import { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/user.js'; import { UserIps } from '@/models/index.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; -import { IEndpoint } from './endpoints.js'; import authenticate, { AuthenticationError } from './authenticate.js'; import call from './call.js'; import { ApiError } from './error.js'; +import type { IEndpoint } from './endpoints.js'; +import type Koa from 'koa'; const userIpHistories = new Map>(); @@ -14,7 +14,7 @@ setInterval(() => { userIpHistories.clear(); }, 1000 * 60 * 60); -export default (endpoint: IEndpoint, ctx: Koa.Context) => new Promise((res) => { +export default (endpoint: IEndpoint, exec: any, ctx: Koa.Context) => new Promise((res) => { const body = ctx.is('multipart/form-data') ? (ctx.request as any).body : ctx.method === 'GET' @@ -45,7 +45,7 @@ export default (endpoint: IEndpoint, ctx: Koa.Context) => new Promise((res // Authentication authenticate(body['i']).then(([user, app]) => { // API invoking - call(endpoint.name, user, app, body, ctx).then((res: any) => { + call(endpoint, exec, user, app, body, ctx).then((res: any) => { if (ctx.method === 'GET' && endpoint.meta.cacheSec && !body['i'] && !user) { ctx.set('Cache-Control', `public, max-age=${endpoint.meta.cacheSec}`); } diff --git a/packages/backend/src/server/api/call.ts b/packages/backend/src/server/api/call.ts index aa130459a3d8..46d02d891d15 100644 --- a/packages/backend/src/server/api/call.ts +++ b/packages/backend/src/server/api/call.ts @@ -1,12 +1,12 @@ import { performance } from 'perf_hooks'; -import Koa from 'koa'; -import { CacheableLocalUser, User } from '@/models/entities/user.js'; -import { AccessToken } from '@/models/entities/access-token.js'; +import type { CacheableLocalUser } from '@/models/entities/user.js'; +import type { AccessToken } from '@/models/entities/access-token.js'; import { getIpHash } from '@/misc/get-ip-hash.js'; import { limiter } from './limiter.js'; -import endpoints, { IEndpointMeta } from './endpoints.js'; import { ApiError } from './error.js'; import { apiLogger } from './logger.js'; +import type { IEndpointMeta, IEndpoint } from './endpoints.js'; +import type Koa from 'koa'; const accessDenied = { message: 'Access denied.', @@ -14,21 +14,10 @@ const accessDenied = { id: '56f35758-7dd5-468b-8439-5d6fb8ec9b8e', }; -export default async (endpoint: string, user: CacheableLocalUser | null | undefined, token: AccessToken | null | undefined, data: any, ctx?: Koa.Context) => { +export default async (ep: IEndpoint, exec: any, user: CacheableLocalUser | null | undefined, token: AccessToken | null | undefined, data: any, ctx?: Koa.Context) => { const isSecure = user != null && token == null; const isModerator = user != null && (user.isModerator || user.isAdmin); - const ep = endpoints.find(e => e.name === endpoint); - - if (ep == null) { - throw new ApiError({ - message: 'No such endpoint.', - code: 'NO_SUCH_ENDPOINT', - id: 'f8080b67-5f9c-4eb7-8c18-7f1eeae8f709', - httpStatusCode: 404, - }); - } - if (ep.meta.secure && !isSecure) { throw new ApiError(accessDenied); } @@ -116,7 +105,7 @@ export default async (endpoint: string, user: CacheableLocalUser | null | undefi // API invoking const before = performance.now(); - return await ep.exec(data, user, token, ctx?.file, ctx?.ip, ctx?.headers).catch((e: Error) => { + return await exec(data, user, token, ctx?.file, ctx?.ip, ctx?.headers).catch((e: Error) => { if (e instanceof ApiError) { throw e; } else { diff --git a/packages/backend/src/server/api/endpoints.module.ts b/packages/backend/src/server/api/endpoints.module.ts new file mode 100644 index 000000000000..6fb21e6e6e5e --- /dev/null +++ b/packages/backend/src/server/api/endpoints.module.ts @@ -0,0 +1,640 @@ +import { Module } from '@nestjs/common'; + +import * as ep___admin_meta from './endpoints/admin/meta.js'; +import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js'; +import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js'; +import * as ep___admin_accounts_delete from './endpoints/admin/accounts/delete.js'; +import * as ep___admin_ad_create from './endpoints/admin/ad/create.js'; +import * as ep___admin_ad_delete from './endpoints/admin/ad/delete.js'; +import * as ep___admin_ad_list from './endpoints/admin/ad/list.js'; +import * as ep___admin_ad_update from './endpoints/admin/ad/update.js'; +import * as ep___admin_announcements_create from './endpoints/admin/announcements/create.js'; +import * as ep___admin_announcements_delete from './endpoints/admin/announcements/delete.js'; +import * as ep___admin_announcements_list from './endpoints/admin/announcements/list.js'; +import * as ep___admin_announcements_update from './endpoints/admin/announcements/update.js'; +import * as ep___admin_deleteAllFilesOfAUser from './endpoints/admin/delete-all-files-of-a-user.js'; +import * as ep___admin_drive_cleanRemoteFiles from './endpoints/admin/drive/clean-remote-files.js'; +import * as ep___admin_drive_cleanup from './endpoints/admin/drive/cleanup.js'; +import * as ep___admin_drive_files from './endpoints/admin/drive/files.js'; +import * as ep___admin_drive_showFile from './endpoints/admin/drive/show-file.js'; +import * as ep___admin_emoji_addAliasesBulk from './endpoints/admin/emoji/add-aliases-bulk.js'; +import * as ep___admin_emoji_add from './endpoints/admin/emoji/add.js'; +import * as ep___admin_emoji_copy from './endpoints/admin/emoji/copy.js'; +import * as ep___admin_emoji_deleteBulk from './endpoints/admin/emoji/delete-bulk.js'; +import * as ep___admin_emoji_delete from './endpoints/admin/emoji/delete.js'; +import * as ep___admin_emoji_importZip from './endpoints/admin/emoji/import-zip.js'; +import * as ep___admin_emoji_listRemote from './endpoints/admin/emoji/list-remote.js'; +import * as ep___admin_emoji_list from './endpoints/admin/emoji/list.js'; +import * as ep___admin_emoji_removeAliasesBulk from './endpoints/admin/emoji/remove-aliases-bulk.js'; +import * as ep___admin_emoji_setAliasesBulk from './endpoints/admin/emoji/set-aliases-bulk.js'; +import * as ep___admin_emoji_setCategoryBulk from './endpoints/admin/emoji/set-category-bulk.js'; +import * as ep___admin_emoji_update from './endpoints/admin/emoji/update.js'; +import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federation/delete-all-files.js'; +import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js'; +import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js'; +import * as ep___admin_federation_updateInstance from './endpoints/admin/federation/update-instance.js'; +import * as ep___admin_getIndexStats from './endpoints/admin/get-index-stats.js'; +import * as ep___admin_getTableStats from './endpoints/admin/get-table-stats.js'; +import * as ep___admin_getUserIps from './endpoints/admin/get-user-ips.js'; +import * as ep___admin_invite from './endpoints/admin/invite.js'; +import * as ep___admin_moderators_add from './endpoints/admin/moderators/add.js'; +import * as ep___admin_moderators_remove from './endpoints/admin/moderators/remove.js'; +import * as ep___admin_promo_create from './endpoints/admin/promo/create.js'; +import * as ep___admin_queue_clear from './endpoints/admin/queue/clear.js'; +import * as ep___admin_queue_deliverDelayed from './endpoints/admin/queue/deliver-delayed.js'; +import * as ep___admin_queue_inboxDelayed from './endpoints/admin/queue/inbox-delayed.js'; +import * as ep___admin_queue_stats from './endpoints/admin/queue/stats.js'; +import * as ep___admin_relays_add from './endpoints/admin/relays/add.js'; +import * as ep___admin_relays_list from './endpoints/admin/relays/list.js'; +import * as ep___admin_relays_remove from './endpoints/admin/relays/remove.js'; +import * as ep___admin_resetPassword from './endpoints/admin/reset-password.js'; +import * as ep___admin_resolveAbuseUserReport from './endpoints/admin/resolve-abuse-user-report.js'; +import * as ep___admin_sendEmail from './endpoints/admin/send-email.js'; +import * as ep___admin_serverInfo from './endpoints/admin/server-info.js'; +import * as ep___admin_showModerationLogs from './endpoints/admin/show-moderation-logs.js'; +import * as ep___admin_showUser from './endpoints/admin/show-user.js'; +import * as ep___admin_showUsers from './endpoints/admin/show-users.js'; +import * as ep___admin_silenceUser from './endpoints/admin/silence-user.js'; +import * as ep___admin_suspendUser from './endpoints/admin/suspend-user.js'; +import * as ep___admin_unsilenceUser from './endpoints/admin/unsilence-user.js'; +import * as ep___admin_unsuspendUser from './endpoints/admin/unsuspend-user.js'; +import * as ep___admin_updateMeta from './endpoints/admin/update-meta.js'; +import * as ep___admin_vacuum from './endpoints/admin/vacuum.js'; +import * as ep___admin_deleteAccount from './endpoints/admin/delete-account.js'; +import * as ep___admin_updateUserNote from './endpoints/admin/update-user-note.js'; +import * as ep___announcements from './endpoints/announcements.js'; +import * as ep___antennas_create from './endpoints/antennas/create.js'; +import * as ep___antennas_delete from './endpoints/antennas/delete.js'; +import * as ep___antennas_list from './endpoints/antennas/list.js'; +import * as ep___antennas_notes from './endpoints/antennas/notes.js'; +import * as ep___antennas_show from './endpoints/antennas/show.js'; +import * as ep___antennas_update from './endpoints/antennas/update.js'; +import * as ep___ap_get from './endpoints/ap/get.js'; +import * as ep___ap_show from './endpoints/ap/show.js'; +import * as ep___app_create from './endpoints/app/create.js'; +import * as ep___app_show from './endpoints/app/show.js'; +import * as ep___auth_accept from './endpoints/auth/accept.js'; +import * as ep___auth_session_generate from './endpoints/auth/session/generate.js'; +import * as ep___auth_session_show from './endpoints/auth/session/show.js'; +import * as ep___auth_session_userkey from './endpoints/auth/session/userkey.js'; +import * as ep___blocking_create from './endpoints/blocking/create.js'; +import * as ep___blocking_delete from './endpoints/blocking/delete.js'; +import * as ep___blocking_list from './endpoints/blocking/list.js'; +import * as ep___channels_create from './endpoints/channels/create.js'; +import * as ep___channels_featured from './endpoints/channels/featured.js'; +import * as ep___channels_follow from './endpoints/channels/follow.js'; +import * as ep___channels_followed from './endpoints/channels/followed.js'; +import * as ep___channels_owned from './endpoints/channels/owned.js'; +import * as ep___channels_show from './endpoints/channels/show.js'; +import * as ep___channels_timeline from './endpoints/channels/timeline.js'; +import * as ep___channels_unfollow from './endpoints/channels/unfollow.js'; +import * as ep___channels_update from './endpoints/channels/update.js'; +import * as ep___charts_activeUsers from './endpoints/charts/active-users.js'; +import * as ep___charts_apRequest from './endpoints/charts/ap-request.js'; +import * as ep___charts_drive from './endpoints/charts/drive.js'; +import * as ep___charts_federation from './endpoints/charts/federation.js'; +import * as ep___charts_hashtag from './endpoints/charts/hashtag.js'; +import * as ep___charts_instance from './endpoints/charts/instance.js'; +import * as ep___charts_notes from './endpoints/charts/notes.js'; +import * as ep___charts_user_drive from './endpoints/charts/user/drive.js'; +import * as ep___charts_user_following from './endpoints/charts/user/following.js'; +import * as ep___charts_user_notes from './endpoints/charts/user/notes.js'; +import * as ep___charts_user_reactions from './endpoints/charts/user/reactions.js'; +import * as ep___charts_users from './endpoints/charts/users.js'; +import * as ep___clips_addNote from './endpoints/clips/add-note.js'; +import * as ep___clips_removeNote from './endpoints/clips/remove-note.js'; +import * as ep___clips_create from './endpoints/clips/create.js'; +import * as ep___clips_delete from './endpoints/clips/delete.js'; +import * as ep___clips_list from './endpoints/clips/list.js'; +import * as ep___clips_notes from './endpoints/clips/notes.js'; +import * as ep___clips_show from './endpoints/clips/show.js'; +import * as ep___clips_update from './endpoints/clips/update.js'; +import * as ep___drive from './endpoints/drive.js'; +import * as ep___drive_files from './endpoints/drive/files.js'; +import * as ep___drive_files_attachedNotes from './endpoints/drive/files/attached-notes.js'; +import * as ep___drive_files_checkExistence from './endpoints/drive/files/check-existence.js'; +import * as ep___drive_files_create from './endpoints/drive/files/create.js'; +import * as ep___drive_files_delete from './endpoints/drive/files/delete.js'; +import * as ep___drive_files_findByHash from './endpoints/drive/files/find-by-hash.js'; +import * as ep___drive_files_find from './endpoints/drive/files/find.js'; +import * as ep___drive_files_show from './endpoints/drive/files/show.js'; +import * as ep___drive_files_update from './endpoints/drive/files/update.js'; +import * as ep___drive_files_uploadFromUrl from './endpoints/drive/files/upload-from-url.js'; +import * as ep___drive_folders from './endpoints/drive/folders.js'; +import * as ep___drive_folders_create from './endpoints/drive/folders/create.js'; +import * as ep___drive_folders_delete from './endpoints/drive/folders/delete.js'; +import * as ep___drive_folders_find from './endpoints/drive/folders/find.js'; +import * as ep___drive_folders_show from './endpoints/drive/folders/show.js'; +import * as ep___drive_folders_update from './endpoints/drive/folders/update.js'; +import * as ep___drive_stream from './endpoints/drive/stream.js'; +import * as ep___emailAddress_available from './endpoints/email-address/available.js'; +import * as ep___endpoint from './endpoints/endpoint.js'; +import * as ep___endpoints from './endpoints/endpoints.js'; +import * as ep___exportCustomEmojis from './endpoints/export-custom-emojis.js'; +import * as ep___federation_followers from './endpoints/federation/followers.js'; +import * as ep___federation_following from './endpoints/federation/following.js'; +import * as ep___federation_instances from './endpoints/federation/instances.js'; +import * as ep___federation_showInstance from './endpoints/federation/show-instance.js'; +import * as ep___federation_updateRemoteUser from './endpoints/federation/update-remote-user.js'; +import * as ep___federation_users from './endpoints/federation/users.js'; +import * as ep___federation_stats from './endpoints/federation/stats.js'; +import * as ep___following_create from './endpoints/following/create.js'; +import * as ep___following_delete from './endpoints/following/delete.js'; +import * as ep___following_invalidate from './endpoints/following/invalidate.js'; +import * as ep___following_requests_accept from './endpoints/following/requests/accept.js'; +import * as ep___following_requests_cancel from './endpoints/following/requests/cancel.js'; +import * as ep___following_requests_list from './endpoints/following/requests/list.js'; +import * as ep___following_requests_reject from './endpoints/following/requests/reject.js'; +import * as ep___gallery_featured from './endpoints/gallery/featured.js'; +import * as ep___gallery_popular from './endpoints/gallery/popular.js'; +import * as ep___gallery_posts from './endpoints/gallery/posts.js'; +import * as ep___gallery_posts_create from './endpoints/gallery/posts/create.js'; +import * as ep___gallery_posts_delete from './endpoints/gallery/posts/delete.js'; +import * as ep___gallery_posts_like from './endpoints/gallery/posts/like.js'; +import * as ep___gallery_posts_show from './endpoints/gallery/posts/show.js'; +import * as ep___gallery_posts_unlike from './endpoints/gallery/posts/unlike.js'; +import * as ep___gallery_posts_update from './endpoints/gallery/posts/update.js'; +import * as ep___getOnlineUsersCount from './endpoints/get-online-users-count.js'; +import * as ep___hashtags_list from './endpoints/hashtags/list.js'; +import * as ep___hashtags_search from './endpoints/hashtags/search.js'; +import * as ep___hashtags_show from './endpoints/hashtags/show.js'; +import * as ep___hashtags_trend from './endpoints/hashtags/trend.js'; +import * as ep___hashtags_users from './endpoints/hashtags/users.js'; +import * as ep___i from './endpoints/i.js'; +import * as ep___i_2fa_done from './endpoints/i/2fa/done.js'; +import * as ep___i_2fa_keyDone from './endpoints/i/2fa/key-done.js'; +import * as ep___i_2fa_passwordLess from './endpoints/i/2fa/password-less.js'; +import * as ep___i_2fa_registerKey from './endpoints/i/2fa/register-key.js'; +import * as ep___i_2fa_register from './endpoints/i/2fa/register.js'; +import * as ep___i_2fa_removeKey from './endpoints/i/2fa/remove-key.js'; +import * as ep___i_2fa_unregister from './endpoints/i/2fa/unregister.js'; +import * as ep___i_apps from './endpoints/i/apps.js'; +import * as ep___i_authorizedApps from './endpoints/i/authorized-apps.js'; +import * as ep___i_changePassword from './endpoints/i/change-password.js'; +import * as ep___i_deleteAccount from './endpoints/i/delete-account.js'; +import * as ep___i_exportBlocking from './endpoints/i/export-blocking.js'; +import * as ep___i_exportFollowing from './endpoints/i/export-following.js'; +import * as ep___i_exportMute from './endpoints/i/export-mute.js'; +import * as ep___i_exportNotes from './endpoints/i/export-notes.js'; +import * as ep___i_exportUserLists from './endpoints/i/export-user-lists.js'; +import * as ep___i_favorites from './endpoints/i/favorites.js'; +import * as ep___i_gallery_likes from './endpoints/i/gallery/likes.js'; +import * as ep___i_gallery_posts from './endpoints/i/gallery/posts.js'; +import * as ep___i_getWordMutedNotesCount from './endpoints/i/get-word-muted-notes-count.js'; +import * as ep___i_importBlocking from './endpoints/i/import-blocking.js'; +import * as ep___i_importFollowing from './endpoints/i/import-following.js'; +import * as ep___i_importMuting from './endpoints/i/import-muting.js'; +import * as ep___i_importUserLists from './endpoints/i/import-user-lists.js'; +import * as ep___i_notifications from './endpoints/i/notifications.js'; +import * as ep___i_pageLikes from './endpoints/i/page-likes.js'; +import * as ep___i_pages from './endpoints/i/pages.js'; +import * as ep___i_pin from './endpoints/i/pin.js'; +import * as ep___i_readAllMessagingMessages from './endpoints/i/read-all-messaging-messages.js'; +import * as ep___i_readAllUnreadNotes from './endpoints/i/read-all-unread-notes.js'; +import * as ep___i_readAnnouncement from './endpoints/i/read-announcement.js'; +import * as ep___i_regenerateToken from './endpoints/i/regenerate-token.js'; +import * as ep___i_registry_getAll from './endpoints/i/registry/get-all.js'; +import * as ep___i_registry_getDetail from './endpoints/i/registry/get-detail.js'; +import * as ep___i_registry_get from './endpoints/i/registry/get.js'; +import * as ep___i_registry_keysWithType from './endpoints/i/registry/keys-with-type.js'; +import * as ep___i_registry_keys from './endpoints/i/registry/keys.js'; +import * as ep___i_registry_remove from './endpoints/i/registry/remove.js'; +import * as ep___i_registry_scopes from './endpoints/i/registry/scopes.js'; +import * as ep___i_registry_set from './endpoints/i/registry/set.js'; +import * as ep___i_revokeToken from './endpoints/i/revoke-token.js'; +import * as ep___i_signinHistory from './endpoints/i/signin-history.js'; +import * as ep___i_unpin from './endpoints/i/unpin.js'; +import * as ep___i_updateEmail from './endpoints/i/update-email.js'; +import * as ep___i_update from './endpoints/i/update.js'; +import * as ep___i_userGroupInvites from './endpoints/i/user-group-invites.js'; +import * as ep___i_webhooks_create from './endpoints/i/webhooks/create.js'; +import * as ep___i_webhooks_show from './endpoints/i/webhooks/show.js'; +import * as ep___i_webhooks_list from './endpoints/i/webhooks/list.js'; +import * as ep___i_webhooks_update from './endpoints/i/webhooks/update.js'; +import * as ep___i_webhooks_delete from './endpoints/i/webhooks/delete.js'; +import * as ep___messaging_history from './endpoints/messaging/history.js'; +import * as ep___messaging_messages from './endpoints/messaging/messages.js'; +import * as ep___messaging_messages_create from './endpoints/messaging/messages/create.js'; +import * as ep___messaging_messages_delete from './endpoints/messaging/messages/delete.js'; +import * as ep___messaging_messages_read from './endpoints/messaging/messages/read.js'; +import * as ep___meta from './endpoints/meta.js'; +import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; +import * as ep___mute_create from './endpoints/mute/create.js'; +import * as ep___mute_delete from './endpoints/mute/delete.js'; +import * as ep___mute_list from './endpoints/mute/list.js'; +import * as ep___my_apps from './endpoints/my/apps.js'; +import * as ep___notes from './endpoints/notes.js'; +import * as ep___notes_children from './endpoints/notes/children.js'; +import * as ep___notes_clips from './endpoints/notes/clips.js'; +import * as ep___notes_conversation from './endpoints/notes/conversation.js'; +import * as ep___notes_create from './endpoints/notes/create.js'; +import * as ep___notes_delete from './endpoints/notes/delete.js'; +import * as ep___notes_favorites_create from './endpoints/notes/favorites/create.js'; +import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js'; +import * as ep___notes_featured from './endpoints/notes/featured.js'; +import * as ep___notes_globalTimeline from './endpoints/notes/global-timeline.js'; +import * as ep___notes_hybridTimeline from './endpoints/notes/hybrid-timeline.js'; +import * as ep___notes_localTimeline from './endpoints/notes/local-timeline.js'; +import * as ep___notes_mentions from './endpoints/notes/mentions.js'; +import * as ep___notes_polls_recommendation from './endpoints/notes/polls/recommendation.js'; +import * as ep___notes_polls_vote from './endpoints/notes/polls/vote.js'; +import * as ep___notes_reactions from './endpoints/notes/reactions.js'; +import * as ep___notes_reactions_create from './endpoints/notes/reactions/create.js'; +import * as ep___notes_reactions_delete from './endpoints/notes/reactions/delete.js'; +import * as ep___notes_renotes from './endpoints/notes/renotes.js'; +import * as ep___notes_replies from './endpoints/notes/replies.js'; +import * as ep___notes_searchByTag from './endpoints/notes/search-by-tag.js'; +import * as ep___notes_search from './endpoints/notes/search.js'; +import * as ep___notes_show from './endpoints/notes/show.js'; +import * as ep___notes_state from './endpoints/notes/state.js'; +import * as ep___notes_threadMuting_create from './endpoints/notes/thread-muting/create.js'; +import * as ep___notes_threadMuting_delete from './endpoints/notes/thread-muting/delete.js'; +import * as ep___notes_timeline from './endpoints/notes/timeline.js'; +import * as ep___notes_translate from './endpoints/notes/translate.js'; +import * as ep___notes_unrenote from './endpoints/notes/unrenote.js'; +import * as ep___notes_userListTimeline from './endpoints/notes/user-list-timeline.js'; +import * as ep___notes_watching_create from './endpoints/notes/watching/create.js'; +import * as ep___notes_watching_delete from './endpoints/notes/watching/delete.js'; +import * as ep___notifications_create from './endpoints/notifications/create.js'; +import * as ep___notifications_markAllAsRead from './endpoints/notifications/mark-all-as-read.js'; +import * as ep___notifications_read from './endpoints/notifications/read.js'; +import * as ep___pagePush from './endpoints/page-push.js'; +import * as ep___pages_create from './endpoints/pages/create.js'; +import * as ep___pages_delete from './endpoints/pages/delete.js'; +import * as ep___pages_featured from './endpoints/pages/featured.js'; +import * as ep___pages_like from './endpoints/pages/like.js'; +import * as ep___pages_show from './endpoints/pages/show.js'; +import * as ep___pages_unlike from './endpoints/pages/unlike.js'; +import * as ep___pages_update from './endpoints/pages/update.js'; +import * as ep___ping from './endpoints/ping.js'; +import * as ep___pinnedUsers from './endpoints/pinned-users.js'; +import * as ep___promo_read from './endpoints/promo/read.js'; +import * as ep___requestResetPassword from './endpoints/request-reset-password.js'; +import * as ep___resetDb from './endpoints/reset-db.js'; +import * as ep___resetPassword from './endpoints/reset-password.js'; +import * as ep___serverInfo from './endpoints/server-info.js'; +import * as ep___stats from './endpoints/stats.js'; +import * as ep___sw_register from './endpoints/sw/register.js'; +import * as ep___sw_unregister from './endpoints/sw/unregister.js'; +import * as ep___test from './endpoints/test.js'; +import * as ep___username_available from './endpoints/username/available.js'; +import * as ep___users from './endpoints/users.js'; +import * as ep___users_clips from './endpoints/users/clips.js'; +import * as ep___users_followers from './endpoints/users/followers.js'; +import * as ep___users_following from './endpoints/users/following.js'; +import * as ep___users_gallery_posts from './endpoints/users/gallery/posts.js'; +import * as ep___users_getFrequentlyRepliedUsers from './endpoints/users/get-frequently-replied-users.js'; +import * as ep___users_groups_create from './endpoints/users/groups/create.js'; +import * as ep___users_groups_delete from './endpoints/users/groups/delete.js'; +import * as ep___users_groups_invitations_accept from './endpoints/users/groups/invitations/accept.js'; +import * as ep___users_groups_invitations_reject from './endpoints/users/groups/invitations/reject.js'; +import * as ep___users_groups_invite from './endpoints/users/groups/invite.js'; +import * as ep___users_groups_joined from './endpoints/users/groups/joined.js'; +import * as ep___users_groups_leave from './endpoints/users/groups/leave.js'; +import * as ep___users_groups_owned from './endpoints/users/groups/owned.js'; +import * as ep___users_groups_pull from './endpoints/users/groups/pull.js'; +import * as ep___users_groups_show from './endpoints/users/groups/show.js'; +import * as ep___users_groups_transfer from './endpoints/users/groups/transfer.js'; +import * as ep___users_groups_update from './endpoints/users/groups/update.js'; +import * as ep___users_lists_create from './endpoints/users/lists/create.js'; +import * as ep___users_lists_delete from './endpoints/users/lists/delete.js'; +import * as ep___users_lists_list from './endpoints/users/lists/list.js'; +import * as ep___users_lists_pull from './endpoints/users/lists/pull.js'; +import * as ep___users_lists_push from './endpoints/users/lists/push.js'; +import * as ep___users_lists_show from './endpoints/users/lists/show.js'; +import * as ep___users_lists_update from './endpoints/users/lists/update.js'; +import * as ep___users_notes from './endpoints/users/notes.js'; +import * as ep___users_pages from './endpoints/users/pages.js'; +import * as ep___users_reactions from './endpoints/users/reactions.js'; +import * as ep___users_recommendation from './endpoints/users/recommendation.js'; +import * as ep___users_relation from './endpoints/users/relation.js'; +import * as ep___users_reportAbuse from './endpoints/users/report-abuse.js'; +import * as ep___users_searchByUsernameAndHost from './endpoints/users/search-by-username-and-host.js'; +import * as ep___users_search from './endpoints/users/search.js'; +import * as ep___users_show from './endpoints/users/show.js'; +import * as ep___users_stats from './endpoints/users/stats.js'; +import * as ep___fetchRss from './endpoints/fetch-rss.js'; +import * as ep___admin_driveCapOverride from './endpoints/admin/drive-capacity-override.js'; + +@Module({ + imports: [ + ], + providers: [ + { provide: 'ep:admin/meta', useClass: ep___admin_meta.default }, + { provide: 'ep:admin/abuse-user-reports', useClass: ep___admin_abuseUserReports.default }, + { provide: 'ep:admin/accounts/create', useClass: ep___admin_accounts_create.default }, + { provide: 'ep:admin/accounts/delete', useClass: ep___admin_accounts_delete.default }, + { provide: 'ep:admin/ad/create', useClass: ep___admin_ad_create.default }, + { provide: 'ep:admin/ad/delete', useClass: ep___admin_ad_delete.default }, + { provide: 'ep:admin/ad/list', useClass: ep___admin_ad_list.default }, + { provide: 'ep:admin/ad/update', useClass: ep___admin_ad_update.default }, + { provide: 'ep:admin/announcements/create', useClass: ep___admin_announcements_create.default }, + { provide: 'ep:admin/announcements/delete', useClass: ep___admin_announcements_delete.default }, + { provide: 'ep:admin/announcements/list', useClass: ep___admin_announcements_list.default }, + { provide: 'ep:admin/announcements/update', useClass: ep___admin_announcements_update.default }, + { provide: 'ep:admin/delete-all-files-of-a-user', useClass: ep___admin_deleteAllFilesOfAUser.default }, + { provide: 'ep:admin/drive/clean-remote-files', useClass: ep___admin_drive_cleanRemoteFiles.default }, + { provide: 'ep:admin/drive/cleanup', useClass: ep___admin_drive_cleanup.default }, + { provide: 'ep:admin/drive/files', useClass: ep___admin_drive_files.default }, + { provide: 'ep:admin/drive/show-file', useClass: ep___admin_drive_showFile.default }, + { provide: 'ep:admin/emoji/add-aliases-bulk', useClass: ep___admin_emoji_addAliasesBulk.default }, + { provide: 'ep:admin/emoji/add', useClass: ep___admin_emoji_add.default }, + { provide: 'ep:admin/emoji/copy', useClass: ep___admin_emoji_copy.default }, + { provide: 'ep:admin/emoji/delete-bulk', useClass: ep___admin_emoji_deleteBulk.default }, + { provide: 'ep:admin/emoji/delete', useClass: ep___admin_emoji_delete.default }, + { provide: 'ep:admin/emoji/import-zip', useClass: ep___admin_emoji_importZip.default }, + { provide: 'ep:admin/emoji/list-remote', useClass: ep___admin_emoji_listRemote.default }, + { provide: 'ep:admin/emoji/list', useClass: ep___admin_emoji_list.default }, + { provide: 'ep:admin/emoji/remove-aliases-bulk', useClass: ep___admin_emoji_removeAliasesBulk.default }, + { provide: 'ep:admin/emoji/set-aliases-bulk', useClass: ep___admin_emoji_setAliasesBulk.default }, + { provide: 'ep:admin/emoji/set-category-bulk', useClass: ep___admin_emoji_setCategoryBulk.default }, + { provide: 'ep:admin/emoji/update', useClass: ep___admin_emoji_update.default }, + { provide: 'ep:admin/federation/delete-all-files', useClass: ep___admin_federation_deleteAllFiles.default }, + { provide: 'ep:admin/federation/refresh-remote-instance-metadata', useClass: ep___admin_federation_refreshRemoteInstanceMetadata.default }, + { provide: 'ep:admin/federation/remove-all-following', useClass: ep___admin_federation_removeAllFollowing.default }, + { provide: 'ep:admin/federation/update-instance', useClass: ep___admin_federation_updateInstance.default }, + { provide: 'ep:admin/get-index-stats', useClass: ep___admin_getIndexStats.default }, + { provide: 'ep:admin/get-table-stats', useClass: ep___admin_getTableStats.default }, + { provide: 'ep:admin/get-user-ips', useClass: ep___admin_getUserIps.default }, + { provide: 'ep:admin/invite', useClass: ep___admin_invite.default }, + { provide: 'ep:admin/moderators/add', useClass: ep___admin_moderators_add.default }, + { provide: 'ep:admin/moderators/remove', useClass: ep___admin_moderators_remove.default }, + { provide: 'ep:admin/promo/create', useClass: ep___admin_promo_create.default }, + { provide: 'ep:admin/queue/clear', useClass: ep___admin_queue_clear.default }, + { provide: 'ep:admin/queue/deliver-delayed', useClass: ep___admin_queue_deliverDelayed.default }, + { provide: 'ep:admin/queue/inbox-delayed', useClass: ep___admin_queue_inboxDelayed.default }, + { provide: 'ep:admin/queue/stats', useClass: ep___admin_queue_stats.default }, + { provide: 'ep:admin/relays/add', useClass: ep___admin_relays_add.default }, + { provide: 'ep:admin/relays/list', useClass: ep___admin_relays_list.default }, + { provide: 'ep:admin/relays/remove', useClass: ep___admin_relays_remove.default }, + { provide: 'ep:admin/reset-password', useClass: ep___admin_resetPassword.default }, + { provide: 'ep:admin/resolve-abuse-user-report', useClass: ep___admin_resolveAbuseUserReport.default }, + { provide: 'ep:admin/send-email', useClass: ep___admin_sendEmail.default }, + { provide: 'ep:admin/server-info', useClass: ep___admin_serverInfo.default }, + { provide: 'ep:admin/show-moderation-logs', useClass: ep___admin_showModerationLogs.default }, + { provide: 'ep:admin/show-user', useClass: ep___admin_showUser.default }, + { provide: 'ep:admin/show-users', useClass: ep___admin_showUsers.default }, + { provide: 'ep:admin/silence-user', useClass: ep___admin_silenceUser.default }, + { provide: 'ep:admin/suspend-user', useClass: ep___admin_suspendUser.default }, + { provide: 'ep:admin/unsilence-user', useClass: ep___admin_unsilenceUser.default }, + { provide: 'ep:admin/unsuspend-user', useClass: ep___admin_unsuspendUser.default }, + { provide: 'ep:admin/update-meta', useClass: ep___admin_updateMeta.default }, + { provide: 'ep:admin/vacuum', useClass: ep___admin_vacuum.default }, + { provide: 'ep:admin/delete-account', useClass: ep___admin_deleteAccount.default }, + { provide: 'ep:admin/update-user-note', useClass: ep___admin_updateUserNote.default }, + { provide: 'ep:announcements', useClass: ep___announcements.default }, + { provide: 'ep:antennas/create', useClass: ep___antennas_create.default }, + { provide: 'ep:antennas/delete', useClass: ep___antennas_delete.default }, + { provide: 'ep:antennas/list', useClass: ep___antennas_list.default }, + { provide: 'ep:antennas/notes', useClass: ep___antennas_notes.default }, + { provide: 'ep:antennas/show', useClass: ep___antennas_show.default }, + { provide: 'ep:antennas/update', useClass: ep___antennas_update.default }, + { provide: 'ep:ap/get', useClass: ep___ap_get.default }, + { provide: 'ep:ap/show', useClass: ep___ap_show.default }, + { provide: 'ep:app/create', useClass: ep___app_create.default }, + { provide: 'ep:app/show', useClass: ep___app_show.default }, + { provide: 'ep:auth/accept', useClass: ep___auth_accept.default }, + { provide: 'ep:auth/session/generate', useClass: ep___auth_session_generate.default }, + { provide: 'ep:auth/session/show', useClass: ep___auth_session_show.default }, + { provide: 'ep:auth/session/userkey', useClass: ep___auth_session_userkey.default }, + { provide: 'ep:blocking/create', useClass: ep___blocking_create.default }, + { provide: 'ep:blocking/delete', useClass: ep___blocking_delete.default }, + { provide: 'ep:blocking/list', useClass: ep___blocking_list.default }, + { provide: 'ep:channels/create', useClass: ep___channels_create.default }, + { provide: 'ep:channels/featured', useClass: ep___channels_featured.default }, + { provide: 'ep:channels/follow', useClass: ep___channels_follow.default }, + { provide: 'ep:channels/followed', useClass: ep___channels_followed.default }, + { provide: 'ep:channels/owned', useClass: ep___channels_owned.default }, + { provide: 'ep:channels/show', useClass: ep___channels_show.default }, + { provide: 'ep:channels/timeline', useClass: ep___channels_timeline.default }, + { provide: 'ep:channels/unfollow', useClass: ep___channels_unfollow.default }, + { provide: 'ep:channels/update', useClass: ep___channels_update.default }, + { provide: 'ep:charts/active-users', useClass: ep___charts_activeUsers.default }, + { provide: 'ep:charts/ap-request', useClass: ep___charts_apRequest.default }, + { provide: 'ep:charts/drive', useClass: ep___charts_drive.default }, + { provide: 'ep:charts/federation', useClass: ep___charts_federation.default }, + { provide: 'ep:charts/hashtag', useClass: ep___charts_hashtag.default }, + { provide: 'ep:charts/instance', useClass: ep___charts_instance.default }, + { provide: 'ep:charts/notes', useClass: ep___charts_notes.default }, + { provide: 'ep:charts/user/drive', useClass: ep___charts_user_drive.default }, + { provide: 'ep:charts/user/following', useClass: ep___charts_user_following.default }, + { provide: 'ep:charts/user/notes', useClass: ep___charts_user_notes.default }, + { provide: 'ep:charts/user/reactions', useClass: ep___charts_user_reactions.default }, + { provide: 'ep:charts/users', useClass: ep___charts_users.default }, + { provide: 'ep:clips/add-note', useClass: ep___clips_addNote.default }, + { provide: 'ep:clips/remove-note', useClass: ep___clips_removeNote.default }, + { provide: 'ep:clips/create', useClass: ep___clips_create.default }, + { provide: 'ep:clips/delete', useClass: ep___clips_delete.default }, + { provide: 'ep:clips/list', useClass: ep___clips_list.default }, + { provide: 'ep:clips/notes', useClass: ep___clips_notes.default }, + { provide: 'ep:clips/show', useClass: ep___clips_show.default }, + { provide: 'ep:clips/update', useClass: ep___clips_update.default }, + { provide: 'ep:drive', useClass: ep___drive.default }, + { provide: 'ep:drive/files', useClass: ep___drive_files.default }, + { provide: 'ep:drive/files/attached-notes', useClass: ep___drive_files_attachedNotes.default }, + { provide: 'ep:drive/files/check-existence', useClass: ep___drive_files_checkExistence.default }, + { provide: 'ep:drive/files/create', useClass: ep___drive_files_create.default }, + { provide: 'ep:drive/files/delete', useClass: ep___drive_files_delete.default }, + { provide: 'ep:drive/files/find-by-hash', useClass: ep___drive_files_findByHash.default }, + { provide: 'ep:drive/files/find', useClass: ep___drive_files_find.default }, + { provide: 'ep:drive/files/show', useClass: ep___drive_files_show.default }, + { provide: 'ep:drive/files/update', useClass: ep___drive_files_update.default }, + { provide: 'ep:drive/files/upload-from-url', useClass: ep___drive_files_uploadFromUrl.default }, + { provide: 'ep:drive/folders', useClass: ep___drive_folders.default }, + { provide: 'ep:drive/folders/create', useClass: ep___drive_folders_create.default }, + { provide: 'ep:drive/folders/delete', useClass: ep___drive_folders_delete.default }, + { provide: 'ep:drive/folders/find', useClass: ep___drive_folders_find.default }, + { provide: 'ep:drive/folders/show', useClass: ep___drive_folders_show.default }, + { provide: 'ep:drive/folders/update', useClass: ep___drive_folders_update.default }, + { provide: 'ep:drive/stream', useClass: ep___drive_stream.default }, + { provide: 'ep:email-address/available', useClass: ep___emailAddress_available.default }, + { provide: 'ep:endpoint', useClass: ep___endpoint.default }, + { provide: 'ep:endpoints', useClass: ep___endpoints.default }, + { provide: 'ep:export-custom-emojis', useClass: ep___exportCustomEmojis.default }, + { provide: 'ep:federation/followers', useClass: ep___federation_followers.default }, + { provide: 'ep:federation/following', useClass: ep___federation_following.default }, + { provide: 'ep:federation/instances', useClass: ep___federation_instances.default }, + { provide: 'ep:federation/show-instance', useClass: ep___federation_showInstance.default }, + { provide: 'ep:federation/update-remote-user', useClass: ep___federation_updateRemoteUser.default }, + { provide: 'ep:federation/users', useClass: ep___federation_users.default }, + { provide: 'ep:federation/stats', useClass: ep___federation_stats.default }, + { provide: 'ep:following/create', useClass: ep___following_create.default }, + { provide: 'ep:following/delete', useClass: ep___following_delete.default }, + { provide: 'ep:following/invalidate', useClass: ep___following_invalidate.default }, + { provide: 'ep:following/requests/accept', useClass: ep___following_requests_accept.default }, + { provide: 'ep:following/requests/cancel', useClass: ep___following_requests_cancel.default }, + { provide: 'ep:following/requests/list', useClass: ep___following_requests_list.default }, + { provide: 'ep:following/requests/reject', useClass: ep___following_requests_reject.default }, + { provide: 'ep:gallery/featured', useClass: ep___gallery_featured.default }, + { provide: 'ep:gallery/popular', useClass: ep___gallery_popular.default }, + { provide: 'ep:gallery/posts', useClass: ep___gallery_posts.default }, + { provide: 'ep:gallery/posts/create', useClass: ep___gallery_posts_create.default }, + { provide: 'ep:gallery/posts/delete', useClass: ep___gallery_posts_delete.default }, + { provide: 'ep:gallery/posts/like', useClass: ep___gallery_posts_like.default }, + { provide: 'ep:gallery/posts/show', useClass: ep___gallery_posts_show.default }, + { provide: 'ep:gallery/posts/unlike', useClass: ep___gallery_posts_unlike.default }, + { provide: 'ep:gallery/posts/update', useClass: ep___gallery_posts_update.default }, + { provide: 'ep:get-online-users-count', useClass: ep___getOnlineUsersCount.default }, + { provide: 'ep:hashtags/list', useClass: ep___hashtags_list.default }, + { provide: 'ep:hashtags/search', useClass: ep___hashtags_search.default }, + { provide: 'ep:hashtags/show', useClass: ep___hashtags_show.default }, + { provide: 'ep:hashtags/trend', useClass: ep___hashtags_trend.default }, + { provide: 'ep:hashtags/users', useClass: ep___hashtags_users.default }, + { provide: 'ep:i', useClass: ep___i.default }, + { provide: 'ep:i/2fa/done', useClass: ep___i_2fa_done.default }, + { provide: 'ep:i/2fa/key-done', useClass: ep___i_2fa_keyDone.default }, + { provide: 'ep:i/2fa/password-less', useClass: ep___i_2fa_passwordLess.default }, + { provide: 'ep:i/2fa/register-key', useClass: ep___i_2fa_registerKey.default }, + { provide: 'ep:i/2fa/register', useClass: ep___i_2fa_register.default }, + { provide: 'ep:i/2fa/remove-key', useClass: ep___i_2fa_removeKey.default }, + { provide: 'ep:i/2fa/unregister', useClass: ep___i_2fa_unregister.default }, + { provide: 'ep:i/apps', useClass: ep___i_apps.default }, + { provide: 'ep:i/authorized-apps', useClass: ep___i_authorizedApps.default }, + { provide: 'ep:i/change-password', useClass: ep___i_changePassword.default }, + { provide: 'ep:i/delete-account', useClass: ep___i_deleteAccount.default }, + { provide: 'ep:i/export-blocking', useClass: ep___i_exportBlocking.default }, + { provide: 'ep:i/export-following', useClass: ep___i_exportFollowing.default }, + { provide: 'ep:i/export-mute', useClass: ep___i_exportMute.default }, + { provide: 'ep:i/export-notes', useClass: ep___i_exportNotes.default }, + { provide: 'ep:i/export-user-lists', useClass: ep___i_exportUserLists.default }, + { provide: 'ep:i/favorites', useClass: ep___i_favorites.default }, + { provide: 'ep:i/gallery/likes', useClass: ep___i_gallery_likes.default }, + { provide: 'ep:i/gallery/posts', useClass: ep___i_gallery_posts.default }, + { provide: 'ep:i/get-word-muted-notes-count', useClass: ep___i_getWordMutedNotesCount.default }, + { provide: 'ep:i/import-blocking', useClass: ep___i_importBlocking.default }, + { provide: 'ep:i/import-following', useClass: ep___i_importFollowing.default }, + { provide: 'ep:i/import-muting', useClass: ep___i_importMuting.default }, + { provide: 'ep:i/import-user-lists', useClass: ep___i_importUserLists.default }, + { provide: 'ep:i/notifications', useClass: ep___i_notifications.default }, + { provide: 'ep:i/page-likes', useClass: ep___i_pageLikes.default }, + { provide: 'ep:i/pages', useClass: ep___i_pages.default }, + { provide: 'ep:i/pin', useClass: ep___i_pin.default }, + { provide: 'ep:i/read-all-messaging-messages', useClass: ep___i_readAllMessagingMessages.default }, + { provide: 'ep:i/read-all-unread-notes', useClass: ep___i_readAllUnreadNotes.default }, + { provide: 'ep:i/read-announcement', useClass: ep___i_readAnnouncement.default }, + { provide: 'ep:i/regenerate-token', useClass: ep___i_regenerateToken.default }, + { provide: 'ep:i/registry/get-all', useClass: ep___i_registry_getAll.default }, + { provide: 'ep:i/registry/get-detail', useClass: ep___i_registry_getDetail.default }, + { provide: 'ep:i/registry/get', useClass: ep___i_registry_get.default }, + { provide: 'ep:i/registry/keys-with-type', useClass: ep___i_registry_keysWithType.default }, + { provide: 'ep:i/registry/keys', useClass: ep___i_registry_keys.default }, + { provide: 'ep:i/registry/remove', useClass: ep___i_registry_remove.default }, + { provide: 'ep:i/registry/scopes', useClass: ep___i_registry_scopes.default }, + { provide: 'ep:i/registry/set', useClass: ep___i_registry_set.default }, + { provide: 'ep:i/revoke-token', useClass: ep___i_revokeToken.default }, + { provide: 'ep:i/signin-history', useClass: ep___i_signinHistory.default }, + { provide: 'ep:i/unpin', useClass: ep___i_unpin.default }, + { provide: 'ep:i/update-email', useClass: ep___i_updateEmail.default }, + { provide: 'ep:i/update', useClass: ep___i_update.default }, + { provide: 'ep:i/user-group-invites', useClass: ep___i_userGroupInvites.default }, + { provide: 'ep:i/webhooks/create', useClass: ep___i_webhooks_create.default }, + { provide: 'ep:i/webhooks/list', useClass: ep___i_webhooks_list.default }, + { provide: 'ep:i/webhooks/show', useClass: ep___i_webhooks_show.default }, + { provide: 'ep:i/webhooks/update', useClass: ep___i_webhooks_update.default }, + { provide: 'ep:i/webhooks/delete', useClass: ep___i_webhooks_delete.default }, + { provide: 'ep:messaging/history', useClass: ep___messaging_history.default }, + { provide: 'ep:messaging/messages', useClass: ep___messaging_messages.default }, + { provide: 'ep:messaging/messages/create', useClass: ep___messaging_messages_create.default }, + { provide: 'ep:messaging/messages/delete', useClass: ep___messaging_messages_delete.default }, + { provide: 'ep:messaging/messages/read', useClass: ep___messaging_messages_read.default }, + { provide: 'ep:meta', useClass: ep___meta.default }, + { provide: 'ep:miauth/gen-token', useClass: ep___miauth_genToken.default }, + { provide: 'ep:mute/create', useClass: ep___mute_create.default }, + { provide: 'ep:mute/delete', useClass: ep___mute_delete.default }, + { provide: 'ep:mute/list', useClass: ep___mute_list.default }, + { provide: 'ep:my/apps', useClass: ep___my_apps.default }, + { provide: 'ep:notes', useClass: ep___notes.default }, + { provide: 'ep:notes/children', useClass: ep___notes_children.default }, + { provide: 'ep:notes/clips', useClass: ep___notes_clips.default }, + { provide: 'ep:notes/conversation', useClass: ep___notes_conversation.default }, + { provide: 'ep:notes/create', useClass: ep___notes_create.default }, + { provide: 'ep:notes/delete', useClass: ep___notes_delete.default }, + { provide: 'ep:notes/favorites/create', useClass: ep___notes_favorites_create.default }, + { provide: 'ep:notes/favorites/delete', useClass: ep___notes_favorites_delete.default }, + { provide: 'ep:notes/featured', useClass: ep___notes_featured.default }, + { provide: 'ep:notes/global-timeline', useClass: ep___notes_globalTimeline.default }, + { provide: 'ep:notes/hybrid-timeline', useClass: ep___notes_hybridTimeline.default }, + { provide: 'ep:notes/local-timeline', useClass: ep___notes_localTimeline.default }, + { provide: 'ep:notes/mentions', useClass: ep___notes_mentions.default }, + { provide: 'ep:notes/polls/recommendation', useClass: ep___notes_polls_recommendation.default }, + { provide: 'ep:notes/polls/vote', useClass: ep___notes_polls_vote.default }, + { provide: 'ep:notes/reactions', useClass: ep___notes_reactions.default }, + { provide: 'ep:notes/reactions/create', useClass: ep___notes_reactions_create.default }, + { provide: 'ep:notes/reactions/delete', useClass: ep___notes_reactions_delete.default }, + { provide: 'ep:notes/renotes', useClass: ep___notes_renotes.default }, + { provide: 'ep:notes/replies', useClass: ep___notes_replies.default }, + { provide: 'ep:notes/search-by-tag', useClass: ep___notes_searchByTag.default }, + { provide: 'ep:notes/search', useClass: ep___notes_search.default }, + { provide: 'ep:notes/show', useClass: ep___notes_show.default }, + { provide: 'ep:notes/state', useClass: ep___notes_state.default }, + { provide: 'ep:notes/thread-muting/create', useClass: ep___notes_threadMuting_create.default }, + { provide: 'ep:notes/thread-muting/delete', useClass: ep___notes_threadMuting_delete.default }, + { provide: 'ep:notes/timeline', useClass: ep___notes_timeline.default }, + { provide: 'ep:notes/translate', useClass: ep___notes_translate.default }, + { provide: 'ep:notes/unrenote', useClass: ep___notes_unrenote.default }, + { provide: 'ep:notes/user-list-timeline', useClass: ep___notes_userListTimeline.default }, + { provide: 'ep:notes/watching/create', useClass: ep___notes_watching_create.default }, + { provide: 'ep:notes/watching/delete', useClass: ep___notes_watching_delete.default }, + { provide: 'ep:notifications/create', useClass: ep___notifications_create.default }, + { provide: 'ep:notifications/mark-all-as-read', useClass: ep___notifications_markAllAsRead.default }, + { provide: 'ep:notifications/read', useClass: ep___notifications_read.default }, + { provide: 'ep:page-push', useClass: ep___pagePush.default }, + { provide: 'ep:pages/create', useClass: ep___pages_create.default }, + { provide: 'ep:pages/delete', useClass: ep___pages_delete.default }, + { provide: 'ep:pages/featured', useClass: ep___pages_featured.default }, + { provide: 'ep:pages/like', useClass: ep___pages_like.default }, + { provide: 'ep:pages/show', useClass: ep___pages_show.default }, + { provide: 'ep:pages/unlike', useClass: ep___pages_unlike.default }, + { provide: 'ep:pages/update', useClass: ep___pages_update.default }, + { provide: 'ep:ping', useClass: ep___ping.default }, + { provide: 'ep:pinned-users', useClass: ep___pinnedUsers.default }, + { provide: 'ep:promo/read', useClass: ep___promo_read.default }, + { provide: 'ep:request-reset-password', useClass: ep___requestResetPassword.default }, + { provide: 'ep:reset-db', useClass: ep___resetDb.default }, + { provide: 'ep:reset-password', useClass: ep___resetPassword.default }, + { provide: 'ep:server-info', useClass: ep___serverInfo.default }, + { provide: 'ep:stats', useClass: ep___stats.default }, + { provide: 'ep:sw/register', useClass: ep___sw_register.default }, + { provide: 'ep:sw/unregister', useClass: ep___sw_unregister.default }, + { provide: 'ep:test', useClass: ep___test.default }, + { provide: 'ep:username/available', useClass: ep___username_available.default }, + { provide: 'ep:users', useClass: ep___users.default }, + { provide: 'ep:users/clips', useClass: ep___users_clips.default }, + { provide: 'ep:users/followers', useClass: ep___users_followers.default }, + { provide: 'ep:users/following', useClass: ep___users_following.default }, + { provide: 'ep:users/gallery/posts', useClass: ep___users_gallery_posts.default }, + { provide: 'ep:users/get-frequently-replied-users', useClass: ep___users_getFrequentlyRepliedUsers.default }, + { provide: 'ep:users/groups/create', useClass: ep___users_groups_create.default }, + { provide: 'ep:users/groups/delete', useClass: ep___users_groups_delete.default }, + { provide: 'ep:users/groups/invitations/accept', useClass: ep___users_groups_invitations_accept.default }, + { provide: 'ep:users/groups/invitations/reject', useClass: ep___users_groups_invitations_reject.default }, + { provide: 'ep:users/groups/invite', useClass: ep___users_groups_invite.default }, + { provide: 'ep:users/groups/joined', useClass: ep___users_groups_joined.default }, + { provide: 'ep:users/groups/leave', useClass: ep___users_groups_leave.default }, + { provide: 'ep:users/groups/owned', useClass: ep___users_groups_owned.default }, + { provide: 'ep:users/groups/pull', useClass: ep___users_groups_pull.default }, + { provide: 'ep:users/groups/show', useClass: ep___users_groups_show.default }, + { provide: 'ep:users/groups/transfer', useClass: ep___users_groups_transfer.default }, + { provide: 'ep:users/groups/update', useClass: ep___users_groups_update.default }, + { provide: 'ep:users/lists/create', useClass: ep___users_lists_create.default }, + { provide: 'ep:users/lists/delete', useClass: ep___users_lists_delete.default }, + { provide: 'ep:users/lists/list', useClass: ep___users_lists_list.default }, + { provide: 'ep:users/lists/pull', useClass: ep___users_lists_pull.default }, + { provide: 'ep:users/lists/push', useClass: ep___users_lists_push.default }, + { provide: 'ep:users/lists/show', useClass: ep___users_lists_show.default }, + { provide: 'ep:users/lists/update', useClass: ep___users_lists_update.default }, + { provide: 'ep:users/notes', useClass: ep___users_notes.default }, + { provide: 'ep:users/pages', useClass: ep___users_pages.default }, + { provide: 'ep:users/reactions', useClass: ep___users_reactions.default }, + { provide: 'ep:users/recommendation', useClass: ep___users_recommendation.default }, + { provide: 'ep:users/relation', useClass: ep___users_relation.default }, + { provide: 'ep:users/report-abuse', useClass: ep___users_reportAbuse.default }, + { provide: 'ep:users/search-by-username-and-host', useClass: ep___users_searchByUsernameAndHost.default }, + { provide: 'ep:users/search', useClass: ep___users_search.default }, + { provide: 'ep:users/show', useClass: ep___users_show.default }, + { provide: 'ep:users/stats', useClass: ep___users_stats.default }, + { provide: 'ep:admin/drive-capacity-override', useClass: ep___admin_driveCapOverride.default }, + { provide: 'ep:fetch-rss', useClass: ep___fetchRss.default }, + ], + }) +export class EndpointsModule {} diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 4644f34d9469..cd6eaab0f839 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -1,4 +1,4 @@ -import { Schema } from '@/misc/schema.js'; +import type { Schema } from '@/misc/schema.js'; import * as ep___admin_meta from './endpoints/admin/meta.js'; import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js'; @@ -727,7 +727,6 @@ export interface IEndpointMeta { export interface IEndpoint { name: string; - exec: any; meta: IEndpointMeta; params: Schema; } @@ -735,7 +734,6 @@ export interface IEndpoint { const endpoints: IEndpoint[] = eps.map(([name, ep]) => { return { name: name, - exec: ep.default, meta: ep.meta || {}, params: ep.paramDef, }; diff --git a/packages/backend/src/server/api/index.ts b/packages/backend/src/server/api/index.ts index 83ece51f517b..9ddfa7686317 100644 --- a/packages/backend/src/server/api/index.ts +++ b/packages/backend/src/server/api/index.ts @@ -18,109 +18,118 @@ import signupPending from './private/signup-pending.js'; import discord from './service/discord.js'; import github from './service/github.js'; import twitter from './service/twitter.js'; +import type { INestApplicationContext } from '@nestjs/common'; -// Init app -const app = new Koa(); +export function createApiServer(app: INestApplicationContext) { + const handlers: Record = {}; -app.use(cors({ - origin: '*', -})); + for (const endpoint of endpoints) { + handlers[endpoint.name] = app.get('ep:' + endpoint.name).exec; + } -// No caching -app.use(async (ctx, next) => { - ctx.set('Cache-Control', 'private, max-age=0, must-revalidate'); - await next(); -}); + // Init app + const apiServer = new Koa(); -app.use(bodyParser({ - // リクエストが multipart/form-data でない限りはJSONだと見なす - detectJSON: ctx => !ctx.is('multipart/form-data'), -})); + apiServer.use(cors({ + origin: '*', + })); -// Init multer instance -const upload = multer({ - storage: multer.diskStorage({}), - limits: { - fileSize: config.maxFileSize || 262144000, - files: 1, - }, -}); + // No caching + apiServer.use(async (ctx, next) => { + ctx.set('Cache-Control', 'private, max-age=0, must-revalidate'); + await next(); + }); -// Init router -const router = new Router(); + apiServer.use(bodyParser({ + // リクエストが multipart/form-data でない限りはJSONだと見なす + detectJSON: ctx => !ctx.is('multipart/form-data'), + })); + + // Init multer instance + const upload = multer({ + storage: multer.diskStorage({}), + limits: { + fileSize: config.maxFileSize || 262144000, + files: 1, + }, + }); -/** + // Init router + const router = new Router(); + + /** * Register endpoint handlers */ -for (const endpoint of endpoints) { - if (endpoint.meta.requireFile) { - router.post(`/${endpoint.name}`, upload.single('file'), handler.bind(null, endpoint)); - } else { + for (const endpoint of endpoints) { + if (endpoint.meta.requireFile) { + router.post(`/${endpoint.name}`, upload.single('file'), handler.bind(null, endpoint, handlers[endpoint.name])); + } else { // 後方互換性のため - if (endpoint.name.includes('-')) { - router.post(`/${endpoint.name.replace(/-/g, '_')}`, handler.bind(null, endpoint)); + if (endpoint.name.includes('-')) { + router.post(`/${endpoint.name.replace(/-/g, '_')}`, handler.bind(null, endpoint, handlers[endpoint.name])); + + if (endpoint.meta.allowGet) { + router.get(`/${endpoint.name.replace(/-/g, '_')}`, handler.bind(null, endpoint, handlers[endpoint.name])); + } else { + router.get(`/${endpoint.name.replace(/-/g, '_')}`, async ctx => { ctx.status = 405; }); + } + } + + router.post(`/${endpoint.name}`, handler.bind(null, endpoint, handlers[endpoint.name])); if (endpoint.meta.allowGet) { - router.get(`/${endpoint.name.replace(/-/g, '_')}`, handler.bind(null, endpoint)); + router.get(`/${endpoint.name}`, handler.bind(null, endpoint, handlers[endpoint.name])); } else { - router.get(`/${endpoint.name.replace(/-/g, '_')}`, async ctx => { ctx.status = 405; }); + router.get(`/${endpoint.name}`, async ctx => { ctx.status = 405; }); } } - - router.post(`/${endpoint.name}`, handler.bind(null, endpoint)); - - if (endpoint.meta.allowGet) { - router.get(`/${endpoint.name}`, handler.bind(null, endpoint)); - } else { - router.get(`/${endpoint.name}`, async ctx => { ctx.status = 405; }); - } } -} - -router.post('/signup', signup); -router.post('/signin', signin); -router.post('/signup-pending', signupPending); -router.use(discord.routes()); -router.use(github.routes()); -router.use(twitter.routes()); + router.post('/signup', signup); + router.post('/signin', signin); + router.post('/signup-pending', signupPending); -router.get('/v1/instance/peers', async ctx => { - const instances = await Instances.find({ - select: ['host'], - }); + router.use(discord.routes()); + router.use(github.routes()); + router.use(twitter.routes()); - ctx.body = instances.map(instance => instance.host); -}); + router.get('/v1/instance/peers', async ctx => { + const instances = await Instances.find({ + select: ['host'], + }); -router.post('/miauth/:session/check', async ctx => { - const token = await AccessTokens.findOneBy({ - session: ctx.params.session, + ctx.body = instances.map(instance => instance.host); }); - if (token && token.session != null && !token.fetched) { - AccessTokens.update(token.id, { - fetched: true, + router.post('/miauth/:session/check', async ctx => { + const token = await AccessTokens.findOneBy({ + session: ctx.params.session, }); - ctx.body = { - ok: true, - token: token.token, - user: await Users.pack(token.userId, null, { detail: true }), - }; - } else { - ctx.body = { - ok: false, - }; - } -}); + if (token && token.session != null && !token.fetched) { + AccessTokens.update(token.id, { + fetched: true, + }); -// Return 404 for unknown API -router.all('(.*)', async ctx => { - ctx.status = 404; -}); + ctx.body = { + ok: true, + token: token.token, + user: await Users.pack(token.userId, null, { detail: true }), + }; + } else { + ctx.body = { + ok: false, + }; + } + }); -// Register router -app.use(router.routes()); + // Return 404 for unknown API + router.all('(.*)', async ctx => { + ctx.status = 404; + }); + + // Register router + apiServer.use(router.routes()); -export default app; + return apiServer; +} diff --git a/packages/backend/src/server/index.ts b/packages/backend/src/server/index.ts index ab631cc82e37..27d4e629676d 100644 --- a/packages/backend/src/server/index.ts +++ b/packages/backend/src/server/index.ts @@ -23,15 +23,16 @@ import { envOption } from '../env.js'; import activityPub from './activitypub.js'; import nodeinfo from './nodeinfo.js'; import wellKnown from './well-known.js'; -import apiServer from './api/index.js'; +import { createApiServer } from './api/index.js'; import fileServer from './file/index.js'; import proxyServer from './proxy/index.js'; import webServer from './web/index.js'; import { initializeStreamingServer } from './api/streaming.js'; +import type { INestApplicationContext } from '@nestjs/common'; export const serverLogger = new Logger('server', 'gray', false); -export default () => new Promise(resolve => { +export default (app: INestApplicationContext) => new Promise(resolve => { // Init app const app = new Koa(); app.proxy = true; @@ -59,7 +60,7 @@ export default () => new Promise(resolve => { }); } - app.use(mount('/api', apiServer)); + app.use(mount('/api', createApiServer(app))); app.use(mount('/files', fileServer)); app.use(mount('/proxy', proxyServer)); From c20f26c387bccc9ba6930ffc61bf64e9b3296c06 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 11 Sep 2022 18:55:45 +0900 Subject: [PATCH 011/180] wip --- .../src/server/api/endpoints/antennas/list.ts | 14 +++++---- .../server/api/endpoints/antennas/notes.ts | 30 +++++++++---------- .../src/server/api/endpoints/antennas/show.ts | 26 ++++++++-------- 3 files changed, 37 insertions(+), 33 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/antennas/list.ts b/packages/backend/src/server/api/endpoints/antennas/list.ts index f498365a47fc..6041273a5c7a 100644 --- a/packages/backend/src/server/api/endpoints/antennas/list.ts +++ b/packages/backend/src/server/api/endpoints/antennas/list.ts @@ -37,9 +37,11 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const antennas = await Antennas.findBy({ - userId: me.id, - }); - - return await Promise.all(antennas.map(x => Antennas.pack(x))); -}); + const antennas = await Antennas.findBy({ + userId: me.id, + }); + + return await Promise.all(antennas.map(x => Antennas.pack(x))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index 0dfc82c11781..03ab05bd374a 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -69,27 +69,27 @@ export default class extends Endpoint { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .innerJoin(AntennaNotes.metadata.targetName, 'antennaNote', 'antennaNote.noteId = note.id') - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('user.avatar', 'avatar') - .leftJoinAndSelect('user.banner', 'banner') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') - .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') - .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') - .andWhere('antennaNote.antennaId = :antennaId', { antennaId: antenna.id }); + .innerJoin(AntennaNotes.metadata.targetName, 'antennaNote', 'antennaNote.noteId = note.id') + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('user.avatar', 'avatar') + .leftJoinAndSelect('user.banner', 'banner') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') + .andWhere('antennaNote.antennaId = :antennaId', { antennaId: antenna.id }); generateVisibilityQuery(query, user); generateMutedUserQuery(query, user); generateBlockedUserQuery(query, user); const notes = await query - .take(ps.limit) - .getMany(); + .take(ps.limit) + .getMany(); if (notes.length > 0) { readNote(user.id, notes); diff --git a/packages/backend/src/server/api/endpoints/antennas/show.ts b/packages/backend/src/server/api/endpoints/antennas/show.ts index 3890283178b3..adc11e475328 100644 --- a/packages/backend/src/server/api/endpoints/antennas/show.ts +++ b/packages/backend/src/server/api/endpoints/antennas/show.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ApiError } from '../../error.js'; import { Antennas } from '@/models/index.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['antennas', 'account'], @@ -44,15 +44,17 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - // Fetch the antenna - const antenna = await Antennas.findOneBy({ - id: ps.antennaId, - userId: me.id, - }); - - if (antenna == null) { - throw new ApiError(meta.errors.noSuchAntenna); + // Fetch the antenna + const antenna = await Antennas.findOneBy({ + id: ps.antennaId, + userId: me.id, + }); + + if (antenna == null) { + throw new ApiError(meta.errors.noSuchAntenna); + } + + return await Antennas.pack(antenna); + }); } - - return await Antennas.pack(antenna); -}); +} From 2cb44256dc004288aee024c2a3341120067edd1b Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 11 Sep 2022 19:43:56 +0900 Subject: [PATCH 012/180] wip --- packages/backend/package.json | 1 - packages/backend/src/app.module.ts | 7 +- packages/backend/src/boot/worker.ts | 6 +- packages/backend/src/queue/index.ts | 365 ++++-------------- packages/backend/src/queue/queue.module.ts | 28 ++ packages/backend/src/queue/queue.service.ts | 240 ++++++++++++ packages/backend/src/queue/queues.ts | 21 - .../api/endpoints/admin/accounts/create.ts | 9 +- .../api/endpoints/admin/accounts/delete.ts | 13 +- .../server/api/endpoints/admin/ad/create.ts | 11 +- .../server/api/endpoints/admin/ad/delete.ts | 13 +- .../api/endpoints/admin/delete-account.ts | 2 +- .../admin/drive-capacity-override.ts | 6 +- .../admin/federation/remove-all-following.ts | 4 +- .../api/endpoints/admin/moderators/add.ts | 11 +- .../api/endpoints/admin/moderators/remove.ts | 11 +- .../api/endpoints/admin/reset-password.ts | 10 +- .../admin/resolve-abuse-user-report.ts | 2 +- .../server/api/endpoints/admin/show-user.ts | 4 +- .../server/api/endpoints/admin/show-users.ts | 4 +- .../api/endpoints/admin/silence-user.ts | 4 +- .../api/endpoints/admin/suspend-user.ts | 8 +- .../api/endpoints/admin/unsilence-user.ts | 4 +- .../api/endpoints/admin/unsuspend-user.ts | 4 +- .../server/api/endpoints/admin/update-meta.ts | 2 +- .../api/endpoints/admin/update-user-note.ts | 2 +- .../src/server/api/endpoints/ap/show.ts | 2 +- .../api/endpoints/auth/session/userkey.ts | 2 +- .../server/api/endpoints/blocking/create.ts | 4 +- .../server/api/endpoints/blocking/delete.ts | 4 +- .../server/api/endpoints/federation/users.ts | 4 +- .../server/api/endpoints/following/create.ts | 2 +- .../server/api/endpoints/following/delete.ts | 2 +- .../api/endpoints/following/invalidate.ts | 2 +- .../endpoints/following/requests/cancel.ts | 2 +- .../api/endpoints/get-online-users-count.ts | 2 +- .../server/api/endpoints/hashtags/users.ts | 4 +- .../backend/src/server/api/endpoints/i.ts | 2 +- .../server/api/endpoints/i/2fa/key-done.ts | 2 +- .../server/api/endpoints/i/2fa/remove-key.ts | 2 +- .../server/api/endpoints/i/delete-account.ts | 2 +- .../server/api/endpoints/i/notifications.ts | 2 +- .../backend/src/server/api/endpoints/i/pin.ts | 2 +- .../api/endpoints/i/read-announcement.ts | 2 +- .../api/endpoints/i/regenerate-token.ts | 4 +- .../src/server/api/endpoints/i/unpin.ts | 2 +- .../server/api/endpoints/i/update-email.ts | 2 +- .../src/server/api/endpoints/i/update.ts | 14 +- .../api/endpoints/messaging/messages.ts | 2 +- .../backend/src/server/api/endpoints/meta.ts | 4 +- .../src/server/api/endpoints/notes/create.ts | 2 +- .../src/server/api/endpoints/notes/delete.ts | 2 +- .../server/api/endpoints/notes/polls/vote.ts | 2 +- .../server/api/endpoints/notes/unrenote.ts | 2 +- .../src/server/api/endpoints/page-push.ts | 2 +- .../src/server/api/endpoints/pages/show.ts | 2 +- .../src/server/api/endpoints/pinned-users.ts | 4 +- .../api/endpoints/request-reset-password.ts | 2 +- .../backend/src/server/api/endpoints/stats.ts | 4 +- .../api/endpoints/username/available.ts | 4 +- .../backend/src/server/api/endpoints/users.ts | 6 +- .../server/api/endpoints/users/followers.ts | 2 +- .../server/api/endpoints/users/following.ts | 2 +- .../users/get-frequently-replied-users.ts | 4 +- .../server/api/endpoints/users/lists/pull.ts | 2 +- .../api/endpoints/users/recommendation.ts | 4 +- .../server/api/endpoints/users/relation.ts | 2 +- .../api/endpoints/users/report-abuse.ts | 5 +- .../users/search-by-username-and-host.ts | 15 +- .../src/server/api/endpoints/users/search.ts | 13 +- .../src/server/api/endpoints/users/show.ts | 10 +- .../src/server/api/endpoints/users/stats.ts | 5 +- 72 files changed, 492 insertions(+), 468 deletions(-) create mode 100644 packages/backend/src/queue/queue.module.ts create mode 100644 packages/backend/src/queue/queue.service.ts delete mode 100644 packages/backend/src/queue/queues.ts diff --git a/packages/backend/package.json b/packages/backend/package.json index 3561ed451c4e..bf3575097aca 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,5 +1,4 @@ { - "main": "./index.js", "private": true, "type": "module", "scripts": { diff --git a/packages/backend/src/app.module.ts b/packages/backend/src/app.module.ts index 15c5cb44d140..be3a30b460e3 100644 --- a/packages/backend/src/app.module.ts +++ b/packages/backend/src/app.module.ts @@ -1,12 +1,17 @@ import { Module } from '@nestjs/common'; -import { Notes } from '@/models/index.js'; +import { Notes, Users } from '@/models/index.js'; import { EndpointsModule } from '@/server/api/endpoints.module.js'; +import { QueueModule } from '@/queue/queue.module.js'; @Module({ imports: [ EndpointsModule, + QueueModule, ], providers: [{ + provide: 'usersRepository', + useValue: Users, + }, { provide: 'notesRepository', useValue: Notes, }], diff --git a/packages/backend/src/boot/worker.ts b/packages/backend/src/boot/worker.ts index 0a91cc5b1056..09a34622a0d2 100644 --- a/packages/backend/src/boot/worker.ts +++ b/packages/backend/src/boot/worker.ts @@ -1,7 +1,9 @@ import cluster from 'node:cluster'; import { NestFactory } from '@nestjs/core'; +import { envOption } from '@/env.js'; import { initDb } from '../db/postgre.js'; import createServer from '../server/index.js'; +import initializeQueue from '../queue/index.js'; import { AppModule } from '../app.module.js'; /** @@ -16,7 +18,9 @@ export async function workerMain() { await createServer(app); // start job queue - import('../queue/index.js').then(x => x.default()); + if (!envOption.onlyServer) { + await initializeQueue(app); + } if (cluster.isWorker) { // Send a 'ready' message to parent process diff --git a/packages/backend/src/queue/index.ts b/packages/backend/src/queue/index.ts index ebb3a77cab3a..80e1c7b3ab62 100644 --- a/packages/backend/src/queue/index.ts +++ b/packages/backend/src/queue/index.ts @@ -1,11 +1,5 @@ -import httpSignature from '@peertube/http-signature'; -import { v4 as uuid } from 'uuid'; import config from '@/config/index.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; -import { IActivity } from '@/remote/activitypub/type.js'; -import { Webhook, webhookEventTypes } from '@/models/entities/webhook.js'; -import { envOption } from '../env.js'; import processDeliver from './processors/deliver.js'; import processInbox from './processors/inbox.js'; @@ -16,278 +10,81 @@ import processWebhookDeliver from './processors/webhook-deliver.js'; import { endedPollNotification } from './processors/ended-poll-notification.js'; import { queueLogger } from './logger.js'; import { getJobInfo } from './get-job-info.js'; -import { systemQueue, dbQueue, deliverQueue, inboxQueue, objectStorageQueue, endedPollNotificationQueue, webhookDeliverQueue } from './queues.js'; -import { ThinUser } from './types.js'; - -function renderError(e: Error): any { - return { - stack: e.stack, - message: e.message, - name: e.name, - }; -} - -const systemLogger = queueLogger.createSubLogger('system'); -const deliverLogger = queueLogger.createSubLogger('deliver'); -const webhookLogger = queueLogger.createSubLogger('webhook'); -const inboxLogger = queueLogger.createSubLogger('inbox'); -const dbLogger = queueLogger.createSubLogger('db'); -const objectStorageLogger = queueLogger.createSubLogger('objectStorage'); - -systemQueue - .on('waiting', (jobId) => systemLogger.debug(`waiting id=${jobId}`)) - .on('active', (job) => systemLogger.debug(`active id=${job.id}`)) - .on('completed', (job, result) => systemLogger.debug(`completed(${result}) id=${job.id}`)) - .on('failed', (job, err) => systemLogger.warn(`failed(${err}) id=${job.id}`, { job, e: renderError(err) })) - .on('error', (job: any, err: Error) => systemLogger.error(`error ${err}`, { job, e: renderError(err) })) - .on('stalled', (job) => systemLogger.warn(`stalled id=${job.id}`)); - -deliverQueue - .on('waiting', (jobId) => deliverLogger.debug(`waiting id=${jobId}`)) - .on('active', (job) => deliverLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`)) - .on('completed', (job, result) => deliverLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`)) - .on('failed', (job, err) => deliverLogger.warn(`failed(${err}) ${getJobInfo(job)} to=${job.data.to}`)) - .on('error', (job: any, err: Error) => deliverLogger.error(`error ${err}`, { job, e: renderError(err) })) - .on('stalled', (job) => deliverLogger.warn(`stalled ${getJobInfo(job)} to=${job.data.to}`)); - -inboxQueue - .on('waiting', (jobId) => inboxLogger.debug(`waiting id=${jobId}`)) - .on('active', (job) => inboxLogger.debug(`active ${getJobInfo(job, true)}`)) - .on('completed', (job, result) => inboxLogger.debug(`completed(${result}) ${getJobInfo(job, true)}`)) - .on('failed', (job, err) => inboxLogger.warn(`failed(${err}) ${getJobInfo(job)} activity=${job.data.activity ? job.data.activity.id : 'none'}`, { job, e: renderError(err) })) - .on('error', (job: any, err: Error) => inboxLogger.error(`error ${err}`, { job, e: renderError(err) })) - .on('stalled', (job) => inboxLogger.warn(`stalled ${getJobInfo(job)} activity=${job.data.activity ? job.data.activity.id : 'none'}`)); - -dbQueue - .on('waiting', (jobId) => dbLogger.debug(`waiting id=${jobId}`)) - .on('active', (job) => dbLogger.debug(`active id=${job.id}`)) - .on('completed', (job, result) => dbLogger.debug(`completed(${result}) id=${job.id}`)) - .on('failed', (job, err) => dbLogger.warn(`failed(${err}) id=${job.id}`, { job, e: renderError(err) })) - .on('error', (job: any, err: Error) => dbLogger.error(`error ${err}`, { job, e: renderError(err) })) - .on('stalled', (job) => dbLogger.warn(`stalled id=${job.id}`)); - -objectStorageQueue - .on('waiting', (jobId) => objectStorageLogger.debug(`waiting id=${jobId}`)) - .on('active', (job) => objectStorageLogger.debug(`active id=${job.id}`)) - .on('completed', (job, result) => objectStorageLogger.debug(`completed(${result}) id=${job.id}`)) - .on('failed', (job, err) => objectStorageLogger.warn(`failed(${err}) id=${job.id}`, { job, e: renderError(err) })) - .on('error', (job: any, err: Error) => objectStorageLogger.error(`error ${err}`, { job, e: renderError(err) })) - .on('stalled', (job) => objectStorageLogger.warn(`stalled id=${job.id}`)); - -webhookDeliverQueue - .on('waiting', (jobId) => webhookLogger.debug(`waiting id=${jobId}`)) - .on('active', (job) => webhookLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`)) - .on('completed', (job, result) => webhookLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`)) - .on('failed', (job, err) => webhookLogger.warn(`failed(${err}) ${getJobInfo(job)} to=${job.data.to}`)) - .on('error', (job: any, err: Error) => webhookLogger.error(`error ${err}`, { job, e: renderError(err) })) - .on('stalled', (job) => webhookLogger.warn(`stalled ${getJobInfo(job)} to=${job.data.to}`)); - -export function deliver(user: ThinUser, content: unknown, to: string | null) { - if (content == null) return null; - if (to == null) return null; - - const data = { - user: { - id: user.id, - }, - content, - to, - }; - - return deliverQueue.add(data, { - attempts: config.deliverJobMaxAttempts || 12, - timeout: 1 * 60 * 1000, // 1min - backoff: { - type: 'apBackoff', - }, - removeOnComplete: true, - removeOnFail: true, - }); -} - -export function inbox(activity: IActivity, signature: httpSignature.IParsedSignature) { - const data = { - activity: activity, - signature, - }; - - return inboxQueue.add(data, { - attempts: config.inboxJobMaxAttempts || 8, - timeout: 5 * 60 * 1000, // 5min - backoff: { - type: 'apBackoff', - }, - removeOnComplete: true, - removeOnFail: true, - }); -} - -export function createDeleteDriveFilesJob(user: ThinUser) { - return dbQueue.add('deleteDriveFiles', { - user: user, - }, { - removeOnComplete: true, - removeOnFail: true, - }); -} - -export function createExportCustomEmojisJob(user: ThinUser) { - return dbQueue.add('exportCustomEmojis', { - user: user, - }, { - removeOnComplete: true, - removeOnFail: true, - }); -} - -export function createExportNotesJob(user: ThinUser) { - return dbQueue.add('exportNotes', { - user: user, - }, { - removeOnComplete: true, - removeOnFail: true, - }); -} - -export function createExportFollowingJob(user: ThinUser, excludeMuting = false, excludeInactive = false) { - return dbQueue.add('exportFollowing', { - user: user, - excludeMuting, - excludeInactive, - }, { - removeOnComplete: true, - removeOnFail: true, - }); -} - -export function createExportMuteJob(user: ThinUser) { - return dbQueue.add('exportMute', { - user: user, - }, { - removeOnComplete: true, - removeOnFail: true, - }); -} - -export function createExportBlockingJob(user: ThinUser) { - return dbQueue.add('exportBlocking', { - user: user, - }, { - removeOnComplete: true, - removeOnFail: true, - }); -} - -export function createExportUserListsJob(user: ThinUser) { - return dbQueue.add('exportUserLists', { - user: user, - }, { - removeOnComplete: true, - removeOnFail: true, - }); -} - -export function createImportFollowingJob(user: ThinUser, fileId: DriveFile['id']) { - return dbQueue.add('importFollowing', { - user: user, - fileId: fileId, - }, { - removeOnComplete: true, - removeOnFail: true, - }); -} - -export function createImportMutingJob(user: ThinUser, fileId: DriveFile['id']) { - return dbQueue.add('importMuting', { - user: user, - fileId: fileId, - }, { - removeOnComplete: true, - removeOnFail: true, - }); -} - -export function createImportBlockingJob(user: ThinUser, fileId: DriveFile['id']) { - return dbQueue.add('importBlocking', { - user: user, - fileId: fileId, - }, { - removeOnComplete: true, - removeOnFail: true, - }); -} - -export function createImportUserListsJob(user: ThinUser, fileId: DriveFile['id']) { - return dbQueue.add('importUserLists', { - user: user, - fileId: fileId, - }, { - removeOnComplete: true, - removeOnFail: true, - }); -} - -export function createImportCustomEmojisJob(user: ThinUser, fileId: DriveFile['id']) { - return dbQueue.add('importCustomEmojis', { - user: user, - fileId: fileId, - }, { - removeOnComplete: true, - removeOnFail: true, - }); -} - -export function createDeleteAccountJob(user: ThinUser, opts: { soft?: boolean; } = {}) { - return dbQueue.add('deleteAccount', { - user: user, - soft: opts.soft, - }, { - removeOnComplete: true, - removeOnFail: true, - }); -} - -export function createDeleteObjectStorageFileJob(key: string) { - return objectStorageQueue.add('deleteFile', { - key: key, - }, { - removeOnComplete: true, - removeOnFail: true, - }); -} - -export function createCleanRemoteFilesJob() { - return objectStorageQueue.add('cleanRemoteFiles', {}, { - removeOnComplete: true, - removeOnFail: true, - }); -} - -export function webhookDeliver(webhook: Webhook, type: typeof webhookEventTypes[number], content: unknown) { - const data = { - type, - content, - webhookId: webhook.id, - userId: webhook.userId, - to: webhook.url, - secret: webhook.secret, - createdAt: Date.now(), - eventId: uuid(), - }; - - return webhookDeliverQueue.add(data, { - attempts: 4, - timeout: 1 * 60 * 1000, // 1min - backoff: { - type: 'apBackoff', - }, - removeOnComplete: true, - removeOnFail: true, - }); -} - -export default function() { - if (envOption.onlyServer) return; +import { QueueService } from './queue.service.js'; +import type { INestApplicationContext } from '@nestjs/common'; + +export default function(app: INestApplicationContext) { + const queueService = app.get(QueueService); + const systemQueue = queueService.systemQueue; + const deliverQueue = queueService.deliverQueue; + const inboxQueue = queueService.inboxQueue; + const dbQueue = queueService.dbQueue; + const objectStorageQueue = queueService.objectStorageQueue; + const webhookDeliverQueue = queueService.webhookDeliverQueue; + const endedPollNotificationQueue = queueService.endedPollNotificationQueue; + + function renderError(e: Error): any { + return { + stack: e.stack, + message: e.message, + name: e.name, + }; + } + + const systemLogger = queueLogger.createSubLogger('system'); + const deliverLogger = queueLogger.createSubLogger('deliver'); + const webhookLogger = queueLogger.createSubLogger('webhook'); + const inboxLogger = queueLogger.createSubLogger('inbox'); + const dbLogger = queueLogger.createSubLogger('db'); + const objectStorageLogger = queueLogger.createSubLogger('objectStorage'); + + systemQueue + .on('waiting', (jobId) => systemLogger.debug(`waiting id=${jobId}`)) + .on('active', (job) => systemLogger.debug(`active id=${job.id}`)) + .on('completed', (job, result) => systemLogger.debug(`completed(${result}) id=${job.id}`)) + .on('failed', (job, err) => systemLogger.warn(`failed(${err}) id=${job.id}`, { job, e: renderError(err) })) + .on('error', (job: any, err: Error) => systemLogger.error(`error ${err}`, { job, e: renderError(err) })) + .on('stalled', (job) => systemLogger.warn(`stalled id=${job.id}`)); + + deliverQueue + .on('waiting', (jobId) => deliverLogger.debug(`waiting id=${jobId}`)) + .on('active', (job) => deliverLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`)) + .on('completed', (job, result) => deliverLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`)) + .on('failed', (job, err) => deliverLogger.warn(`failed(${err}) ${getJobInfo(job)} to=${job.data.to}`)) + .on('error', (job: any, err: Error) => deliverLogger.error(`error ${err}`, { job, e: renderError(err) })) + .on('stalled', (job) => deliverLogger.warn(`stalled ${getJobInfo(job)} to=${job.data.to}`)); + + inboxQueue + .on('waiting', (jobId) => inboxLogger.debug(`waiting id=${jobId}`)) + .on('active', (job) => inboxLogger.debug(`active ${getJobInfo(job, true)}`)) + .on('completed', (job, result) => inboxLogger.debug(`completed(${result}) ${getJobInfo(job, true)}`)) + .on('failed', (job, err) => inboxLogger.warn(`failed(${err}) ${getJobInfo(job)} activity=${job.data.activity ? job.data.activity.id : 'none'}`, { job, e: renderError(err) })) + .on('error', (job: any, err: Error) => inboxLogger.error(`error ${err}`, { job, e: renderError(err) })) + .on('stalled', (job) => inboxLogger.warn(`stalled ${getJobInfo(job)} activity=${job.data.activity ? job.data.activity.id : 'none'}`)); + + dbQueue + .on('waiting', (jobId) => dbLogger.debug(`waiting id=${jobId}`)) + .on('active', (job) => dbLogger.debug(`active id=${job.id}`)) + .on('completed', (job, result) => dbLogger.debug(`completed(${result}) id=${job.id}`)) + .on('failed', (job, err) => dbLogger.warn(`failed(${err}) id=${job.id}`, { job, e: renderError(err) })) + .on('error', (job: any, err: Error) => dbLogger.error(`error ${err}`, { job, e: renderError(err) })) + .on('stalled', (job) => dbLogger.warn(`stalled id=${job.id}`)); + + objectStorageQueue + .on('waiting', (jobId) => objectStorageLogger.debug(`waiting id=${jobId}`)) + .on('active', (job) => objectStorageLogger.debug(`active id=${job.id}`)) + .on('completed', (job, result) => objectStorageLogger.debug(`completed(${result}) id=${job.id}`)) + .on('failed', (job, err) => objectStorageLogger.warn(`failed(${err}) id=${job.id}`, { job, e: renderError(err) })) + .on('error', (job: any, err: Error) => objectStorageLogger.error(`error ${err}`, { job, e: renderError(err) })) + .on('stalled', (job) => objectStorageLogger.warn(`stalled id=${job.id}`)); + + webhookDeliverQueue + .on('waiting', (jobId) => webhookLogger.debug(`waiting id=${jobId}`)) + .on('active', (job) => webhookLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`)) + .on('completed', (job, result) => webhookLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`)) + .on('failed', (job, err) => webhookLogger.warn(`failed(${err}) ${getJobInfo(job)} to=${job.data.to}`)) + .on('error', (job: any, err: Error) => webhookLogger.error(`error ${err}`, { job, e: renderError(err) })) + .on('stalled', (job) => webhookLogger.warn(`stalled ${getJobInfo(job)} to=${job.data.to}`)); deliverQueue.process(config.deliverJobConcurrency || 128, processDeliver); inboxQueue.process(config.inboxJobConcurrency || 16, processInbox); @@ -328,15 +125,3 @@ export default function() { processSystemQueue(systemQueue); } - -export function destroy() { - deliverQueue.once('cleaned', (jobs, status) => { - deliverLogger.succ(`Cleaned ${jobs.length} ${status} jobs`); - }); - deliverQueue.clean(0, 'delayed'); - - inboxQueue.once('cleaned', (jobs, status) => { - inboxLogger.succ(`Cleaned ${jobs.length} ${status} jobs`); - }); - inboxQueue.clean(0, 'delayed'); -} diff --git a/packages/backend/src/queue/queue.module.ts b/packages/backend/src/queue/queue.module.ts new file mode 100644 index 000000000000..845f9b30e21c --- /dev/null +++ b/packages/backend/src/queue/queue.module.ts @@ -0,0 +1,28 @@ +import { Module } from '@nestjs/common'; +import config from '@/config/index.js'; +import { initialize as initializeQueue } from './initialize.js'; +import type Bull from 'bull'; +import type { DeliverJobData, InboxJobData, DbJobData, ObjectStorageJobData, EndedPollNotificationJobData, WebhookDeliverJobData } from './types.js'; + +export type SystemQueue = Bull.Queue>; +export type EndedPollNotificationQueue = Bull.Queue; +export type DeliverQueue = Bull.Queue; +export type InboxQueue = Bull.Queue; +export type DbQueue = Bull.Queue; +export type ObjectStorageQueue = Bull.Queue; +export type WebhookDeliverQueue = Bull.Queue; + +@Module({ + imports: [ + ], + providers: [ + { provide: 'queue:system', useValue: initializeQueue('system') }, + { provide: 'queue:endedPollNotification', useValue: initializeQueue('endedPollNotification') }, + { provide: 'queue:deliver', useValue: initializeQueue('deliver', config.deliverJobPerSec || 128) }, + { provide: 'queue:inbox', useValue: initializeQueue('inbox', config.inboxJobPerSec || 16) }, + { provide: 'queue:db', useValue: initializeQueue('db') }, + { provide: 'queue:objectStorage', useValue: initializeQueue('objectStorage') }, + { provide: 'queue:webhookDeliver', useValue: initializeQueue('webhookDeliver', 64) }, + ], + }) +export class QueueModule {} diff --git a/packages/backend/src/queue/queue.service.ts b/packages/backend/src/queue/queue.service.ts new file mode 100644 index 000000000000..8a6585f0f31b --- /dev/null +++ b/packages/backend/src/queue/queue.service.ts @@ -0,0 +1,240 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { v4 as uuid } from 'uuid'; +import config from '@/config/index.js'; +import type { IActivity } from '@/remote/activitypub/type.js'; +import type { DriveFile } from '@/models/entities/drive-file.js'; + +import type { Webhook, webhookEventTypes } from '@/models/entities/webhook.js'; +import { envOption } from '../env.js'; +import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from './queue.module.js'; +import type { ThinUser } from './types.js'; +import type httpSignature from '@peertube/http-signature'; + +@Injectable() +export class QueueService { + constructor( + @Inject('queue:system') public systemQueue: SystemQueue, + @Inject('queue:endedPollNotification') public endedPollNotificationQueue: EndedPollNotificationQueue, + @Inject('queue:deliver') public deliverQueue: DeliverQueue, + @Inject('queue:inbox') public inboxQueue: InboxQueue, + @Inject('queue:db') public dbQueue: DbQueue, + @Inject('queue:objectStorage') public objectStorageQueue: ObjectStorageQueue, + @Inject('queue:webhookDeliver') public webhookDeliverQueue: WebhookDeliverQueue, + ) {} + + public deliver(user: ThinUser, content: unknown, to: string | null) { + if (content == null) return null; + if (to == null) return null; + + const data = { + user: { + id: user.id, + }, + content, + to, + }; + + return this.deliverQueue.add(data, { + attempts: config.deliverJobMaxAttempts || 12, + timeout: 1 * 60 * 1000, // 1min + backoff: { + type: 'apBackoff', + }, + removeOnComplete: true, + removeOnFail: true, + }); + } + + public inbox(activity: IActivity, signature: httpSignature.IParsedSignature) { + const data = { + activity: activity, + signature, + }; + + return this.inboxQueue.add(data, { + attempts: config.inboxJobMaxAttempts || 8, + timeout: 5 * 60 * 1000, // 5min + backoff: { + type: 'apBackoff', + }, + removeOnComplete: true, + removeOnFail: true, + }); + } + + public createDeleteDriveFilesJob(user: ThinUser) { + return this.dbQueue.add('deleteDriveFiles', { + user: user, + }, { + removeOnComplete: true, + removeOnFail: true, + }); + } + + public createExportCustomEmojisJob(user: ThinUser) { + return this.dbQueue.add('exportCustomEmojis', { + user: user, + }, { + removeOnComplete: true, + removeOnFail: true, + }); + } + + public createExportNotesJob(user: ThinUser) { + return this.dbQueue.add('exportNotes', { + user: user, + }, { + removeOnComplete: true, + removeOnFail: true, + }); + } + + public createExportFollowingJob(user: ThinUser, excludeMuting = false, excludeInactive = false) { + return this.dbQueue.add('exportFollowing', { + user: user, + excludeMuting, + excludeInactive, + }, { + removeOnComplete: true, + removeOnFail: true, + }); + } + + public createExportMuteJob(user: ThinUser) { + return this.dbQueue.add('exportMute', { + user: user, + }, { + removeOnComplete: true, + removeOnFail: true, + }); + } + + public createExportBlockingJob(user: ThinUser) { + return this.dbQueue.add('exportBlocking', { + user: user, + }, { + removeOnComplete: true, + removeOnFail: true, + }); + } + + public createExportUserListsJob(user: ThinUser) { + return this.dbQueue.add('exportUserLists', { + user: user, + }, { + removeOnComplete: true, + removeOnFail: true, + }); + } + + public createImportFollowingJob(user: ThinUser, fileId: DriveFile['id']) { + return this.dbQueue.add('importFollowing', { + user: user, + fileId: fileId, + }, { + removeOnComplete: true, + removeOnFail: true, + }); + } + + public createImportMutingJob(user: ThinUser, fileId: DriveFile['id']) { + return this.dbQueue.add('importMuting', { + user: user, + fileId: fileId, + }, { + removeOnComplete: true, + removeOnFail: true, + }); + } + + public createImportBlockingJob(user: ThinUser, fileId: DriveFile['id']) { + return this.dbQueue.add('importBlocking', { + user: user, + fileId: fileId, + }, { + removeOnComplete: true, + removeOnFail: true, + }); + } + + public createImportUserListsJob(user: ThinUser, fileId: DriveFile['id']) { + return this.dbQueue.add('importUserLists', { + user: user, + fileId: fileId, + }, { + removeOnComplete: true, + removeOnFail: true, + }); + } + + public createImportCustomEmojisJob(user: ThinUser, fileId: DriveFile['id']) { + return this.dbQueue.add('importCustomEmojis', { + user: user, + fileId: fileId, + }, { + removeOnComplete: true, + removeOnFail: true, + }); + } + + public createDeleteAccountJob(user: ThinUser, opts: { soft?: boolean; } = {}) { + return this.dbQueue.add('deleteAccount', { + user: user, + soft: opts.soft, + }, { + removeOnComplete: true, + removeOnFail: true, + }); + } + + public createDeleteObjectStorageFileJob(key: string) { + return this.objectStorageQueue.add('deleteFile', { + key: key, + }, { + removeOnComplete: true, + removeOnFail: true, + }); + } + + public createCleanRemoteFilesJob() { + return this.objectStorageQueue.add('cleanRemoteFiles', {}, { + removeOnComplete: true, + removeOnFail: true, + }); + } + + public webhookDeliver(webhook: Webhook, type: typeof webhookEventTypes[number], content: unknown) { + const data = { + type, + content, + webhookId: webhook.id, + userId: webhook.userId, + to: webhook.url, + secret: webhook.secret, + createdAt: Date.now(), + eventId: uuid(), + }; + + return this.webhookDeliverQueue.add(data, { + attempts: 4, + timeout: 1 * 60 * 1000, // 1min + backoff: { + type: 'apBackoff', + }, + removeOnComplete: true, + removeOnFail: true, + }); + } + + public destroy() { + this.deliverQueue.once('cleaned', (jobs, status) => { + //deliverLogger.succ(`Cleaned ${jobs.length} ${status} jobs`); + }); + this.deliverQueue.clean(0, 'delayed'); + + this.inboxQueue.once('cleaned', (jobs, status) => { + //inboxLogger.succ(`Cleaned ${jobs.length} ${status} jobs`); + }); + this.inboxQueue.clean(0, 'delayed'); + } +} diff --git a/packages/backend/src/queue/queues.ts b/packages/backend/src/queue/queues.ts deleted file mode 100644 index f3a267790c81..000000000000 --- a/packages/backend/src/queue/queues.ts +++ /dev/null @@ -1,21 +0,0 @@ -import config from '@/config/index.js'; -import { initialize as initializeQueue } from './initialize.js'; -import { DeliverJobData, InboxJobData, DbJobData, ObjectStorageJobData, EndedPollNotificationJobData, WebhookDeliverJobData } from './types.js'; - -export const systemQueue = initializeQueue>('system'); -export const endedPollNotificationQueue = initializeQueue('endedPollNotification'); -export const deliverQueue = initializeQueue('deliver', config.deliverJobPerSec || 128); -export const inboxQueue = initializeQueue('inbox', config.inboxJobPerSec || 16); -export const dbQueue = initializeQueue('db'); -export const objectStorageQueue = initializeQueue('objectStorage'); -export const webhookDeliverQueue = initializeQueue('webhookDeliver', 64); - -export const queues = [ - systemQueue, - endedPollNotificationQueue, - deliverQueue, - inboxQueue, - dbQueue, - objectStorageQueue, - webhookDeliverQueue, -]; diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts index c48f92151c35..af5399519d31 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts @@ -35,13 +35,10 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, _me) => { - const me = _me ? await Users.findOneByOrFail({ id: _me.id }) : null; - const noUsers = (await Users.countBy({ + const me = _me ? await this.usersRepository.findOneByOrFail({ id: _me.id }) : null; + const noUsers = (await this.usersRepository.countBy({ host: IsNull(), })) === 0; if (!noUsers && !me?.isAdmin) throw new Error('access denied'); @@ -51,7 +48,7 @@ export default class extends Endpoint { password: ps.password, }); - const res = await Users.pack(account, account, { + const res = await this.usersRepository.pack(account, account, { detail: true, includeSecrets: true, }); diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts index 1273d172f6b7..c51016deb922 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; import { doPostSuspend } from '@/services/suspend-user.js'; import { publishUserEvent } from '@/services/stream.js'; import { createDeleteAccountJob } from '@/queue/index.js'; @@ -26,12 +26,9 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const user = await Users.findOneBy({ id: ps.userId }); + const user = await this.usersRepository.findOneBy({ id: ps.userId }); if (user == null) { throw new Error('user not found'); @@ -45,7 +42,7 @@ export default class extends Endpoint { throw new Error('cannot suspend moderator'); } - if (Users.isLocalUser(user)) { + if (this.usersRepository.isLocalUser(user)) { // 物理削除する前にDelete activityを送信する await doPostSuspend(user).catch(e => {}); @@ -58,11 +55,11 @@ export default class extends Endpoint { }); } - await Users.update(user.id, { + await this.usersRepository.update(user.id, { isDeleted: true, }); - if (Users.isLocalUser(user)) { + if (this.usersRepository.isLocalUser(user)) { // Terminate streaming publishUserEvent(user.id, 'terminate', {}); } diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts index 4895b589fda3..b8c39b9a8137 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Ads } from '@/models/index.js'; +import type { Ads } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; export const meta = { @@ -28,14 +28,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('adsRepository') + private adsRepository: typeof Ads, ) { super(meta, paramDef, async (ps, user) => { - await Ads.insert({ + await this.adsRepository.insert({ id: genId(), createdAt: new Date(), expiresAt: new Date(ps.expiresAt), diff --git a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts index f4acb53a68d5..a03076fec2b9 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Ads } from '@/models/index.js'; +import type { Ads } from '@/models/index.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -30,18 +30,15 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('adsRepository') + private adsRepository: typeof Ads, ) { super(meta, paramDef, async (ps, me) => { - const ad = await Ads.findOneBy({ id: ps.id }); + const ad = await this.adsRepository.findOneBy({ id: ps.id }); if (ad == null) throw new ApiError(meta.errors.noSuchAd); - await Ads.delete(ad.id); + await this.adsRepository.delete(ad.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/delete-account.ts b/packages/backend/src/server/api/endpoints/admin/delete-account.ts index 700015e7484c..c728932b65b6 100644 --- a/packages/backend/src/server/api/endpoints/admin/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/admin/delete-account.ts @@ -32,7 +32,7 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const user = await Users.findOneByOrFail({ id: ps.userId }); + const user = await this.usersRepository.findOneByOrFail({ id: ps.userId }); if (user.isDeleted) { return; } diff --git a/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts b/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts index 78078091df89..f05264830b99 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts @@ -30,13 +30,13 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const user = await Users.findOneBy({ id: ps.userId }); + const user = await this.usersRepository.findOneBy({ id: ps.userId }); if (user == null) { throw new Error('user not found'); } - if (!Users.isLocalUser(user)) { + if (!this.usersRepository.isLocalUser(user)) { throw new Error('user is not local user'); } @@ -47,7 +47,7 @@ export default class extends Endpoint { throw new Error('cannot suspend moderator'); }*/ - await Users.update(user.id, { + await this.usersRepository.update(user.id, { driveCapacityOverrideMb: ps.overrideMb, }); diff --git a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts index 40821733df91..53492b7f64d2 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts @@ -34,8 +34,8 @@ export default class extends Endpoint { }); const pairs = await Promise.all(followings.map(f => Promise.all([ - Users.findOneByOrFail({ id: f.followerId }), - Users.findOneByOrFail({ id: f.followeeId }), + this.usersRepository.findOneByOrFail({ id: f.followerId }), + this.usersRepository.findOneByOrFail({ id: f.followeeId }), ]))); for (const pair of pairs) { diff --git a/packages/backend/src/server/api/endpoints/admin/moderators/add.ts b/packages/backend/src/server/api/endpoints/admin/moderators/add.ts index 773dc1e6df8e..61016f20feb1 100644 --- a/packages/backend/src/server/api/endpoints/admin/moderators/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/moderators/add.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; import { publishInternalEvent } from '@/services/stream.js'; export const meta = { @@ -24,12 +24,9 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - const user = await Users.findOneBy({ id: ps.userId }); + super(meta, paramDef, async (ps) => { + const user = await this.usersRepository.findOneBy({ id: ps.userId }); if (user == null) { throw new Error('user not found'); @@ -39,7 +36,7 @@ export default class extends Endpoint { throw new Error('cannot mark as moderator if admin user'); } - await Users.update(user.id, { + await this.usersRepository.update(user.id, { isModerator: true, }); diff --git a/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts b/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts index b37474f98183..79be9b1e0dac 100644 --- a/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts +++ b/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; import { publishInternalEvent } from '@/services/stream.js'; export const meta = { @@ -24,18 +24,15 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - const user = await Users.findOneBy({ id: ps.userId }); + super(meta, paramDef, async (ps) => { + const user = await this.usersRepository.findOneBy({ id: ps.userId }); if (user == null) { throw new Error('user not found'); } - await Users.update(user.id, { + await this.usersRepository.update(user.id, { isModerator: false, }); diff --git a/packages/backend/src/server/api/endpoints/admin/reset-password.ts b/packages/backend/src/server/api/endpoints/admin/reset-password.ts index 5b9df8d1fe4e..1e5326d6c425 100644 --- a/packages/backend/src/server/api/endpoints/admin/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/admin/reset-password.ts @@ -2,7 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; import rndstr from 'rndstr'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Users, UserProfiles } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { UserProfiles } from '@/models/index.js'; export const meta = { tags: ['admin'], @@ -38,12 +39,9 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - const user = await Users.findOneBy({ id: ps.userId }); + super(meta, paramDef, async (ps) => { + const user = await this.usersRepository.findOneBy({ id: ps.userId }); if (user == null) { throw new Error('user not found'); diff --git a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts index ab7b82360e5f..bd31a3b8f829 100644 --- a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts +++ b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts @@ -41,7 +41,7 @@ export default class extends Endpoint { if (ps.forward && report.targetUserHost != null) { const actor = await getInstanceActor(); - const targetUser = await Users.findOneByOrFail({ id: report.targetUserId }); + const targetUser = await this.usersRepository.findOneByOrFail({ id: report.targetUserId }); deliver(actor, renderActivity(renderFlag(actor, [targetUser.uri!], report.comment)), targetUser.inbox); } diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index 4c6d65453eb5..83ef06f6139c 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -34,7 +34,7 @@ export default class extends Endpoint { ) { super(meta, paramDef, async (ps, me) => { const [user, profile] = await Promise.all([ - Users.findOneBy({ id: ps.userId }), + this.usersRepository.findOneBy({ id: ps.userId }), UserProfiles.findOneBy({ userId: ps.userId }), ]); @@ -42,7 +42,7 @@ export default class extends Endpoint { throw new Error('user not found'); } - const _me = await Users.findOneByOrFail({ id: me.id }); + const _me = await this.usersRepository.findOneByOrFail({ id: me.id }); if ((_me.isModerator && !_me.isAdmin) && user.isAdmin) { throw new Error('cannot show info of admin'); } diff --git a/packages/backend/src/server/api/endpoints/admin/show-users.ts b/packages/backend/src/server/api/endpoints/admin/show-users.ts index b29f774ee976..d60e25608c1f 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-users.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts @@ -49,7 +49,7 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const query = Users.createQueryBuilder('user'); + const query = this.usersRepository.createQueryBuilder('user'); switch (ps.state) { case 'available': query.where('user.isSuspended = FALSE'); break; @@ -89,7 +89,7 @@ export default class extends Endpoint { const users = await query.getMany(); - return await Users.packMany(users, me, { detail: true }); + return await this.usersRepository.packMany(users, me, { detail: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/silence-user.ts b/packages/backend/src/server/api/endpoints/admin/silence-user.ts index fb3d51da5dc3..200903d901f2 100644 --- a/packages/backend/src/server/api/endpoints/admin/silence-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/silence-user.ts @@ -30,7 +30,7 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const user = await Users.findOneBy({ id: ps.userId }); + const user = await this.usersRepository.findOneBy({ id: ps.userId }); if (user == null) { throw new Error('user not found'); @@ -40,7 +40,7 @@ export default class extends Endpoint { throw new Error('cannot silence admin'); } - await Users.update(user.id, { + await this.usersRepository.update(user.id, { isSilenced: true, }); diff --git a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts index 49dbb23c8104..3b5f4e9b6b92 100644 --- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts @@ -33,7 +33,7 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const user = await Users.findOneBy({ id: ps.userId }); + const user = await this.usersRepository.findOneBy({ id: ps.userId }); if (user == null) { throw new Error('user not found'); @@ -47,7 +47,7 @@ export default class extends Endpoint { throw new Error('cannot suspend moderator'); } - await Users.update(user.id, { + await this.usersRepository.update(user.id, { isSuspended: true, }); @@ -56,7 +56,7 @@ export default class extends Endpoint { }); // Terminate streaming - if (Users.isLocalUser(user)) { + if (this.usersRepository.isLocalUser(user)) { publishUserEvent(user.id, 'terminate', {}); } @@ -75,7 +75,7 @@ async function unFollowAll(follower: User) { }); for (const following of followings) { - const followee = await Users.findOneBy({ + const followee = await this.usersRepository.findOneBy({ id: following.followeeId, }); diff --git a/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts b/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts index 52c06fbee43a..25c62ae849c3 100644 --- a/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts @@ -30,13 +30,13 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const user = await Users.findOneBy({ id: ps.userId }); + const user = await this.usersRepository.findOneBy({ id: ps.userId }); if (user == null) { throw new Error('user not found'); } - await Users.update(user.id, { + await this.usersRepository.update(user.id, { isSilenced: false, }); diff --git a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts index f1991fe181f2..d57dd9154097 100644 --- a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts @@ -30,13 +30,13 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const user = await Users.findOneBy({ id: ps.userId }); + const user = await this.usersRepository.findOneBy({ id: ps.userId }); if (user == null) { throw new Error('user not found'); } - await Users.update(user.id, { + await this.usersRepository.update(user.id, { isSuspended: false, }); diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 82b739b956b4..adb8a0c4d914 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -137,7 +137,7 @@ export default class extends Endpoint { } if (Array.isArray(ps.pinnedUsers)) { - set.pinnedUsers = ps.pinnedUsers.filter(Boolean); + set.pinnedUsers = ps.pinnedthis.usersRepository.filter(Boolean); } if (Array.isArray(ps.hiddenTags)) { diff --git a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts index 3e55d5c66bed..8dac56a11095 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts @@ -29,7 +29,7 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const user = await Users.findOneBy({ id: ps.userId }); + const user = await this.usersRepository.findOneBy({ id: ps.userId }); if (user == null) { throw new Error('user not found'); diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index 00f56981702c..00dbba4ba09f 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -140,7 +140,7 @@ async function mergePack(me: CacheableLocalUser | null | undefined, user: User | if (user != null) { return { type: 'User', - object: await Users.pack(user, me, { detail: true }), + object: await this.usersRepository.pack(user, me, { detail: true }), }; } else if (note != null) { try { diff --git a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts index f4e8d0c0d8fb..5eb461f955f1 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts @@ -100,7 +100,7 @@ export default class extends Endpoint { return { accessToken: accessToken.token, - user: await Users.pack(session.userId, null, { + user: await this.usersRepository.pack(session.userId, null, { detail: true, }), }; diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts index e7b279ff4bdd..abe6f1786e5c 100644 --- a/packages/backend/src/server/api/endpoints/blocking/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking/create.ts @@ -64,7 +64,7 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const blocker = await Users.findOneByOrFail({ id: user.id }); + const blocker = await this.usersRepository.findOneByOrFail({ id: user.id }); // 自分自身 if (user.id === ps.userId) { @@ -94,7 +94,7 @@ export default class extends Endpoint { noteUserId: blockee.id, }); - return await Users.pack(blockee.id, blocker, { + return await this.usersRepository.pack(blockee.id, blocker, { detail: true, }); }); diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts index f110d4f6cb4f..2624670972fd 100644 --- a/packages/backend/src/server/api/endpoints/blocking/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts @@ -64,7 +64,7 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const blocker = await Users.findOneByOrFail({ id: user.id }); + const blocker = await this.usersRepository.findOneByOrFail({ id: user.id }); // Check if the blockee is yourself if (user.id === ps.userId) { @@ -90,7 +90,7 @@ export default class extends Endpoint { // Delete blocking await deleteBlocking(blocker, blockee); - return await Users.pack(blockee.id, blocker, { + return await this.usersRepository.pack(blockee.id, blocker, { detail: true, }); }); diff --git a/packages/backend/src/server/api/endpoints/federation/users.ts b/packages/backend/src/server/api/endpoints/federation/users.ts index 77c8533e2f92..3e7fcada514a 100644 --- a/packages/backend/src/server/api/endpoints/federation/users.ts +++ b/packages/backend/src/server/api/endpoints/federation/users.ts @@ -41,14 +41,14 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Users.createQueryBuilder('user'), ps.sinceId, ps.untilId) + const query = makePaginationQuery(this.usersRepository.createQueryBuilder('user'), ps.sinceId, ps.untilId) .andWhere('user.host = :host', { host: ps.host }); const users = await query .take(ps.limit) .getMany(); - return await Users.packMany(users, me, { detail: true }); + return await this.usersRepository.packMany(users, me, { detail: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts index 111de351cd1f..b6486c7f900c 100644 --- a/packages/backend/src/server/api/endpoints/following/create.ts +++ b/packages/backend/src/server/api/endpoints/following/create.ts @@ -110,7 +110,7 @@ export default class extends Endpoint { throw e; } - return await Users.pack(followee.id, user); + return await this.usersRepository.pack(followee.id, user); }); } } diff --git a/packages/backend/src/server/api/endpoints/following/delete.ts b/packages/backend/src/server/api/endpoints/following/delete.ts index b51d2da8094d..2bc5adfc1ee8 100644 --- a/packages/backend/src/server/api/endpoints/following/delete.ts +++ b/packages/backend/src/server/api/endpoints/following/delete.ts @@ -89,7 +89,7 @@ export default class extends Endpoint { await deleteFollowing(follower, followee); - return await Users.pack(followee.id, user); + return await this.usersRepository.pack(followee.id, user); }); } } diff --git a/packages/backend/src/server/api/endpoints/following/invalidate.ts b/packages/backend/src/server/api/endpoints/following/invalidate.ts index 28c7ae1d4445..149e318a96ec 100644 --- a/packages/backend/src/server/api/endpoints/following/invalidate.ts +++ b/packages/backend/src/server/api/endpoints/following/invalidate.ts @@ -89,7 +89,7 @@ export default class extends Endpoint { await deleteFollowing(follower, followee); - return await Users.pack(followee.id, user); + return await this.usersRepository.pack(followee.id, user); }); } } diff --git a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts index 12ed92b9c116..b072e6a60632 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts @@ -68,7 +68,7 @@ export default class extends Endpoint { throw e; } - return await Users.pack(followee.id, user); + return await this.usersRepository.pack(followee.id, user); }); } } diff --git a/packages/backend/src/server/api/endpoints/get-online-users-count.ts b/packages/backend/src/server/api/endpoints/get-online-users-count.ts index 82f87cd723bb..81fdab30481e 100644 --- a/packages/backend/src/server/api/endpoints/get-online-users-count.ts +++ b/packages/backend/src/server/api/endpoints/get-online-users-count.ts @@ -27,7 +27,7 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async () => { - const count = await Users.countBy({ + const count = await this.usersRepository.countBy({ lastActiveDate: MoreThan(new Date(Date.now() - USER_ONLINE_THRESHOLD)), }); diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts index 2969bd89f3eb..9879cf524c4b 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/users.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts @@ -42,7 +42,7 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const query = Users.createQueryBuilder('user') + const query = this.usersRepository.createQueryBuilder('user') .where(':tag = ANY(user.tags)', { tag: normalizeForSearch(ps.tag) }); const recent = new Date(Date.now() - (1000 * 60 * 60 * 24 * 5)); @@ -68,7 +68,7 @@ export default class extends Endpoint { const users = await query.take(ps.limit).getMany(); - return await Users.packMany(users, me, { detail: true }); + return await this.usersRepository.packMany(users, me, { detail: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index 7415b4500dde..da8cd4790129 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -34,7 +34,7 @@ export default class extends Endpoint { const isSecure = token == null; // ここで渡ってきている user はキャッシュされていて古い可能性もあるので id だけ渡す - return await Users.pack(user.id, user, { + return await this.usersRepository.pack(user.id, user, { detail: true, includeSecrets: isSecure, }); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts index 1800860c7ea6..8ef399af7f62 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts @@ -141,7 +141,7 @@ export default class extends Endpoint { }); // Publish meUpdated event - publishMainStream(user.id, 'meUpdated', await Users.pack(user.id, user, { + publishMainStream(user.id, 'meUpdated', await this.usersRepository.pack(user.id, user, { detail: true, includeSecrets: true, })); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts index 9e589b8b8694..9ce659a1c919 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts @@ -46,7 +46,7 @@ export default class extends Endpoint { }); // Publish meUpdated event - publishMainStream(user.id, 'meUpdated', await Users.pack(user.id, user, { + publishMainStream(user.id, 'meUpdated', await this.usersRepository.pack(user.id, user, { detail: true, includeSecrets: true, })); diff --git a/packages/backend/src/server/api/endpoints/i/delete-account.ts b/packages/backend/src/server/api/endpoints/i/delete-account.ts index 1492630888ee..c487b91d67f5 100644 --- a/packages/backend/src/server/api/endpoints/i/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts @@ -30,7 +30,7 @@ export default class extends Endpoint { ) { super(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - const userDetailed = await Users.findOneByOrFail({ id: user.id }); + const userDetailed = await this.usersRepository.findOneByOrFail({ id: user.id }); if (userDetailed.isDeleted) { return; } diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index 849eda135d8e..8d0787cbb7aa 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -80,7 +80,7 @@ export default class extends Endpoint { .select('user_profile.mutedInstances') .where('user_profile.userId = :muterId', { muterId: user.id }); - const suspendedQuery = Users.createQueryBuilder('users') + const suspendedQuery = this.usersRepository.createQueryBuilder('users') .select('users.id') .where('users.isSuspended = TRUE'); diff --git a/packages/backend/src/server/api/endpoints/i/pin.ts b/packages/backend/src/server/api/endpoints/i/pin.ts index 8a4de033edec..f3b9bbcca11d 100644 --- a/packages/backend/src/server/api/endpoints/i/pin.ts +++ b/packages/backend/src/server/api/endpoints/i/pin.ts @@ -64,7 +64,7 @@ export default class extends Endpoint { throw e; }); - return await Users.pack(user.id, user, { + return await this.usersRepository.pack(user.id, user, { detail: true, }); }); diff --git a/packages/backend/src/server/api/endpoints/i/read-announcement.ts b/packages/backend/src/server/api/endpoints/i/read-announcement.ts index 355cb88f2ddc..fdef5fe7ff96 100644 --- a/packages/backend/src/server/api/endpoints/i/read-announcement.ts +++ b/packages/backend/src/server/api/endpoints/i/read-announcement.ts @@ -65,7 +65,7 @@ export default class extends Endpoint { userId: user.id, }); - if (!await Users.getHasUnreadAnnouncement(user.id)) { + if (!await this.usersRepository.getHasUnreadAnnouncement(user.id)) { publishMainStream(user.id, 'readAllAnnouncements'); } }); diff --git a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts index 9efc78a51240..abced33dffb1 100644 --- a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts +++ b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts @@ -30,7 +30,7 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const freshUser = await Users.findOneByOrFail({ id: user.id }); + const freshUser = await this.usersRepository.findOneByOrFail({ id: user.id }); const oldToken = freshUser.token; const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); @@ -44,7 +44,7 @@ export default class extends Endpoint { const newToken = generateUserToken(); - await Users.update(user.id, { + await this.usersRepository.update(user.id, { token: newToken, }); diff --git a/packages/backend/src/server/api/endpoints/i/unpin.ts b/packages/backend/src/server/api/endpoints/i/unpin.ts index 57d71637e4d4..eb9474b447cc 100644 --- a/packages/backend/src/server/api/endpoints/i/unpin.ts +++ b/packages/backend/src/server/api/endpoints/i/unpin.ts @@ -50,7 +50,7 @@ export default class extends Endpoint { throw e; }); - return await Users.pack(user.id, user, { + return await this.usersRepository.pack(user.id, user, { detail: true, }); }); diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts index fff105f36cae..a773e5593dea 100644 --- a/packages/backend/src/server/api/endpoints/i/update-email.ts +++ b/packages/backend/src/server/api/endpoints/i/update-email.ts @@ -77,7 +77,7 @@ export default class extends Endpoint { emailVerifyCode: null, }); - const iObj = await Users.pack(user.id, user, { + const iObj = await this.usersRepository.pack(user.id, user, { detail: true, includeSecrets: true, }); diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 2ba40b869da1..adf4eca125fa 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -71,10 +71,10 @@ export const meta = { export const paramDef = { type: 'object', properties: { - name: { ...Users.nameSchema, nullable: true }, - description: { ...Users.descriptionSchema, nullable: true }, - location: { ...Users.locationSchema, nullable: true }, - birthday: { ...Users.birthdaySchema, nullable: true }, + name: { ...this.usersRepository.nameSchema, nullable: true }, + description: { ...this.usersRepository.descriptionSchema, nullable: true }, + location: { ...this.usersRepository.locationSchema, nullable: true }, + birthday: { ...this.usersRepository.birthdaySchema, nullable: true }, lang: { type: 'string', enum: [null, ...Object.keys(langmap)], nullable: true }, avatarId: { type: 'string', format: 'misskey:id', nullable: true }, bannerId: { type: 'string', format: 'misskey:id', nullable: true }, @@ -133,7 +133,7 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, _user, token) => { - const user = await Users.findOneByOrFail({ id: _user.id }); + const user = await this.usersRepository.findOneByOrFail({ id: _user.id }); const isSecure = token == null; const updates = {} as Partial; @@ -241,10 +241,10 @@ export default class extends Endpoint { updateUsertags(user, tags); //#endregion - if (Object.keys(updates).length > 0) await Users.update(user.id, updates); + if (Object.keys(updates).length > 0) await this.usersRepository.update(user.id, updates); if (Object.keys(profileUpdates).length > 0) await UserProfiles.update(user.id, profileUpdates); - const iObj = await Users.pack(user.id, user, { + const iObj = await this.usersRepository.pack(user.id, user, { detail: true, includeSecrets: isSecure, }); diff --git a/packages/backend/src/server/api/endpoints/messaging/messages.ts b/packages/backend/src/server/api/endpoints/messaging/messages.ts index 78164fd48437..aed02d399a6d 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages.ts @@ -108,7 +108,7 @@ export default class extends Endpoint { readUserMessagingMessage(user.id, recipient.id, messages.filter(m => m.recipientId === user.id).map(x => x.id)); // リモートユーザーとのメッセージだったら既読配信 - if (Users.isLocalUser(user) && Users.isRemoteUser(recipient)) { + if (this.usersRepository.isLocalUser(user) && this.usersRepository.isRemoteUser(recipient)) { deliverReadActivity(user, recipient, messages); } } diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index e082d9e9afc6..c672a64e1482 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -393,14 +393,14 @@ export default class extends Endpoint { pinnedPages: instance.pinnedPages, pinnedClipId: instance.pinnedClipId, cacheRemoteFiles: instance.cacheRemoteFiles, - requireSetup: (await Users.countBy({ + requireSetup: (await this.usersRepository.countBy({ host: IsNull(), })) === 0, } : {}), }; if (ps.detail) { - const proxyAccount = instance.proxyAccountId ? await Users.pack(instance.proxyAccountId).catch(() => null) : null; + const proxyAccount = instance.proxyAccountId ? await this.usersRepository.pack(instance.proxyAccountId).catch(() => null) : null; response.proxyAccountName = proxyAccount ? proxyAccount.username : null; response.features = { diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index ffa81b1a742d..c7a28bcb2587 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -174,7 +174,7 @@ export default class extends Endpoint { super(meta, paramDef, async (ps, user) => { let visibleUsers: User[] = []; if (ps.visibleUserIds) { - visibleUsers = await Users.findBy({ + visibleUsers = await this.usersRepository.findBy({ id: In(ps.visibleUserIds), }); } diff --git a/packages/backend/src/server/api/endpoints/notes/delete.ts b/packages/backend/src/server/api/endpoints/notes/delete.ts index 3bc6dd1716e9..c2fd2d1ca3ce 100644 --- a/packages/backend/src/server/api/endpoints/notes/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/delete.ts @@ -63,7 +63,7 @@ export default class extends Endpoint { } // この操作を行うのが投稿者とは限らない(例えばモデレーター)ため - await deleteNote(await Users.findOneByOrFail({ id: note.userId }), note); + await deleteNote(await this.usersRepository.findOneByOrFail({ id: note.userId }), note); }); } } 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 812feca72587..aebf682a861f 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -169,7 +169,7 @@ export default class extends Endpoint { // リモート投票の場合リプライ送信 if (note.userHost != null) { - const pollOwner = await Users.findOneByOrFail({ id: note.userId }) as IRemoteUser; + const pollOwner = await this.usersRepository.findOneByOrFail({ id: note.userId }) as IRemoteUser; deliver(user, renderActivity(await renderVote(user, vote, note, poll, pollOwner)), pollOwner.inbox); } diff --git a/packages/backend/src/server/api/endpoints/notes/unrenote.ts b/packages/backend/src/server/api/endpoints/notes/unrenote.ts index 3c67eb618816..9ff0215586e4 100644 --- a/packages/backend/src/server/api/endpoints/notes/unrenote.ts +++ b/packages/backend/src/server/api/endpoints/notes/unrenote.ts @@ -58,7 +58,7 @@ export default class extends Endpoint { }); for (const note of renotes) { - deleteNote(await Users.findOneByOrFail({ id: user.id }), note); + deleteNote(await this.usersRepository.findOneByOrFail({ id: user.id }), note); } }); } diff --git a/packages/backend/src/server/api/endpoints/page-push.ts b/packages/backend/src/server/api/endpoints/page-push.ts index 7e51b3c1ce31..e6fdd635caf4 100644 --- a/packages/backend/src/server/api/endpoints/page-push.ts +++ b/packages/backend/src/server/api/endpoints/page-push.ts @@ -48,7 +48,7 @@ export default class extends Endpoint { event: ps.event, var: ps.var, userId: user.id, - user: await Users.pack(user.id, { id: page.userId }, { + user: await this.usersRepository.pack(user.id, { id: page.userId }, { detail: true, }), }); diff --git a/packages/backend/src/server/api/endpoints/pages/show.ts b/packages/backend/src/server/api/endpoints/pages/show.ts index 0cf0e7e0ba4b..ed8ab90a67c4 100644 --- a/packages/backend/src/server/api/endpoints/pages/show.ts +++ b/packages/backend/src/server/api/endpoints/pages/show.ts @@ -60,7 +60,7 @@ export default class extends Endpoint { if (ps.pageId) { page = await Pages.findOneBy({ id: ps.pageId }); } else if (ps.name && ps.username) { - const author = await Users.findOneBy({ + const author = await this.usersRepository.findOneBy({ host: IsNull(), usernameLower: ps.username.toLowerCase(), }); diff --git a/packages/backend/src/server/api/endpoints/pinned-users.ts b/packages/backend/src/server/api/endpoints/pinned-users.ts index 0d9ddbfd30e8..e74fc6c18340 100644 --- a/packages/backend/src/server/api/endpoints/pinned-users.ts +++ b/packages/backend/src/server/api/endpoints/pinned-users.ts @@ -41,12 +41,12 @@ export default class extends Endpoint { super(meta, paramDef, async (ps, me) => { const meta = await fetchMeta(); - const users = await Promise.all(meta.pinnedUsers.map(acct => Acct.parse(acct)).map(acct => Users.findOneBy({ + const users = await Promise.all(meta.pinnedthis.usersRepository.map(acct => Acct.parse(acct)).map(acct => this.usersRepository.findOneBy({ usernameLower: acct.username.toLowerCase(), host: acct.host ?? IsNull(), }))); - return await Users.packMany(users.filter(x => x !== undefined) as User[], me, { detail: true }); + return await this.usersRepository.packMany(users.filter(x => x !== undefined) as User[], me, { detail: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/request-reset-password.ts b/packages/backend/src/server/api/endpoints/request-reset-password.ts index 37a04b6686d6..057e1e38e382 100644 --- a/packages/backend/src/server/api/endpoints/request-reset-password.ts +++ b/packages/backend/src/server/api/endpoints/request-reset-password.ts @@ -47,7 +47,7 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user) => { - const user = await Users.findOneBy({ + const user = await this.usersRepository.findOneBy({ usernameLower: ps.username.toLowerCase(), host: IsNull(), }); diff --git a/packages/backend/src/server/api/endpoints/stats.ts b/packages/backend/src/server/api/endpoints/stats.ts index 82bf1237d114..d09d9399c489 100644 --- a/packages/backend/src/server/api/endpoints/stats.ts +++ b/packages/backend/src/server/api/endpoints/stats.ts @@ -73,8 +73,8 @@ export default class extends Endpoint { ] = await Promise.all([ Notes.count({ cache: 3600000 }), // 1 hour Notes.count({ where: { userHost: IsNull() }, cache: 3600000 }), - Users.count({ cache: 3600000 }), - Users.count({ where: { host: IsNull() }, cache: 3600000 }), + this.usersRepository.count({ cache: 3600000 }), + this.usersRepository.count({ where: { host: IsNull() }, cache: 3600000 }), NoteReactions.count({ cache: 3600000 }), // 1 hour //NoteReactions.count({ where: { userHost: IsNull() }, cache: 3600000 }), Instances.count({ cache: 3600000 }), diff --git a/packages/backend/src/server/api/endpoints/username/available.ts b/packages/backend/src/server/api/endpoints/username/available.ts index 94fc1384a807..acb825521c87 100644 --- a/packages/backend/src/server/api/endpoints/username/available.ts +++ b/packages/backend/src/server/api/endpoints/username/available.ts @@ -23,7 +23,7 @@ export const meta = { export const paramDef = { type: 'object', properties: { - username: Users.localUsernameSchema, + username: this.usersRepository.localUsernameSchema, }, required: ['username'], } as const; @@ -40,7 +40,7 @@ export default class extends Endpoint { ) { super(meta, paramDef, async (ps, user) => { // Get exist - const exist = await Users.countBy({ + const exist = await this.usersRepository.countBy({ host: IsNull(), usernameLower: ps.username.toLowerCase(), }); diff --git a/packages/backend/src/server/api/endpoints/users.ts b/packages/backend/src/server/api/endpoints/users.ts index 48f826e7e6a6..fb40916370fb 100644 --- a/packages/backend/src/server/api/endpoints/users.ts +++ b/packages/backend/src/server/api/endpoints/users.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { generateMutedUserQueryForUsers } from '../common/generate-muted-user-query.js'; import { generateBlockQueryForUsers } from '../common/generate-block-query.js'; @@ -49,7 +49,7 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const query = Users.createQueryBuilder('user'); + const query = this.usersRepository.createQueryBuilder('user'); query.where('user.isExplorable = TRUE'); switch (ps.state) { @@ -86,7 +86,7 @@ export default class extends Endpoint { const users = await query.getMany(); - return await Users.packMany(users, me, { detail: true }); + return await this.usersRepository.packMany(users, me, { detail: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index c8ba6ae6e0b8..fe6649514641 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -77,7 +77,7 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const user = await Users.findOneBy(ps.userId != null + const user = await this.usersRepository.findOneBy(ps.userId != null ? { id: ps.userId } : { usernameLower: ps.username!.toLowerCase(), host: toPunyNullable(ps.host) ?? IsNull() }); diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index b8fd71fd1dcc..fb8ff20d6478 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -77,7 +77,7 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const user = await Users.findOneBy(ps.userId != null + const user = await this.usersRepository.findOneBy(ps.userId != null ? { id: ps.userId } : { usernameLower: ps.username!.toLowerCase(), host: toPunyNullable(ps.host) ?? IsNull() }); diff --git a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts index f1366037be62..d49cdf1031d3 100644 --- a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts +++ b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts @@ -115,8 +115,8 @@ export default class extends Endpoint { const topRepliedUsers = repliedUsersSorted.slice(0, ps.limit); // Make replies object (includes weights) - const repliesObj = await Promise.all(topRepliedUsers.map(async (user) => ({ - user: await Users.pack(user, me, { detail: true }), + const repliesObj = await Promise.all(topRepliedthis.usersRepository.map(async (user) => ({ + user: await this.usersRepository.pack(user, me, { detail: true }), weight: repliedUsers[user] / peak, }))); diff --git a/packages/backend/src/server/api/endpoints/users/lists/pull.ts b/packages/backend/src/server/api/endpoints/users/lists/pull.ts index b7f231f2ee8b..c6d6bd5c89ea 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/pull.ts @@ -68,7 +68,7 @@ export default class extends Endpoint { // Pull the user await UserListJoinings.delete({ userListId: userList.id, userId: user.id }); - publishUserListStream(userList.id, 'userRemoved', await Users.pack(user)); + publishUserListStream(userList.id, 'userRemoved', await this.usersRepository.pack(user)); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/recommendation.ts b/packages/backend/src/server/api/endpoints/users/recommendation.ts index fb1678db9794..7f571ca8178e 100644 --- a/packages/backend/src/server/api/endpoints/users/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/users/recommendation.ts @@ -45,7 +45,7 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const query = Users.createQueryBuilder('user') + const query = this.usersRepository.createQueryBuilder('user') .where('user.isLocked = FALSE') .andWhere('user.isExplorable = TRUE') .andWhere('user.host IS NULL') @@ -68,7 +68,7 @@ export default class extends Endpoint { const users = await query.take(ps.limit).skip(ps.offset).getMany(); - return await Users.packMany(users, me, { detail: true }); + return await this.usersRepository.packMany(users, me, { detail: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/relation.ts b/packages/backend/src/server/api/endpoints/users/relation.ts index f087825e8369..75d7481bcda5 100644 --- a/packages/backend/src/server/api/endpoints/users/relation.ts +++ b/packages/backend/src/server/api/endpoints/users/relation.ts @@ -125,7 +125,7 @@ export default class extends Endpoint { super(meta, paramDef, async (ps, me) => { const ids = Array.isArray(ps.userId) ? ps.userId : [ps.userId]; - const relations = await Promise.all(ids.map(id => Users.getRelation(me.id, id))); + const relations = await Promise.all(ids.map(id => this.usersRepository.getRelation(me.id, id))); return Array.isArray(ps.userId) ? relations : relations[0]; }); diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts index a305d94c2147..2e656f8cfd94 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -1,7 +1,8 @@ import * as sanitizeHtml from 'sanitize-html'; import { Inject, Injectable } from '@nestjs/common'; import { publishAdminStream } from '@/services/stream.js'; -import { AbuseUserReports, Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { AbuseUserReports } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { sendEmail } from '@/services/send-email.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; @@ -83,7 +84,7 @@ export default class extends Endpoint { // Publish event to moderators setImmediate(async () => { - const moderators = await Users.find({ + const moderators = await this.usersRepository.find({ where: [{ isAdmin: true, }, { diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index 96f7448cf5ef..71be7ecae6da 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -1,6 +1,7 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { Followings, Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { Followings } from '@/models/index.js'; import { USER_ACTIVE_THRESHOLD } from '@/const.js'; import type { User } from '@/models/entities/user.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -53,7 +54,7 @@ export default class extends Endpoint { const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日 if (ps.host) { - const q = Users.createQueryBuilder('user') + const q = this.usersRepository.createQueryBuilder('user') .where('user.isSuspended = FALSE') .andWhere('user.host LIKE :host', { host: ps.host.toLowerCase() + '%' }); @@ -66,7 +67,7 @@ export default class extends Endpoint { const users = await q.take(ps.limit).getMany(); - return await Users.packMany(users, me, { detail: ps.detail }); + return await this.usersRepository.packMany(users, me, { detail: ps.detail }); } else if (ps.username) { let users: User[] = []; @@ -75,7 +76,7 @@ export default class extends Endpoint { .select('following.followeeId') .where('following.followerId = :followerId', { followerId: me.id }); - const query = Users.createQueryBuilder('user') + const query = this.usersRepository.createQueryBuilder('user') .where(`user.id IN (${ followingQuery.getQuery() })`) .andWhere('user.id != :meId', { meId: me.id }) .andWhere('user.isSuspended = FALSE') @@ -93,7 +94,7 @@ export default class extends Endpoint { .getMany(); if (users.length < ps.limit) { - const otherQuery = await Users.createQueryBuilder('user') + const otherQuery = await this.usersRepository.createQueryBuilder('user') .where(`user.id NOT IN (${ followingQuery.getQuery() })`) .andWhere('user.id != :meId', { meId: me.id }) .andWhere('user.isSuspended = FALSE') @@ -110,7 +111,7 @@ export default class extends Endpoint { users = users.concat(otherUsers); } } else { - users = await Users.createQueryBuilder('user') + users = await this.usersRepository.createQueryBuilder('user') .where('user.isSuspended = FALSE') .andWhere('user.usernameLower LIKE :username', { username: ps.username.toLowerCase() + '%' }) .andWhere('user.updatedAt IS NOT NULL') @@ -119,7 +120,7 @@ export default class extends Endpoint { .getMany(); } - return await Users.packMany(users, me, { detail: !!ps.detail }); + return await this.usersRepository.packMany(users, me, { detail: !!ps.detail }); } return []; diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index c37cf29e1a49..b76283b77a79 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -1,6 +1,7 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { UserProfiles, Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { UserProfiles } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -52,7 +53,7 @@ export default class extends Endpoint { let users: User[] = []; if (isUsername) { - const usernameQuery = Users.createQueryBuilder('user') + const usernameQuery = this.usersRepository.createQueryBuilder('user') .where('user.usernameLower LIKE :username', { username: ps.query.replace('@', '').toLowerCase() + '%' }) .andWhere(new Brackets(qb => { qb .where('user.updatedAt IS NULL') @@ -72,12 +73,12 @@ export default class extends Endpoint { .skip(ps.offset) .getMany(); } else { - const nameQuery = Users.createQueryBuilder('user') + const nameQuery = this.usersRepository.createQueryBuilder('user') .where(new Brackets(qb => { qb.where('user.name ILIKE :query', { query: '%' + ps.query + '%' }); // Also search username if it qualifies as username - if (Users.validateLocalUsername(ps.query)) { + if (this.usersRepository.validateLocalUsername(ps.query)) { qb.orWhere('user.usernameLower LIKE :username', { username: '%' + ps.query.toLowerCase() + '%' }); } })) @@ -110,7 +111,7 @@ export default class extends Endpoint { profQuery.andWhere('prof.userHost IS NOT NULL'); } - const query = Users.createQueryBuilder('user') + const query = this.usersRepository.createQueryBuilder('user') .where(`user.id IN (${ profQuery.getQuery() })`) .andWhere(new Brackets(qb => { qb .where('user.updatedAt IS NULL') @@ -128,7 +129,7 @@ export default class extends Endpoint { } } - return await Users.packMany(users, me, { detail: ps.detail }); + return await this.usersRepository.packMany(users, me, { detail: ps.detail }); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts index 7ba8ec1f35b6..5cd4961c4855 100644 --- a/packages/backend/src/server/api/endpoints/users/show.ts +++ b/packages/backend/src/server/api/endpoints/users/show.ts @@ -1,7 +1,7 @@ import { In, IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { resolveUser } from '@/remote/resolve-user.js'; -import { Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { apiLogger } from '../../logger.js'; @@ -99,7 +99,7 @@ export default class extends Endpoint { return []; } - const users = await Users.findBy(isAdminOrModerator ? { + const users = await this.usersRepository.findBy(isAdminOrModerator ? { id: In(ps.userIds), } : { id: In(ps.userIds), @@ -112,7 +112,7 @@ export default class extends Endpoint { _users.push(users.find(x => x.id === id)!); } - return await Promise.all(_users.map(u => Users.pack(u, me, { + return await Promise.all(_users.map(u => this.usersRepository.pack(u, me, { detail: true, }))); } else { @@ -127,14 +127,14 @@ export default class extends Endpoint { ? { id: ps.userId } : { usernameLower: ps.username!.toLowerCase(), host: IsNull() }; - user = await Users.findOneBy(q); + user = await this.usersRepository.findOneBy(q); } if (user == null || (!isAdminOrModerator && user.isSuspended)) { throw new ApiError(meta.errors.noSuchUser); } - return await Users.pack(user, me, { + return await this.usersRepository.pack(user, me, { detail: true, }); } diff --git a/packages/backend/src/server/api/endpoints/users/stats.ts b/packages/backend/src/server/api/endpoints/users/stats.ts index d46aeb691c7f..ebf01dba9b27 100644 --- a/packages/backend/src/server/api/endpoints/users/stats.ts +++ b/packages/backend/src/server/api/endpoints/users/stats.ts @@ -1,5 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DriveFiles, Followings, NoteFavorites, NoteReactions, Notes, PageLikes, PollVotes, Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { DriveFiles, Followings, NoteFavorites, NoteReactions, Notes, PageLikes, PollVotes } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; @@ -127,7 +128,7 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const user = await Users.findOneBy({ id: ps.userId }); + const user = await this.usersRepository.findOneBy({ id: ps.userId }); if (user == null) { throw new ApiError(meta.errors.noSuchUser); } From f7499fc820321261203185d8e8dee7237bafc014 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 11 Sep 2022 20:34:38 +0900 Subject: [PATCH 013/180] wip --- .../api/endpoints/admin/abuse-user-reports.ts | 7 +-- .../server/api/endpoints/admin/ad/create.ts | 2 +- .../src/server/api/endpoints/admin/ad/list.ts | 7 +-- .../endpoints/admin/announcements/create.ts | 7 +-- .../api/endpoints/admin/announcements/list.ts | 7 +-- .../api/endpoints/admin/delete-account.ts | 7 +-- .../endpoints/admin/emoji/add-aliases-bulk.ts | 7 +-- .../api/endpoints/admin/emoji/import-zip.ts | 9 +--- .../api/endpoints/admin/emoji/list-remote.ts | 7 +-- .../server/api/endpoints/admin/emoji/list.ts | 7 +-- .../admin/emoji/remove-aliases-bulk.ts | 7 +-- .../endpoints/admin/emoji/set-aliases-bulk.ts | 6 +-- .../admin/emoji/set-category-bulk.ts | 7 +-- .../api/endpoints/admin/emoji/update.ts | 7 +-- .../api/endpoints/admin/promo/create.ts | 7 +-- .../endpoints/admin/queue/deliver-delayed.ts | 7 +-- .../endpoints/admin/queue/inbox-delayed.ts | 7 +-- .../server/api/endpoints/admin/queue/stats.ts | 7 +-- .../server/api/endpoints/admin/relays/add.ts | 7 +-- .../server/api/endpoints/admin/relays/list.ts | 7 +-- .../api/endpoints/admin/relays/remove.ts | 7 +-- .../server/api/endpoints/admin/send-email.ts | 6 +-- .../endpoints/admin/show-moderation-logs.ts | 6 +-- .../src/server/api/endpoints/announcements.ts | 11 ++--- .../server/api/endpoints/antennas/create.ts | 13 ++--- .../server/api/endpoints/antennas/delete.ts | 9 +--- .../server/api/endpoints/antennas/notes.ts | 19 +++---- .../server/api/endpoints/antennas/update.ts | 13 ++--- .../src/server/api/endpoints/ap/get.ts | 7 +-- .../src/server/api/endpoints/app/create.ts | 9 +--- .../src/server/api/endpoints/auth/accept.ts | 13 ++--- .../api/endpoints/auth/session/generate.ts | 7 +-- .../server/api/endpoints/auth/session/show.ts | 9 +--- .../api/endpoints/auth/session/userkey.ts | 8 ++- .../server/api/endpoints/blocking/create.ts | 12 ++--- .../server/api/endpoints/blocking/delete.ts | 12 ++--- .../server/api/endpoints/channels/create.ts | 13 ++--- .../server/api/endpoints/channels/follow.ts | 11 ++--- .../server/api/endpoints/channels/timeline.ts | 37 ++++++-------- .../server/api/endpoints/channels/unfollow.ts | 11 ++--- .../api/endpoints/charts/active-users.ts | 15 +++--- .../server/api/endpoints/charts/ap-request.ts | 15 +++--- .../src/server/api/endpoints/charts/drive.ts | 15 +++--- .../server/api/endpoints/charts/federation.ts | 15 +++--- .../server/api/endpoints/charts/hashtag.ts | 15 +++--- .../server/api/endpoints/charts/instance.ts | 15 +++--- .../src/server/api/endpoints/charts/notes.ts | 15 +++--- .../server/api/endpoints/charts/user/drive.ts | 15 +++--- .../api/endpoints/charts/user/following.ts | 15 +++--- .../server/api/endpoints/charts/user/notes.ts | 15 +++--- .../api/endpoints/charts/user/reactions.ts | 15 +++--- .../src/server/api/endpoints/charts/users.ts | 15 +++--- .../server/api/endpoints/clips/add-note.ts | 9 +--- .../src/server/api/endpoints/clips/create.ts | 9 +--- .../src/server/api/endpoints/clips/delete.ts | 9 +--- .../src/server/api/endpoints/clips/notes.ts | 49 +++++++++---------- .../server/api/endpoints/clips/remove-note.ts | 9 +--- .../src/server/api/endpoints/clips/update.ts | 9 +--- .../backend/src/server/api/endpoints/drive.ts | 11 ++--- .../src/server/api/endpoints/drive/files.ts | 9 +--- .../endpoints/drive/files/attached-notes.ts | 15 ++---- .../endpoints/drive/files/check-existence.ts | 9 +--- .../api/endpoints/drive/files/delete.ts | 11 ++--- .../api/endpoints/drive/files/find-by-hash.ts | 9 +--- .../server/api/endpoints/drive/files/find.ts | 9 +--- .../server/api/endpoints/drive/files/show.ts | 9 +--- .../api/endpoints/drive/files/update.ts | 13 ++--- .../src/server/api/endpoints/drive/folders.ts | 9 +--- .../api/endpoints/drive/folders/create.ts | 13 ++--- .../api/endpoints/drive/folders/delete.ts | 11 ++--- .../api/endpoints/drive/folders/find.ts | 9 +--- .../api/endpoints/drive/folders/show.ts | 9 +--- .../api/endpoints/drive/folders/update.ts | 13 ++--- .../src/server/api/endpoints/drive/stream.ts | 9 +--- .../api/endpoints/email-address/available.ts | 7 +-- .../src/server/api/endpoints/endpoint.ts | 6 +-- .../api/endpoints/export-custom-emojis.ts | 8 +-- .../server/api/endpoints/federation/stats.ts | 7 +-- .../federation/update-remote-user.ts | 7 +-- .../server/api/endpoints/federation/users.ts | 11 ++--- .../src/server/api/endpoints/fetch-rss.ts | 7 +-- .../server/api/endpoints/following/create.ts | 14 +++--- .../server/api/endpoints/following/delete.ts | 14 +++--- .../api/endpoints/following/invalidate.ts | 14 +++--- .../endpoints/following/requests/accept.ts | 9 +--- .../endpoints/following/requests/cancel.ts | 11 ++--- .../api/endpoints/following/requests/list.ts | 9 +--- .../endpoints/following/requests/reject.ts | 9 +--- .../api/endpoints/gallery/posts/create.ts | 13 ++--- .../api/endpoints/gallery/posts/delete.ts | 9 +--- .../api/endpoints/gallery/posts/like.ts | 13 ++--- .../api/endpoints/gallery/posts/unlike.ts | 9 +--- .../api/endpoints/gallery/posts/update.ts | 13 ++--- .../server/api/endpoints/hashtags/search.ts | 19 +++---- .../src/server/api/endpoints/hashtags/show.ts | 7 +-- .../src/server/api/endpoints/i/2fa/done.ts | 11 ++--- .../server/api/endpoints/i/2fa/key-done.ts | 17 +++---- .../api/endpoints/i/2fa/password-less.ts | 9 +--- .../api/endpoints/i/2fa/register-key.ts | 11 ++--- .../server/api/endpoints/i/2fa/register.ts | 15 ++---- .../server/api/endpoints/i/2fa/remove-key.ts | 14 +++--- .../server/api/endpoints/i/2fa/unregister.ts | 11 ++--- .../src/server/api/endpoints/i/apps.ts | 9 +--- .../server/api/endpoints/i/authorized-apps.ts | 11 ++--- .../server/api/endpoints/i/change-password.ts | 11 ++--- .../server/api/endpoints/i/delete-account.ts | 14 +++--- .../server/api/endpoints/i/export-blocking.ts | 9 +--- .../api/endpoints/i/export-following.ts | 9 +--- .../src/server/api/endpoints/i/export-mute.ts | 9 +--- .../server/api/endpoints/i/export-notes.ts | 9 +--- .../api/endpoints/i/export-user-lists.ts | 9 +--- .../src/server/api/endpoints/i/favorites.ts | 11 ++--- .../server/api/endpoints/i/gallery/likes.ts | 11 ++--- .../server/api/endpoints/i/gallery/posts.ts | 11 ++--- .../endpoints/i/get-word-muted-notes-count.ts | 9 +--- .../server/api/endpoints/i/import-blocking.ts | 9 +--- .../api/endpoints/i/import-following.ts | 9 +--- .../server/api/endpoints/i/import-muting.ts | 9 +--- .../api/endpoints/i/import-user-lists.ts | 8 +-- .../server/api/endpoints/i/notifications.ts | 24 +++++---- .../src/server/api/endpoints/i/page-likes.ts | 11 ++--- .../src/server/api/endpoints/i/pages.ts | 9 +--- .../backend/src/server/api/endpoints/i/pin.ts | 11 ++--- .../i/read-all-messaging-messages.ts | 19 +++---- .../api/endpoints/i/read-all-unread-notes.ts | 13 ++--- .../api/endpoints/i/read-announcement.ts | 16 +++--- .../api/endpoints/i/regenerate-token.ts | 20 ++++---- .../api/endpoints/i/registry/get-all.ts | 9 +--- .../api/endpoints/i/registry/get-detail.ts | 9 +--- .../server/api/endpoints/i/registry/get.ts | 9 +--- .../endpoints/i/registry/keys-with-type.ts | 9 +--- .../server/api/endpoints/i/registry/keys.ts | 9 +--- .../server/api/endpoints/i/registry/remove.ts | 9 +--- .../server/api/endpoints/i/registry/scopes.ts | 9 +--- .../server/api/endpoints/i/registry/set.ts | 13 ++--- .../server/api/endpoints/i/revoke-token.ts | 11 ++--- .../server/api/endpoints/i/signin-history.ts | 9 +--- .../src/server/api/endpoints/i/unpin.ts | 11 ++--- .../server/api/endpoints/i/update-email.ts | 18 +++---- .../api/endpoints/i/user-group-invites.ts | 9 +--- .../server/api/endpoints/i/webhooks/create.ts | 9 +--- .../server/api/endpoints/i/webhooks/delete.ts | 9 +--- .../server/api/endpoints/i/webhooks/show.ts | 9 +--- .../server/api/endpoints/i/webhooks/update.ts | 9 +--- .../server/api/endpoints/messaging/history.ts | 19 +++---- .../api/endpoints/messaging/messages.ts | 24 +++++---- .../endpoints/messaging/messages/create.ts | 17 +++---- .../endpoints/messaging/messages/delete.ts | 9 +--- .../api/endpoints/messaging/messages/read.ts | 11 ++--- .../server/api/endpoints/miauth/gen-token.ts | 9 +--- .../src/server/api/endpoints/mute/create.ts | 13 ++--- .../src/server/api/endpoints/mute/delete.ts | 13 ++--- .../src/server/api/endpoints/my/apps.ts | 11 ++--- .../backend/src/server/api/endpoints/notes.ts | 5 +- .../server/api/endpoints/notes/children.ts | 17 +++---- .../api/endpoints/notes/conversation.ts | 9 +--- .../src/server/api/endpoints/notes/create.ts | 22 ++++----- .../src/server/api/endpoints/notes/delete.ts | 9 ++-- .../api/endpoints/notes/favorites/create.ts | 10 ++-- .../api/endpoints/notes/favorites/delete.ts | 8 +-- .../server/api/endpoints/notes/featured.ts | 13 ++--- .../api/endpoints/notes/global-timeline.ts | 25 ++++------ .../api/endpoints/notes/hybrid-timeline.ts | 33 ++++++------- .../api/endpoints/notes/local-timeline.ts | 27 +++++----- .../server/api/endpoints/notes/mentions.ts | 27 +++++----- .../endpoints/notes/polls/recommendation.ts | 14 ++---- .../server/api/endpoints/notes/polls/vote.ts | 26 +++++----- .../server/api/endpoints/notes/reactions.ts | 9 +--- .../api/endpoints/notes/reactions/create.ts | 9 +--- .../api/endpoints/notes/reactions/delete.ts | 9 +--- .../src/server/api/endpoints/notes/renotes.ts | 15 ++---- .../src/server/api/endpoints/notes/replies.ts | 15 ++---- .../src/server/api/endpoints/notes/show.ts | 9 +--- .../src/server/api/endpoints/notes/state.ts | 13 ++--- .../endpoints/notes/thread-muting/create.ts | 11 ++--- .../endpoints/notes/thread-muting/delete.ts | 9 +--- .../server/api/endpoints/notes/timeline.ts | 33 ++++++------- .../server/api/endpoints/notes/translate.ts | 9 +--- .../server/api/endpoints/notes/unrenote.ts | 12 ++--- .../api/endpoints/notes/user-list-timeline.ts | 19 +++---- .../api/endpoints/notes/watching/create.ts | 9 +--- .../api/endpoints/notes/watching/delete.ts | 9 +--- .../notifications/mark-all-as-read.ts | 13 ++--- .../api/endpoints/notifications/read.ts | 11 ++--- .../src/server/api/endpoints/page-push.ts | 12 ++--- .../src/server/api/endpoints/pages/create.ts | 13 ++--- .../src/server/api/endpoints/pages/delete.ts | 9 +--- .../src/server/api/endpoints/pages/like.ts | 13 ++--- .../src/server/api/endpoints/pages/show.ts | 10 ++-- .../src/server/api/endpoints/pages/unlike.ts | 9 +--- .../src/server/api/endpoints/pages/update.ts | 13 ++--- .../src/server/api/endpoints/promo/read.ts | 11 ++--- .../api/endpoints/request-reset-password.ts | 8 ++- .../src/server/api/endpoints/reset-db.ts | 7 +-- .../server/api/endpoints/reset-password.ts | 7 +-- .../src/server/api/endpoints/sw/register.ts | 11 ++--- .../src/server/api/endpoints/sw/unregister.ts | 9 +--- .../api/endpoints/username/available.ts | 9 ++-- .../src/server/api/endpoints/users/clips.ts | 7 +-- .../api/endpoints/users/gallery/posts.ts | 9 +--- .../api/endpoints/users/groups/create.ts | 11 ++--- .../api/endpoints/users/groups/delete.ts | 9 +--- .../users/groups/invitations/accept.ts | 11 ++--- .../users/groups/invitations/reject.ts | 9 +--- .../api/endpoints/users/lists/create.ts | 9 +--- .../api/endpoints/users/lists/delete.ts | 9 +--- .../api/endpoints/users/lists/update.ts | 9 +--- .../src/server/api/endpoints/users/pages.ts | 7 +-- .../services/chart/ChartManagementService.ts | 24 ++++----- packages/backend/src/services/fooService.ts | 11 ----- packages/backend/src/services/index.ts | 4 -- .../backend/src/services/webhookService.ts | 14 ------ packages/backend/test/tests/note.ts | 4 +- 213 files changed, 747 insertions(+), 1701 deletions(-) delete mode 100644 packages/backend/src/services/fooService.ts delete mode 100644 packages/backend/src/services/index.ts delete mode 100644 packages/backend/src/services/webhookService.ts diff --git a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts index 035df998797f..38e0160efb47 100644 --- a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts +++ b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts @@ -89,13 +89,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(AbuseUserReports.createQueryBuilder('report'), ps.sinceId, ps.untilId); switch (ps.state) { diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts index b8c39b9a8137..e749e93eed6d 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts @@ -31,7 +31,7 @@ export default class extends Endpoint { @Inject('adsRepository') private adsRepository: typeof Ads, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { await this.adsRepository.insert({ id: genId(), createdAt: new Date(), diff --git a/packages/backend/src/server/api/endpoints/admin/ad/list.ts b/packages/backend/src/server/api/endpoints/admin/ad/list.ts index 955a6c8cf940..10813b7a1410 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/list.ts @@ -24,13 +24,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(Ads.createQueryBuilder('ad'), ps.sinceId, ps.untilId) .andWhere('ad.expiresAt > :now', { now: new Date() }); diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts index 20a76f95e93c..c080ff2dd5d0 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts @@ -59,13 +59,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const announcement = await Announcements.insert({ id: genId(), createdAt: new Date(), diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts index df23f732a89d..92543926f9ac 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts @@ -68,13 +68,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(Announcements.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); const announcements = await query.take(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/admin/delete-account.ts b/packages/backend/src/server/api/endpoints/admin/delete-account.ts index c728932b65b6..7e465e11c73c 100644 --- a/packages/backend/src/server/api/endpoints/admin/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/admin/delete-account.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; import { deleteAccount } from '@/services/delete-account.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -27,11 +27,8 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps) => { const user = await this.usersRepository.findOneByOrFail({ id: ps.userId }); if (user.isDeleted) { return; diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts index 356430601279..fb13059cc261 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts @@ -29,13 +29,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const emojis = await Emojis.findBy({ id: In(ps.ids), }); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts index 76e1f76c017c..56846fe53759 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts @@ -21,14 +21,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - createImportCustomEmojisJob(user, ps.fileId); + super(meta, paramDef, async (ps, me) => { + createImportCustomEmojisJob(me, ps.fileId); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts index 4a044c888245..d9771896abb4 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts @@ -73,13 +73,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const q = makePaginationQuery(Emojis.createQueryBuilder('emoji'), ps.sinceId, ps.untilId); if (ps.host == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts index 6c05cd4346f2..5758b709d93e 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts @@ -67,13 +67,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const q = makePaginationQuery(Emojis.createQueryBuilder('emoji'), ps.sinceId, ps.untilId) .andWhere('emoji.host IS NULL'); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts index 295292a6691c..d4ea51b29c83 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts @@ -29,13 +29,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const emojis = await Emojis.findBy({ id: In(ps.ids), }); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts index 77538d666847..a258ab734abb 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts @@ -29,13 +29,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { await Emojis.update({ id: In(ps.ids), }, { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts index 209f91df642e..4d6e6fe84466 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts @@ -31,13 +31,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { await Emojis.update({ id: In(ps.ids), }, { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index 78e063ddc05b..7fbc9c2e4f00 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -40,13 +40,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const emoji = await Emojis.findOneBy({ id: ps.id }); if (emoji == null) throw new ApiError(meta.errors.noSuchEmoji); diff --git a/packages/backend/src/server/api/endpoints/admin/promo/create.ts b/packages/backend/src/server/api/endpoints/admin/promo/create.ts index cb9155dd033e..7c369d0169e0 100644 --- a/packages/backend/src/server/api/endpoints/admin/promo/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/promo/create.ts @@ -38,13 +38,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts index 116ad33f9be5..5a1e48021d22 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts @@ -43,13 +43,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const jobs = await deliverQueue.getJobs(['delayed']); const res = [] as [string, number][]; diff --git a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts index b94559d93b2d..062642a71b2f 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts @@ -43,13 +43,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const jobs = await inboxQueue.getJobs(['delayed']); const res = [] as [string, number][]; diff --git a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts index c6747cd262e9..ff0482c46eb7 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts @@ -42,13 +42,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const deliverJobCounts = await deliverQueue.getJobCounts(); const inboxJobCounts = await inboxQueue.getJobCounts(); const dbJobCounts = await dbQueue.getJobCounts(); diff --git a/packages/backend/src/server/api/endpoints/admin/relays/add.ts b/packages/backend/src/server/api/endpoints/admin/relays/add.ts index 23d914661fe2..1750a8d0f766 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/add.ts @@ -58,13 +58,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { try { if (new URL(ps.inbox).protocol !== 'https:') throw 'https only'; } catch { diff --git a/packages/backend/src/server/api/endpoints/admin/relays/list.ts b/packages/backend/src/server/api/endpoints/admin/relays/list.ts index 9b383dc7fdc3..5ac2f05d20c6 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/list.ts @@ -50,13 +50,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { return await listRelay(); }); } diff --git a/packages/backend/src/server/api/endpoints/admin/relays/remove.ts b/packages/backend/src/server/api/endpoints/admin/relays/remove.ts index 1904d8183d5e..808a7afcab84 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/remove.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/remove.ts @@ -21,13 +21,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { return await removeRelay(ps.inbox); }); } diff --git a/packages/backend/src/server/api/endpoints/admin/send-email.ts b/packages/backend/src/server/api/endpoints/admin/send-email.ts index f9b8b7ee83fe..51465dbf1017 100644 --- a/packages/backend/src/server/api/endpoints/admin/send-email.ts +++ b/packages/backend/src/server/api/endpoints/admin/send-email.ts @@ -23,13 +23,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { await sendEmail(ps.to, ps.subject, ps.text, ps.text); }); } diff --git a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts index db7efa2ae895..c9fa9e535af2 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts @@ -63,13 +63,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(ModerationLogs.createQueryBuilder('report'), ps.sinceId, ps.untilId); const reports = await query.take(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/announcements.ts b/packages/backend/src/server/api/endpoints/announcements.ts index 783f3121d3f2..bf26baeeb372 100644 --- a/packages/backend/src/server/api/endpoints/announcements.ts +++ b/packages/backend/src/server/api/endpoints/announcements.ts @@ -67,20 +67,15 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(Announcements.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); const announcements = await query.take(ps.limit).getMany(); - if (user) { + if (me) { const reads = (await AnnouncementReads.findBy({ - userId: user.id, + userId: me.id, })).map(x => x.announcementId); for (const announcement of announcements) { diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index b29c80493664..633d3f6eb763 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -65,20 +65,15 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { let userList; let userGroupJoining; if (ps.src === 'list' && ps.userListId) { userList = await UserLists.findOneBy({ id: ps.userListId, - userId: user.id, + userId: me.id, }); if (userList == null) { @@ -87,7 +82,7 @@ export default class extends Endpoint { } else if (ps.src === 'group' && ps.userGroupId) { userGroupJoining = await UserGroupJoinings.findOneBy({ userGroupId: ps.userGroupId, - userId: user.id, + userId: me.id, }); if (userGroupJoining == null) { @@ -98,7 +93,7 @@ export default class extends Endpoint { const antenna = await Antennas.insert({ id: genId(), createdAt: new Date(), - userId: user.id, + userId: me.id, name: ps.name, src: ps.src, userListId: userList ? userList.id : null, diff --git a/packages/backend/src/server/api/endpoints/antennas/delete.ts b/packages/backend/src/server/api/endpoints/antennas/delete.ts index 1408ca50b3e9..bb5f1b4b39e3 100644 --- a/packages/backend/src/server/api/endpoints/antennas/delete.ts +++ b/packages/backend/src/server/api/endpoints/antennas/delete.ts @@ -32,16 +32,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const antenna = await Antennas.findOneBy({ id: ps.antennaId, - userId: user.id, + userId: me.id, }); if (antenna == null) { diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index 03ab05bd374a..49a125fe7190 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -51,16 +51,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const antenna = await Antennas.findOneBy({ id: ps.antennaId, - userId: user.id, + userId: me.id, }); if (antenna == null) { @@ -83,19 +78,19 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') .andWhere('antennaNote.antennaId = :antennaId', { antennaId: antenna.id }); - generateVisibilityQuery(query, user); - generateMutedUserQuery(query, user); - generateBlockedUserQuery(query, user); + generateVisibilityQuery(query, me); + generateMutedUserQuery(query, me); + generateBlockedUserQuery(query, me); const notes = await query .take(ps.limit) .getMany(); if (notes.length > 0) { - readNote(user.id, notes); + readNote(me.id, notes); } - return await Notes.packMany(notes, user); + return await Notes.packMany(notes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts index 5cce376d6d42..39c65c7f2c74 100644 --- a/packages/backend/src/server/api/endpoints/antennas/update.ts +++ b/packages/backend/src/server/api/endpoints/antennas/update.ts @@ -71,17 +71,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Fetch the antenna const antenna = await Antennas.findOneBy({ id: ps.antennaId, - userId: user.id, + userId: me.id, }); if (antenna == null) { @@ -94,7 +89,7 @@ export default class extends Endpoint { if (ps.src === 'list' && ps.userListId) { userList = await UserLists.findOneBy({ id: ps.userListId, - userId: user.id, + userId: me.id, }); if (userList == null) { @@ -103,7 +98,7 @@ export default class extends Endpoint { } else if (ps.src === 'group' && ps.userGroupId) { userGroupJoining = await UserGroupJoinings.findOneBy({ userGroupId: ps.userGroupId, - userId: user.id, + userId: me.id, }); if (userGroupJoining == null) { diff --git a/packages/backend/src/server/api/endpoints/ap/get.ts b/packages/backend/src/server/api/endpoints/ap/get.ts index 2ff3bbe97b85..ebea57a83000 100644 --- a/packages/backend/src/server/api/endpoints/ap/get.ts +++ b/packages/backend/src/server/api/endpoints/ap/get.ts @@ -35,13 +35,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const resolver = new Resolver(); const object = await resolver.resolve(ps.uri); return object; diff --git a/packages/backend/src/server/api/endpoints/app/create.ts b/packages/backend/src/server/api/endpoints/app/create.ts index 34efe8963d6e..6d2892ffb981 100644 --- a/packages/backend/src/server/api/endpoints/app/create.ts +++ b/packages/backend/src/server/api/endpoints/app/create.ts @@ -34,13 +34,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Generate secret const secret = secureRndstr(32, true); @@ -51,7 +46,7 @@ export default class extends Endpoint { const app = await Apps.insert({ id: genId(), createdAt: new Date(), - userId: user ? user.id : null, + userId: me ? me.id : null, name: ps.name, description: ps.description, permission, diff --git a/packages/backend/src/server/api/endpoints/auth/accept.ts b/packages/backend/src/server/api/endpoints/auth/accept.ts index 594a16723cc3..bf9146bdb2b8 100644 --- a/packages/backend/src/server/api/endpoints/auth/accept.ts +++ b/packages/backend/src/server/api/endpoints/auth/accept.ts @@ -34,13 +34,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Fetch token const session = await AuthSessions .findOneBy({ token: ps.token }); @@ -55,7 +50,7 @@ export default class extends Endpoint { // Fetch exist access token const exist = await AccessTokens.findOneBy({ appId: session.appId, - userId: user.id, + userId: me.id, }); if (exist == null) { @@ -75,7 +70,7 @@ export default class extends Endpoint { createdAt: now, lastUsedAt: now, appId: session.appId, - userId: user.id, + userId: me.id, token: accessToken, hash: hash, }); @@ -83,7 +78,7 @@ export default class extends Endpoint { // Update session await AuthSessions.update(session.id, { - userId: user.id, + userId: me.id, }); }); } diff --git a/packages/backend/src/server/api/endpoints/auth/session/generate.ts b/packages/backend/src/server/api/endpoints/auth/session/generate.ts index 6342d5e086c0..aa42b29b5dd4 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/generate.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/generate.ts @@ -48,13 +48,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Lookup app const app = await Apps.findOneBy({ secret: ps.appSecret, diff --git a/packages/backend/src/server/api/endpoints/auth/session/show.ts b/packages/backend/src/server/api/endpoints/auth/session/show.ts index 8d6134901ca0..2d3e21a68ebf 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/show.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/show.ts @@ -50,13 +50,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Lookup session const session = await AuthSessions.findOneBy({ token: ps.token, @@ -66,7 +61,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchSession); } - return await AuthSessions.pack(session, user); + return await AuthSessions.pack(session, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts index 5eb461f955f1..b614d97cedc7 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Apps, AuthSessions, AccessTokens, Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { Apps, AuthSessions, AccessTokens } from '@/models/index.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -61,11 +62,8 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Lookup app const app = await Apps.findOneBy({ secret: ps.appSecret, diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts index abe6f1786e5c..c4bb7f81cd45 100644 --- a/packages/backend/src/server/api/endpoints/blocking/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking/create.ts @@ -2,7 +2,8 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import create from '@/services/blocking/create.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Blockings, NoteWatchings, Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { Blockings, NoteWatchings } from '@/models/index.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; @@ -59,15 +60,12 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - const blocker = await this.usersRepository.findOneByOrFail({ id: user.id }); + super(meta, paramDef, async (ps, me) => { + const blocker = await this.usersRepository.findOneByOrFail({ id: me.id }); // 自分自身 - if (user.id === ps.userId) { + if (me.id === ps.userId) { throw new ApiError(meta.errors.blockeeIsYourself); } diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts index 2624670972fd..0ad52a32f87c 100644 --- a/packages/backend/src/server/api/endpoints/blocking/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts @@ -2,7 +2,8 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import deleteBlocking from '@/services/blocking/delete.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Blockings, Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { Blockings } from '@/models/index.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; @@ -59,15 +60,12 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - const blocker = await this.usersRepository.findOneByOrFail({ id: user.id }); + super(meta, paramDef, async (ps, me) => { + const blocker = await this.usersRepository.findOneByOrFail({ id: me.id }); // Check if the blockee is yourself - if (user.id === ps.userId) { + if (me.id === ps.userId) { throw new ApiError(meta.errors.blockeeIsYourself); } diff --git a/packages/backend/src/server/api/endpoints/channels/create.ts b/packages/backend/src/server/api/endpoints/channels/create.ts index 1c75c22d0eab..78aaba56ea5a 100644 --- a/packages/backend/src/server/api/endpoints/channels/create.ts +++ b/packages/backend/src/server/api/endpoints/channels/create.ts @@ -41,18 +41,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { let banner = null; if (ps.bannerId != null) { banner = await DriveFiles.findOneBy({ id: ps.bannerId, - userId: user.id, + userId: me.id, }); if (banner == null) { @@ -63,13 +58,13 @@ export default class extends Endpoint { const channel = await Channels.insert({ id: genId(), createdAt: new Date(), - userId: user.id, + userId: me.id, name: ps.name, description: ps.description || null, bannerId: banner ? banner.id : null, } as Channel).then(x => Channels.findOneByOrFail(x.identifiers[0])); - return await Channels.pack(channel, user); + return await Channels.pack(channel, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/channels/follow.ts b/packages/backend/src/server/api/endpoints/channels/follow.ts index aa3df1f676a7..3eb77975746e 100644 --- a/packages/backend/src/server/api/endpoints/channels/follow.ts +++ b/packages/backend/src/server/api/endpoints/channels/follow.ts @@ -33,13 +33,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const channel = await Channels.findOneBy({ id: ps.channelId, }); @@ -51,11 +46,11 @@ export default class extends Endpoint { await ChannelFollowings.insert({ id: genId(), createdAt: new Date(), - followerId: user.id, + followerId: me.id, followeeId: channel.id, }); - publishUserEvent(user.id, 'followChannel', channel); + publishUserEvent(me.id, 'followChannel', channel); }); } } diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index 915669663fc8..d5edb9e7791a 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -46,13 +46,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const channel = await Channels.findOneBy({ id: ps.channelId, }); @@ -63,26 +58,26 @@ export default class extends Endpoint { //#region Construct query const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere('note.channelId = :channelId', { channelId: channel.id }) - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('user.avatar', 'avatar') - .leftJoinAndSelect('user.banner', 'banner') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') - .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') - .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') - .leftJoinAndSelect('note.channel', 'channel'); + .andWhere('note.channelId = :channelId', { channelId: channel.id }) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('user.avatar', 'avatar') + .leftJoinAndSelect('user.banner', 'banner') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') + .leftJoinAndSelect('note.channel', 'channel'); //#endregion const timeline = await query.take(ps.limit).getMany(); - if (user) activeUsersChart.read(user); + if (me) activeUsersChart.read(me); - return await Notes.packMany(timeline, user); + return await Notes.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/channels/unfollow.ts b/packages/backend/src/server/api/endpoints/channels/unfollow.ts index 119dac55fdb7..e4fd7457a07a 100644 --- a/packages/backend/src/server/api/endpoints/channels/unfollow.ts +++ b/packages/backend/src/server/api/endpoints/channels/unfollow.ts @@ -32,13 +32,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const channel = await Channels.findOneBy({ id: ps.channelId, }); @@ -48,11 +43,11 @@ export default class extends Endpoint { } await ChannelFollowings.delete({ - followerId: user.id, + followerId: me.id, followeeId: channel.id, }); - publishUserEvent(user.id, 'unfollowChannel', channel); + publishUserEvent(me.id, 'unfollowChannel', channel); }); } } diff --git a/packages/backend/src/server/api/endpoints/charts/active-users.ts b/packages/backend/src/server/api/endpoints/charts/active-users.ts index 16955e8b462b..2baaf3be3a80 100644 --- a/packages/backend/src/server/api/endpoints/charts/active-users.ts +++ b/packages/backend/src/server/api/endpoints/charts/active-users.ts @@ -1,12 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; -import { activeUsersChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import type ActiveUsersChart from '@/services/chart/charts/active-users.js'; +import { schema } from '@/services/chart/charts/entities/active-users.js'; export const meta = { tags: ['charts', 'users'], - res: getJsonSchema(activeUsersChart.schema), + res: getJsonSchema(schema), allowGet: true, cacheSec: 60 * 60, @@ -26,14 +27,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + private activeUsersChart: ActiveUsersChart, ) { - super(meta, paramDef, async (ps, user) => { - return await activeUsersChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); + super(meta, paramDef, async (ps, me) => { + return await this.activeUsersChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); }); } } diff --git a/packages/backend/src/server/api/endpoints/charts/ap-request.ts b/packages/backend/src/server/api/endpoints/charts/ap-request.ts index 391d757d0037..91465f22a231 100644 --- a/packages/backend/src/server/api/endpoints/charts/ap-request.ts +++ b/packages/backend/src/server/api/endpoints/charts/ap-request.ts @@ -1,12 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; -import { apRequestChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import type ApRequestChart from '@/services/chart/charts/ap-request.js'; +import { schema } from '@/services/chart/charts/entities/ap-request.js'; export const meta = { tags: ['charts'], - res: getJsonSchema(apRequestChart.schema), + res: getJsonSchema(schema), allowGet: true, cacheSec: 60 * 60, @@ -26,14 +27,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + private apRequestChart: ApRequestChart, ) { - super(meta, paramDef, async (ps, user) => { - return await apRequestChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); + super(meta, paramDef, async (ps, me) => { + return await this.apRequestChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); }); } } diff --git a/packages/backend/src/server/api/endpoints/charts/drive.ts b/packages/backend/src/server/api/endpoints/charts/drive.ts index 99469791de0c..3d2049152461 100644 --- a/packages/backend/src/server/api/endpoints/charts/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/drive.ts @@ -1,12 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; -import { driveChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import type DriveChart from '@/services/chart/charts/drive.js'; +import { schema } from '@/services/chart/charts/entities/drive.js'; export const meta = { tags: ['charts', 'drive'], - res: getJsonSchema(driveChart.schema), + res: getJsonSchema(schema), allowGet: true, cacheSec: 60 * 60, @@ -26,14 +27,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + private driveChart: DriveChart, ) { - super(meta, paramDef, async (ps, user) => { - return await driveChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); + super(meta, paramDef, async (ps, me) => { + return await this.driveChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); }); } } diff --git a/packages/backend/src/server/api/endpoints/charts/federation.ts b/packages/backend/src/server/api/endpoints/charts/federation.ts index ff765274a719..299b9fc0d0ed 100644 --- a/packages/backend/src/server/api/endpoints/charts/federation.ts +++ b/packages/backend/src/server/api/endpoints/charts/federation.ts @@ -1,12 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; -import { federationChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import type FederationChart from '@/services/chart/charts/federation.js'; +import { schema } from '@/services/chart/charts/entities/federation.js'; export const meta = { tags: ['charts'], - res: getJsonSchema(federationChart.schema), + res: getJsonSchema(schema), allowGet: true, cacheSec: 60 * 60, @@ -26,14 +27,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + private federationChart: FederationChart, ) { - super(meta, paramDef, async (ps, user) => { - return await federationChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); + super(meta, paramDef, async (ps, me) => { + return await this.federationChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); }); } } diff --git a/packages/backend/src/server/api/endpoints/charts/hashtag.ts b/packages/backend/src/server/api/endpoints/charts/hashtag.ts index 0d8340cdb47c..88238af02a26 100644 --- a/packages/backend/src/server/api/endpoints/charts/hashtag.ts +++ b/packages/backend/src/server/api/endpoints/charts/hashtag.ts @@ -1,12 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; -import { hashtagChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import type HashtagChart from '@/services/chart/charts/hashtag.js'; +import { schema } from '@/services/chart/charts/entities/hashtag.js'; export const meta = { tags: ['charts', 'hashtags'], - res: getJsonSchema(hashtagChart.schema), + res: getJsonSchema(schema), allowGet: true, cacheSec: 60 * 60, @@ -27,14 +28,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + private hashtagChart: HashtagChart, ) { - super(meta, paramDef, async (ps, user) => { - return await hashtagChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.tag); + super(meta, paramDef, async (ps, me) => { + return await this.hashtagChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.tag); }); } } diff --git a/packages/backend/src/server/api/endpoints/charts/instance.ts b/packages/backend/src/server/api/endpoints/charts/instance.ts index ecc8858d5cfb..51dd0dcb6144 100644 --- a/packages/backend/src/server/api/endpoints/charts/instance.ts +++ b/packages/backend/src/server/api/endpoints/charts/instance.ts @@ -1,12 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; -import { instanceChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import type InstanceChart from '@/services/chart/charts/instance.js'; +import { schema } from '@/services/chart/charts/entities/instance.js'; export const meta = { tags: ['charts'], - res: getJsonSchema(instanceChart.schema), + res: getJsonSchema(schema), allowGet: true, cacheSec: 60 * 60, @@ -27,14 +28,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + private instanceChart: InstanceChart, ) { - super(meta, paramDef, async (ps, user) => { - return await instanceChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.host); + super(meta, paramDef, async (ps, me) => { + return await this.instanceChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.host); }); } } diff --git a/packages/backend/src/server/api/endpoints/charts/notes.ts b/packages/backend/src/server/api/endpoints/charts/notes.ts index f850d227fd8b..6a7683f60e0f 100644 --- a/packages/backend/src/server/api/endpoints/charts/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/notes.ts @@ -1,12 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; -import { notesChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import type NotesChart from '@/services/chart/charts/notes.js'; +import { schema } from '@/services/chart/charts/entities/notes.js'; export const meta = { tags: ['charts', 'notes'], - res: getJsonSchema(notesChart.schema), + res: getJsonSchema(schema), allowGet: true, cacheSec: 60 * 60, @@ -26,14 +27,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesChart: NotesChart, ) { - super(meta, paramDef, async (ps, user) => { - return await notesChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); + super(meta, paramDef, async (ps, me) => { + return await this.notesChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); }); } } diff --git a/packages/backend/src/server/api/endpoints/charts/user/drive.ts b/packages/backend/src/server/api/endpoints/charts/user/drive.ts index 6ebd1cf084b8..8125028d6118 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/drive.ts @@ -1,12 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; -import { perUserDriveChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import type PerUserDriveChart from '@/services/chart/charts/per-user-drive.js'; +import { schema } from '@/services/chart/charts/entities/per-user-drive.js'; export const meta = { tags: ['charts', 'drive', 'users'], - res: getJsonSchema(perUserDriveChart.schema), + res: getJsonSchema(schema), allowGet: true, cacheSec: 60 * 60, @@ -27,14 +28,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + private perUserDriveChart: PerUserDriveChart, ) { - super(meta, paramDef, async (ps, user) => { - return await perUserDriveChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId); + super(meta, paramDef, async (ps, me) => { + return await this.perUserDriveChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId); }); } } diff --git a/packages/backend/src/server/api/endpoints/charts/user/following.ts b/packages/backend/src/server/api/endpoints/charts/user/following.ts index 1749285e0404..f06ed2873bb1 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/following.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/following.ts @@ -1,12 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getJsonSchema } from '@/services/chart/core.js'; -import { perUserFollowingChart } from '@/services/chart/index.js'; +import type PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; +import { schema } from '@/services/chart/charts/entities/per-user-following.js'; export const meta = { tags: ['charts', 'users', 'following'], - res: getJsonSchema(perUserFollowingChart.schema), + res: getJsonSchema(schema), allowGet: true, cacheSec: 60 * 60, @@ -27,14 +28,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + private perUserFollowingChart: PerUserFollowingChart, ) { - super(meta, paramDef, async (ps, user) => { - return await perUserFollowingChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId); + super(meta, paramDef, async (ps, me) => { + return await this.perUserFollowingChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId); }); } } diff --git a/packages/backend/src/server/api/endpoints/charts/user/notes.ts b/packages/backend/src/server/api/endpoints/charts/user/notes.ts index 9626c08c3122..2359d82912a5 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/notes.ts @@ -1,12 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; -import { perUserNotesChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import type PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; +import { schema } from '@/services/chart/charts/entities/per-user-notes.js'; export const meta = { tags: ['charts', 'users', 'notes'], - res: getJsonSchema(perUserNotesChart.schema), + res: getJsonSchema(schema), allowGet: true, cacheSec: 60 * 60, @@ -27,14 +28,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + private perUserNotesChart: PerUserNotesChart, ) { - super(meta, paramDef, async (ps, user) => { - return await perUserNotesChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId); + super(meta, paramDef, async (ps, me) => { + return await this.perUserNotesChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId); }); } } diff --git a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts index 6e37d01e9027..93011051b36d 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts @@ -1,12 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; -import { perUserReactionsChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import type PerUserReactionsChart from '@/services/chart/charts/per-user-reactions.js'; +import { schema } from '@/services/chart/charts/entities/per-user-reactions.js'; export const meta = { tags: ['charts', 'users', 'reactions'], - res: getJsonSchema(perUserReactionsChart.schema), + res: getJsonSchema(schema), allowGet: true, cacheSec: 60 * 60, @@ -27,14 +28,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + private perUserReactionsChart: PerUserReactionsChart, ) { - super(meta, paramDef, async (ps, user) => { - return await perUserReactionsChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId); + super(meta, paramDef, async (ps, me) => { + return await this.perUserReactionsChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId); }); } } diff --git a/packages/backend/src/server/api/endpoints/charts/users.ts b/packages/backend/src/server/api/endpoints/charts/users.ts index 80c8f348f4ff..6f620d1f960a 100644 --- a/packages/backend/src/server/api/endpoints/charts/users.ts +++ b/packages/backend/src/server/api/endpoints/charts/users.ts @@ -1,12 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; -import { usersChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import type UsersChart from '@/services/chart/charts/users.js'; +import { schema } from '@/services/chart/charts/entities/users.js'; export const meta = { tags: ['charts', 'users'], - res: getJsonSchema(usersChart.schema), + res: getJsonSchema(schema), allowGet: true, cacheSec: 60 * 60, @@ -26,14 +27,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + private usersChart: UsersChart, ) { - super(meta, paramDef, async (ps, user) => { - return await usersChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); + super(meta, paramDef, async (ps, me) => { + return await this.usersChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); }); } } diff --git a/packages/backend/src/server/api/endpoints/clips/add-note.ts b/packages/backend/src/server/api/endpoints/clips/add-note.ts index c9031e14e282..f9d6c2a70cc1 100644 --- a/packages/backend/src/server/api/endpoints/clips/add-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/add-note.ts @@ -46,16 +46,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const clip = await Clips.findOneBy({ id: ps.clipId, - userId: user.id, + userId: me.id, }); if (clip == null) { diff --git a/packages/backend/src/server/api/endpoints/clips/create.ts b/packages/backend/src/server/api/endpoints/clips/create.ts index f3022adde615..f011bb1fc7bf 100644 --- a/packages/backend/src/server/api/endpoints/clips/create.ts +++ b/packages/backend/src/server/api/endpoints/clips/create.ts @@ -31,17 +31,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const clip = await Clips.insert({ id: genId(), createdAt: new Date(), - userId: user.id, + userId: me.id, name: ps.name, isPublic: ps.isPublic, description: ps.description, diff --git a/packages/backend/src/server/api/endpoints/clips/delete.ts b/packages/backend/src/server/api/endpoints/clips/delete.ts index 7cfb56f9313c..a434f903f506 100644 --- a/packages/backend/src/server/api/endpoints/clips/delete.ts +++ b/packages/backend/src/server/api/endpoints/clips/delete.ts @@ -31,16 +31,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const clip = await Clips.findOneBy({ id: ps.clipId, - userId: user.id, + userId: me.id, }); if (clip == null) { diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts index 74b708f29ad0..300d8a0b5f25 100644 --- a/packages/backend/src/server/api/endpoints/clips/notes.ts +++ b/packages/backend/src/server/api/endpoints/clips/notes.ts @@ -48,13 +48,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const clip = await Clips.findOneBy({ id: ps.clipId, }); @@ -63,36 +58,36 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchClip); } - if (!clip.isPublic && (user == null || (clip.userId !== user.id))) { + if (!clip.isPublic && (me == null || (clip.userId !== me.id))) { throw new ApiError(meta.errors.noSuchClip); } const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .innerJoin(ClipNotes.metadata.targetName, 'clipNote', 'clipNote.noteId = note.id') - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('user.avatar', 'avatar') - .leftJoinAndSelect('user.banner', 'banner') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') - .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') - .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') - .andWhere('clipNote.clipId = :clipId', { clipId: clip.id }); + .innerJoin(ClipNotes.metadata.targetName, 'clipNote', 'clipNote.noteId = note.id') + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('user.avatar', 'avatar') + .leftJoinAndSelect('user.banner', 'banner') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') + .andWhere('clipNote.clipId = :clipId', { clipId: clip.id }); - if (user) { - generateVisibilityQuery(query, user); - generateMutedUserQuery(query, user); - generateBlockedUserQuery(query, user); + if (me) { + generateVisibilityQuery(query, me); + generateMutedUserQuery(query, me); + generateBlockedUserQuery(query, me); } const notes = await query - .take(ps.limit) - .getMany(); + .take(ps.limit) + .getMany(); - return await Notes.packMany(notes, user); + return await Notes.packMany(notes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/clips/remove-note.ts b/packages/backend/src/server/api/endpoints/clips/remove-note.ts index 26909659810b..118d000d319c 100644 --- a/packages/backend/src/server/api/endpoints/clips/remove-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/remove-note.ts @@ -39,16 +39,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const clip = await Clips.findOneBy({ id: ps.clipId, - userId: user.id, + userId: me.id, }); if (clip == null) { diff --git a/packages/backend/src/server/api/endpoints/clips/update.ts b/packages/backend/src/server/api/endpoints/clips/update.ts index 8f02ad3c618f..943634a403c5 100644 --- a/packages/backend/src/server/api/endpoints/clips/update.ts +++ b/packages/backend/src/server/api/endpoints/clips/update.ts @@ -40,17 +40,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Fetch the clip const clip = await Clips.findOneBy({ id: ps.clipId, - userId: user.id, + userId: me.id, }); if (clip == null) { diff --git a/packages/backend/src/server/api/endpoints/drive.ts b/packages/backend/src/server/api/endpoints/drive.ts index ca3238fa1597..ea4cef042654 100644 --- a/packages/backend/src/server/api/endpoints/drive.ts +++ b/packages/backend/src/server/api/endpoints/drive.ts @@ -36,20 +36,15 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const instance = await fetchMeta(true); // Calculate drive usage - const usage = await DriveFiles.calcDriveUsageOf(user.id); + const usage = await DriveFiles.calcDriveUsageOf(me.id); return { - capacity: 1024 * 1024 * (user.driveCapacityOverrideMb || instance.localDriveCapacityMb), + capacity: 1024 * 1024 * (me.driveCapacityOverrideMb || instance.localDriveCapacityMb), usage: usage, }; }); diff --git a/packages/backend/src/server/api/endpoints/drive/files.ts b/packages/backend/src/server/api/endpoints/drive/files.ts index 2d89d925b2da..093363085806 100644 --- a/packages/backend/src/server/api/endpoints/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/drive/files.ts @@ -37,15 +37,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId) - .andWhere('file.userId = :userId', { userId: user.id }); + .andWhere('file.userId = :userId', { userId: me.id }); if (ps.folderId) { query.andWhere('file.folderId = :folderId', { folderId: ps.folderId }); diff --git a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts index 9d1888dd7860..a0788cd2d0d6 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts @@ -43,17 +43,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Fetch file const file = await DriveFiles.findOneBy({ id: ps.fileId, - userId: user.id, + userId: me.id, }); if (file == null) { @@ -61,10 +56,10 @@ export default class extends Endpoint { } const notes = await Notes.createQueryBuilder('note') - .where(':file = ANY(note.fileIds)', { file: file.id }) - .getMany(); + .where(':file = ANY(note.fileIds)', { file: file.id }) + .getMany(); - return await Notes.packMany(notes, user, { + return await Notes.packMany(notes, me, { detail: true, }); }); diff --git a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts index f4a26eccd0fa..ede78131ad29 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts @@ -29,16 +29,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const file = await DriveFiles.findOneBy({ md5: ps.md5, - userId: user.id, + userId: me.id, }); return file != null; diff --git a/packages/backend/src/server/api/endpoints/drive/files/delete.ts b/packages/backend/src/server/api/endpoints/drive/files/delete.ts index 8f8eb85d9d0f..6db4ec5b0e0e 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/delete.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/delete.ts @@ -41,20 +41,15 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const file = await DriveFiles.findOneBy({ id: ps.fileId }); if (file == null) { throw new ApiError(meta.errors.noSuchFile); } - if ((!user.isAdmin && !user.isModerator) && (file.userId !== user.id)) { + if ((!me.isAdmin && !me.isModerator) && (file.userId !== me.id)) { throw new ApiError(meta.errors.accessDenied); } @@ -62,7 +57,7 @@ export default class extends Endpoint { await deleteFile(file); // Publish fileDeleted event - publishDriveStream(user.id, 'fileDeleted', file.id); + publishDriveStream(me.id, 'fileDeleted', file.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts index 2096e6d9884b..f0e0a60a9644 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts @@ -34,16 +34,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const files = await DriveFiles.findBy({ md5: ps.md5, - userId: user.id, + userId: me.id, }); return await DriveFiles.packMany(files, { self: true }); diff --git a/packages/backend/src/server/api/endpoints/drive/files/find.ts b/packages/backend/src/server/api/endpoints/drive/files/find.ts index 0294b337421c..0db6b1d005cf 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/find.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/find.ts @@ -36,16 +36,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const files = await DriveFiles.findBy({ name: ps.name, - userId: user.id, + userId: me.id, folderId: ps.folderId ?? IsNull(), }); diff --git a/packages/backend/src/server/api/endpoints/drive/files/show.ts b/packages/backend/src/server/api/endpoints/drive/files/show.ts index c99219cca363..d8057822ff59 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/show.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/show.ts @@ -56,13 +56,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { let file: DriveFile | null = null; if (ps.fileId) { @@ -83,7 +78,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchFile); } - if ((!user.isAdmin && !user.isModerator) && (file.userId !== user.id)) { + if ((!me.isAdmin && !me.isModerator) && (file.userId !== me.id)) { throw new ApiError(meta.errors.accessDenied); } diff --git a/packages/backend/src/server/api/endpoints/drive/files/update.ts b/packages/backend/src/server/api/endpoints/drive/files/update.ts index 89876bc37134..da45da62dd36 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts @@ -63,20 +63,15 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const file = await DriveFiles.findOneBy({ id: ps.fileId }); if (file == null) { throw new ApiError(meta.errors.noSuchFile); } - if ((!user.isAdmin && !user.isModerator) && (file.userId !== user.id)) { + if ((!me.isAdmin && !me.isModerator) && (file.userId !== me.id)) { throw new ApiError(meta.errors.accessDenied); } @@ -95,7 +90,7 @@ export default class extends Endpoint { } else { const folder = await DriveFolders.findOneBy({ id: ps.folderId, - userId: user.id, + userId: me.id, }); if (folder == null) { @@ -116,7 +111,7 @@ export default class extends Endpoint { const fileObj = await DriveFiles.pack(file, { self: true }); // Publish fileUpdated event - publishDriveStream(user.id, 'fileUpdated', fileObj); + publishDriveStream(me.id, 'fileUpdated', fileObj); return fileObj; }); diff --git a/packages/backend/src/server/api/endpoints/drive/folders.ts b/packages/backend/src/server/api/endpoints/drive/folders.ts index 9605ba61e894..b3c8950592b9 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders.ts @@ -36,15 +36,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(DriveFolders.createQueryBuilder('folder'), ps.sinceId, ps.untilId) - .andWhere('folder.userId = :userId', { userId: user.id }); + .andWhere('folder.userId = :userId', { userId: me.id }); if (ps.folderId) { query.andWhere('folder.parentId = :parentId', { parentId: ps.folderId }); diff --git a/packages/backend/src/server/api/endpoints/drive/folders/create.ts b/packages/backend/src/server/api/endpoints/drive/folders/create.ts index b01bbc5808cb..b2e5c0ec6ac9 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/create.ts @@ -40,20 +40,15 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // If the parent folder is specified let parent = null; if (ps.parentId) { // Fetch parent folder parent = await DriveFolders.findOneBy({ id: ps.parentId, - userId: user.id, + userId: me.id, }); if (parent == null) { @@ -67,13 +62,13 @@ export default class extends Endpoint { createdAt: new Date(), name: ps.name, parentId: parent !== null ? parent.id : null, - userId: user.id, + userId: me.id, }).then(x => DriveFolders.findOneByOrFail(x.identifiers[0])); const folderObj = await DriveFolders.pack(folder); // Publish folderCreated event - publishDriveStream(user.id, 'folderCreated', folderObj); + publishDriveStream(me.id, 'folderCreated', folderObj); return folderObj; }); diff --git a/packages/backend/src/server/api/endpoints/drive/folders/delete.ts b/packages/backend/src/server/api/endpoints/drive/folders/delete.ts index 3a5244bb55c7..9d775445ec80 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/delete.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/delete.ts @@ -38,17 +38,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Get folder const folder = await DriveFolders.findOneBy({ id: ps.folderId, - userId: user.id, + userId: me.id, }); if (folder == null) { @@ -67,7 +62,7 @@ export default class extends Endpoint { await DriveFolders.delete(folder.id); // Publish folderCreated event - publishDriveStream(user.id, 'folderDeleted', folder.id); + publishDriveStream(me.id, 'folderDeleted', folder.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/drive/folders/find.ts b/packages/backend/src/server/api/endpoints/drive/folders/find.ts index f8305522938b..65b70783347f 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/find.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/find.ts @@ -34,16 +34,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const folders = await DriveFolders.findBy({ name: ps.name, - userId: user.id, + userId: me.id, parentId: ps.parentId ?? IsNull(), }); diff --git a/packages/backend/src/server/api/endpoints/drive/folders/show.ts b/packages/backend/src/server/api/endpoints/drive/folders/show.ts index 9c492b0e5c31..d422ded6a9b4 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/show.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/show.ts @@ -37,17 +37,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Get folder const folder = await DriveFolders.findOneBy({ id: ps.folderId, - userId: user.id, + userId: me.id, }); if (folder == null) { diff --git a/packages/backend/src/server/api/endpoints/drive/folders/update.ts b/packages/backend/src/server/api/endpoints/drive/folders/update.ts index 0b95b33adf4c..35dd9960b2c4 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/update.ts @@ -52,17 +52,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Fetch folder const folder = await DriveFolders.findOneBy({ id: ps.folderId, - userId: user.id, + userId: me.id, }); if (folder == null) { @@ -80,7 +75,7 @@ export default class extends Endpoint { // Get parent folder const parent = await DriveFolders.findOneBy({ id: ps.parentId, - userId: user.id, + userId: me.id, }); if (parent == null) { @@ -122,7 +117,7 @@ export default class extends Endpoint { const folderObj = await DriveFolders.pack(folder); // Publish folderUpdated event - publishDriveStream(user.id, 'folderUpdated', folderObj); + publishDriveStream(me.id, 'folderUpdated', folderObj); return folderObj; }); diff --git a/packages/backend/src/server/api/endpoints/drive/stream.ts b/packages/backend/src/server/api/endpoints/drive/stream.ts index 51ae9bd7527b..c41b707b22e2 100644 --- a/packages/backend/src/server/api/endpoints/drive/stream.ts +++ b/packages/backend/src/server/api/endpoints/drive/stream.ts @@ -36,15 +36,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId) - .andWhere('file.userId = :userId', { userId: user.id }); + .andWhere('file.userId = :userId', { userId: me.id }); if (ps.type) { if (ps.type.endsWith('/*')) { diff --git a/packages/backend/src/server/api/endpoints/email-address/available.ts b/packages/backend/src/server/api/endpoints/email-address/available.ts index 0189fd50d1af..696409bbf6a9 100644 --- a/packages/backend/src/server/api/endpoints/email-address/available.ts +++ b/packages/backend/src/server/api/endpoints/email-address/available.ts @@ -35,13 +35,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { return await validateEmailForAccount(ps.emailAddress); }); } diff --git a/packages/backend/src/server/api/endpoints/endpoint.ts b/packages/backend/src/server/api/endpoints/endpoint.ts index fb1e4229abb4..d12856cc6c25 100644 --- a/packages/backend/src/server/api/endpoints/endpoint.ts +++ b/packages/backend/src/server/api/endpoints/endpoint.ts @@ -20,13 +20,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps) => { const ep = endpoints.find(x => x.name === ps.endpoint); if (ep == null) return null; return { diff --git a/packages/backend/src/server/api/endpoints/export-custom-emojis.ts b/packages/backend/src/server/api/endpoints/export-custom-emojis.ts index 4da4b2d500f0..a33d1576b5fe 100644 --- a/packages/backend/src/server/api/endpoints/export-custom-emojis.ts +++ b/packages/backend/src/server/api/endpoints/export-custom-emojis.ts @@ -22,14 +22,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - createExportCustomEmojisJob(user); + super(meta, paramDef, async (ps, me) => { + createExportCustomEmojisJob(me); }); } } diff --git a/packages/backend/src/server/api/endpoints/federation/stats.ts b/packages/backend/src/server/api/endpoints/federation/stats.ts index e6a4967f7951..cde5bc607050 100644 --- a/packages/backend/src/server/api/endpoints/federation/stats.ts +++ b/packages/backend/src/server/api/endpoints/federation/stats.ts @@ -25,13 +25,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const [topSubInstances, topPubInstances, allSubCount, allPubCount] = await Promise.all([ Instances.find({ where: { diff --git a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts index 824a4780ea6d..c4e59dac63a7 100644 --- a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts +++ b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts @@ -21,13 +21,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps) => { const user = await getRemoteUser(ps.userId); await updatePerson(user.uri!); }); diff --git a/packages/backend/src/server/api/endpoints/federation/users.ts b/packages/backend/src/server/api/endpoints/federation/users.ts index 3e7fcada514a..2e315c91f09d 100644 --- a/packages/backend/src/server/api/endpoints/federation/users.ts +++ b/packages/backend/src/server/api/endpoints/federation/users.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; export const meta = { @@ -36,17 +36,14 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(this.usersRepository.createQueryBuilder('user'), ps.sinceId, ps.untilId) - .andWhere('user.host = :host', { host: ps.host }); + .andWhere('user.host = :host', { host: ps.host }); const users = await query - .take(ps.limit) - .getMany(); + .take(ps.limit) + .getMany(); return await this.usersRepository.packMany(users, me, { detail: true }); }); diff --git a/packages/backend/src/server/api/endpoints/fetch-rss.ts b/packages/backend/src/server/api/endpoints/fetch-rss.ts index 4d3687456127..cefbb36bb6aa 100644 --- a/packages/backend/src/server/api/endpoints/fetch-rss.ts +++ b/packages/backend/src/server/api/endpoints/fetch-rss.ts @@ -26,13 +26,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const res = await getResponse({ url: ps.url, method: 'GET', diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts index b6486c7f900c..5eb6a5541768 100644 --- a/packages/backend/src/server/api/endpoints/following/create.ts +++ b/packages/backend/src/server/api/endpoints/following/create.ts @@ -2,7 +2,8 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import create from '@/services/following/create.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Followings, Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { Followings } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; @@ -72,15 +73,12 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - const follower = user; + super(meta, paramDef, async (ps, me) => { + const follower = me; // 自分自身 - if (user.id === ps.userId) { + if (me.id === ps.userId) { throw new ApiError(meta.errors.followeeIsYourself); } @@ -110,7 +108,7 @@ export default class extends Endpoint { throw e; } - return await this.usersRepository.pack(followee.id, user); + return await this.usersRepository.pack(followee.id, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/following/delete.ts b/packages/backend/src/server/api/endpoints/following/delete.ts index 2bc5adfc1ee8..d2ba53633b18 100644 --- a/packages/backend/src/server/api/endpoints/following/delete.ts +++ b/packages/backend/src/server/api/endpoints/following/delete.ts @@ -2,7 +2,8 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import deleteFollowing from '@/services/following/delete.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Followings, Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { Followings } from '@/models/index.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; @@ -59,15 +60,12 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - const follower = user; + super(meta, paramDef, async (ps, me) => { + const follower = me; // Check if the followee is yourself - if (user.id === ps.userId) { + if (me.id === ps.userId) { throw new ApiError(meta.errors.followeeIsYourself); } @@ -89,7 +87,7 @@ export default class extends Endpoint { await deleteFollowing(follower, followee); - return await this.usersRepository.pack(followee.id, user); + return await this.usersRepository.pack(followee.id, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/following/invalidate.ts b/packages/backend/src/server/api/endpoints/following/invalidate.ts index 149e318a96ec..f9386a18170d 100644 --- a/packages/backend/src/server/api/endpoints/following/invalidate.ts +++ b/packages/backend/src/server/api/endpoints/following/invalidate.ts @@ -2,7 +2,8 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import deleteFollowing from '@/services/following/delete.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Followings, Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { Followings } from '@/models/index.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; @@ -59,15 +60,12 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - const followee = user; + super(meta, paramDef, async (ps, me) => { + const followee = me; // Check if the follower is yourself - if (user.id === ps.userId) { + if (me.id === ps.userId) { throw new ApiError(meta.errors.followerIsYourself); } @@ -89,7 +87,7 @@ export default class extends Endpoint { await deleteFollowing(follower, followee); - return await this.usersRepository.pack(followee.id, user); + return await this.usersRepository.pack(followee.id, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/following/requests/accept.ts b/packages/backend/src/server/api/endpoints/following/requests/accept.ts index a6c059599441..f2f47d8806c2 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/accept.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/accept.ts @@ -37,20 +37,15 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Fetch follower const follower = await getUser(ps.userId).catch(e => { if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); throw e; }); - await acceptFollowRequest(user, follower).catch(e => { + await acceptFollowRequest(me, follower).catch(e => { if (e.id === '8884c2dd-5795-4ac9-b27e-6a01d38190f9') throw new ApiError(meta.errors.noFollowRequest); throw e; }); diff --git a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts index b072e6a60632..6eea1f38f8d3 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import cancelFollowRequest from '@/services/following/requests/cancel.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { ApiError } from '../../../error.js'; import { getUser } from '../../../common/getters.js'; @@ -48,11 +48,8 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Fetch followee const followee = await getUser(ps.userId).catch(e => { if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); @@ -60,7 +57,7 @@ export default class extends Endpoint { }); try { - await cancelFollowRequest(followee, user); + await cancelFollowRequest(followee, me); } catch (e) { if (e instanceof IdentifiableError) { if (e.id === '17447091-ce07-46dd-b331-c1fd4f15b1e7') throw new ApiError(meta.errors.followRequestNotFound); @@ -68,7 +65,7 @@ export default class extends Endpoint { throw e; } - return await this.usersRepository.pack(followee.id, user); + return await this.usersRepository.pack(followee.id, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/following/requests/list.ts b/packages/backend/src/server/api/endpoints/following/requests/list.ts index f446a70bbda5..aed3ff6ced74 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/list.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/list.ts @@ -46,15 +46,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const reqs = await FollowRequests.findBy({ - followeeId: user.id, + followeeId: me.id, }); return await Promise.all(reqs.map(req => FollowRequests.pack(req))); diff --git a/packages/backend/src/server/api/endpoints/following/requests/reject.ts b/packages/backend/src/server/api/endpoints/following/requests/reject.ts index c2b8c384e6f8..ff9d055f8683 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/reject.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/reject.ts @@ -32,20 +32,15 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Fetch follower const follower = await getUser(ps.userId).catch(e => { if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); throw e; }); - await rejectFollowRequest(user, follower); + await rejectFollowRequest(me, follower); return; }); diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts index 3fb9664b57b1..c1b222c3b643 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts @@ -47,17 +47,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const files = (await Promise.all(ps.fileIds.map(fileId => DriveFiles.findOneBy({ id: fileId, - userId: user.id, + userId: me.id, }), ))).filter((file): file is DriveFile => file != null); @@ -71,12 +66,12 @@ export default class extends Endpoint { updatedAt: new Date(), title: ps.title, description: ps.description, - userId: user.id, + userId: me.id, isSensitive: ps.isSensitive, fileIds: files.map(file => file.id), })).then(x => GalleryPosts.findOneByOrFail(x.identifiers[0])); - return await GalleryPosts.pack(post, user); + return await GalleryPosts.pack(post, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts b/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts index 4fd01c9572ef..742fd8c671cd 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts @@ -31,16 +31,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const post = await GalleryPosts.findOneBy({ id: ps.postId, - userId: user.id, + userId: me.id, }); if (post == null) { diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts index fcf1284b48c5..c59acf0db014 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts @@ -44,26 +44,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const post = await GalleryPosts.findOneBy({ id: ps.postId }); if (post == null) { throw new ApiError(meta.errors.noSuchPost); } - if (post.userId === user.id) { + if (post.userId === me.id) { throw new ApiError(meta.errors.yourPost); } // if already liked const exist = await GalleryLikes.findOneBy({ postId: post.id, - userId: user.id, + userId: me.id, }); if (exist != null) { @@ -75,7 +70,7 @@ export default class extends Endpoint { id: genId(), createdAt: new Date(), postId: post.id, - userId: user.id, + userId: me.id, }); GalleryPosts.increment({ id: post.id }, 'likedCount', 1); diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts index de36c323b1c5..1df20f9ac33b 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts @@ -37,13 +37,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const post = await GalleryPosts.findOneBy({ id: ps.postId }); if (post == null) { throw new ApiError(meta.errors.noSuchPost); @@ -51,7 +46,7 @@ export default class extends Endpoint { const exist = await GalleryLikes.findOneBy({ postId: post.id, - userId: user.id, + userId: me.id, }); if (exist == null) { diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts index d8e79a2ce45f..56cfbcfd0931 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts @@ -47,17 +47,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const files = (await Promise.all(ps.fileIds.map(fileId => DriveFiles.findOneBy({ id: fileId, - userId: user.id, + userId: me.id, }), ))).filter((file): file is DriveFile => file != null); @@ -67,7 +62,7 @@ export default class extends Endpoint { await GalleryPosts.update({ id: ps.postId, - userId: user.id, + userId: me.id, }, { updatedAt: new Date(), title: ps.title, @@ -78,7 +73,7 @@ export default class extends Endpoint { const post = await GalleryPosts.findOneByOrFail({ id: ps.postId }); - return await GalleryPosts.pack(post, user); + return await GalleryPosts.pack(post, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/hashtags/search.ts b/packages/backend/src/server/api/endpoints/hashtags/search.ts index 7c3c50f678b5..447d4306703b 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/search.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/search.ts @@ -31,20 +31,15 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const hashtags = await Hashtags.createQueryBuilder('tag') - .where('tag.name like :q', { q: ps.query.toLowerCase() + '%' }) - .orderBy('tag.count', 'DESC') - .groupBy('tag.id') - .take(ps.limit) - .skip(ps.offset) - .getMany(); + .where('tag.name like :q', { q: ps.query.toLowerCase() + '%' }) + .orderBy('tag.count', 'DESC') + .groupBy('tag.id') + .take(ps.limit) + .skip(ps.offset) + .getMany(); return hashtags.map(tag => tag.name); }); diff --git a/packages/backend/src/server/api/endpoints/hashtags/show.ts b/packages/backend/src/server/api/endpoints/hashtags/show.ts index 768459229e43..f51cef9661fc 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/show.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/show.ts @@ -36,13 +36,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const hashtag = await Hashtags.findOneBy({ name: normalizeForSearch(ps.tag) }); if (hashtag == null) { throw new ApiError(meta.errors.noSuchHashtag); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/done.ts b/packages/backend/src/server/api/endpoints/i/2fa/done.ts index 4a4840b1bccb..b6e43846d73d 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/done.ts @@ -21,16 +21,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const token = ps.token.replace(/\s/g, ''); - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); if (profile.twoFactorTempSecret == null) { throw new Error('二段階認証の設定が開始されていません'); @@ -46,7 +41,7 @@ export default class extends Endpoint { throw new Error('not verified'); } - await UserProfiles.update(user.id, { + await UserProfiles.update(me.id, { twoFactorSecret: profile.twoFactorTempSecret, twoFactorEnabled: true, }); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts index 8ef399af7f62..ff281c8c85e3 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts @@ -3,11 +3,12 @@ import bcrypt from 'bcryptjs'; import * as cbor from 'cbor'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { + Users } from '@/models/index.js'; import { UserProfiles, UserSecurityKeys, AttestationChallenges, - Users, } from '@/models/index.js'; import config from '@/config/index.js'; import { publishMainStream } from '@/services/stream.js'; @@ -41,11 +42,9 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + super(meta, paramDef, async (ps, me) => { + const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); @@ -107,7 +106,7 @@ export default class extends Endpoint { if (!verificationData.valid) throw new Error('signature invalid'); const attestationChallenge = await AttestationChallenges.findOneBy({ - userId: user.id, + userId: me.id, id: ps.challengeId, registrationChallenge: true, challenge: hash(clientData.challenge).toString('hex'), @@ -118,7 +117,7 @@ export default class extends Endpoint { } await AttestationChallenges.delete({ - userId: user.id, + userId: me.id, id: ps.challengeId, }); @@ -133,7 +132,7 @@ export default class extends Endpoint { const credentialIdString = credentialId.toString('hex'); await UserSecurityKeys.insert({ - userId: user.id, + userId: me.id, id: credentialIdString, lastUsed: new Date(), name: ps.name, @@ -141,7 +140,7 @@ export default class extends Endpoint { }); // Publish meUpdated event - publishMainStream(user.id, 'meUpdated', await this.usersRepository.pack(user.id, user, { + publishMainStream(me.id, 'meUpdated', await this.usersRepository.pack(me.id, me, { detail: true, includeSecrets: true, })); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts index 492b87223156..feaf88f0f15d 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts @@ -20,14 +20,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - await UserProfiles.update(user.id, { + super(meta, paramDef, async (ps, me) => { + await UserProfiles.update(me.id, { usePasswordLessLogin: ps.value, }); }); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts index f33228a6a87a..934f5ba33052 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts @@ -27,14 +27,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + super(meta, paramDef, async (ps, me) => { + const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); @@ -57,7 +52,7 @@ export default class extends Endpoint { const challengeId = genId(); await AttestationChallenges.insert({ - userId: user.id, + userId: me.id, id: challengeId, challenge: hash(Buffer.from(challenge, 'utf-8')).toString('hex'), createdAt: new Date(), diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register.ts b/packages/backend/src/server/api/endpoints/i/2fa/register.ts index d6145295f188..d6eb6bd17fac 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts @@ -24,14 +24,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + super(meta, paramDef, async (ps, me) => { + const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); @@ -45,7 +40,7 @@ export default class extends Endpoint { length: 32, }); - await UserProfiles.update(user.id, { + await UserProfiles.update(me.id, { twoFactorTempSecret: secret.base32, }); @@ -53,7 +48,7 @@ export default class extends Endpoint { const url = speakeasy.otpauthURL({ secret: secret.base32, encoding: 'base32', - label: user.username, + label: me.username, issuer: config.host, }); const dataUrl = await QRCode.toDataURL(url); @@ -62,7 +57,7 @@ export default class extends Endpoint { qr: dataUrl, url, secret: secret.base32, - label: user.username, + label: me.username, issuer: config.host, }; }); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts index 9ce659a1c919..01af620568b8 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts @@ -1,7 +1,8 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserProfiles, UserSecurityKeys, Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { UserProfiles, UserSecurityKeys } from '@/models/index.js'; import { publishMainStream } from '@/services/stream.js'; export const meta = { @@ -25,12 +26,9 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + super(meta, paramDef, async (ps, me) => { + const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); @@ -41,12 +39,12 @@ export default class extends Endpoint { // Make sure we only delete the user's own creds await UserSecurityKeys.delete({ - userId: user.id, + userId: me.id, id: ps.credentialId, }); // Publish meUpdated event - publishMainStream(user.id, 'meUpdated', await this.usersRepository.pack(user.id, user, { + publishMainStream(me.id, 'meUpdated', await this.usersRepository.pack(me.id, me, { detail: true, includeSecrets: true, })); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts index eda5e4e941d1..ef107ac9649f 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts @@ -21,14 +21,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + super(meta, paramDef, async (ps, me) => { + const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); @@ -37,7 +32,7 @@ export default class extends Endpoint { throw new Error('incorrect password'); } - await UserProfiles.update(user.id, { + await UserProfiles.update(me.id, { twoFactorSecret: null, twoFactorEnabled: false, }); diff --git a/packages/backend/src/server/api/endpoints/i/apps.ts b/packages/backend/src/server/api/endpoints/i/apps.ts index 5b7a62217e54..f1487d673614 100644 --- a/packages/backend/src/server/api/endpoints/i/apps.ts +++ b/packages/backend/src/server/api/endpoints/i/apps.ts @@ -20,15 +20,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = AccessTokens.createQueryBuilder('token') - .where('token.userId = :userId', { userId: user.id }); + .where('token.userId = :userId', { userId: me.id }); switch (ps.sort) { case '+createdAt': query.orderBy('token.createdAt', 'DESC'); break; diff --git a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts index 2177020a2ba0..8f865a64e59c 100644 --- a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts +++ b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts @@ -22,17 +22,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Get tokens const tokens = await AccessTokens.find({ where: { - userId: user.id, + userId: me.id, }, take: ps.limit, skip: ps.offset, @@ -41,7 +36,7 @@ export default class extends Endpoint { }, }); - return await Promise.all(tokens.map(token => Apps.pack(token.appId, user, { + return await Promise.all(tokens.map(token => Apps.pack(token.appId, me, { detail: true, }))); }); diff --git a/packages/backend/src/server/api/endpoints/i/change-password.ts b/packages/backend/src/server/api/endpoints/i/change-password.ts index bc4e34d00256..1e9482d81e82 100644 --- a/packages/backend/src/server/api/endpoints/i/change-password.ts +++ b/packages/backend/src/server/api/endpoints/i/change-password.ts @@ -22,14 +22,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + super(meta, paramDef, async (ps, me) => { + const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); // Compare password const same = await bcrypt.compare(ps.currentPassword, profile.password!); @@ -42,7 +37,7 @@ export default class extends Endpoint { const salt = await bcrypt.genSalt(8); const hash = await bcrypt.hash(ps.newPassword, salt); - await UserProfiles.update(user.id, { + await UserProfiles.update(me.id, { password: hash, }); }); diff --git a/packages/backend/src/server/api/endpoints/i/delete-account.ts b/packages/backend/src/server/api/endpoints/i/delete-account.ts index c487b91d67f5..0d0467a9dd69 100644 --- a/packages/backend/src/server/api/endpoints/i/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts @@ -1,6 +1,7 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; -import { UserProfiles, Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { UserProfiles } from '@/models/index.js'; import { deleteAccount } from '@/services/delete-account.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -24,13 +25,10 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - const userDetailed = await this.usersRepository.findOneByOrFail({ id: user.id }); + super(meta, paramDef, async (ps, me) => { + const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); + const userDetailed = await this.usersRepository.findOneByOrFail({ id: me.id }); if (userDetailed.isDeleted) { return; } @@ -42,7 +40,7 @@ export default class extends Endpoint { throw new Error('incorrect password'); } - await deleteAccount(user); + await deleteAccount(me); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/export-blocking.ts b/packages/backend/src/server/api/endpoints/i/export-blocking.ts index ef445dcbb28d..a35ba6f0c54f 100644 --- a/packages/backend/src/server/api/endpoints/i/export-blocking.ts +++ b/packages/backend/src/server/api/endpoints/i/export-blocking.ts @@ -22,14 +22,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - createExportBlockingJob(user); + super(meta, paramDef, async (ps, me) => { + createExportBlockingJob(me); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/export-following.ts b/packages/backend/src/server/api/endpoints/i/export-following.ts index c75a14eb644f..ed3c21a83a5e 100644 --- a/packages/backend/src/server/api/endpoints/i/export-following.ts +++ b/packages/backend/src/server/api/endpoints/i/export-following.ts @@ -25,14 +25,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - createExportFollowingJob(user, ps.excludeMuting, ps.excludeInactive); + super(meta, paramDef, async (ps, me) => { + createExportFollowingJob(me, ps.excludeMuting, ps.excludeInactive); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/export-mute.ts b/packages/backend/src/server/api/endpoints/i/export-mute.ts index 27a57b37d98e..5cc71eec4894 100644 --- a/packages/backend/src/server/api/endpoints/i/export-mute.ts +++ b/packages/backend/src/server/api/endpoints/i/export-mute.ts @@ -22,14 +22,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - createExportMuteJob(user); + super(meta, paramDef, async (ps, me) => { + createExportMuteJob(me); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/export-notes.ts b/packages/backend/src/server/api/endpoints/i/export-notes.ts index def82fc9142c..10bab872bfb9 100644 --- a/packages/backend/src/server/api/endpoints/i/export-notes.ts +++ b/packages/backend/src/server/api/endpoints/i/export-notes.ts @@ -22,14 +22,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - createExportNotesJob(user); + super(meta, paramDef, async (ps, me) => { + createExportNotesJob(me); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/export-user-lists.ts b/packages/backend/src/server/api/endpoints/i/export-user-lists.ts index 39b857bffec7..63d9dff7cdab 100644 --- a/packages/backend/src/server/api/endpoints/i/export-user-lists.ts +++ b/packages/backend/src/server/api/endpoints/i/export-user-lists.ts @@ -22,14 +22,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - createExportUserListsJob(user); + super(meta, paramDef, async (ps, me) => { + createExportUserListsJob(me); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/favorites.ts b/packages/backend/src/server/api/endpoints/i/favorites.ts index 3f3fede7c59e..a44086516106 100644 --- a/packages/backend/src/server/api/endpoints/i/favorites.ts +++ b/packages/backend/src/server/api/endpoints/i/favorites.ts @@ -35,22 +35,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(NoteFavorites.createQueryBuilder('favorite'), ps.sinceId, ps.untilId) - .andWhere('favorite.userId = :meId', { meId: user.id }) + .andWhere('favorite.userId = :meId', { meId: me.id }) .leftJoinAndSelect('favorite.note', 'note'); const favorites = await query .take(ps.limit) .getMany(); - return await NoteFavorites.packMany(favorites, user); + return await NoteFavorites.packMany(favorites, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts index b03a5e4e3ea8..e602d56c5cf1 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts @@ -46,22 +46,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(GalleryLikes.createQueryBuilder('like'), ps.sinceId, ps.untilId) - .andWhere('like.userId = :meId', { meId: user.id }) + .andWhere('like.userId = :meId', { meId: me.id }) .leftJoinAndSelect('like.post', 'post'); const likes = await query .take(ps.limit) .getMany(); - return await GalleryLikes.packMany(likes, user); + return await GalleryLikes.packMany(likes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts index c6358ead7f13..f74268a54b8d 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts @@ -35,21 +35,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) - .andWhere('post.userId = :meId', { meId: user.id }); + .andWhere('post.userId = :meId', { meId: me.id }); const posts = await query .take(ps.limit) .getMany(); - return await GalleryPosts.packMany(posts, user); + return await GalleryPosts.packMany(posts, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts b/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts index 52324041890f..4fc62eaea296 100644 --- a/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts +++ b/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts @@ -31,16 +31,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { return { count: await MutedNotes.countBy({ - userId: user.id, + userId: me.id, reason: 'word', }), }; diff --git a/packages/backend/src/server/api/endpoints/i/import-blocking.ts b/packages/backend/src/server/api/endpoints/i/import-blocking.ts index 4fa9e72dd171..3a40a70e3e11 100644 --- a/packages/backend/src/server/api/endpoints/i/import-blocking.ts +++ b/packages/backend/src/server/api/endpoints/i/import-blocking.ts @@ -53,13 +53,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const file = await DriveFiles.findOneBy({ id: ps.fileId }); if (file == null) throw new ApiError(meta.errors.noSuchFile); @@ -67,7 +62,7 @@ export default class extends Endpoint { if (file.size > 50000) throw new ApiError(meta.errors.tooBigFile); if (file.size === 0) throw new ApiError(meta.errors.emptyFile); - createImportBlockingJob(user, file.id); + createImportBlockingJob(me, file.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/import-following.ts b/packages/backend/src/server/api/endpoints/i/import-following.ts index 1965c380d853..b1c7fe631b0f 100644 --- a/packages/backend/src/server/api/endpoints/i/import-following.ts +++ b/packages/backend/src/server/api/endpoints/i/import-following.ts @@ -52,13 +52,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const file = await DriveFiles.findOneBy({ id: ps.fileId }); if (file == null) throw new ApiError(meta.errors.noSuchFile); @@ -66,7 +61,7 @@ export default class extends Endpoint { if (file.size > 50000) throw new ApiError(meta.errors.tooBigFile); if (file.size === 0) throw new ApiError(meta.errors.emptyFile); - createImportFollowingJob(user, file.id); + createImportFollowingJob(me, file.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/import-muting.ts b/packages/backend/src/server/api/endpoints/i/import-muting.ts index b54933802a8d..2673543cdb8e 100644 --- a/packages/backend/src/server/api/endpoints/i/import-muting.ts +++ b/packages/backend/src/server/api/endpoints/i/import-muting.ts @@ -53,13 +53,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const file = await DriveFiles.findOneBy({ id: ps.fileId }); if (file == null) throw new ApiError(meta.errors.noSuchFile); @@ -67,7 +62,7 @@ export default class extends Endpoint { if (file.size > 50000) throw new ApiError(meta.errors.tooBigFile); if (file.size === 0) throw new ApiError(meta.errors.emptyFile); - createImportMutingJob(user, file.id); + createImportMutingJob(me, file.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/import-user-lists.ts b/packages/backend/src/server/api/endpoints/i/import-user-lists.ts index 8e1b23fd7bd0..8e6dfc9a5fa2 100644 --- a/packages/backend/src/server/api/endpoints/i/import-user-lists.ts +++ b/packages/backend/src/server/api/endpoints/i/import-user-lists.ts @@ -52,13 +52,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const file = await DriveFiles.findOneBy({ id: ps.fileId }); if (file == null) throw new ApiError(meta.errors.noSuchFile); @@ -66,7 +62,7 @@ export default class extends Endpoint { if (file.size > 30000) throw new ApiError(meta.errors.tooBigFile); if (file.size === 0) throw new ApiError(meta.errors.emptyFile); - createImportUserListsJob(user, file.id); + createImportUserListsJob(me, file.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index 8d0787cbb7aa..b4328e79dc1b 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -1,6 +1,7 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { Notifications, Followings, Mutings, Users, UserProfiles } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { Notifications, Followings, Mutings, UserProfiles } from '@/models/index.js'; import { notificationTypes } from '@/types.js'; import read from '@/services/note/read.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -55,11 +56,8 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // includeTypes が空の場合はクエリしない if (ps.includeTypes && ps.includeTypes.length === 0) { return []; @@ -70,22 +68,22 @@ export default class extends Endpoint { } const followingQuery = Followings.createQueryBuilder('following') .select('following.followeeId') - .where('following.followerId = :followerId', { followerId: user.id }); + .where('following.followerId = :followerId', { followerId: me.id }); const mutingQuery = Mutings.createQueryBuilder('muting') .select('muting.muteeId') - .where('muting.muterId = :muterId', { muterId: user.id }); + .where('muting.muterId = :muterId', { muterId: me.id }); const mutingInstanceQuery = UserProfiles.createQueryBuilder('user_profile') .select('user_profile.mutedInstances') - .where('user_profile.userId = :muterId', { muterId: user.id }); + .where('user_profile.userId = :muterId', { muterId: me.id }); const suspendedQuery = this.usersRepository.createQueryBuilder('users') .select('users.id') .where('users.isSuspended = TRUE'); const query = makePaginationQuery(Notifications.createQueryBuilder('notification'), ps.sinceId, ps.untilId) - .andWhere('notification.notifieeId = :meId', { meId: user.id }) + .andWhere('notification.notifieeId = :meId', { meId: me.id }) .leftJoinAndSelect('notification.notifier', 'notifier') .leftJoinAndSelect('notification.note', 'note') .leftJoinAndSelect('notifier.avatar', 'notifierAvatar') @@ -123,7 +121,7 @@ export default class extends Endpoint { })); if (ps.following) { - query.andWhere(`((notification.notifierId IN (${ followingQuery.getQuery() })) OR (notification.notifierId = :meId))`, { meId: user.id }); + query.andWhere(`((notification.notifierId IN (${ followingQuery.getQuery() })) OR (notification.notifierId = :meId))`, { meId: me.id }); query.setParameters(followingQuery.getParameters()); } @@ -141,16 +139,16 @@ export default class extends Endpoint { // Mark all as read if (notifications.length > 0 && ps.markAsRead) { - readNotification(user.id, notifications.map(x => x.id)); + readNotification(me.id, notifications.map(x => x.id)); } const notes = notifications.filter(notification => ['mention', 'reply', 'quote'].includes(notification.type)).map(notification => notification.note!); if (notes.length > 0) { - read(user.id, notes); + read(me.id, notes); } - return await Notifications.packMany(notifications, user.id); + return await Notifications.packMany(notifications, me.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/page-likes.ts b/packages/backend/src/server/api/endpoints/i/page-likes.ts index abd2a2e8a581..e2f1469c969c 100644 --- a/packages/backend/src/server/api/endpoints/i/page-likes.ts +++ b/packages/backend/src/server/api/endpoints/i/page-likes.ts @@ -45,22 +45,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(PageLikes.createQueryBuilder('like'), ps.sinceId, ps.untilId) - .andWhere('like.userId = :meId', { meId: user.id }) + .andWhere('like.userId = :meId', { meId: me.id }) .leftJoinAndSelect('like.page', 'page'); const likes = await query .take(ps.limit) .getMany(); - return PageLikes.packMany(likes, user); + return PageLikes.packMany(likes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/pages.ts b/packages/backend/src/server/api/endpoints/i/pages.ts index c3662a29f7ad..9dd008eea447 100644 --- a/packages/backend/src/server/api/endpoints/i/pages.ts +++ b/packages/backend/src/server/api/endpoints/i/pages.ts @@ -35,15 +35,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(Pages.createQueryBuilder('page'), ps.sinceId, ps.untilId) - .andWhere('page.userId = :meId', { meId: user.id }); + .andWhere('page.userId = :meId', { meId: me.id }); const pages = await query .take(ps.limit) diff --git a/packages/backend/src/server/api/endpoints/i/pin.ts b/packages/backend/src/server/api/endpoints/i/pin.ts index f3b9bbcca11d..5a6d783533f6 100644 --- a/packages/backend/src/server/api/endpoints/i/pin.ts +++ b/packages/backend/src/server/api/endpoints/i/pin.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { addPinned } from '@/services/i/pin.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -52,19 +52,16 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - await addPinned(user, ps.noteId).catch(e => { + super(meta, paramDef, async (ps, me) => { + await addPinned(me, ps.noteId).catch(e => { if (e.id === '70c4e51f-5bea-449c-a030-53bee3cce202') throw new ApiError(meta.errors.noSuchNote); if (e.id === '15a018eb-58e5-4da1-93be-330fcc5e4e1a') throw new ApiError(meta.errors.pinLimitExceeded); if (e.id === '23f0cf4e-59a3-4276-a91d-61a5891c1514') throw new ApiError(meta.errors.alreadyPinned); throw e; }); - return await this.usersRepository.pack(user.id, user, { + return await this.usersRepository.pack(me.id, me, { detail: true, }); }); diff --git a/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts b/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts index 3ce07b9dc0ff..fb2fa4e9091e 100644 --- a/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts +++ b/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts @@ -21,33 +21,28 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Update documents await MessagingMessages.update({ - recipientId: user.id, + recipientId: me.id, isRead: false, }, { isRead: true, }); - const joinings = await UserGroupJoinings.findBy({ userId: user.id }); + const joinings = await UserGroupJoinings.findBy({ userId: me.id }); await Promise.all(joinings.map(j => MessagingMessages.createQueryBuilder().update() .set({ - reads: (() => `array_append("reads", '${user.id}')`) as any, + reads: (() => `array_append("reads", '${me.id}')`) as any, }) .where('groupId = :groupId', { groupId: j.userGroupId }) - .andWhere('userId != :userId', { userId: user.id }) - .andWhere('NOT (:userId = ANY(reads))', { userId: user.id }) + .andWhere('userId != :userId', { userId: me.id }) + .andWhere('NOT (:userId = ANY(reads))', { userId: me.id }) .execute())); - publishMainStream(user.id, 'readAllMessagingMessages'); + publishMainStream(me.id, 'readAllMessagingMessages'); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts b/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts index 021fa5d98698..8fb9df4617d4 100644 --- a/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts +++ b/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts @@ -21,21 +21,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Remove documents await NoteUnreads.delete({ - userId: user.id, + userId: me.id, }); // 全て既読になったイベントを発行 - publishMainStream(user.id, 'readAllUnreadMentions'); - publishMainStream(user.id, 'readAllUnreadSpecifiedNotes'); + publishMainStream(me.id, 'readAllUnreadMentions'); + publishMainStream(me.id, 'readAllUnreadSpecifiedNotes'); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/read-announcement.ts b/packages/backend/src/server/api/endpoints/i/read-announcement.ts index fdef5fe7ff96..fb3a10679f15 100644 --- a/packages/backend/src/server/api/endpoints/i/read-announcement.ts +++ b/packages/backend/src/server/api/endpoints/i/read-announcement.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { genId } from '@/misc/gen-id.js'; -import { AnnouncementReads, Announcements, Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { AnnouncementReads, Announcements } from '@/models/index.js'; import { publishMainStream } from '@/services/stream.js'; import { ApiError } from '../../error.js'; @@ -35,11 +36,8 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Check if announcement exists const announcement = await Announcements.findOneBy({ id: ps.announcementId }); @@ -50,7 +48,7 @@ export default class extends Endpoint { // Check if already read const read = await AnnouncementReads.findOneBy({ announcementId: ps.announcementId, - userId: user.id, + userId: me.id, }); if (read != null) { @@ -62,11 +60,11 @@ export default class extends Endpoint { id: genId(), createdAt: new Date(), announcementId: ps.announcementId, - userId: user.id, + userId: me.id, }); - if (!await this.usersRepository.getHasUnreadAnnouncement(user.id)) { - publishMainStream(user.id, 'readAllAnnouncements'); + if (!await this.usersRepository.getHasUnreadAnnouncement(me.id)) { + publishMainStream(me.id, 'readAllAnnouncements'); } }); } diff --git a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts index abced33dffb1..483a21add35c 100644 --- a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts +++ b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts @@ -2,7 +2,8 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { publishInternalEvent, publishMainStream, publishUserEvent } from '@/services/stream.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Users, UserProfiles } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { UserProfiles } from '@/models/index.js'; import generateUserToken from '../../common/generate-native-user-token.js'; export const meta = { @@ -25,15 +26,12 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - const freshUser = await this.usersRepository.findOneByOrFail({ id: user.id }); + super(meta, paramDef, async (ps, me) => { + const freshUser = await this.usersRepository.findOneByOrFail({ id: me.id }); const oldToken = freshUser.token; - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); @@ -44,17 +42,17 @@ export default class extends Endpoint { const newToken = generateUserToken(); - await this.usersRepository.update(user.id, { + await this.usersRepository.update(me.id, { token: newToken, }); // Publish event - publishInternalEvent('userTokenRegenerated', { id: user.id, oldToken, newToken }); - publishMainStream(user.id, 'myTokenRegenerated'); + publishInternalEvent('userTokenRegenerated', { id: me.id, oldToken, newToken }); + publishMainStream(me.id, 'myTokenRegenerated'); // Terminate streaming setTimeout(() => { - publishUserEvent(user.id, 'terminate', {}); + publishUserEvent(me.id, 'terminate', {}); }, 5000); }); } diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts index c6de3d02e654..edc8e7de16fb 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts @@ -22,16 +22,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = RegistryItems.createQueryBuilder('item') .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }) + .andWhere('item.userId = :userId', { userId: me.id }) .andWhere('item.scope = :scope', { scope: ps.scope }); const items = await query.getMany(); diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts index 3390f0f2a22a..cb776dc09c55 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts @@ -32,16 +32,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = RegistryItems.createQueryBuilder('item') .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }) + .andWhere('item.userId = :userId', { userId: me.id }) .andWhere('item.key = :key', { key: ps.key }) .andWhere('item.scope = :scope', { scope: ps.scope }); diff --git a/packages/backend/src/server/api/endpoints/i/registry/get.ts b/packages/backend/src/server/api/endpoints/i/registry/get.ts index 3cf9b387e797..ba8ccfbdb40f 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get.ts @@ -32,16 +32,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = RegistryItems.createQueryBuilder('item') .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }) + .andWhere('item.userId = :userId', { userId: me.id }) .andWhere('item.key = :key', { key: ps.key }) .andWhere('item.scope = :scope', { scope: ps.scope }); diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts index d980b752ec1c..16d575f8cb31 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts @@ -22,16 +22,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = RegistryItems.createQueryBuilder('item') .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }) + .andWhere('item.userId = :userId', { userId: me.id }) .andWhere('item.scope = :scope', { scope: ps.scope }); const items = await query.getMany(); diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys.ts b/packages/backend/src/server/api/endpoints/i/registry/keys.ts index d21ff885a631..aad49a9b4674 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/keys.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/keys.ts @@ -22,17 +22,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = RegistryItems.createQueryBuilder('item') .select('item.key') .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }) + .andWhere('item.userId = :userId', { userId: me.id }) .andWhere('item.scope = :scope', { scope: ps.scope }); const items = await query.getMany(); diff --git a/packages/backend/src/server/api/endpoints/i/registry/remove.ts b/packages/backend/src/server/api/endpoints/i/registry/remove.ts index 063dc54046b9..f39a7e60cb69 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/remove.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/remove.ts @@ -32,16 +32,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = RegistryItems.createQueryBuilder('item') .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }) + .andWhere('item.userId = :userId', { userId: me.id }) .andWhere('item.key = :key', { key: ps.key }) .andWhere('item.scope = :scope', { scope: ps.scope }); diff --git a/packages/backend/src/server/api/endpoints/i/registry/scopes.ts b/packages/backend/src/server/api/endpoints/i/registry/scopes.ts index 65dd6073f547..e15591df3607 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/scopes.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/scopes.ts @@ -18,17 +18,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = RegistryItems.createQueryBuilder('item') .select('item.scope') .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }); + .andWhere('item.userId = :userId', { userId: me.id }); const items = await query.getMany(); diff --git a/packages/backend/src/server/api/endpoints/i/registry/set.ts b/packages/backend/src/server/api/endpoints/i/registry/set.ts index b7e540f5dc72..88af9a29346e 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/set.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/set.ts @@ -26,16 +26,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = RegistryItems.createQueryBuilder('item') .where('item.domain IS NULL') - .andWhere('item.userId = :userId', { userId: user.id }) + .andWhere('item.userId = :userId', { userId: me.id }) .andWhere('item.key = :key', { key: ps.key }) .andWhere('item.scope = :scope', { scope: ps.scope }); @@ -51,7 +46,7 @@ export default class extends Endpoint { id: genId(), createdAt: new Date(), updatedAt: new Date(), - userId: user.id, + userId: me.id, domain: null, scope: ps.scope, key: ps.key, @@ -60,7 +55,7 @@ export default class extends Endpoint { } // TODO: サードパーティアプリが傍受出来てしまうのでどうにかする - publishMainStream(user.id, 'registryUpdated', { + publishMainStream(me.id, 'registryUpdated', { scope: ps.scope, key: ps.key, value: ps.value, diff --git a/packages/backend/src/server/api/endpoints/i/revoke-token.ts b/packages/backend/src/server/api/endpoints/i/revoke-token.ts index 90e5c6155f1f..9680eacdf1b6 100644 --- a/packages/backend/src/server/api/endpoints/i/revoke-token.ts +++ b/packages/backend/src/server/api/endpoints/i/revoke-token.ts @@ -21,23 +21,18 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const token = await AccessTokens.findOneBy({ id: ps.tokenId }); if (token) { await AccessTokens.delete({ id: ps.tokenId, - userId: user.id, + userId: me.id, }); // Terminate streaming - publishUserEvent(user.id, 'terminate'); + publishUserEvent(me.id, 'terminate'); } }); } diff --git a/packages/backend/src/server/api/endpoints/i/signin-history.ts b/packages/backend/src/server/api/endpoints/i/signin-history.ts index 3a87d7546cd6..5b34969f1cb5 100644 --- a/packages/backend/src/server/api/endpoints/i/signin-history.ts +++ b/packages/backend/src/server/api/endpoints/i/signin-history.ts @@ -23,15 +23,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(Signins.createQueryBuilder('signin'), ps.sinceId, ps.untilId) - .andWhere('signin.userId = :meId', { meId: user.id }); + .andWhere('signin.userId = :meId', { meId: me.id }); const history = await query.take(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/i/unpin.ts b/packages/backend/src/server/api/endpoints/i/unpin.ts index eb9474b447cc..ccf2ced0a176 100644 --- a/packages/backend/src/server/api/endpoints/i/unpin.ts +++ b/packages/backend/src/server/api/endpoints/i/unpin.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { removePinned } from '@/services/i/pin.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -40,17 +40,14 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - await removePinned(user, ps.noteId).catch(e => { + super(meta, paramDef, async (ps, me) => { + await removePinned(me, ps.noteId).catch(e => { if (e.id === 'b302d4cf-c050-400a-bbb3-be208681f40c') throw new ApiError(meta.errors.noSuchNote); throw e; }); - return await this.usersRepository.pack(user.id, user, { + return await this.usersRepository.pack(me.id, me, { detail: true, }); }); diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts index a773e5593dea..9a8ad3d9218d 100644 --- a/packages/backend/src/server/api/endpoints/i/update-email.ts +++ b/packages/backend/src/server/api/endpoints/i/update-email.ts @@ -5,7 +5,8 @@ import bcrypt from 'bcryptjs'; import config from '@/config/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { publishMainStream } from '@/services/stream.js'; -import { Users, UserProfiles } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { UserProfiles } from '@/models/index.js'; import { sendEmail } from '@/services/send-email.js'; import { validateEmailForAccount } from '@/services/validate-email-for-account.js'; import { ApiError } from '../../error.js'; @@ -50,12 +51,9 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + super(meta, paramDef, async (ps, me) => { + const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); @@ -71,24 +69,24 @@ export default class extends Endpoint { } } - await UserProfiles.update(user.id, { + await UserProfiles.update(me.id, { email: ps.email, emailVerified: false, emailVerifyCode: null, }); - const iObj = await this.usersRepository.pack(user.id, user, { + const iObj = await this.usersRepository.pack(me.id, me, { detail: true, includeSecrets: true, }); // Publish meUpdated event - publishMainStream(user.id, 'meUpdated', iObj); + publishMainStream(me.id, 'meUpdated', iObj); if (ps.email != null) { const code = rndstr('a-z0-9', 16); - await UserProfiles.update(user.id, { + await UserProfiles.update(me.id, { emailVerifyCode: code, }); diff --git a/packages/backend/src/server/api/endpoints/i/user-group-invites.ts b/packages/backend/src/server/api/endpoints/i/user-group-invites.ts index 6f1e59abe94c..4502e63e21f3 100644 --- a/packages/backend/src/server/api/endpoints/i/user-group-invites.ts +++ b/packages/backend/src/server/api/endpoints/i/user-group-invites.ts @@ -46,15 +46,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(UserGroupInvitations.createQueryBuilder('invitation'), ps.sinceId, ps.untilId) - .andWhere('invitation.userId = :meId', { meId: user.id }) + .andWhere('invitation.userId = :meId', { meId: me.id }) .leftJoinAndSelect('invitation.userGroup', 'user_group'); const invitations = await query diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts index 96b6475c2f35..9fe700c2184f 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts @@ -30,17 +30,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const webhook = await Webhooks.insert({ id: genId(), createdAt: new Date(), - userId: user.id, + userId: me.id, name: ps.name, url: ps.url, secret: ps.secret, diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts b/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts index 199061738284..88350af066f5 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts @@ -32,16 +32,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const webhook = await Webhooks.findOneBy({ id: ps.webhookId, - userId: user.id, + userId: me.id, }); if (webhook == null) { diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/show.ts b/packages/backend/src/server/api/endpoints/i/webhooks/show.ts index 72007b7c910e..73d0ed08b602 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/show.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/show.ts @@ -31,16 +31,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const webhook = await Webhooks.findOneBy({ id: ps.webhookId, - userId: user.id, + userId: me.id, }); if (webhook == null) { diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/update.ts b/packages/backend/src/server/api/endpoints/i/webhooks/update.ts index bac3f9c1efc5..fdd86ca36626 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/update.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/update.ts @@ -41,16 +41,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const webhook = await Webhooks.findOneBy({ id: ps.webhookId, - userId: user.id, + userId: me.id, }); if (webhook == null) { diff --git a/packages/backend/src/server/api/endpoints/messaging/history.ts b/packages/backend/src/server/api/endpoints/messaging/history.ts index b1df658288ba..e995c0a7036b 100644 --- a/packages/backend/src/server/api/endpoints/messaging/history.ts +++ b/packages/backend/src/server/api/endpoints/messaging/history.ts @@ -35,19 +35,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const mute = await Mutings.findBy({ - muterId: user.id, + muterId: me.id, }); const groups = ps.group ? await UserGroupJoinings.findBy({ - userId: user.id, + userId: me.id, }).then(xs => xs.map(x => x.userGroupId)) : []; if (ps.group && groups.length === 0) { @@ -59,7 +54,7 @@ export default class extends Endpoint { for (let i = 0; i < ps.limit; i++) { const found = ps.group ? history.map(m => m.groupId!) - : history.map(m => (m.userId === user.id) ? m.recipientId! : m.userId!); + : history.map(m => (m.userId === me.id) ? m.recipientId! : m.userId!); const query = MessagingMessages.createQueryBuilder('message') .orderBy('message.createdAt', 'DESC'); @@ -72,8 +67,8 @@ export default class extends Endpoint { } } else { query.where(new Brackets(qb => { qb - .where('message.userId = :userId', { userId: user.id }) - .orWhere('message.recipientId = :userId', { userId: user.id }); + .where('message.userId = :userId', { userId: me.id }) + .orWhere('message.recipientId = :userId', { userId: me.id }); })); query.andWhere('message.groupId IS NULL'); @@ -97,7 +92,7 @@ export default class extends Endpoint { } } - return await Promise.all(history.map(h => MessagingMessages.pack(h.id, user))); + return await Promise.all(history.map(h => MessagingMessages.pack(h.id, me))); }); } } diff --git a/packages/backend/src/server/api/endpoints/messaging/messages.ts b/packages/backend/src/server/api/endpoints/messaging/messages.ts index aed02d399a6d..fcef633ce23a 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Brackets } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { MessagingMessages, UserGroups, UserGroupJoinings, Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { MessagingMessages, UserGroups, UserGroupJoinings } from '@/models/index.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; @@ -75,11 +76,8 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { if (ps.userId != null) { // Fetch recipient (user) const recipient = await getUser(ps.userId).catch(e => { @@ -98,22 +96,22 @@ export default class extends Endpoint { .andWhere('message.recipientId = :meId'); })); })) - .setParameter('meId', user.id) + .setParameter('meId', me.id) .setParameter('recipientId', recipient.id); const messages = await query.take(ps.limit).getMany(); // Mark all as read if (ps.markAsRead) { - readUserMessagingMessage(user.id, recipient.id, messages.filter(m => m.recipientId === user.id).map(x => x.id)); + readUserMessagingMessage(me.id, recipient.id, messages.filter(m => m.recipientId === me.id).map(x => x.id)); // リモートユーザーとのメッセージだったら既読配信 - if (this.usersRepository.isLocalUser(user) && this.usersRepository.isRemoteUser(recipient)) { - deliverReadActivity(user, recipient, messages); + if (this.usersRepository.isLocalUser(me) && this.usersRepository.isRemoteUser(recipient)) { + deliverReadActivity(me, recipient, messages); } } - return await Promise.all(messages.map(message => MessagingMessages.pack(message, user, { + return await Promise.all(messages.map(message => MessagingMessages.pack(message, me, { populateRecipient: false, }))); } else if (ps.groupId != null) { @@ -126,7 +124,7 @@ export default class extends Endpoint { // check joined const joining = await UserGroupJoinings.findOneBy({ - userId: user.id, + userId: me.id, userGroupId: recipientGroup.id, }); @@ -141,10 +139,10 @@ export default class extends Endpoint { // Mark all as read if (ps.markAsRead) { - readGroupMessagingMessage(user.id, recipientGroup.id, messages.map(x => x.id)); + readGroupMessagingMessage(me.id, recipientGroup.id, messages.map(x => x.id)); } - return await Promise.all(messages.map(message => MessagingMessages.pack(message, user, { + return await Promise.all(messages.map(message => MessagingMessages.pack(message, me, { populateGroup: false, }))); } diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts index fc3a8b4f0969..a4117e3f8c1a 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts @@ -91,19 +91,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { let recipientUser: User | null; let recipientGroup: UserGroup | null; if (ps.userId != null) { // Myself - if (ps.userId === user.id) { + if (ps.userId === me.id) { throw new ApiError(meta.errors.recipientIsYourself); } @@ -116,7 +111,7 @@ export default class extends Endpoint { // Check blocking const block = await Blockings.findOneBy({ blockerId: recipientUser.id, - blockeeId: user.id, + blockeeId: me.id, }); if (block) { throw new ApiError(meta.errors.youHaveBeenBlocked); @@ -131,7 +126,7 @@ export default class extends Endpoint { // check joined const joining = await UserGroupJoinings.findOneBy({ - userId: user.id, + userId: me.id, userGroupId: recipientGroup.id, }); @@ -144,7 +139,7 @@ export default class extends Endpoint { if (ps.fileId != null) { file = await DriveFiles.findOneBy({ id: ps.fileId, - userId: user.id, + userId: me.id, }); if (file == null) { @@ -157,7 +152,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.contentRequired); } - return await createMessage(user, recipientUser, recipientGroup, ps.text, file); + return await createMessage(me, recipientUser, recipientGroup, ps.text, file); }); } } diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts b/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts index 34d13966b634..3e76d25ad09c 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts @@ -39,16 +39,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const message = await MessagingMessages.findOneBy({ id: ps.messageId, - userId: user.id, + userId: me.id, }); if (message == null) { diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/read.ts b/packages/backend/src/server/api/endpoints/messaging/messages/read.ts index 85044b3d1fc0..a7e38d084f6a 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/read.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/read.ts @@ -32,13 +32,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const message = await MessagingMessages.findOneBy({ id: ps.messageId }); if (message == null) { @@ -46,12 +41,12 @@ export default class extends Endpoint { } if (message.recipientId) { - await readUserMessagingMessage(user.id, message.userId, [message.id]).catch(e => { + await readUserMessagingMessage(me.id, message.userId, [message.id]).catch(e => { if (e.id === 'e140a4bf-49ce-4fb6-b67c-b78dadf6b52f') throw new ApiError(meta.errors.noSuchMessage); throw e; }); } else if (message.groupId) { - await readGroupMessagingMessage(user.id, message.groupId, [message.id]).catch(e => { + await readGroupMessagingMessage(me.id, message.groupId, [message.id]).catch(e => { if (e.id === '930a270c-714a-46b2-b776-ad27276dc569') throw new ApiError(meta.errors.noSuchMessage); throw e; }); diff --git a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts index 268f90da6670..a78df77d310e 100644 --- a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts +++ b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts @@ -41,13 +41,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Generate access token const accessToken = secureRndstr(32, true); @@ -59,7 +54,7 @@ export default class extends Endpoint { createdAt: now, lastUsedAt: now, session: ps.session, - userId: user.id, + userId: me.id, token: accessToken, hash: accessToken, name: ps.name, diff --git a/packages/backend/src/server/api/endpoints/mute/create.ts b/packages/backend/src/server/api/endpoints/mute/create.ts index 8bbc3e95f487..3befee46b61a 100644 --- a/packages/backend/src/server/api/endpoints/mute/create.ts +++ b/packages/backend/src/server/api/endpoints/mute/create.ts @@ -52,17 +52,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - const muter = user; + super(meta, paramDef, async (ps, me) => { + const muter = me; // 自分自身 - if (user.id === ps.userId) { + if (me.id === ps.userId) { throw new ApiError(meta.errors.muteeIsYourself); } @@ -95,7 +90,7 @@ export default class extends Endpoint { muteeId: mutee.id, } as Muting); - publishUserEvent(user.id, 'mute', mutee); + publishUserEvent(me.id, 'mute', mutee); NoteWatchings.delete({ userId: muter.id, diff --git a/packages/backend/src/server/api/endpoints/mute/delete.ts b/packages/backend/src/server/api/endpoints/mute/delete.ts index 911e6aab054d..08ab99180c36 100644 --- a/packages/backend/src/server/api/endpoints/mute/delete.ts +++ b/packages/backend/src/server/api/endpoints/mute/delete.ts @@ -45,17 +45,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - const muter = user; + super(meta, paramDef, async (ps, me) => { + const muter = me; // Check if the mutee is yourself - if (user.id === ps.userId) { + if (me.id === ps.userId) { throw new ApiError(meta.errors.muteeIsYourself); } @@ -80,7 +75,7 @@ export default class extends Endpoint { id: exist.id, }); - publishUserEvent(user.id, 'unmute', mutee); + publishUserEvent(me.id, 'unmute', mutee); }); } } diff --git a/packages/backend/src/server/api/endpoints/my/apps.ts b/packages/backend/src/server/api/endpoints/my/apps.ts index f2ee083f7d0f..7e4a6b3923f8 100644 --- a/packages/backend/src/server/api/endpoints/my/apps.ts +++ b/packages/backend/src/server/api/endpoints/my/apps.ts @@ -31,15 +31,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = { - userId: user.id, + userId: me.id, }; const apps = await Apps.find({ @@ -48,7 +43,7 @@ export default class extends Endpoint { skip: ps.offset, }); - return await Promise.all(apps.map(app => Apps.pack(app, user, { + return await Promise.all(apps.map(app => Apps.pack(app, me, { detail: true, }))); }); diff --git a/packages/backend/src/server/api/endpoints/notes.ts b/packages/backend/src/server/api/endpoints/notes.ts index e82826bda32b..a71b4c3e1d2b 100644 --- a/packages/backend/src/server/api/endpoints/notes.ts +++ b/packages/backend/src/server/api/endpoints/notes.ts @@ -36,13 +36,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - @Inject('notesRepository') private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere('note.visibility = \'public\'') .andWhere('note.localOnly = FALSE') diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index 27d75a6faa10..a8b4a0794bc6 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -38,13 +38,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(new Brackets(qb => { qb .where('note.replyId = :noteId', { noteId: ps.noteId }) @@ -69,15 +64,15 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateVisibilityQuery(query, user); - if (user) { - generateMutedUserQuery(query, user); - generateBlockedUserQuery(query, user); + generateVisibilityQuery(query, me); + if (me) { + generateMutedUserQuery(query, me); + generateBlockedUserQuery(query, me); } const notes = await query.take(ps.limit).getMany(); - return await Notes.packMany(notes, user); + return await Notes.packMany(notes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/conversation.ts b/packages/backend/src/server/api/endpoints/notes/conversation.ts index f558ae4d3399..a4f6f0fe7358 100644 --- a/packages/backend/src/server/api/endpoints/notes/conversation.ts +++ b/packages/backend/src/server/api/endpoints/notes/conversation.ts @@ -43,13 +43,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; @@ -80,7 +75,7 @@ export default class extends Endpoint { await get(note.replyId); } - return await Notes.packMany(conversation, user); + return await Notes.packMany(conversation, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index c7a28bcb2587..1ede64617525 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -2,7 +2,8 @@ import ms from 'ms'; import { In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { User } from '@/models/entities/user.js'; -import { Users, DriveFiles, Notes, Channels, Blockings } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { DriveFiles, Notes, Channels, Blockings } from '@/models/index.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import type { Note } from '@/models/entities/note.js'; import type { Channel } from '@/models/entities/channel.js'; @@ -167,11 +168,8 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { let visibleUsers: User[] = []; if (ps.visibleUserIds) { visibleUsers = await this.usersRepository.findBy({ @@ -184,7 +182,7 @@ export default class extends Endpoint { if (fileIds != null) { files = await DriveFiles.createQueryBuilder('file') .where('file.userId = :userId AND file.id IN (:...fileIds)', { - userId: user.id, + userId: me.id, fileIds, }) .orderBy('array_position(ARRAY[:...fileIds], "id"::text)') @@ -204,10 +202,10 @@ export default class extends Endpoint { } // Check blocking - if (renote.userId !== user.id) { + if (renote.userId !== me.id) { const block = await Blockings.findOneBy({ blockerId: renote.userId, - blockeeId: user.id, + blockeeId: me.id, }); if (block) { throw new ApiError(meta.errors.youHaveBeenBlocked); @@ -227,10 +225,10 @@ export default class extends Endpoint { } // Check blocking - if (reply.userId !== user.id) { + if (reply.userId !== me.id) { const block = await Blockings.findOneBy({ blockerId: reply.userId, - blockeeId: user.id, + blockeeId: me.id, }); if (block) { throw new ApiError(meta.errors.youHaveBeenBlocked); @@ -258,7 +256,7 @@ export default class extends Endpoint { } // 投稿を作成 - const note = await noteService.create(user, { + const note = await noteService.create(me, { createdAt: new Date(), files: files, poll: ps.poll ? { @@ -280,7 +278,7 @@ export default class extends Endpoint { }); return { - createdNote: await Notes.pack(note, user), + createdNote: await Notes.pack(note, me), }; }); } diff --git a/packages/backend/src/server/api/endpoints/notes/delete.ts b/packages/backend/src/server/api/endpoints/notes/delete.ts index c2fd2d1ca3ce..67c10eec3598 100644 --- a/packages/backend/src/server/api/endpoints/notes/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/delete.ts @@ -1,7 +1,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import deleteNote from '@/services/note/delete.js'; -import { Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../common/getters.js'; import { ApiError } from '../../error.js'; @@ -48,17 +48,14 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; }); - if ((!user.isAdmin && !user.isModerator) && (note.userId !== user.id)) { + if ((!me.isAdmin && !me.isModerator) && (note.userId !== me.id)) { throw new ApiError(meta.errors.accessDenied); } diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts index 876d278eda60..fa2cf7d12b3a 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts @@ -39,13 +39,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Get favoritee const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); @@ -55,7 +51,7 @@ export default class extends Endpoint { // if already favorited const exist = await NoteFavorites.findOneBy({ noteId: note.id, - userId: user.id, + userId: me.id, }); if (exist != null) { @@ -67,7 +63,7 @@ export default class extends Endpoint { id: genId(), createdAt: new Date(), noteId: note.id, - userId: user.id, + userId: me.id, }); }); } diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts index 0e844a38db7e..a3097f957050 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts @@ -38,13 +38,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Get favoritee const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); @@ -54,7 +50,7 @@ export default class extends Endpoint { // if already favorited const exist = await NoteFavorites.findOneBy({ noteId: note.id, - userId: user.id, + userId: me.id, }); if (exist == null) { diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index 2b5f9b86379e..f767266d997f 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -33,13 +33,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const max = 30; const day = 1000 * 60 * 60 * 24 * 3; // 3日前まで @@ -61,8 +56,8 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - if (user) generateMutedUserQuery(query, user); - if (user) generateBlockedUserQuery(query, user); + if (me) generateMutedUserQuery(query, me); + if (me) generateBlockedUserQuery(query, me); let notes = await query .orderBy('note.score', 'DESC') @@ -73,7 +68,7 @@ export default class extends Endpoint { notes = notes.slice(ps.offset, ps.offset + ps.limit); - return await Notes.packMany(notes, user); + return await Notes.packMany(notes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index 78dfa1320a53..d4a8a4eefff9 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -53,16 +53,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const m = await fetchMeta(); if (m.disableGlobalTimeline) { - if (user == null || (!user.isAdmin && !user.isModerator)) { + if (me == null || (!me.isAdmin && !me.isModerator)) { throw new ApiError(meta.errors.gtlDisabled); } } @@ -84,11 +79,11 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateRepliesQuery(query, user); - if (user) { - generateMutedUserQuery(query, user); - generateMutedNoteQuery(query, user); - generateBlockedUserQuery(query, user); + generateRepliesQuery(query, me); + if (me) { + generateMutedUserQuery(query, me); + generateMutedNoteQuery(query, me); + generateBlockedUserQuery(query, me); } if (ps.withFiles) { @@ -99,12 +94,12 @@ export default class extends Endpoint { const timeline = await query.take(ps.limit).getMany(); process.nextTick(() => { - if (user) { - activeUsersChart.read(user); + if (me) { + activeUsersChart.read(me); } }); - return await Notes.packMany(timeline, user); + return await Notes.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 8689ee19b91e..deff4135be5c 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -61,27 +61,22 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const m = await fetchMeta(); - if (m.disableLocalTimeline && (!user.isAdmin && !user.isModerator)) { + if (m.disableLocalTimeline && (!me.isAdmin && !me.isModerator)) { throw new ApiError(meta.errors.stlDisabled); } //#region Construct query const followingQuery = Followings.createQueryBuilder('following') .select('following.followeeId') - .where('following.followerId = :followerId', { followerId: user.id }); + .where('following.followerId = :followerId', { followerId: me.id }); const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere(new Brackets(qb => { - qb.where(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: user.id }) + qb.where(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: me.id }) .orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); })) .innerJoinAndSelect('note.user', 'user') @@ -97,16 +92,16 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') .setParameters(followingQuery.getParameters()); - generateChannelQuery(query, user); - generateRepliesQuery(query, user); - generateVisibilityQuery(query, user); - generateMutedUserQuery(query, user); - generateMutedNoteQuery(query, user); - generateBlockedUserQuery(query, user); + generateChannelQuery(query, me); + generateRepliesQuery(query, me); + generateVisibilityQuery(query, me); + generateMutedUserQuery(query, me); + generateMutedNoteQuery(query, me); + generateBlockedUserQuery(query, me); if (ps.includeMyRenotes === false) { query.andWhere(new Brackets(qb => { - qb.orWhere('note.userId != :meId', { meId: user.id }); + qb.orWhere('note.userId != :meId', { meId: me.id }); qb.orWhere('note.renoteId IS NULL'); qb.orWhere('note.text IS NOT NULL'); qb.orWhere('note.fileIds != \'{}\''); @@ -116,7 +111,7 @@ export default class extends Endpoint { if (ps.includeRenotedMyNotes === false) { query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteUserId != :meId', { meId: user.id }); + qb.orWhere('note.renoteUserId != :meId', { meId: me.id }); qb.orWhere('note.renoteId IS NULL'); qb.orWhere('note.text IS NOT NULL'); qb.orWhere('note.fileIds != \'{}\''); @@ -142,10 +137,10 @@ export default class extends Endpoint { const timeline = await query.take(ps.limit).getMany(); process.nextTick(() => { - activeUsersChart.read(user); + activeUsersChart.read(me); }); - return await Notes.packMany(timeline, user); + return await Notes.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 7f980273d921..0b945e1599a5 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -61,16 +61,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const m = await fetchMeta(); if (m.disableLocalTimeline) { - if (user == null || (!user.isAdmin && !user.isModerator)) { + if (me == null || (!me.isAdmin && !me.isModerator)) { throw new ApiError(meta.errors.ltlDisabled); } } @@ -91,12 +86,12 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateChannelQuery(query, user); - generateRepliesQuery(query, user); - generateVisibilityQuery(query, user); - if (user) generateMutedUserQuery(query, user); - if (user) generateMutedNoteQuery(query, user); - if (user) generateBlockedUserQuery(query, user); + generateChannelQuery(query, me); + generateRepliesQuery(query, me); + generateVisibilityQuery(query, me); + if (me) generateMutedUserQuery(query, me); + if (me) generateMutedNoteQuery(query, me); + if (me) generateBlockedUserQuery(query, me); if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); @@ -121,12 +116,12 @@ export default class extends Endpoint { const timeline = await query.take(ps.limit).getMany(); process.nextTick(() => { - if (user) { - activeUsersChart.read(user); + if (me) { + activeUsersChart.read(me); } }); - return await Notes.packMany(timeline, user); + return await Notes.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index 02df7f07eca0..e100e7df72e6 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -41,21 +41,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const followingQuery = Followings.createQueryBuilder('following') .select('following.followeeId') - .where('following.followerId = :followerId', { followerId: user.id }); + .where('following.followerId = :followerId', { followerId: me.id }); const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(new Brackets(qb => { qb - .where(`'{"${user.id}"}' <@ note.mentions`) - .orWhere(`'{"${user.id}"}' <@ note.visibleUserIds`); + .where(`'{"${me.id}"}' <@ note.mentions`) + .orWhere(`'{"${me.id}"}' <@ note.visibleUserIds`); })) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') @@ -69,25 +64,25 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateVisibilityQuery(query, user); - generateMutedUserQuery(query, user); - generateMutedNoteThreadQuery(query, user); - generateBlockedUserQuery(query, user); + generateVisibilityQuery(query, me); + generateMutedUserQuery(query, me); + generateMutedNoteThreadQuery(query, me); + generateBlockedUserQuery(query, me); if (ps.visibility) { query.andWhere('note.visibility = :visibility', { visibility: ps.visibility }); } if (ps.following) { - query.andWhere(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: user.id }); + query.andWhere(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: me.id }); query.setParameters(followingQuery.getParameters()); } const mentions = await query.take(ps.limit).getMany(); - read(user.id, mentions); + read(me.id, mentions); - return await Notes.packMany(mentions, user); + return await Notes.packMany(mentions, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts index d145a7bbd79d..a6704fa76973 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts @@ -32,16 +32,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = Polls.createQueryBuilder('poll') .where('poll.userHost IS NULL') - .andWhere('poll.userId != :meId', { meId: user.id }) + .andWhere('poll.userId != :meId', { meId: me.id }) .andWhere('poll.noteVisibility = \'public\'') .andWhere(new Brackets(qb => { qb .where('poll.expiresAt IS NULL') @@ -51,7 +47,7 @@ export default class extends Endpoint { //#region exclude arleady voted polls const votedQuery = PollVotes.createQueryBuilder('vote') .select('vote.noteId') - .where('vote.userId = :meId', { meId: user.id }); + .where('vote.userId = :meId', { meId: me.id }); query .andWhere(`poll.noteId NOT IN (${ votedQuery.getQuery() })`); @@ -62,7 +58,7 @@ export default class extends Endpoint { //#region mute const mutingQuery = Mutings.createQueryBuilder('muting') .select('muting.muteeId') - .where('muting.muterId = :muterId', { muterId: user.id }); + .where('muting.muterId = :muterId', { muterId: me.id }); query .andWhere(`poll.userId NOT IN (${ mutingQuery.getQuery() })`); @@ -87,7 +83,7 @@ export default class extends Endpoint { }, }); - return await Notes.packMany(notes, user, { + return await Notes.packMany(notes, me, { detail: true, }); }); 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 aebf682a861f..848cf1210e05 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -6,7 +6,8 @@ import { deliver } from '@/queue/index.js'; import { renderActivity } from '@/remote/activitypub/renderer/index.js'; import renderVote from '@/remote/activitypub/renderer/vote.js'; import { deliverQuestionUpdate } from '@/services/note/polls/update.js'; -import { PollVotes, NoteWatchings, Users, Polls, Blockings } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { PollVotes, NoteWatchings, Polls, Blockings } from '@/models/index.js'; import type { IRemoteUser } from '@/models/entities/user.js'; import { genId } from '@/misc/gen-id.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -74,11 +75,8 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const createdAt = new Date(); // Get votee @@ -92,10 +90,10 @@ export default class extends Endpoint { } // Check blocking - if (note.userId !== user.id) { + if (note.userId !== me.id) { const block = await Blockings.findOneBy({ blockerId: note.userId, - blockeeId: user.id, + blockeeId: me.id, }); if (block) { throw new ApiError(meta.errors.youHaveBeenBlocked); @@ -115,7 +113,7 @@ export default class extends Endpoint { // if already voted const exist = await PollVotes.findBy({ noteId: note.id, - userId: user.id, + userId: me.id, }); if (exist.length) { @@ -133,7 +131,7 @@ export default class extends Endpoint { id: genId(), createdAt, noteId: note.id, - userId: user.id, + userId: me.id, choice: ps.choice, }).then(x => PollVotes.findOneByOrFail(x.identifiers[0])); @@ -143,12 +141,12 @@ export default class extends Endpoint { publishNoteStream(note.id, 'pollVoted', { choice: ps.choice, - userId: user.id, + userId: me.id, }); // Notify createNotification(note.userId, 'pollVote', { - notifierId: user.id, + notifierId: me.id, noteId: note.id, choice: ps.choice, }); @@ -156,11 +154,11 @@ export default class extends Endpoint { // Fetch watchers NoteWatchings.findBy({ noteId: note.id, - userId: Not(user.id), + userId: Not(me.id), }).then(watchers => { for (const watcher of watchers) { createNotification(watcher.userId, 'pollVote', { - notifierId: user.id, + notifierId: me.id, noteId: note.id, choice: ps.choice, }); @@ -171,7 +169,7 @@ export default class extends Endpoint { if (note.userHost != null) { const pollOwner = await this.usersRepository.findOneByOrFail({ id: note.userId }) as IRemoteUser; - deliver(user, renderActivity(await renderVote(user, vote, note, poll, pollOwner)), pollOwner.inbox); + deliver(me, renderActivity(await renderVote(me, vote, note, poll, pollOwner)), pollOwner.inbox); } // リモートフォロワーにUpdate配信 diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts index 6e1aa9b056f7..9fbf1bc12492 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts @@ -50,13 +50,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = { noteId: ps.noteId, } as FindOptionsWhere; @@ -79,7 +74,7 @@ export default class extends Endpoint { relations: ['user', 'user.avatar', 'user.banner', 'note'], }); - return await Promise.all(reactions.map(reaction => NoteReactions.pack(reaction, user))); + return await Promise.all(reactions.map(reaction => NoteReactions.pack(reaction, me))); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts index 6b6c9d8e701f..3bd4ffd05cc7 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts @@ -45,18 +45,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; }); - await createReaction(user, note, ps.reaction).catch(e => { + await createReaction(me, note, ps.reaction).catch(e => { if (e.id === '51c42bb4-931a-456b-bff7-e5a8a70dd298') throw new ApiError(meta.errors.alreadyReacted); if (e.id === 'e70412a4-7197-4726-8e74-f3e0deb92aa7') throw new ApiError(meta.errors.youHaveBeenBlocked); throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts index c7f2bad0d96a..71b128212ce2 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts @@ -45,18 +45,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; }); - await deleteReaction(user, note).catch(e => { + await deleteReaction(me, note).catch(e => { if (e.id === '60527ec9-b4cb-4a88-a6bd-32d3ad26817d') throw new ApiError(meta.errors.notReacted); throw e; }); diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index 91909ddc9aa2..4e787a9d58a8 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -47,13 +47,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; @@ -73,13 +68,13 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateVisibilityQuery(query, user); - if (user) generateMutedUserQuery(query, user); - if (user) generateBlockedUserQuery(query, user); + generateVisibilityQuery(query, me); + if (me) generateMutedUserQuery(query, me); + if (me) generateBlockedUserQuery(query, me); const renotes = await query.take(ps.limit).getMany(); - return await Notes.packMany(renotes, user); + return await Notes.packMany(renotes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index 1d5065dde57f..38ff5852886e 100644 --- a/packages/backend/src/server/api/endpoints/notes/replies.ts +++ b/packages/backend/src/server/api/endpoints/notes/replies.ts @@ -37,13 +37,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere('note.replyId = :replyId', { replyId: ps.noteId }) .innerJoinAndSelect('note.user', 'user') @@ -58,13 +53,13 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateVisibilityQuery(query, user); - if (user) generateMutedUserQuery(query, user); - if (user) generateBlockedUserQuery(query, user); + generateVisibilityQuery(query, me); + if (me) generateMutedUserQuery(query, me); + if (me) generateBlockedUserQuery(query, me); const timeline = await query.take(ps.limit).getMany(); - return await Notes.packMany(timeline, user); + return await Notes.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts index e51979ea5987..211d293d51ff 100644 --- a/packages/backend/src/server/api/endpoints/notes/show.ts +++ b/packages/backend/src/server/api/endpoints/notes/show.ts @@ -36,19 +36,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; }); - return await Notes.pack(note, user, { + return await Notes.pack(note, me, { detail: true, }); }); diff --git a/packages/backend/src/server/api/endpoints/notes/state.ts b/packages/backend/src/server/api/endpoints/notes/state.ts index 7a38e26ed546..2d8c83318faf 100644 --- a/packages/backend/src/server/api/endpoints/notes/state.ts +++ b/packages/backend/src/server/api/endpoints/notes/state.ts @@ -39,33 +39,28 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const note = await Notes.findOneByOrFail({ id: ps.noteId }); const [favorite, watching, threadMuting] = await Promise.all([ NoteFavorites.count({ where: { - userId: user.id, + userId: me.id, noteId: note.id, }, take: 1, }), NoteWatchings.count({ where: { - userId: user.id, + userId: me.id, noteId: note.id, }, take: 1, }), NoteThreadMutings.count({ where: { - userId: user.id, + userId: me.id, threadId: note.threadId || note.id, }, take: 1, diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts index 8add08da618f..0bae91a19dd6 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts @@ -34,13 +34,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; @@ -54,13 +49,13 @@ export default class extends Endpoint { }], }); - await readNote(user.id, mutedNotes); + await readNote(me.id, mutedNotes); await NoteThreadMutings.insert({ id: genId(), createdAt: new Date(), threadId: note.threadId || note.id, - userId: user.id, + userId: me.id, }); }); } diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts index 3f7dbc3fa793..c3db7b34e34c 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts @@ -32,13 +32,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; @@ -46,7 +41,7 @@ export default class extends Endpoint { await NoteThreadMutings.delete({ threadId: note.threadId || note.id, - userId: user.id, + userId: me.id, }); }); } diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index e42b97fca7af..9732c9302afd 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -51,16 +51,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const hasFollowing = (await Followings.count({ where: { - followerId: user.id, + followerId: me.id, }, take: 1, })) !== 0; @@ -68,12 +63,12 @@ export default class extends Endpoint { //#region Construct query const followingQuery = Followings.createQueryBuilder('following') .select('following.followeeId') - .where('following.followerId = :followerId', { followerId: user.id }); + .where('following.followerId = :followerId', { followerId: me.id }); const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere(new Brackets(qb => { qb - .where('note.userId = :meId', { meId: user.id }); + .where('note.userId = :meId', { meId: me.id }); if (hasFollowing) qb.orWhere(`note.userId IN (${ followingQuery.getQuery() })`); })) .innerJoinAndSelect('note.user', 'user') @@ -89,16 +84,16 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') .setParameters(followingQuery.getParameters()); - generateChannelQuery(query, user); - generateRepliesQuery(query, user); - generateVisibilityQuery(query, user); - generateMutedUserQuery(query, user); - generateMutedNoteQuery(query, user); - generateBlockedUserQuery(query, user); + generateChannelQuery(query, me); + generateRepliesQuery(query, me); + generateVisibilityQuery(query, me); + generateMutedUserQuery(query, me); + generateMutedNoteQuery(query, me); + generateBlockedUserQuery(query, me); if (ps.includeMyRenotes === false) { query.andWhere(new Brackets(qb => { - qb.orWhere('note.userId != :meId', { meId: user.id }); + qb.orWhere('note.userId != :meId', { meId: me.id }); qb.orWhere('note.renoteId IS NULL'); qb.orWhere('note.text IS NOT NULL'); qb.orWhere('note.fileIds != \'{}\''); @@ -108,7 +103,7 @@ export default class extends Endpoint { if (ps.includeRenotedMyNotes === false) { query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteUserId != :meId', { meId: user.id }); + qb.orWhere('note.renoteUserId != :meId', { meId: me.id }); qb.orWhere('note.renoteId IS NULL'); qb.orWhere('note.text IS NOT NULL'); qb.orWhere('note.fileIds != \'{}\''); @@ -134,10 +129,10 @@ export default class extends Endpoint { const timeline = await query.take(ps.limit).getMany(); process.nextTick(() => { - activeUsersChart.read(user); + activeUsersChart.read(me); }); - return await Notes.packMany(timeline, user); + return await Notes.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts index 54b0d6210833..3ec562df791b 100644 --- a/packages/backend/src/server/api/endpoints/notes/translate.ts +++ b/packages/backend/src/server/api/endpoints/notes/translate.ts @@ -41,19 +41,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; }); - if (!(await Notes.isVisibleForMe(note, user ? user.id : null))) { + if (!(await Notes.isVisibleForMe(note, me ? me.id : null))) { return 204; // TODO: 良い感じのエラー返す } diff --git a/packages/backend/src/server/api/endpoints/notes/unrenote.ts b/packages/backend/src/server/api/endpoints/notes/unrenote.ts index 9ff0215586e4..dae2fd142423 100644 --- a/packages/backend/src/server/api/endpoints/notes/unrenote.ts +++ b/packages/backend/src/server/api/endpoints/notes/unrenote.ts @@ -1,7 +1,8 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import deleteNote from '@/services/note/delete.js'; -import { Notes, Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../common/getters.js'; import { ApiError } from '../../error.js'; @@ -42,23 +43,20 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; }); const renotes = await Notes.findBy({ - userId: user.id, + userId: me.id, renoteId: note.id, }); for (const note of renotes) { - deleteNote(await this.usersRepository.findOneByOrFail({ id: user.id }), note); + deleteNote(await this.usersRepository.findOneByOrFail({ id: me.id }), note); } }); } diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 0a89c3b97ba0..524635bc2e22 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -56,16 +56,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const list = await UserLists.findOneBy({ id: ps.listId, - userId: user.id, + userId: me.id, }); if (list == null) { @@ -88,11 +83,11 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') .andWhere('userListJoining.userListId = :userListId', { userListId: list.id }); - generateVisibilityQuery(query, user); + generateVisibilityQuery(query, me); if (ps.includeMyRenotes === false) { query.andWhere(new Brackets(qb => { - qb.orWhere('note.userId != :meId', { meId: user.id }); + qb.orWhere('note.userId != :meId', { meId: me.id }); qb.orWhere('note.renoteId IS NULL'); qb.orWhere('note.text IS NOT NULL'); qb.orWhere('note.fileIds != \'{}\''); @@ -102,7 +97,7 @@ export default class extends Endpoint { if (ps.includeRenotedMyNotes === false) { query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteUserId != :meId', { meId: user.id }); + qb.orWhere('note.renoteUserId != :meId', { meId: me.id }); qb.orWhere('note.renoteId IS NULL'); qb.orWhere('note.text IS NOT NULL'); qb.orWhere('note.fileIds != \'{}\''); @@ -127,9 +122,9 @@ export default class extends Endpoint { const timeline = await query.take(ps.limit).getMany(); - activeUsersChart.read(user); + activeUsersChart.read(me); - return await Notes.packMany(timeline, user); + return await Notes.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/watching/create.ts b/packages/backend/src/server/api/endpoints/notes/watching/create.ts index 9e1b96fea547..a9bdbd9fb62e 100644 --- a/packages/backend/src/server/api/endpoints/notes/watching/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/watching/create.ts @@ -32,19 +32,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; }); - await watch(user.id, note); + await watch(me.id, note); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/watching/delete.ts b/packages/backend/src/server/api/endpoints/notes/watching/delete.ts index 6cb88c0bd232..64da18e990dc 100644 --- a/packages/backend/src/server/api/endpoints/notes/watching/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/watching/delete.ts @@ -32,19 +32,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; }); - await unwatch(user.id, note); + await unwatch(me.id, note); }); } } diff --git a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts index c74dc696affc..43ba486d29ad 100644 --- a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts @@ -22,24 +22,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Update documents await Notifications.update({ - notifieeId: user.id, + notifieeId: me.id, isRead: false, }, { isRead: true, }); // 全ての通知を読みましたよというイベントを発行 - publishMainStream(user.id, 'readAllNotifications'); - pushNotification(user.id, 'readAllNotifications', undefined); + publishMainStream(me.id, 'readAllNotifications'); + pushNotification(me.id, 'readAllNotifications', undefined); }); } } diff --git a/packages/backend/src/server/api/endpoints/notifications/read.ts b/packages/backend/src/server/api/endpoints/notifications/read.ts index 569d277a7839..acf75be869b4 100644 --- a/packages/backend/src/server/api/endpoints/notifications/read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/read.ts @@ -47,15 +47,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { - if ('notificationId' in ps) return readNotification(user.id, [ps.notificationId]); - return readNotification(user.id, ps.notificationIds); + super(meta, paramDef, async (ps, me) => { + if ('notificationId' in ps) return readNotification(me.id, [ps.notificationId]); + return readNotification(me.id, ps.notificationIds); }); } } diff --git a/packages/backend/src/server/api/endpoints/page-push.ts b/packages/backend/src/server/api/endpoints/page-push.ts index e6fdd635caf4..1ed97ae7b12d 100644 --- a/packages/backend/src/server/api/endpoints/page-push.ts +++ b/packages/backend/src/server/api/endpoints/page-push.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { publishMainStream } from '@/services/stream.js'; -import { Users, Pages } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { Pages } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../error.js'; @@ -33,11 +34,8 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const page = await Pages.findOneBy({ id: ps.pageId }); if (page == null) { throw new ApiError(meta.errors.noSuchPage); @@ -47,8 +45,8 @@ export default class extends Endpoint { pageId: ps.pageId, event: ps.event, var: ps.var, - userId: user.id, - user: await this.usersRepository.pack(user.id, { id: page.userId }, { + userId: me.id, + user: await this.usersRepository.pack(me.id, { id: page.userId }, { detail: true, }), }); diff --git a/packages/backend/src/server/api/endpoints/pages/create.ts b/packages/backend/src/server/api/endpoints/pages/create.ts index b1d15acee4fa..7521ad2838de 100644 --- a/packages/backend/src/server/api/endpoints/pages/create.ts +++ b/packages/backend/src/server/api/endpoints/pages/create.ts @@ -63,18 +63,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { let eyeCatchingImage = null; if (ps.eyeCatchingImageId != null) { eyeCatchingImage = await DriveFiles.findOneBy({ id: ps.eyeCatchingImageId, - userId: user.id, + userId: me.id, }); if (eyeCatchingImage == null) { @@ -83,7 +78,7 @@ export default class extends Endpoint { } await Pages.findBy({ - userId: user.id, + userId: me.id, name: ps.name, }).then(result => { if (result.length > 0) { @@ -102,7 +97,7 @@ export default class extends Endpoint { variables: ps.variables, script: ps.script, eyeCatchingImageId: eyeCatchingImage ? eyeCatchingImage.id : null, - userId: user.id, + userId: me.id, visibility: 'public', alignCenter: ps.alignCenter, hideTitleWhenPinned: ps.hideTitleWhenPinned, diff --git a/packages/backend/src/server/api/endpoints/pages/delete.ts b/packages/backend/src/server/api/endpoints/pages/delete.ts index 059b980553bd..cd248f7ae961 100644 --- a/packages/backend/src/server/api/endpoints/pages/delete.ts +++ b/packages/backend/src/server/api/endpoints/pages/delete.ts @@ -37,18 +37,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const page = await Pages.findOneBy({ id: ps.pageId }); if (page == null) { throw new ApiError(meta.errors.noSuchPage); } - if (page.userId !== user.id) { + if (page.userId !== me.id) { throw new ApiError(meta.errors.accessDenied); } diff --git a/packages/backend/src/server/api/endpoints/pages/like.ts b/packages/backend/src/server/api/endpoints/pages/like.ts index 3362c771843c..60be55795490 100644 --- a/packages/backend/src/server/api/endpoints/pages/like.ts +++ b/packages/backend/src/server/api/endpoints/pages/like.ts @@ -44,26 +44,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const page = await Pages.findOneBy({ id: ps.pageId }); if (page == null) { throw new ApiError(meta.errors.noSuchPage); } - if (page.userId === user.id) { + if (page.userId === me.id) { throw new ApiError(meta.errors.yourPage); } // if already liked const exist = await PageLikes.findOneBy({ pageId: page.id, - userId: user.id, + userId: me.id, }); if (exist != null) { @@ -75,7 +70,7 @@ export default class extends Endpoint { id: genId(), createdAt: new Date(), pageId: page.id, - userId: user.id, + userId: me.id, }); Pages.increment({ id: page.id }, 'likedCount', 1); diff --git a/packages/backend/src/server/api/endpoints/pages/show.ts b/packages/backend/src/server/api/endpoints/pages/show.ts index ed8ab90a67c4..91da8933c808 100644 --- a/packages/backend/src/server/api/endpoints/pages/show.ts +++ b/packages/backend/src/server/api/endpoints/pages/show.ts @@ -1,6 +1,7 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { Pages, Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { Pages } from '@/models/index.js'; import type { Page } from '@/models/entities/page.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; @@ -50,11 +51,8 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { let page: Page | null = null; if (ps.pageId) { @@ -76,7 +74,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchPage); } - return await Pages.pack(page, user); + return await Pages.pack(page, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/pages/unlike.ts b/packages/backend/src/server/api/endpoints/pages/unlike.ts index 569efe38f275..7622641da2e1 100644 --- a/packages/backend/src/server/api/endpoints/pages/unlike.ts +++ b/packages/backend/src/server/api/endpoints/pages/unlike.ts @@ -37,13 +37,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const page = await Pages.findOneBy({ id: ps.pageId }); if (page == null) { throw new ApiError(meta.errors.noSuchPage); @@ -51,7 +46,7 @@ export default class extends Endpoint { const exist = await PageLikes.findOneBy({ pageId: page.id, - userId: user.id, + userId: me.id, }); if (exist == null) { diff --git a/packages/backend/src/server/api/endpoints/pages/update.ts b/packages/backend/src/server/api/endpoints/pages/update.ts index 4391854d7e89..d1d8cebf89c6 100644 --- a/packages/backend/src/server/api/endpoints/pages/update.ts +++ b/packages/backend/src/server/api/endpoints/pages/update.ts @@ -69,18 +69,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const page = await Pages.findOneBy({ id: ps.pageId }); if (page == null) { throw new ApiError(meta.errors.noSuchPage); } - if (page.userId !== user.id) { + if (page.userId !== me.id) { throw new ApiError(meta.errors.accessDenied); } @@ -88,7 +83,7 @@ export default class extends Endpoint { if (ps.eyeCatchingImageId != null) { eyeCatchingImage = await DriveFiles.findOneBy({ id: ps.eyeCatchingImageId, - userId: user.id, + userId: me.id, }); if (eyeCatchingImage == null) { @@ -98,7 +93,7 @@ export default class extends Endpoint { await Pages.findBy({ id: Not(ps.pageId), - userId: user.id, + userId: me.id, name: ps.name, }).then(result => { if (result.length > 0) { diff --git a/packages/backend/src/server/api/endpoints/promo/read.ts b/packages/backend/src/server/api/endpoints/promo/read.ts index 020ddeba5338..e98245564fc7 100644 --- a/packages/backend/src/server/api/endpoints/promo/read.ts +++ b/packages/backend/src/server/api/endpoints/promo/read.ts @@ -31,13 +31,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; @@ -45,7 +40,7 @@ export default class extends Endpoint { const exist = await PromoReads.findOneBy({ noteId: note.id, - userId: user.id, + userId: me.id, }); if (exist != null) { @@ -56,7 +51,7 @@ export default class extends Endpoint { id: genId(), createdAt: new Date(), noteId: note.id, - userId: user.id, + userId: me.id, }); }); } diff --git a/packages/backend/src/server/api/endpoints/request-reset-password.ts b/packages/backend/src/server/api/endpoints/request-reset-password.ts index 057e1e38e382..4dae4fc69d61 100644 --- a/packages/backend/src/server/api/endpoints/request-reset-password.ts +++ b/packages/backend/src/server/api/endpoints/request-reset-password.ts @@ -4,7 +4,8 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { publishMainStream } from '@/services/stream.js'; import config from '@/config/index.js'; -import { Users, UserProfiles, PasswordResetRequests } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { UserProfiles, PasswordResetRequests } from '@/models/index.js'; import { sendEmail } from '@/services/send-email.js'; import { genId } from '@/misc/gen-id.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -42,11 +43,8 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy({ usernameLower: ps.username.toLowerCase(), host: IsNull(), diff --git a/packages/backend/src/server/api/endpoints/reset-db.ts b/packages/backend/src/server/api/endpoints/reset-db.ts index d8cedc37bd8f..7b154b2443c5 100644 --- a/packages/backend/src/server/api/endpoints/reset-db.ts +++ b/packages/backend/src/server/api/endpoints/reset-db.ts @@ -25,13 +25,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { if (process.env.NODE_ENV !== 'test') throw 'NODE_ENV is not a test'; await resetDb(); diff --git a/packages/backend/src/server/api/endpoints/reset-password.ts b/packages/backend/src/server/api/endpoints/reset-password.ts index ed5c43375f87..0008efa2cadc 100644 --- a/packages/backend/src/server/api/endpoints/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/reset-password.ts @@ -31,13 +31,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const req = await PasswordResetRequests.findOneByOrFail({ token: ps.token, }); diff --git a/packages/backend/src/server/api/endpoints/sw/register.ts b/packages/backend/src/server/api/endpoints/sw/register.ts index db52b7601f6f..a13593351ac4 100644 --- a/packages/backend/src/server/api/endpoints/sw/register.ts +++ b/packages/backend/src/server/api/endpoints/sw/register.ts @@ -42,16 +42,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // if already subscribed const exist = await SwSubscriptions.findOneBy({ - userId: user.id, + userId: me.id, endpoint: ps.endpoint, auth: ps.auth, publickey: ps.publickey, @@ -69,7 +64,7 @@ export default class extends Endpoint { await SwSubscriptions.insert({ id: genId(), createdAt: new Date(), - userId: user.id, + userId: me.id, endpoint: ps.endpoint, auth: ps.auth, publickey: ps.publickey, diff --git a/packages/backend/src/server/api/endpoints/sw/unregister.ts b/packages/backend/src/server/api/endpoints/sw/unregister.ts index dbf5e23e049b..da0b9c044064 100644 --- a/packages/backend/src/server/api/endpoints/sw/unregister.ts +++ b/packages/backend/src/server/api/endpoints/sw/unregister.ts @@ -22,15 +22,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { await SwSubscriptions.delete({ - userId: user.id, + userId: me.id, endpoint: ps.endpoint, }); }); diff --git a/packages/backend/src/server/api/endpoints/username/available.ts b/packages/backend/src/server/api/endpoints/username/available.ts index acb825521c87..fcee55d27a8a 100644 --- a/packages/backend/src/server/api/endpoints/username/available.ts +++ b/packages/backend/src/server/api/endpoints/username/available.ts @@ -1,6 +1,6 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { Users, UsedUsernames } from '@/models/index.js'; +import { Users , UsedUsernames } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -23,7 +23,7 @@ export const meta = { export const paramDef = { type: 'object', properties: { - username: this.usersRepository.localUsernameSchema, + username: Users.localUsernameSchema, }, required: ['username'], } as const; @@ -34,11 +34,8 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Get exist const exist = await this.usersRepository.countBy({ host: IsNull(), diff --git a/packages/backend/src/server/api/endpoints/users/clips.ts b/packages/backend/src/server/api/endpoints/users/clips.ts index b44bc8f7f29b..2d5bba6c45ba 100644 --- a/packages/backend/src/server/api/endpoints/users/clips.ts +++ b/packages/backend/src/server/api/endpoints/users/clips.ts @@ -34,13 +34,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(Clips.createQueryBuilder('clip'), ps.sinceId, ps.untilId) .andWhere('clip.userId = :userId', { userId: ps.userId }) .andWhere('clip.isPublic = true'); diff --git a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts index 053ac6928ca1..4e5a9225308a 100644 --- a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts @@ -34,13 +34,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) .andWhere('post.userId = :userId', { userId: ps.userId }); @@ -48,7 +43,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return await GalleryPosts.packMany(posts, user); + return await GalleryPosts.packMany(posts, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/groups/create.ts b/packages/backend/src/server/api/endpoints/users/groups/create.ts index 4f7ce904ec33..127ea2f3f331 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/create.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/create.ts @@ -33,17 +33,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const userGroup = await UserGroups.insert({ id: genId(), createdAt: new Date(), - userId: user.id, + userId: me.id, name: ps.name, } as UserGroup).then(x => UserGroups.findOneByOrFail(x.identifiers[0])); @@ -51,7 +46,7 @@ export default class extends Endpoint { await UserGroupJoinings.insert({ id: genId(), createdAt: new Date(), - userId: user.id, + userId: me.id, userGroupId: userGroup.id, } as UserGroupJoining); diff --git a/packages/backend/src/server/api/endpoints/users/groups/delete.ts b/packages/backend/src/server/api/endpoints/users/groups/delete.ts index 5492946cb14c..00e2639443bb 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/delete.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/delete.ts @@ -33,16 +33,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const userGroup = await UserGroups.findOneBy({ id: ps.groupId, - userId: user.id, + userId: me.id, }); if (userGroup == null) { diff --git a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts b/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts index e0250b92f327..8e2843444a6b 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts @@ -35,13 +35,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Fetch the invitation const invitation = await UserGroupInvitations.findOneBy({ id: ps.invitationId, @@ -51,7 +46,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchInvitation); } - if (invitation.userId !== user.id) { + if (invitation.userId !== me.id) { throw new ApiError(meta.errors.noSuchInvitation); } @@ -59,7 +54,7 @@ export default class extends Endpoint { await UserGroupJoinings.insert({ id: genId(), createdAt: new Date(), - userId: user.id, + userId: me.id, userGroupId: invitation.userGroupId, } as UserGroupJoining); diff --git a/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts b/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts index ffd11e9ece64..dd714191a782 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts @@ -33,13 +33,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Fetch the invitation const invitation = await UserGroupInvitations.findOneBy({ id: ps.invitationId, @@ -49,7 +44,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchInvitation); } - if (invitation.userId !== user.id) { + if (invitation.userId !== me.id) { throw new ApiError(meta.errors.noSuchInvitation); } diff --git a/packages/backend/src/server/api/endpoints/users/lists/create.ts b/packages/backend/src/server/api/endpoints/users/lists/create.ts index 7d4f31f83c34..41013065af16 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts @@ -32,17 +32,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const userList = await UserLists.insert({ id: genId(), createdAt: new Date(), - userId: user.id, + userId: me.id, name: ps.name, } as UserList).then(x => UserLists.findOneByOrFail(x.identifiers[0])); diff --git a/packages/backend/src/server/api/endpoints/users/lists/delete.ts b/packages/backend/src/server/api/endpoints/users/lists/delete.ts index 08862f95a665..a4578516724f 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/delete.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/delete.ts @@ -33,16 +33,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const userList = await UserLists.findOneBy({ id: ps.listId, - userId: user.id, + userId: me.id, }); if (userList == null) { diff --git a/packages/backend/src/server/api/endpoints/users/lists/update.ts b/packages/backend/src/server/api/endpoints/users/lists/update.ts index 31b435bfc585..473132b1b688 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/update.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/update.ts @@ -40,17 +40,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { // Fetch the list const userList = await UserLists.findOneBy({ id: ps.listId, - userId: user.id, + userId: me.id, }); if (userList == null) { diff --git a/packages/backend/src/server/api/endpoints/users/pages.ts b/packages/backend/src/server/api/endpoints/users/pages.ts index b3c716cad77b..6179289d923a 100644 --- a/packages/backend/src/server/api/endpoints/users/pages.ts +++ b/packages/backend/src/server/api/endpoints/users/pages.ts @@ -34,13 +34,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { - super(meta, paramDef, async (ps, user) => { + super(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(Pages.createQueryBuilder('page'), ps.sinceId, ps.untilId) .andWhere('page.userId = :userId', { userId: ps.userId }) .andWhere('page.visibility = \'public\''); diff --git a/packages/backend/src/services/chart/ChartManagementService.ts b/packages/backend/src/services/chart/ChartManagementService.ts index 2020ca0ca6af..683ef8f930ba 100644 --- a/packages/backend/src/services/chart/ChartManagementService.ts +++ b/packages/backend/src/services/chart/ChartManagementService.ts @@ -1,18 +1,18 @@ import { Injectable, Inject } from '@nestjs/common'; import { beforeShutdown } from '@/misc/before-shutdown.js'; -import FederationChart from './charts/federation.js'; -import NotesChart from './charts/notes.js'; -import UsersChart from './charts/users.js'; -import ActiveUsersChart from './charts/active-users.js'; -import InstanceChart from './charts/instance.js'; -import PerUserNotesChart from './charts/per-user-notes.js'; -import DriveChart from './charts/drive.js'; -import PerUserReactionsChart from './charts/per-user-reactions.js'; -import HashtagChart from './charts/hashtag.js'; -import PerUserFollowingChart from './charts/per-user-following.js'; -import PerUserDriveChart from './charts/per-user-drive.js'; -import ApRequestChart from './charts/ap-request.js'; +import type FederationChart from './charts/federation.js'; +import type NotesChart from './charts/notes.js'; +import type UsersChart from './charts/users.js'; +import type ActiveUsersChart from './charts/active-users.js'; +import type InstanceChart from './charts/instance.js'; +import type PerUserNotesChart from './charts/per-user-notes.js'; +import type DriveChart from './charts/drive.js'; +import type PerUserReactionsChart from './charts/per-user-reactions.js'; +import type HashtagChart from './charts/hashtag.js'; +import type PerUserFollowingChart from './charts/per-user-following.js'; +import type PerUserDriveChart from './charts/per-user-drive.js'; +import type ApRequestChart from './charts/ap-request.js'; @Injectable() export class ChartManagementService { diff --git a/packages/backend/src/services/fooService.ts b/packages/backend/src/services/fooService.ts deleted file mode 100644 index 5936da40dccb..000000000000 --- a/packages/backend/src/services/fooService.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Container, Service } from 'typedi'; - -@Injectable() -export class FooService { - constructor( - ) {} - - public foo() { - console.log('foo'); - } -} diff --git a/packages/backend/src/services/index.ts b/packages/backend/src/services/index.ts deleted file mode 100644 index e4f8cc0fe110..000000000000 --- a/packages/backend/src/services/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Container, Service } from 'typedi'; -import { NoteCreateService } from './note/NoteCreateService.js'; - -export const noteCreateService = Container.get(NoteCreateService); diff --git a/packages/backend/src/services/webhookService.ts b/packages/backend/src/services/webhookService.ts deleted file mode 100644 index b8cf19000a9e..000000000000 --- a/packages/backend/src/services/webhookService.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Container, Service } from 'typedi'; -import { FooService } from './fooService.js'; - -@Injectable() -export class WebhookService { - constructor( - private fooService: FooService, - ) {} - - public deliver() { - this.fooService.foo(); - console.log('delivered'); - } -} diff --git a/packages/backend/test/tests/note.ts b/packages/backend/test/tests/note.ts index 71608173e5f2..7ec2fd9295d4 100644 --- a/packages/backend/test/tests/note.ts +++ b/packages/backend/test/tests/note.ts @@ -2,12 +2,12 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import { jest } from '@jest/globals'; -import { Container, Service } from 'typedi'; + import { initDb } from '@/db/postgre.js'; import { Notes } from '@/models/index.js'; import { FooService } from '@/services/fooService.js'; -import { WebhookService } from '../../src/services/webhookService.js'; import { NoteCreateService } from '../../src/services/note/NoteCreateService.js'; +import type { WebhookService } from '../../src/services/webhookService.js'; describe('NoteCreateService', () => { beforeAll(async () => { From 467e0fe65d241b1d1e133d4c2f95a2fb9dfbd86e Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 11 Sep 2022 22:35:47 +0900 Subject: [PATCH 014/180] wip --- .../src/services/CreateSystemUserService.ts | 79 +++++++++++++++++++ .../src/services/InstanceActorService.ts | 42 ++++++++++ .../src/services/create-system-user.ts | 68 ---------------- .../backend/src/services/instance-actor.ts | 28 ------- 4 files changed, 121 insertions(+), 96 deletions(-) create mode 100644 packages/backend/src/services/CreateSystemUserService.ts create mode 100644 packages/backend/src/services/InstanceActorService.ts delete mode 100644 packages/backend/src/services/create-system-user.ts delete mode 100644 packages/backend/src/services/instance-actor.ts diff --git a/packages/backend/src/services/CreateSystemUserService.ts b/packages/backend/src/services/CreateSystemUserService.ts new file mode 100644 index 000000000000..ade251c4d892 --- /dev/null +++ b/packages/backend/src/services/CreateSystemUserService.ts @@ -0,0 +1,79 @@ +import { Inject, Injectable } from '@nestjs/common'; + +import bcrypt from 'bcryptjs'; +import { v4 as uuid } from 'uuid'; +import { IsNull } from 'typeorm'; +import { genRsaKeyPair } from '@/misc/gen-key-pair.js'; +import { User } from '@/models/entities/user.js'; +import { UserProfile } from '@/models/entities/user-profile.js'; +import { genId } from '@/misc/gen-id.js'; +import { UserKeypair } from '@/models/entities/user-keypair.js'; +import { UsedUsername } from '@/models/entities/used-username.js'; +import generateNativeUserToken from '../server/api/common/generate-native-user-token.js'; +import type { DataSource } from 'typeorm'; + +@Injectable() +export class CreateSystemUserService { + constructor( + @Inject('db') + private db: DataSource, + ) { + } + + public async createSystemUser(username: string): Promise { + const password = uuid(); + + // Generate hash of password + const salt = await bcrypt.genSalt(8); + const hash = await bcrypt.hash(password, salt); + + // Generate secret + const secret = generateNativeUserToken(); + + const keyPair = await genRsaKeyPair(4096); + + let account!: User; + + // Start transaction + await this.db.transaction(async transactionalEntityManager => { + const exist = await transactionalEntityManager.findOneBy(User, { + usernameLower: username.toLowerCase(), + host: IsNull(), + }); + + if (exist) throw new Error('the user is already exists'); + + account = await transactionalEntityManager.insert(User, { + id: genId(), + createdAt: new Date(), + username: username, + usernameLower: username.toLowerCase(), + host: null, + token: secret, + isAdmin: false, + isLocked: true, + isExplorable: false, + isBot: true, + }).then(x => transactionalEntityManager.findOneByOrFail(User, x.identifiers[0])); + + await transactionalEntityManager.insert(UserKeypair, { + publicKey: keyPair.publicKey, + privateKey: keyPair.privateKey, + userId: account.id, + }); + + await transactionalEntityManager.insert(UserProfile, { + userId: account.id, + autoAcceptFollowed: false, + password: hash, + }); + + await transactionalEntityManager.insert(UsedUsername, { + createdAt: new Date(), + username: username.toLowerCase(), + }); + }); + + return account; + } +} diff --git a/packages/backend/src/services/InstanceActorService.ts b/packages/backend/src/services/InstanceActorService.ts new file mode 100644 index 000000000000..da64be3c2c0b --- /dev/null +++ b/packages/backend/src/services/InstanceActorService.ts @@ -0,0 +1,42 @@ +import { Inject, Injectable } from '@nestjs/common'; + +import { IsNull } from 'typeorm'; +import type { ILocalUser } from '@/models/entities/user.js'; +import type { Users } from '@/models/index.js'; +import { Cache } from '@/misc/cache.js'; +import type { CreateSystemUserService } from './CreateSystemUserService.js'; + +const ACTOR_USERNAME = 'instance.actor' as const; + +@Injectable() +export class InstanceActorService { + #cache: Cache; + + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + private createSystemUserService: CreateSystemUserService, + ) { + this.#cache = new Cache(Infinity); + } + + public async getInstanceActor(): Promise { + const cached = this.#cache.get(null); + if (cached) return cached; + + const user = await this.usersRepository.findOneBy({ + host: IsNull(), + username: ACTOR_USERNAME, + }) as ILocalUser | undefined; + + if (user) { + this.#cache.set(null, user); + return user; + } else { + const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME) as ILocalUser; + this.#cache.set(null, created); + return created; + } + } +} diff --git a/packages/backend/src/services/create-system-user.ts b/packages/backend/src/services/create-system-user.ts deleted file mode 100644 index bae91ec4c3fe..000000000000 --- a/packages/backend/src/services/create-system-user.ts +++ /dev/null @@ -1,68 +0,0 @@ -import bcrypt from 'bcryptjs'; -import { v4 as uuid } from 'uuid'; -import generateNativeUserToken from '../server/api/common/generate-native-user-token.js'; -import { genRsaKeyPair } from '@/misc/gen-key-pair.js'; -import { User } from '@/models/entities/user.js'; -import { UserProfile } from '@/models/entities/user-profile.js'; -import { IsNull } from 'typeorm'; -import { genId } from '@/misc/gen-id.js'; -import { UserKeypair } from '@/models/entities/user-keypair.js'; -import { UsedUsername } from '@/models/entities/used-username.js'; -import { db } from '@/db/postgre.js'; - -export async function createSystemUser(username: string) { - const password = uuid(); - - // Generate hash of password - const salt = await bcrypt.genSalt(8); - const hash = await bcrypt.hash(password, salt); - - // Generate secret - const secret = generateNativeUserToken(); - - const keyPair = await genRsaKeyPair(4096); - - let account!: User; - - // Start transaction - await db.transaction(async transactionalEntityManager => { - const exist = await transactionalEntityManager.findOneBy(User, { - usernameLower: username.toLowerCase(), - host: IsNull(), - }); - - if (exist) throw new Error('the user is already exists'); - - account = await transactionalEntityManager.insert(User, { - id: genId(), - createdAt: new Date(), - username: username, - usernameLower: username.toLowerCase(), - host: null, - token: secret, - isAdmin: false, - isLocked: true, - isExplorable: false, - isBot: true, - }).then(x => transactionalEntityManager.findOneByOrFail(User, x.identifiers[0])); - - await transactionalEntityManager.insert(UserKeypair, { - publicKey: keyPair.publicKey, - privateKey: keyPair.privateKey, - userId: account.id, - }); - - await transactionalEntityManager.insert(UserProfile, { - userId: account.id, - autoAcceptFollowed: false, - password: hash, - }); - - await transactionalEntityManager.insert(UsedUsername, { - createdAt: new Date(), - username: username.toLowerCase(), - }); - }); - - return account; -} diff --git a/packages/backend/src/services/instance-actor.ts b/packages/backend/src/services/instance-actor.ts deleted file mode 100644 index bddd0355a239..000000000000 --- a/packages/backend/src/services/instance-actor.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { createSystemUser } from './create-system-user.js'; -import { ILocalUser } from '@/models/entities/user.js'; -import { Users } from '@/models/index.js'; -import { Cache } from '@/misc/cache.js'; -import { IsNull } from 'typeorm'; - -const ACTOR_USERNAME = 'instance.actor' as const; - -const cache = new Cache(Infinity); - -export async function getInstanceActor(): Promise { - const cached = cache.get(null); - if (cached) return cached; - - const user = await Users.findOneBy({ - host: IsNull(), - username: ACTOR_USERNAME, - }) as ILocalUser | undefined; - - if (user) { - cache.set(null, user); - return user; - } else { - const created = await createSystemUser(ACTOR_USERNAME) as ILocalUser; - cache.set(null, created); - return created; - } -} From 8cc44cd30e9b425f7cc3aa4d38e341bf029b4754 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 11 Sep 2022 23:52:38 +0900 Subject: [PATCH 015/180] Update resolver.ts --- .../src/remote/activitypub/resolver.ts | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/packages/backend/src/remote/activitypub/resolver.ts b/packages/backend/src/remote/activitypub/resolver.ts index 2f9af43c0cb0..dc134e52576b 100644 --- a/packages/backend/src/remote/activitypub/resolver.ts +++ b/packages/backend/src/remote/activitypub/resolver.ts @@ -1,13 +1,10 @@ import config from '@/config/index.js'; import { getJson } from '@/misc/fetch.js'; -import { ILocalUser } from '@/models/entities/user.js'; -import { getInstanceActor } from '@/services/instance-actor.js'; +import type { ILocalUser } from '@/models/entities/user.js'; +import type { InstanceActorService } from '@/services/InstanceActorService.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { extractDbHost, isSelfHost } from '@/misc/convert-host.js'; -import { signedGet } from './request.js'; -import { IObject, isCollectionOrOrderedCollection, ICollection, IOrderedCollection } from './type.js'; import { FollowRequests, Notes, NoteReactions, Polls, Users } from '@/models/index.js'; -import { parseUri } from './db-resolver.js'; import renderNote from '@/remote/activitypub/renderer/note.js'; import { renderLike } from '@/remote/activitypub/renderer/like.js'; import { renderPerson } from '@/remote/activitypub/renderer/person.js'; @@ -15,12 +12,18 @@ import renderQuestion from '@/remote/activitypub/renderer/question.js'; import renderCreate from '@/remote/activitypub/renderer/create.js'; import { renderActivity } from '@/remote/activitypub/renderer/index.js'; import renderFollow from '@/remote/activitypub/renderer/follow.js'; +import { isCollectionOrOrderedCollection } from './type.js'; +import { parseUri } from './db-resolver.js'; +import { signedGet } from './request.js'; +import type { IObject, ICollection, IOrderedCollection } from './type.js'; export default class Resolver { private history: Set; private user?: ILocalUser; - constructor() { + constructor( + private instanceActorService: InstanceActorService, + ) { this.history = new Set(); } @@ -73,7 +76,7 @@ export default class Resolver { } if (config.signToActivityPubGet && !this.user) { - this.user = await getInstanceActor(); + this.user = await this.instanceActorService.getInstanceActor(); } const object = (this.user @@ -98,24 +101,24 @@ export default class Resolver { switch (parsed.type) { case 'notes': return Notes.findOneByOrFail({ id: parsed.id }) - .then(note => { - if (parsed.rest === 'activity') { + .then(note => { + if (parsed.rest === 'activity') { // this refers to the create activity and not the note itself - return renderActivity(renderCreate(renderNote(note))); - } else { - return renderNote(note); - } - }); + return renderActivity(renderCreate(renderNote(note))); + } else { + return renderNote(note); + } + }); case 'users': return Users.findOneByOrFail({ id: parsed.id }) - .then(user => renderPerson(user as ILocalUser)); + .then(user => renderPerson(user as ILocalUser)); case 'questions': // Polls are indexed by the note they are attached to. return Promise.all([ Notes.findOneByOrFail({ id: parsed.id }), Polls.findOneByOrFail({ noteId: parsed.id }), ]) - .then(([note, poll]) => renderQuestion({ id: note.userId }, note, poll)); + .then(([note, poll]) => renderQuestion({ id: note.userId }, note, poll)); case 'likes': return NoteReactions.findOneByOrFail({ id: parsed.id }).then(reaction => renderActivity(renderLike(reaction, { uri: null }))); case 'follows': @@ -123,9 +126,9 @@ export default class Resolver { if (parsed.rest == null || !/^\w+$/.test(parsed.rest)) throw new Error('resolveLocal: invalid follow URI'); return Promise.all( - [parsed.id, parsed.rest].map(id => Users.findOneByOrFail({ id })) + [parsed.id, parsed.rest].map(id => Users.findOneByOrFail({ id })), ) - .then(([follower, followee]) => renderActivity(renderFollow(follower, followee, url))); + .then(([follower, followee]) => renderActivity(renderFollow(follower, followee, url))); default: throw new Error(`resolveLocal: type ${type} unhandled`); } From a8be7e6fd3a201d659c9550d4f6582ae16484585 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 00:06:30 +0900 Subject: [PATCH 016/180] wip --- .../src/remote/activitypub/renderer/flag.ts | 4 +- .../admin/resolve-abuse-user-report.ts | 18 +-- packages/backend/src/services/RelayService.ts | 118 ++++++++++++++++++ packages/backend/src/services/relay.ts | 101 --------------- 4 files changed, 130 insertions(+), 111 deletions(-) create mode 100644 packages/backend/src/services/RelayService.ts delete mode 100644 packages/backend/src/services/relay.ts diff --git a/packages/backend/src/remote/activitypub/renderer/flag.ts b/packages/backend/src/remote/activitypub/renderer/flag.ts index 58eadddbaa6e..91a65c1db0af 100644 --- a/packages/backend/src/remote/activitypub/renderer/flag.ts +++ b/packages/backend/src/remote/activitypub/renderer/flag.ts @@ -1,7 +1,7 @@ import config from '@/config/index.js'; import { IObject, IActivity } from '@/remote/activitypub/type.js'; -import { ILocalUser, IRemoteUser } from '@/models/entities/user.js'; -import { getInstanceActor } from '@/services/instance-actor.js'; +import type { ILocalUser } from '@/models/entities/user.js'; +import { IRemoteUser } from '@/models/entities/user.js'; // to anonymise reporters, the reporting actor must be a system user // object has to be a uri or array of uris diff --git a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts index bd31a3b8f829..9740c6b62766 100644 --- a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts +++ b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts @@ -1,10 +1,11 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { AbuseUserReports, Users } from '@/models/index.js'; -import { getInstanceActor } from '@/services/instance-actor.js'; -import { deliver } from '@/queue/index.js'; +import type { Users } from '@/models/index.js'; +import { AbuseUserReports } from '@/models/index.js'; import { renderActivity } from '@/remote/activitypub/renderer/index.js'; import { renderFlag } from '@/remote/activitypub/renderer/flag.js'; +import type { InstanceActorService } from '@/services/InstanceActorService'; +import type { QueueService } from '@/queue/queue.service'; export const meta = { tags: ['admin'], @@ -29,21 +30,22 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private queueService: QueueService, + + private instanceActorService: InstanceActorService, ) { super(meta, paramDef, async (ps, me) => { - const report = await AbuseUserReports.findOneByOrFail({ id: ps.reportId }); + const report = await AbuseUserReports.findOneBy({ id: ps.reportId }); if (report == null) { throw new Error('report not found'); } if (ps.forward && report.targetUserHost != null) { - const actor = await getInstanceActor(); + const actor = await this.instanceActorService.getInstanceActor(); const targetUser = await this.usersRepository.findOneByOrFail({ id: report.targetUserId }); - deliver(actor, renderActivity(renderFlag(actor, [targetUser.uri!], report.comment)), targetUser.inbox); + this.queueService.deliver(actor, renderActivity(renderFlag(actor, [targetUser.uri!], report.comment)), targetUser.inbox); } await AbuseUserReports.update(report.id, { diff --git a/packages/backend/src/services/RelayService.ts b/packages/backend/src/services/RelayService.ts new file mode 100644 index 000000000000..ad639025ad63 --- /dev/null +++ b/packages/backend/src/services/RelayService.ts @@ -0,0 +1,118 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { IsNull } from 'typeorm'; +import { renderFollowRelay } from '@/remote/activitypub/renderer/follow-relay.js'; +import { renderActivity, attachLdSignature } from '@/remote/activitypub/renderer/index.js'; +import renderUndo from '@/remote/activitypub/renderer/undo.js'; +import type { ILocalUser, User } from '@/models/entities/user.js'; +import type { Relays, Users } from '@/models/index.js'; +import { genId } from '@/misc/gen-id.js'; +import { Cache } from '@/misc/cache.js'; +import type { Relay } from '@/models/entities/relay.js'; +import type { QueueService } from '@/queue/queue.service.js'; +import type { CreateSystemUserService } from './CreateSystemUserService.js'; + +const ACTOR_USERNAME = 'relay.actor' as const; + +@Injectable() +export class RelayService { + #relaysCache: Cache; + + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('relaysRepository') + private relaysRepository: typeof Relays, + + private queueService: QueueService, + private createSystemUserService: CreateSystemUserService, + ) { + this.#relaysCache = new Cache(1000 * 60 * 10); + } + + async getRelayActor(): Promise { + const user = await this.usersRepository.findOneBy({ + host: IsNull(), + username: ACTOR_USERNAME, + }); + + if (user) return user as ILocalUser; + + const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME); + return created as ILocalUser; + } + + async addRelay(inbox: string): Promise { + const relay = await this.relaysRepository.insert({ + id: genId(), + inbox, + status: 'requesting', + }).then(x => this.relaysRepository.findOneByOrFail(x.identifiers[0])); + + const relayActor = await this.getRelayActor(); + const follow = await renderFollowRelay(relay, relayActor); + const activity = renderActivity(follow); + this.queueService.deliver(relayActor, activity, relay.inbox); + + return relay; + } + + async removeRelay(inbox: string): Promise { + const relay = await this.relaysRepository.findOneBy({ + inbox, + }); + + if (relay == null) { + throw 'relay not found'; + } + + const relayActor = await this.getRelayActor(); + const follow = renderFollowRelay(relay, relayActor); + const undo = renderUndo(follow, relayActor); + const activity = renderActivity(undo); + this.queueService.deliver(relayActor, activity, relay.inbox); + + await this.relaysRepository.delete(relay.id); + } + + async listRelay(): Promise { + const relays = await this.relaysRepository.find(); + return relays; + } + + async relayAccepted(id: string): Promise { + const result = await this.relaysRepository.update(id, { + status: 'accepted', + }); + + return JSON.stringify(result); + } + + async relayRejected(id: string): Promise { + const result = await this.relaysRepository.update(id, { + status: 'rejected', + }); + + return JSON.stringify(result); + } + + async deliverToRelays(user: { id: User['id']; host: null; }, activity: any): Promise { + if (activity == null) return; + + const relays = await this.#relaysCache.fetch(null, () => this.relaysRepository.findBy({ + status: 'accepted', + })); + if (relays.length === 0) return; + + // TODO + //const copy = structuredClone(activity); + const copy = JSON.parse(JSON.stringify(activity)); + if (!copy.to) copy.to = ['https://www.w3.org/ns/activitystreams#Public']; + + const signed = await attachLdSignature(copy, user); + + for (const relay of relays) { + this.queueService.deliver(user, signed, relay.inbox); + } + } +} diff --git a/packages/backend/src/services/relay.ts b/packages/backend/src/services/relay.ts deleted file mode 100644 index 6bc4304436d1..000000000000 --- a/packages/backend/src/services/relay.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { IsNull } from 'typeorm'; -import { renderFollowRelay } from '@/remote/activitypub/renderer/follow-relay.js'; -import { renderActivity, attachLdSignature } from '@/remote/activitypub/renderer/index.js'; -import renderUndo from '@/remote/activitypub/renderer/undo.js'; -import { deliver } from '@/queue/index.js'; -import { ILocalUser, User } from '@/models/entities/user.js'; -import { Users, Relays } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; -import { Cache } from '@/misc/cache.js'; -import { Relay } from '@/models/entities/relay.js'; -import { createSystemUser } from './create-system-user.js'; - -const ACTOR_USERNAME = 'relay.actor' as const; - -const relaysCache = new Cache(1000 * 60 * 10); - -export async function getRelayActor(): Promise { - const user = await Users.findOneBy({ - host: IsNull(), - username: ACTOR_USERNAME, - }); - - if (user) return user as ILocalUser; - - const created = await createSystemUser(ACTOR_USERNAME); - return created as ILocalUser; -} - -export async function addRelay(inbox: string) { - const relay = await Relays.insert({ - id: genId(), - inbox, - status: 'requesting', - }).then(x => Relays.findOneByOrFail(x.identifiers[0])); - - const relayActor = await getRelayActor(); - const follow = await renderFollowRelay(relay, relayActor); - const activity = renderActivity(follow); - deliver(relayActor, activity, relay.inbox); - - return relay; -} - -export async function removeRelay(inbox: string) { - const relay = await Relays.findOneBy({ - inbox, - }); - - if (relay == null) { - throw 'relay not found'; - } - - const relayActor = await getRelayActor(); - const follow = renderFollowRelay(relay, relayActor); - const undo = renderUndo(follow, relayActor); - const activity = renderActivity(undo); - deliver(relayActor, activity, relay.inbox); - - await Relays.delete(relay.id); -} - -export async function listRelay() { - const relays = await Relays.find(); - return relays; -} - -export async function relayAccepted(id: string) { - const result = await Relays.update(id, { - status: 'accepted', - }); - - return JSON.stringify(result); -} - -export async function relayRejected(id: string) { - const result = await Relays.update(id, { - status: 'rejected', - }); - - return JSON.stringify(result); -} - -export async function deliverToRelays(user: { id: User['id']; host: null; }, activity: any) { - if (activity == null) return; - - const relays = await relaysCache.fetch(null, () => Relays.findBy({ - status: 'accepted', - })); - if (relays.length === 0) return; - - // TODO - //const copy = structuredClone(activity); - const copy = JSON.parse(JSON.stringify(activity)); - if (!copy.to) copy.to = ['https://www.w3.org/ns/activitystreams#Public']; - - const signed = await attachLdSignature(copy, user); - - for (const relay of relays) { - deliver(user, signed, relay.inbox); - } -} From 5c5681702872d6695c0b92165d3a008f1831a7e7 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 00:57:10 +0900 Subject: [PATCH 017/180] wip --- packages/backend/src/misc/app-lock.ts | 31 -- .../backend/src/services/AppLockService.ts | 40 +++ .../services/FetchInstanceMetadataService.ts | 281 ++++++++++++++++++ packages/backend/src/services/RelayService.ts | 6 +- .../src/services/fetch-instance-metadata.ts | 268 ----------------- 5 files changed, 324 insertions(+), 302 deletions(-) delete mode 100644 packages/backend/src/misc/app-lock.ts create mode 100644 packages/backend/src/services/AppLockService.ts create mode 100644 packages/backend/src/services/FetchInstanceMetadataService.ts delete mode 100644 packages/backend/src/services/fetch-instance-metadata.ts diff --git a/packages/backend/src/misc/app-lock.ts b/packages/backend/src/misc/app-lock.ts deleted file mode 100644 index b5089cc6a6ec..000000000000 --- a/packages/backend/src/misc/app-lock.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { redisClient } from '../db/redis.js'; -import { promisify } from 'node:util'; -import redisLock from 'redis-lock'; - -/** - * Retry delay (ms) for lock acquisition - */ -const retryDelay = 100; - -const lock: (key: string, timeout?: number) => Promise<() => void> - = redisClient - ? promisify(redisLock(redisClient, retryDelay)) - : async () => () => { }; - -/** - * Get AP Object lock - * @param uri AP object ID - * @param timeout Lock timeout (ms), The timeout releases previous lock. - * @returns Unlock function - */ -export function getApLock(uri: string, timeout = 30 * 1000) { - return lock(`ap-object:${uri}`, timeout); -} - -export function getFetchInstanceMetadataLock(host: string, timeout = 30 * 1000) { - return lock(`instance:${host}`, timeout); -} - -export function getChartInsertLock(lockKey: string, timeout = 30 * 1000) { - return lock(`chart-insert:${lockKey}`, timeout); -} diff --git a/packages/backend/src/services/AppLockService.ts b/packages/backend/src/services/AppLockService.ts new file mode 100644 index 000000000000..50dce1132d4e --- /dev/null +++ b/packages/backend/src/services/AppLockService.ts @@ -0,0 +1,40 @@ +import { promisify } from 'node:util'; +import { Inject, Injectable } from '@nestjs/common'; +import redisLock from 'redis-lock'; +import type Redis from 'ioredis'; + +/** + * Retry delay (ms) for lock acquisition + */ +const retryDelay = 100; + +@Injectable() +export class AppLockService { + #lock: (key: string, timeout?: number) => Promise<() => void>; + + constructor( + @Inject('redis') + private redisClient: Redis.Redis, + ) { + console.warn(); + this.#lock = promisify(redisLock(this.redisClient, retryDelay)); + } + + /** + * Get AP Object lock + * @param uri AP object ID + * @param timeout Lock timeout (ms), The timeout releases previous lock. + * @returns Unlock function + */ + public getApLock(uri: string, timeout = 30 * 1000): Promise<() => void> { + return this.#lock(`ap-object:${uri}`, timeout); + } + + public getFetchInstanceMetadataLock(host: string, timeout = 30 * 1000): Promise<() => void> { + return this.#lock(`instance:${host}`, timeout); + } + + public getChartInsertLock(lockKey: string, timeout = 30 * 1000): Promise<() => void> { + return this.#lock(`chart-insert:${lockKey}`, timeout); + } +} diff --git a/packages/backend/src/services/FetchInstanceMetadataService.ts b/packages/backend/src/services/FetchInstanceMetadataService.ts new file mode 100644 index 000000000000..2ade526a3aa0 --- /dev/null +++ b/packages/backend/src/services/FetchInstanceMetadataService.ts @@ -0,0 +1,281 @@ +import { URL } from 'node:url'; +import { Inject, Injectable } from '@nestjs/common'; +import { JSDOM } from 'jsdom'; +import fetch from 'node-fetch'; +import tinycolor from 'tinycolor2'; +import { getJson, getHtml, getAgentByUrl } from '@/misc/fetch.js'; +import type { Instance } from '@/models/entities/instance.js'; +import type { Instances } from '@/models/index.js'; +import type { AppLockService } from '@/services/AppLockService.js'; +import Logger from './logger.js'; +import type { DOMWindow } from 'jsdom'; + +const logger = new Logger('metadata', 'cyan'); + +type NodeInfo = { + openRegistrations?: any; + software?: { + name?: any; + version?: any; + }; + metadata?: { + name?: any; + nodeName?: any; + nodeDescription?: any; + description?: any; + maintainer?: { + name?: any; + email?: any; + }; + }; +}; + +@Injectable() +export class FetchInstanceMetadataService { + constructor( + @Inject('instancesRepository') + private instancesRepository: typeof Instances, + + private appLockService: AppLockService, + ) { + } + + public async fetchInstanceMetadata(instance: Instance, force = false): Promise { + const unlock = await this.appLockService.getFetchInstanceMetadataLock(instance.host); + + if (!force) { + const _instance = await this.instancesRepository.findOneBy({ host: instance.host }); + const now = Date.now(); + if (_instance && _instance.infoUpdatedAt && (now - _instance.infoUpdatedAt.getTime() < 1000 * 60 * 60 * 24)) { + unlock(); + return; + } + } + + logger.info(`Fetching metadata of ${instance.host} ...`); + + try { + const [info, dom, manifest] = await Promise.all([ + this.#fetchNodeinfo(instance).catch(() => null), + this.#fetchDom(instance).catch(() => null), + this.#fetchManifest(instance).catch(() => null), + ]); + + const [favicon, icon, themeColor, name, description] = await Promise.all([ + this.#fetchFaviconUrl(instance, dom).catch(() => null), + this.#fetchIconUrl(instance, dom, manifest).catch(() => null), + this.#getThemeColor(info, dom, manifest).catch(() => null), + this.#getSiteName(info, dom, manifest).catch(() => null), + this.#getDescription(info, dom, manifest).catch(() => null), + ]); + + logger.succ(`Successfuly fetched metadata of ${instance.host}`); + + const updates = { + infoUpdatedAt: new Date(), + } as Record; + + if (info) { + updates.softwareName = info.software?.name.toLowerCase(); + updates.softwareVersion = info.software?.version; + updates.openRegistrations = info.openRegistrations; + updates.maintainerName = info.metadata ? info.metadata.maintainer ? (info.metadata.maintainer.name || null) : null : null; + updates.maintainerEmail = info.metadata ? info.metadata.maintainer ? (info.metadata.maintainer.email || null) : null : null; + } + + if (name) updates.name = name; + if (description) updates.description = description; + if (icon || favicon) updates.iconUrl = icon || favicon; + if (favicon) updates.faviconUrl = favicon; + if (themeColor) updates.themeColor = themeColor; + + await this.instancesRepository.update(instance.id, updates); + + logger.succ(`Successfuly updated metadata of ${instance.host}`); + } catch (e) { + logger.error(`Failed to update metadata of ${instance.host}: ${e}`); + } finally { + unlock(); + } + } + + async #fetchNodeinfo(instance: Instance): Promise { + logger.info(`Fetching nodeinfo of ${instance.host} ...`); + + try { + const wellknown = await getJson('https://' + instance.host + '/.well-known/nodeinfo') + .catch(e => { + if (e.statusCode === 404) { + throw 'No nodeinfo provided'; + } else { + throw e.statusCode || e.message; + } + }) as Record; + + if (wellknown.links == null || !Array.isArray(wellknown.links)) { + throw 'No wellknown links'; + } + + const links = wellknown.links as any[]; + + const lnik1_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/1.0'); + const lnik2_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.0'); + const lnik2_1 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.1'); + const link = lnik2_1 || lnik2_0 || lnik1_0; + + if (link == null) { + throw 'No nodeinfo link provided'; + } + + const info = await getJson(link.href) + .catch(e => { + throw e.statusCode || e.message; + }); + + logger.succ(`Successfuly fetched nodeinfo of ${instance.host}`); + + return info as NodeInfo; + } catch (e) { + logger.error(`Failed to fetch nodeinfo of ${instance.host}: ${e}`); + + throw e; + } + } + + async #fetchDom(instance: Instance): Promise { + logger.info(`Fetching HTML of ${instance.host} ...`); + + const url = 'https://' + instance.host; + + const html = await getHtml(url); + + const { window } = new JSDOM(html); + const doc = window.document; + + return doc; + } + + async #fetchManifest(instance: Instance): Promise | null> { + const url = 'https://' + instance.host; + + const manifestUrl = url + '/manifest.json'; + + const manifest = await getJson(manifestUrl) as Record; + + return manifest; + } + + async #fetchFaviconUrl(instance: Instance, doc: DOMWindow['document'] | null): Promise { + const url = 'https://' + instance.host; + + if (doc) { + // https://github.com/misskey-dev/misskey/pull/8220#issuecomment-1025104043 + const href = Array.from(doc.getElementsByTagName('link')).reverse().find(link => link.relList.contains('icon'))?.href; + + if (href) { + return (new URL(href, url)).href; + } + } + + const faviconUrl = url + '/favicon.ico'; + + const favicon = await fetch(faviconUrl, { + // TODO + //timeout: 10000, + agent: getAgentByUrl, + }); + + if (favicon.ok) { + return faviconUrl; + } + + return null; + } + + async #fetchIconUrl(instance: Instance, doc: DOMWindow['document'] | null, manifest: Record | null): Promise { + if (manifest && manifest.icons && manifest.icons.length > 0 && manifest.icons[0].src) { + const url = 'https://' + instance.host; + return (new URL(manifest.icons[0].src, url)).href; + } + + if (doc) { + const url = 'https://' + instance.host; + + // https://github.com/misskey-dev/misskey/pull/8220#issuecomment-1025104043 + const links = Array.from(doc.getElementsByTagName('link')).reverse(); + // https://github.com/misskey-dev/misskey/pull/8220/files/0ec4eba22a914e31b86874f12448f88b3e58dd5a#r796487559 + const href = + [ + links.find(link => link.relList.contains('apple-touch-icon-precomposed'))?.href, + links.find(link => link.relList.contains('apple-touch-icon'))?.href, + links.find(link => link.relList.contains('icon'))?.href, + ] + .find(href => href); + + if (href) { + return (new URL(href, url)).href; + } + } + + return null; + } + + async #getThemeColor(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record | null): Promise { + const themeColor = info?.metadata?.themeColor || doc?.querySelector('meta[name="theme-color"]')?.getAttribute('content') || manifest?.theme_color; + + if (themeColor) { + const color = new tinycolor(themeColor); + if (color.isValid()) return color.toHexString(); + } + + return null; + } + + async #getSiteName(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record | null): Promise { + if (info && info.metadata) { + if (info.metadata.nodeName || info.metadata.name) { + return info.metadata.nodeName || info.metadata.name; + } + } + + if (doc) { + const og = doc.querySelector('meta[property="og:title"]')?.getAttribute('content'); + + if (og) { + return og; + } + } + + if (manifest) { + return manifest.name || manifest.short_name; + } + + return null; + } + + async #getDescription(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record | null): Promise { + if (info && info.metadata) { + if (info.metadata.nodeDescription || info.metadata.description) { + return info.metadata.nodeDescription || info.metadata.description; + } + } + + if (doc) { + const meta = doc.querySelector('meta[name="description"]')?.getAttribute('content'); + if (meta) { + return meta; + } + + const og = doc.querySelector('meta[property="og:description"]')?.getAttribute('content'); + if (og) { + return og; + } + } + + if (manifest) { + return manifest.name || manifest.short_name; + } + + return null; + } +} diff --git a/packages/backend/src/services/RelayService.ts b/packages/backend/src/services/RelayService.ts index ad639025ad63..1b66a834d15b 100644 --- a/packages/backend/src/services/RelayService.ts +++ b/packages/backend/src/services/RelayService.ts @@ -30,7 +30,7 @@ export class RelayService { this.#relaysCache = new Cache(1000 * 60 * 10); } - async getRelayActor(): Promise { + async #getRelayActor(): Promise { const user = await this.usersRepository.findOneBy({ host: IsNull(), username: ACTOR_USERNAME, @@ -49,7 +49,7 @@ export class RelayService { status: 'requesting', }).then(x => this.relaysRepository.findOneByOrFail(x.identifiers[0])); - const relayActor = await this.getRelayActor(); + const relayActor = await this.#getRelayActor(); const follow = await renderFollowRelay(relay, relayActor); const activity = renderActivity(follow); this.queueService.deliver(relayActor, activity, relay.inbox); @@ -66,7 +66,7 @@ export class RelayService { throw 'relay not found'; } - const relayActor = await this.getRelayActor(); + const relayActor = await this.#getRelayActor(); const follow = renderFollowRelay(relay, relayActor); const undo = renderUndo(follow, relayActor); const activity = renderActivity(undo); diff --git a/packages/backend/src/services/fetch-instance-metadata.ts b/packages/backend/src/services/fetch-instance-metadata.ts deleted file mode 100644 index ee1245132a96..000000000000 --- a/packages/backend/src/services/fetch-instance-metadata.ts +++ /dev/null @@ -1,268 +0,0 @@ -import { DOMWindow, JSDOM } from 'jsdom'; -import fetch from 'node-fetch'; -import tinycolor from 'tinycolor2'; -import { getJson, getHtml, getAgentByUrl } from '@/misc/fetch.js'; -import { Instance } from '@/models/entities/instance.js'; -import { Instances } from '@/models/index.js'; -import { getFetchInstanceMetadataLock } from '@/misc/app-lock.js'; -import Logger from './logger.js'; -import { URL } from 'node:url'; - -const logger = new Logger('metadata', 'cyan'); - -export async function fetchInstanceMetadata(instance: Instance, force = false): Promise { - const unlock = await getFetchInstanceMetadataLock(instance.host); - - if (!force) { - const _instance = await Instances.findOneBy({ host: instance.host }); - const now = Date.now(); - if (_instance && _instance.infoUpdatedAt && (now - _instance.infoUpdatedAt.getTime() < 1000 * 60 * 60 * 24)) { - unlock(); - return; - } - } - - logger.info(`Fetching metadata of ${instance.host} ...`); - - try { - const [info, dom, manifest] = await Promise.all([ - fetchNodeinfo(instance).catch(() => null), - fetchDom(instance).catch(() => null), - fetchManifest(instance).catch(() => null), - ]); - - const [favicon, icon, themeColor, name, description] = await Promise.all([ - fetchFaviconUrl(instance, dom).catch(() => null), - fetchIconUrl(instance, dom, manifest).catch(() => null), - getThemeColor(info, dom, manifest).catch(() => null), - getSiteName(info, dom, manifest).catch(() => null), - getDescription(info, dom, manifest).catch(() => null), - ]); - - logger.succ(`Successfuly fetched metadata of ${instance.host}`); - - const updates = { - infoUpdatedAt: new Date(), - } as Record; - - if (info) { - updates.softwareName = info.software?.name.toLowerCase(); - updates.softwareVersion = info.software?.version; - updates.openRegistrations = info.openRegistrations; - updates.maintainerName = info.metadata ? info.metadata.maintainer ? (info.metadata.maintainer.name || null) : null : null; - updates.maintainerEmail = info.metadata ? info.metadata.maintainer ? (info.metadata.maintainer.email || null) : null : null; - } - - if (name) updates.name = name; - if (description) updates.description = description; - if (icon || favicon) updates.iconUrl = icon || favicon; - if (favicon) updates.faviconUrl = favicon; - if (themeColor) updates.themeColor = themeColor; - - await Instances.update(instance.id, updates); - - logger.succ(`Successfuly updated metadata of ${instance.host}`); - } catch (e) { - logger.error(`Failed to update metadata of ${instance.host}: ${e}`); - } finally { - unlock(); - } -} - -type NodeInfo = { - openRegistrations?: any; - software?: { - name?: any; - version?: any; - }; - metadata?: { - name?: any; - nodeName?: any; - nodeDescription?: any; - description?: any; - maintainer?: { - name?: any; - email?: any; - }; - }; -}; - -async function fetchNodeinfo(instance: Instance): Promise { - logger.info(`Fetching nodeinfo of ${instance.host} ...`); - - try { - const wellknown = await getJson('https://' + instance.host + '/.well-known/nodeinfo') - .catch(e => { - if (e.statusCode === 404) { - throw 'No nodeinfo provided'; - } else { - throw e.statusCode || e.message; - } - }) as Record; - - if (wellknown.links == null || !Array.isArray(wellknown.links)) { - throw 'No wellknown links'; - } - - const links = wellknown.links as any[]; - - const lnik1_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/1.0'); - const lnik2_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.0'); - const lnik2_1 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.1'); - const link = lnik2_1 || lnik2_0 || lnik1_0; - - if (link == null) { - throw 'No nodeinfo link provided'; - } - - const info = await getJson(link.href) - .catch(e => { - throw e.statusCode || e.message; - }); - - logger.succ(`Successfuly fetched nodeinfo of ${instance.host}`); - - return info as NodeInfo; - } catch (e) { - logger.error(`Failed to fetch nodeinfo of ${instance.host}: ${e}`); - - throw e; - } -} - -async function fetchDom(instance: Instance): Promise { - logger.info(`Fetching HTML of ${instance.host} ...`); - - const url = 'https://' + instance.host; - - const html = await getHtml(url); - - const { window } = new JSDOM(html); - const doc = window.document; - - return doc; -} - -async function fetchManifest(instance: Instance): Promise | null> { - const url = 'https://' + instance.host; - - const manifestUrl = url + '/manifest.json'; - - const manifest = await getJson(manifestUrl) as Record; - - return manifest; -} - -async function fetchFaviconUrl(instance: Instance, doc: DOMWindow['document'] | null): Promise { - const url = 'https://' + instance.host; - - if (doc) { - // https://github.com/misskey-dev/misskey/pull/8220#issuecomment-1025104043 - const href = Array.from(doc.getElementsByTagName('link')).reverse().find(link => link.relList.contains('icon'))?.href; - - if (href) { - return (new URL(href, url)).href; - } - } - - const faviconUrl = url + '/favicon.ico'; - - const favicon = await fetch(faviconUrl, { - // TODO - //timeout: 10000, - agent: getAgentByUrl, - }); - - if (favicon.ok) { - return faviconUrl; - } - - return null; -} - -async function fetchIconUrl(instance: Instance, doc: DOMWindow['document'] | null, manifest: Record | null): Promise { - if (manifest && manifest.icons && manifest.icons.length > 0 && manifest.icons[0].src) { - const url = 'https://' + instance.host; - return (new URL(manifest.icons[0].src, url)).href; - } - - if (doc) { - const url = 'https://' + instance.host; - - // https://github.com/misskey-dev/misskey/pull/8220#issuecomment-1025104043 - const links = Array.from(doc.getElementsByTagName('link')).reverse(); - // https://github.com/misskey-dev/misskey/pull/8220/files/0ec4eba22a914e31b86874f12448f88b3e58dd5a#r796487559 - const href = - [ - links.find(link => link.relList.contains('apple-touch-icon-precomposed'))?.href, - links.find(link => link.relList.contains('apple-touch-icon'))?.href, - links.find(link => link.relList.contains('icon'))?.href, - ] - .find(href => href); - - if (href) { - return (new URL(href, url)).href; - } - } - - return null; -} - -async function getThemeColor(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record | null): Promise { - const themeColor = info?.metadata?.themeColor || doc?.querySelector('meta[name="theme-color"]')?.getAttribute('content') || manifest?.theme_color; - - if (themeColor) { - const color = new tinycolor(themeColor); - if (color.isValid()) return color.toHexString(); - } - - return null; -} - -async function getSiteName(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record | null): Promise { - if (info && info.metadata) { - if (info.metadata.nodeName || info.metadata.name) { - return info.metadata.nodeName || info.metadata.name; - } - } - - if (doc) { - const og = doc.querySelector('meta[property="og:title"]')?.getAttribute('content'); - - if (og) { - return og; - } - } - - if (manifest) { - return manifest?.name || manifest?.short_name; - } - - return null; -} - -async function getDescription(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record | null): Promise { - if (info && info.metadata) { - if (info.metadata.nodeDescription || info.metadata.description) { - return info.metadata.nodeDescription || info.metadata.description; - } - } - - if (doc) { - const meta = doc.querySelector('meta[name="description"]')?.getAttribute('content'); - if (meta) { - return meta; - } - - const og = doc.querySelector('meta[property="og:description"]')?.getAttribute('content'); - if (og) { - return og; - } - } - - if (manifest) { - return manifest?.name || manifest?.short_name; - } - - return null; -} From 943a6f8f9fc2a5e6c8fc41a64ceed68b6b4d9deb Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 01:07:55 +0900 Subject: [PATCH 018/180] wip --- .../src/services/UserSuspendService.ts | 83 +++++++++++++++++++ packages/backend/src/services/suspend-user.ts | 37 --------- .../backend/src/services/unsuspend-user.ts | 38 --------- 3 files changed, 83 insertions(+), 75 deletions(-) create mode 100644 packages/backend/src/services/UserSuspendService.ts delete mode 100644 packages/backend/src/services/suspend-user.ts delete mode 100644 packages/backend/src/services/unsuspend-user.ts diff --git a/packages/backend/src/services/UserSuspendService.ts b/packages/backend/src/services/UserSuspendService.ts new file mode 100644 index 000000000000..72a9d6ae8d35 --- /dev/null +++ b/packages/backend/src/services/UserSuspendService.ts @@ -0,0 +1,83 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Not, IsNull } from 'typeorm'; +import type { Followings , Users } from '@/models/index.js'; + +import type { User } from '@/models/entities/user'; +import type { QueueService } from '@/queue/queue.service'; +import renderDelete from '@/remote/activitypub/renderer/delete.js'; +import renderUndo from '@/remote/activitypub/renderer/undo.js'; +import { renderActivity } from '@/remote/activitypub/renderer/index.js'; +import { publishInternalEvent } from '@/services/stream.js'; +import config from '@/config/index.js'; + +@Injectable() +export class UserSuspendService { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + private queueService: QueueService, + ) { + } + + public async doPostSuspend(user: { id: User['id']; host: User['host'] }): Promise { + publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true }); + + if (this.usersRepository.isLocalUser(user)) { + // 知り得る全SharedInboxにDelete配信 + const content = renderActivity(renderDelete(`${config.url}/users/${user.id}`, user)); + + const queue: string[] = []; + + const followings = await this.followingsRepository.find({ + where: [ + { followerSharedInbox: Not(IsNull()) }, + { followeeSharedInbox: Not(IsNull()) }, + ], + select: ['followerSharedInbox', 'followeeSharedInbox'], + }); + + const inboxes = followings.map(x => x.followerSharedInbox || x.followeeSharedInbox); + + for (const inbox of inboxes) { + if (inbox != null && !queue.includes(inbox)) queue.push(inbox); + } + + for (const inbox of queue) { + this.queueService.deliver(user, content, inbox); + } + } + } + + public async doPostUnsuspend(user: User): Promise { + publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false }); + + if (this.usersRepository.isLocalUser(user)) { + // 知り得る全SharedInboxにUndo Delete配信 + const content = renderActivity(renderUndo(renderDelete(`${config.url}/users/${user.id}`, user), user)); + + const queue: string[] = []; + + const followings = await this.followingsRepository.find({ + where: [ + { followerSharedInbox: Not(IsNull()) }, + { followeeSharedInbox: Not(IsNull()) }, + ], + select: ['followerSharedInbox', 'followeeSharedInbox'], + }); + + const inboxes = followings.map(x => x.followerSharedInbox || x.followeeSharedInbox); + + for (const inbox of inboxes) { + if (inbox != null && !queue.includes(inbox)) queue.push(inbox); + } + + for (const inbox of queue) { + this.queueService.deliver(user as any, content, inbox); + } + } + } +} diff --git a/packages/backend/src/services/suspend-user.ts b/packages/backend/src/services/suspend-user.ts deleted file mode 100644 index e96b06a35176..000000000000 --- a/packages/backend/src/services/suspend-user.ts +++ /dev/null @@ -1,37 +0,0 @@ -import renderDelete from '@/remote/activitypub/renderer/delete.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import { deliver } from '@/queue/index.js'; -import config from '@/config/index.js'; -import { User } from '@/models/entities/user.js'; -import { Users, Followings } from '@/models/index.js'; -import { Not, IsNull } from 'typeorm'; -import { publishInternalEvent } from '@/services/stream.js'; - -export async function doPostSuspend(user: { id: User['id']; host: User['host'] }) { - publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true }); - - if (Users.isLocalUser(user)) { - // 知り得る全SharedInboxにDelete配信 - const content = renderActivity(renderDelete(`${config.url}/users/${user.id}`, user)); - - const queue: string[] = []; - - const followings = await Followings.find({ - where: [ - { followerSharedInbox: Not(IsNull()) }, - { followeeSharedInbox: Not(IsNull()) }, - ], - select: ['followerSharedInbox', 'followeeSharedInbox'], - }); - - const inboxes = followings.map(x => x.followerSharedInbox || x.followeeSharedInbox); - - for (const inbox of inboxes) { - if (inbox != null && !queue.includes(inbox)) queue.push(inbox); - } - - for (const inbox of queue) { - deliver(user, content, inbox); - } - } -} diff --git a/packages/backend/src/services/unsuspend-user.ts b/packages/backend/src/services/unsuspend-user.ts deleted file mode 100644 index 44a0d01ca290..000000000000 --- a/packages/backend/src/services/unsuspend-user.ts +++ /dev/null @@ -1,38 +0,0 @@ -import renderDelete from '@/remote/activitypub/renderer/delete.js'; -import renderUndo from '@/remote/activitypub/renderer/undo.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import { deliver } from '@/queue/index.js'; -import config from '@/config/index.js'; -import { User } from '@/models/entities/user.js'; -import { Users, Followings } from '@/models/index.js'; -import { Not, IsNull } from 'typeorm'; -import { publishInternalEvent } from '@/services/stream.js'; - -export async function doPostUnsuspend(user: User) { - publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false }); - - if (Users.isLocalUser(user)) { - // 知り得る全SharedInboxにUndo Delete配信 - const content = renderActivity(renderUndo(renderDelete(`${config.url}/users/${user.id}`, user), user)); - - const queue: string[] = []; - - const followings = await Followings.find({ - where: [ - { followerSharedInbox: Not(IsNull()) }, - { followeeSharedInbox: Not(IsNull()) }, - ], - select: ['followerSharedInbox', 'followeeSharedInbox'], - }); - - const inboxes = followings.map(x => x.followerSharedInbox || x.followeeSharedInbox); - - for (const inbox of inboxes) { - if (inbox != null && !queue.includes(inbox)) queue.push(inbox); - } - - for (const inbox of queue) { - deliver(user as any, content, inbox); - } - } -} From 607220f999f0bae59aee3adbfe2b722863dbb92d Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 01:19:49 +0900 Subject: [PATCH 019/180] wip --- .../src/services/GlobalEventService.ts | 105 ++++++++++++++++ .../src/services/UserSuspendService.ts | 7 +- packages/backend/src/services/stream.ts | 116 ------------------ 3 files changed, 109 insertions(+), 119 deletions(-) create mode 100644 packages/backend/src/services/GlobalEventService.ts delete mode 100644 packages/backend/src/services/stream.ts diff --git a/packages/backend/src/services/GlobalEventService.ts b/packages/backend/src/services/GlobalEventService.ts new file mode 100644 index 000000000000..f59d1d4f4aa5 --- /dev/null +++ b/packages/backend/src/services/GlobalEventService.ts @@ -0,0 +1,105 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { User } from '@/models/entities/user.js'; +import type { Note } from '@/models/entities/note.js'; +import type { UserList } from '@/models/entities/user-list.js'; +import type { UserGroup } from '@/models/entities/user-group.js'; +import config from '@/config/index.js'; +import type { Antenna } from '@/models/entities/antenna.js'; +import type { Channel } from '@/models/entities/channel.js'; +import type { + StreamChannels, + AdminStreamTypes, + AntennaStreamTypes, + BroadcastTypes, + ChannelStreamTypes, + DriveStreamTypes, + GroupMessagingStreamTypes, + InternalStreamTypes, + MainStreamTypes, + MessagingIndexStreamTypes, + MessagingStreamTypes, + NoteStreamTypes, + UserListStreamTypes, + UserStreamTypes, +} from '@/server/api/stream/types.js'; +import type { Packed } from '@/misc/schema.js'; +import type Redis from 'ioredis'; + +@Injectable() +export class GlobalEventService { + constructor( + @Inject('redis') + private redisClient: Redis.Redis, + ) { + } + + private publish(channel: StreamChannels, type: string | null, value?: any): void { + const message = type == null ? value : value == null ? + { type: type, body: null } : + { type: type, body: value }; + + this.redisClient.publish(config.host, JSON.stringify({ + channel: channel, + message: message, + })); + } + + public publishInternalEvent(type: K, value?: InternalStreamTypes[K]): void { + this.publish('internal', type, typeof value === 'undefined' ? null : value); + } + + public publishUserEvent(userId: User['id'], type: K, value?: UserStreamTypes[K]): void { + this.publish(`user:${userId}`, type, typeof value === 'undefined' ? null : value); + } + + public publishBroadcastStream(type: K, value?: BroadcastTypes[K]): void { + this.publish('broadcast', type, typeof value === 'undefined' ? null : value); + } + + public publishMainStream(userId: User['id'], type: K, value?: MainStreamTypes[K]): void { + this.publish(`mainStream:${userId}`, type, typeof value === 'undefined' ? null : value); + } + + public publishDriveStream(userId: User['id'], type: K, value?: DriveStreamTypes[K]): void { + this.publish(`driveStream:${userId}`, type, typeof value === 'undefined' ? null : value); + } + + public publishNoteStream(noteId: Note['id'], type: K, value?: NoteStreamTypes[K]): void { + this.publish(`noteStream:${noteId}`, type, { + id: noteId, + body: value, + }); + } + + public publishChannelStream(channelId: Channel['id'], type: K, value?: ChannelStreamTypes[K]): void { + this.publish(`channelStream:${channelId}`, type, typeof value === 'undefined' ? null : value); + } + + public publishUserListStream(listId: UserList['id'], type: K, value?: UserListStreamTypes[K]): void { + this.publish(`userListStream:${listId}`, type, typeof value === 'undefined' ? null : value); + } + + public publishAntennaStream(antennaId: Antenna['id'], type: K, value?: AntennaStreamTypes[K]): void { + this.publish(`antennaStream:${antennaId}`, type, typeof value === 'undefined' ? null : value); + } + + public publishMessagingStream(userId: User['id'], otherpartyId: User['id'], type: K, value?: MessagingStreamTypes[K]): void { + this.publish(`messagingStream:${userId}-${otherpartyId}`, type, typeof value === 'undefined' ? null : value); + } + + public publishGroupMessagingStream(groupId: UserGroup['id'], type: K, value?: GroupMessagingStreamTypes[K]): void { + this.publish(`messagingStream:${groupId}`, type, typeof value === 'undefined' ? null : value); + } + + public publishMessagingIndexStream(userId: User['id'], type: K, value?: MessagingIndexStreamTypes[K]): void { + this.publish(`messagingIndexStream:${userId}`, type, typeof value === 'undefined' ? null : value); + } + + public publishNotesStream(note: Packed<'Note'>): void { + this.publish('notesStream', null, note); + } + + public publishAdminStream(userId: User['id'], type: K, value?: AdminStreamTypes[K]): void { + this.publish(`adminStream:${userId}`, type, typeof value === 'undefined' ? null : value); + } +} diff --git a/packages/backend/src/services/UserSuspendService.ts b/packages/backend/src/services/UserSuspendService.ts index 72a9d6ae8d35..0a6872bbf1e2 100644 --- a/packages/backend/src/services/UserSuspendService.ts +++ b/packages/backend/src/services/UserSuspendService.ts @@ -7,8 +7,8 @@ import type { QueueService } from '@/queue/queue.service'; import renderDelete from '@/remote/activitypub/renderer/delete.js'; import renderUndo from '@/remote/activitypub/renderer/undo.js'; import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import { publishInternalEvent } from '@/services/stream.js'; import config from '@/config/index.js'; +import type { GlobalEventService } from '@/services/GlobalEventService.js'; @Injectable() export class UserSuspendService { @@ -20,11 +20,12 @@ export class UserSuspendService { private followingsRepository: typeof Followings, private queueService: QueueService, + private globalEventService: GlobalEventService, ) { } public async doPostSuspend(user: { id: User['id']; host: User['host'] }): Promise { - publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true }); + this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true }); if (this.usersRepository.isLocalUser(user)) { // 知り得る全SharedInboxにDelete配信 @@ -53,7 +54,7 @@ export class UserSuspendService { } public async doPostUnsuspend(user: User): Promise { - publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false }); + this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false }); if (this.usersRepository.isLocalUser(user)) { // 知り得る全SharedInboxにUndo Delete配信 diff --git a/packages/backend/src/services/stream.ts b/packages/backend/src/services/stream.ts deleted file mode 100644 index 9fa2b97135de..000000000000 --- a/packages/backend/src/services/stream.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { redisClient } from '../db/redis.js'; -import { User } from '@/models/entities/user.js'; -import { Note } from '@/models/entities/note.js'; -import { UserList } from '@/models/entities/user-list.js'; -import { UserGroup } from '@/models/entities/user-group.js'; -import config from '@/config/index.js'; -import { Antenna } from '@/models/entities/antenna.js'; -import { Channel } from '@/models/entities/channel.js'; -import { - StreamChannels, - AdminStreamTypes, - AntennaStreamTypes, - BroadcastTypes, - ChannelStreamTypes, - DriveStreamTypes, - GroupMessagingStreamTypes, - InternalStreamTypes, - MainStreamTypes, - MessagingIndexStreamTypes, - MessagingStreamTypes, - NoteStreamTypes, - UserListStreamTypes, - UserStreamTypes, -} from '@/server/api/stream/types.js'; -import { Packed } from '@/misc/schema.js'; - -class Publisher { - private publish = (channel: StreamChannels, type: string | null, value?: any): void => { - const message = type == null ? value : value == null ? - { type: type, body: null } : - { type: type, body: value }; - - redisClient.publish(config.host, JSON.stringify({ - channel: channel, - message: message, - })); - }; - - public publishInternalEvent = (type: K, value?: InternalStreamTypes[K]): void => { - this.publish('internal', type, typeof value === 'undefined' ? null : value); - }; - - public publishUserEvent = (userId: User['id'], type: K, value?: UserStreamTypes[K]): void => { - this.publish(`user:${userId}`, type, typeof value === 'undefined' ? null : value); - }; - - public publishBroadcastStream = (type: K, value?: BroadcastTypes[K]): void => { - this.publish('broadcast', type, typeof value === 'undefined' ? null : value); - }; - - public publishMainStream = (userId: User['id'], type: K, value?: MainStreamTypes[K]): void => { - this.publish(`mainStream:${userId}`, type, typeof value === 'undefined' ? null : value); - }; - - public publishDriveStream = (userId: User['id'], type: K, value?: DriveStreamTypes[K]): void => { - this.publish(`driveStream:${userId}`, type, typeof value === 'undefined' ? null : value); - }; - - public publishNoteStream = (noteId: Note['id'], type: K, value?: NoteStreamTypes[K]): void => { - this.publish(`noteStream:${noteId}`, type, { - id: noteId, - body: value, - }); - }; - - public publishChannelStream = (channelId: Channel['id'], type: K, value?: ChannelStreamTypes[K]): void => { - this.publish(`channelStream:${channelId}`, type, typeof value === 'undefined' ? null : value); - }; - - public publishUserListStream = (listId: UserList['id'], type: K, value?: UserListStreamTypes[K]): void => { - this.publish(`userListStream:${listId}`, type, typeof value === 'undefined' ? null : value); - }; - - public publishAntennaStream = (antennaId: Antenna['id'], type: K, value?: AntennaStreamTypes[K]): void => { - this.publish(`antennaStream:${antennaId}`, type, typeof value === 'undefined' ? null : value); - }; - - public publishMessagingStream = (userId: User['id'], otherpartyId: User['id'], type: K, value?: MessagingStreamTypes[K]): void => { - this.publish(`messagingStream:${userId}-${otherpartyId}`, type, typeof value === 'undefined' ? null : value); - }; - - public publishGroupMessagingStream = (groupId: UserGroup['id'], type: K, value?: GroupMessagingStreamTypes[K]): void => { - this.publish(`messagingStream:${groupId}`, type, typeof value === 'undefined' ? null : value); - }; - - public publishMessagingIndexStream = (userId: User['id'], type: K, value?: MessagingIndexStreamTypes[K]): void => { - this.publish(`messagingIndexStream:${userId}`, type, typeof value === 'undefined' ? null : value); - }; - - public publishNotesStream = (note: Packed<'Note'>): void => { - this.publish('notesStream', null, note); - }; - - public publishAdminStream = (userId: User['id'], type: K, value?: AdminStreamTypes[K]): void => { - this.publish(`adminStream:${userId}`, type, typeof value === 'undefined' ? null : value); - }; -} - -const publisher = new Publisher(); - -export default publisher; - -export const publishInternalEvent = publisher.publishInternalEvent; -export const publishUserEvent = publisher.publishUserEvent; -export const publishBroadcastStream = publisher.publishBroadcastStream; -export const publishMainStream = publisher.publishMainStream; -export const publishDriveStream = publisher.publishDriveStream; -export const publishNoteStream = publisher.publishNoteStream; -export const publishNotesStream = publisher.publishNotesStream; -export const publishChannelStream = publisher.publishChannelStream; -export const publishUserListStream = publisher.publishUserListStream; -export const publishAntennaStream = publisher.publishAntennaStream; -export const publishMessagingStream = publisher.publishMessagingStream; -export const publishGroupMessagingStream = publisher.publishGroupMessagingStream; -export const publishMessagingIndexStream = publisher.publishMessagingIndexStream; -export const publishAdminStream = publisher.publishAdminStream; From 7cc1908b78cdaaaf00c1c7e6b652ccff2cc44bf8 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 01:51:27 +0900 Subject: [PATCH 020/180] wip --- .../backend/src/services/UserCacheService.ts | 67 +++++++++++++++++++ packages/backend/src/services/user-cache.ts | 44 ------------ 2 files changed, 67 insertions(+), 44 deletions(-) create mode 100644 packages/backend/src/services/UserCacheService.ts delete mode 100644 packages/backend/src/services/user-cache.ts diff --git a/packages/backend/src/services/UserCacheService.ts b/packages/backend/src/services/UserCacheService.ts new file mode 100644 index 000000000000..0a6738213754 --- /dev/null +++ b/packages/backend/src/services/UserCacheService.ts @@ -0,0 +1,67 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Users } from '@/models/index.js'; +import { Cache } from '@/misc/cache.js'; +import type { CacheableLocalUser, CacheableUser, ILocalUser } from '@/models/entities/user.js'; +import type Redis from 'ioredis'; +import type { OnApplicationShutdown } from '@nestjs/common'; + +@Injectable() +export class UserSuspendService implements OnApplicationShutdown { + public userByIdCache: Cache; + public localUserByNativeTokenCache: Cache; + public localUserByIdCache: Cache; + public uriPersonCache: Cache; + + constructor( + @Inject('redisSubscriber') + private redisSubscriber: Redis.Redis, + ) { + this.onMessage = this.onMessage.bind(this); + + this.userByIdCache = new Cache(Infinity); + this.localUserByNativeTokenCache = new Cache(Infinity); + this.localUserByIdCache = new Cache(Infinity); + this.uriPersonCache = new Cache(Infinity); + + this.redisSubscriber.on('message', this.onMessage); + } + + private async onMessage(_, data) { + const obj = JSON.parse(data); + + if (obj.channel === 'internal') { + const { type, body } = obj.message; + switch (type) { + case 'userChangeSuspendedState': + case 'userChangeSilencedState': + case 'userChangeModeratorState': + case 'remoteUserUpdated': { + const user = await Users.findOneByOrFail({ id: body.id }); + this.userByIdCache.set(user.id, user); + for (const [k, v] of this.uriPersonCache.cache.entries()) { + if (v.value?.id === user.id) { + this.uriPersonCache.set(k, user); + } + } + if (Users.isLocalUser(user)) { + this.localUserByNativeTokenCache.set(user.token, user); + this.localUserByIdCache.set(user.id, user); + } + break; + } + case 'userTokenRegenerated': { + const user = await Users.findOneByOrFail({ id: body.id }) as ILocalUser; + this.localUserByNativeTokenCache.delete(body.oldToken); + this.localUserByNativeTokenCache.set(body.newToken, user); + break; + } + default: + break; + } + } + } + + public onApplicationShutdown(signal?: string | undefined) { + this.redisSubscriber.off('message', this.onMessage); + } +} diff --git a/packages/backend/src/services/user-cache.ts b/packages/backend/src/services/user-cache.ts deleted file mode 100644 index 407301f2fdc1..000000000000 --- a/packages/backend/src/services/user-cache.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { CacheableLocalUser, CacheableUser, ILocalUser, User } from '@/models/entities/user.js'; -import { Users } from '@/models/index.js'; -import { Cache } from '@/misc/cache.js'; -import { subsdcriber } from '@/db/redis.js'; - -export const userByIdCache = new Cache(Infinity); -export const localUserByNativeTokenCache = new Cache(Infinity); -export const localUserByIdCache = new Cache(Infinity); -export const uriPersonCache = new Cache(Infinity); - -subsdcriber.on('message', async (_, data) => { - const obj = JSON.parse(data); - - if (obj.channel === 'internal') { - const { type, body } = obj.message; - switch (type) { - case 'userChangeSuspendedState': - case 'userChangeSilencedState': - case 'userChangeModeratorState': - case 'remoteUserUpdated': { - const user = await Users.findOneByOrFail({ id: body.id }); - userByIdCache.set(user.id, user); - for (const [k, v] of uriPersonCache.cache.entries()) { - if (v.value?.id === user.id) { - uriPersonCache.set(k, user); - } - } - if (Users.isLocalUser(user)) { - localUserByNativeTokenCache.set(user.token, user); - localUserByIdCache.set(user.id, user); - } - break; - } - case 'userTokenRegenerated': { - const user = await Users.findOneByOrFail({ id: body.id }) as ILocalUser; - localUserByNativeTokenCache.delete(body.oldToken); - localUserByNativeTokenCache.set(body.newToken, user); - break; - } - default: - break; - } - } -}); From 050deeea260bad5aca36bb4fe2c561726f8ba6b5 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 02:13:09 +0900 Subject: [PATCH 021/180] wip --- packages/backend/src/app.module.ts | 2 ++ packages/backend/src/boot/worker.ts | 3 ++ .../src/services/chart/charts.module.ts | 36 +++++++++++++++++++ .../src/services/chart/charts/active-users.ts | 17 ++++++--- .../src/services/chart/charts/ap-request.ts | 14 ++++++-- .../src/services/chart/charts/drive.ts | 16 ++++++--- .../src/services/chart/charts/federation.ts | 14 ++++++-- .../src/services/chart/charts/hashtag.ts | 16 ++++++--- .../src/services/chart/charts/instance.ts | 18 +++++++--- .../src/services/chart/charts/notes.ts | 16 ++++++--- .../services/chart/charts/per-user-drive.ts | 16 ++++++--- .../chart/charts/per-user-following.ts | 16 ++++++--- .../services/chart/charts/per-user-notes.ts | 18 +++++++--- .../chart/charts/per-user-reactions.ts | 18 +++++++--- .../src/services/chart/charts/test-grouped.ts | 15 +++++--- .../chart/charts/test-intersection.ts | 15 +++++--- .../src/services/chart/charts/test-unique.ts | 15 +++++--- .../backend/src/services/chart/charts/test.ts | 15 +++++--- .../src/services/chart/charts/users.ts | 16 ++++++--- packages/backend/src/services/chart/core.ts | 11 +++--- 20 files changed, 237 insertions(+), 70 deletions(-) create mode 100644 packages/backend/src/services/chart/charts.module.ts diff --git a/packages/backend/src/app.module.ts b/packages/backend/src/app.module.ts index be3a30b460e3..59b80b096552 100644 --- a/packages/backend/src/app.module.ts +++ b/packages/backend/src/app.module.ts @@ -2,9 +2,11 @@ import { Module } from '@nestjs/common'; import { Notes, Users } from '@/models/index.js'; import { EndpointsModule } from '@/server/api/endpoints.module.js'; import { QueueModule } from '@/queue/queue.module.js'; +import { ChartsModule } from './services/chart/charts.module'; @Module({ imports: [ + ChartsModule, EndpointsModule, QueueModule, ], diff --git a/packages/backend/src/boot/worker.ts b/packages/backend/src/boot/worker.ts index 09a34622a0d2..687d53ce4d5d 100644 --- a/packages/backend/src/boot/worker.ts +++ b/packages/backend/src/boot/worker.ts @@ -1,6 +1,7 @@ import cluster from 'node:cluster'; import { NestFactory } from '@nestjs/core'; import { envOption } from '@/env.js'; +import { ChartManagementService } from '@/services/chart/ChartManagementService.js'; import { initDb } from '../db/postgre.js'; import createServer from '../server/index.js'; import initializeQueue from '../queue/index.js'; @@ -22,6 +23,8 @@ export async function workerMain() { await initializeQueue(app); } + app.get(ChartManagementService).run(); + if (cluster.isWorker) { // Send a 'ready' message to parent process process.send!('ready'); diff --git a/packages/backend/src/services/chart/charts.module.ts b/packages/backend/src/services/chart/charts.module.ts new file mode 100644 index 000000000000..3ccbc7d3df53 --- /dev/null +++ b/packages/backend/src/services/chart/charts.module.ts @@ -0,0 +1,36 @@ +import { Module } from '@nestjs/common'; +import FederationChart from './charts/federation.js'; +import NotesChart from './charts/notes.js'; +import UsersChart from './charts/users.js'; +import ActiveUsersChart from './charts/active-users.js'; +import InstanceChart from './charts/instance.js'; +import PerUserNotesChart from './charts/per-user-notes.js'; +import DriveChart from './charts/drive.js'; +import PerUserReactionsChart from './charts/per-user-reactions.js'; +import HashtagChart from './charts/hashtag.js'; +import PerUserFollowingChart from './charts/per-user-following.js'; +import PerUserDriveChart from './charts/per-user-drive.js'; +import ApRequestChart from './charts/ap-request.js'; +import { ChartManagementService } from './ChartManagementService.js'; + +@Module({ + imports: [ + ], + providers: [ + FederationChart, + NotesChart, + UsersChart, + ActiveUsersChart, + InstanceChart, + PerUserNotesChart, + DriveChart, + PerUserReactionsChart, + HashtagChart, + PerUserFollowingChart, + PerUserDriveChart, + ApRequestChart, + + ChartManagementService, + ], + }) +export class ChartsModule {} diff --git a/packages/backend/src/services/chart/charts/active-users.ts b/packages/backend/src/services/chart/charts/active-users.ts index f0a298bc321a..40fe20e731da 100644 --- a/packages/backend/src/services/chart/charts/active-users.ts +++ b/packages/backend/src/services/chart/charts/active-users.ts @@ -1,8 +1,10 @@ import { Injectable, Inject } from '@nestjs/common'; -import { User } from '@/models/entities/user.js'; -import { Users } from '@/models/index.js'; -import Chart, { KVs } from '../core.js'; +import type { AppLockService } from '@/services/AppLockService.js'; +import type { User } from '@/models/entities/user.js'; +import Chart from '../core.js'; import { name, schema } from './entities/active-users.js'; +import type { DataSource } from 'typeorm'; +import type { KVs } from '../core.js'; const week = 1000 * 60 * 60 * 24 * 7; const month = 1000 * 60 * 60 * 24 * 30; @@ -14,8 +16,13 @@ const year = 1000 * 60 * 60 * 24 * 365; // eslint-disable-next-line import/no-default-export @Injectable() export default class ActiveUsersChart extends Chart { - constructor() { - super(name, schema); + constructor( + @Inject('db') + private db: DataSource, + + private appLockService: AppLockService, + ) { + super(db, appLockService.getChartInsertLock, name, schema); } protected async tickMajor(): Promise>> { diff --git a/packages/backend/src/services/chart/charts/ap-request.ts b/packages/backend/src/services/chart/charts/ap-request.ts index 2d047633c4ff..83c6fa89c88c 100644 --- a/packages/backend/src/services/chart/charts/ap-request.ts +++ b/packages/backend/src/services/chart/charts/ap-request.ts @@ -1,6 +1,9 @@ import { Injectable, Inject } from '@nestjs/common'; -import Chart, { KVs } from '../core.js'; +import type { AppLockService } from '@/services/AppLockService.js'; +import Chart from '../core.js'; import { name, schema } from './entities/ap-request.js'; +import type { KVs } from '../core.js'; +import type { DataSource } from 'typeorm'; /** * Chart about ActivityPub requests @@ -8,8 +11,13 @@ import { name, schema } from './entities/ap-request.js'; // eslint-disable-next-line import/no-default-export @Injectable() export default class ApRequestChart extends Chart { - constructor() { - super(name, schema); + constructor( + @Inject('db') + private db: DataSource, + + private appLockService: AppLockService, + ) { + super(db, appLockService.getChartInsertLock, name, schema); } protected async tickMajor(): Promise>> { diff --git a/packages/backend/src/services/chart/charts/drive.ts b/packages/backend/src/services/chart/charts/drive.ts index 1ce371563db4..77c7ed7a8fd6 100644 --- a/packages/backend/src/services/chart/charts/drive.ts +++ b/packages/backend/src/services/chart/charts/drive.ts @@ -1,9 +1,12 @@ import { Injectable, Inject } from '@nestjs/common'; import { Not, IsNull } from 'typeorm'; import { DriveFiles } from '@/models/index.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; -import Chart, { KVs } from '../core.js'; +import type { DriveFile } from '@/models/entities/drive-file.js'; +import type { AppLockService } from '@/services/AppLockService.js'; +import Chart from '../core.js'; import { name, schema } from './entities/drive.js'; +import type { KVs } from '../core.js'; +import type { DataSource } from 'typeorm'; /** * ドライブに関するチャート @@ -11,8 +14,13 @@ import { name, schema } from './entities/drive.js'; // eslint-disable-next-line import/no-default-export @Injectable() export default class DriveChart extends Chart { - constructor() { - super(name, schema); + constructor( + @Inject('db') + private db: DataSource, + + private appLockService: AppLockService, + ) { + super(db, appLockService.getChartInsertLock, name, schema); } protected async tickMajor(): Promise>> { diff --git a/packages/backend/src/services/chart/charts/federation.ts b/packages/backend/src/services/chart/charts/federation.ts index da1f3ab1e76d..1a6fe617c5dc 100644 --- a/packages/backend/src/services/chart/charts/federation.ts +++ b/packages/backend/src/services/chart/charts/federation.ts @@ -1,8 +1,11 @@ import { Injectable, Inject } from '@nestjs/common'; import { Followings, Instances } from '@/models/index.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; -import Chart, { KVs } from '../core.js'; +import type { AppLockService } from '@/services/AppLockService.js'; +import Chart from '../core.js'; import { name, schema } from './entities/federation.js'; +import type { KVs } from '../core.js'; +import type { DataSource } from 'typeorm'; /** * フェデレーションに関するチャート @@ -10,8 +13,13 @@ import { name, schema } from './entities/federation.js'; // eslint-disable-next-line import/no-default-export @Injectable() export default class FederationChart extends Chart { - constructor() { - super(name, schema); + constructor( + @Inject('db') + private db: DataSource, + + private appLockService: AppLockService, + ) { + super(db, appLockService.getChartInsertLock, name, schema); } protected async tickMajor(): Promise>> { diff --git a/packages/backend/src/services/chart/charts/hashtag.ts b/packages/backend/src/services/chart/charts/hashtag.ts index 8b7feaf9288d..b388de667073 100644 --- a/packages/backend/src/services/chart/charts/hashtag.ts +++ b/packages/backend/src/services/chart/charts/hashtag.ts @@ -1,8 +1,11 @@ import { Injectable, Inject } from '@nestjs/common'; -import { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/user.js'; import { Users } from '@/models/index.js'; -import Chart, { KVs } from '../core.js'; +import type { AppLockService } from '@/services/AppLockService.js'; +import Chart from '../core.js'; import { name, schema } from './entities/hashtag.js'; +import type { KVs } from '../core.js'; +import type { DataSource } from 'typeorm'; /** * ハッシュタグに関するチャート @@ -10,8 +13,13 @@ import { name, schema } from './entities/hashtag.js'; // eslint-disable-next-line import/no-default-export @Injectable() export default class HashtagChart extends Chart { - constructor() { - super(name, schema, true); + constructor( + @Inject('db') + private db: DataSource, + + private appLockService: AppLockService, + ) { + super(db, appLockService.getChartInsertLock, name, schema, true); } protected async tickMajor(): Promise>> { diff --git a/packages/backend/src/services/chart/charts/instance.ts b/packages/backend/src/services/chart/charts/instance.ts index 73189d6630f8..c0d45ab028ab 100644 --- a/packages/backend/src/services/chart/charts/instance.ts +++ b/packages/backend/src/services/chart/charts/instance.ts @@ -1,10 +1,13 @@ import { Injectable, Inject } from '@nestjs/common'; import { DriveFiles, Followings, Users, Notes } from '@/models/index.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; -import { Note } from '@/models/entities/note.js'; +import type { DriveFile } from '@/models/entities/drive-file.js'; +import type { Note } from '@/models/entities/note.js'; import { toPuny } from '@/misc/convert-host.js'; -import Chart, { KVs } from '../core.js'; +import type { AppLockService } from '@/services/AppLockService.js'; +import Chart from '../core.js'; import { name, schema } from './entities/instance.js'; +import type { KVs } from '../core.js'; +import type { DataSource } from 'typeorm'; /** * インスタンスごとのチャート @@ -12,8 +15,13 @@ import { name, schema } from './entities/instance.js'; // eslint-disable-next-line import/no-default-export @Injectable() export default class InstanceChart extends Chart { - constructor() { - super(name, schema, true); + constructor( + @Inject('db') + private db: DataSource, + + private appLockService: AppLockService, + ) { + super(db, appLockService.getChartInsertLock, name, schema, true); } protected async tickMajor(group: string): Promise>> { diff --git a/packages/backend/src/services/chart/charts/notes.ts b/packages/backend/src/services/chart/charts/notes.ts index ccacf06bbb08..c234d4a00352 100644 --- a/packages/backend/src/services/chart/charts/notes.ts +++ b/packages/backend/src/services/chart/charts/notes.ts @@ -1,9 +1,12 @@ import { Injectable, Inject } from '@nestjs/common'; import { Not, IsNull } from 'typeorm'; import { Notes } from '@/models/index.js'; -import { Note } from '@/models/entities/note.js'; -import Chart, { KVs } from '../core.js'; +import type { Note } from '@/models/entities/note.js'; +import type { AppLockService } from '@/services/AppLockService.js'; +import Chart from '../core.js'; import { name, schema } from './entities/notes.js'; +import type { KVs } from '../core.js'; +import type { DataSource } from 'typeorm'; /** * ノートに関するチャート @@ -11,8 +14,13 @@ import { name, schema } from './entities/notes.js'; // eslint-disable-next-line import/no-default-export @Injectable() export default class NotesChart extends Chart { - constructor() { - super(name, schema); + constructor( + @Inject('db') + private db: DataSource, + + private appLockService: AppLockService, + ) { + super(db, appLockService.getChartInsertLock, name, schema); } protected async tickMajor(): Promise>> { diff --git a/packages/backend/src/services/chart/charts/per-user-drive.ts b/packages/backend/src/services/chart/charts/per-user-drive.ts index 31a00adec8d8..5a2cb3db42c0 100644 --- a/packages/backend/src/services/chart/charts/per-user-drive.ts +++ b/packages/backend/src/services/chart/charts/per-user-drive.ts @@ -1,8 +1,11 @@ import { Injectable, Inject } from '@nestjs/common'; import { DriveFiles } from '@/models/index.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; -import Chart, { KVs } from '../core.js'; +import type { DriveFile } from '@/models/entities/drive-file.js'; +import type { AppLockService } from '@/services/AppLockService.js'; +import Chart from '../core.js'; import { name, schema } from './entities/per-user-drive.js'; +import type { KVs } from '../core.js'; +import type { DataSource } from 'typeorm'; /** * ユーザーごとのドライブに関するチャート @@ -10,8 +13,13 @@ import { name, schema } from './entities/per-user-drive.js'; // eslint-disable-next-line import/no-default-export @Injectable() export default class PerUserDriveChart extends Chart { - constructor() { - super(name, schema, true); + constructor( + @Inject('db') + private db: DataSource, + + private appLockService: AppLockService, + ) { + super(db, appLockService.getChartInsertLock, name, schema, true); } protected async tickMajor(group: string): Promise>> { diff --git a/packages/backend/src/services/chart/charts/per-user-following.ts b/packages/backend/src/services/chart/charts/per-user-following.ts index f541383209ce..a262c0d83a07 100644 --- a/packages/backend/src/services/chart/charts/per-user-following.ts +++ b/packages/backend/src/services/chart/charts/per-user-following.ts @@ -1,9 +1,12 @@ import { Injectable, Inject } from '@nestjs/common'; import { Not, IsNull } from 'typeorm'; import { Followings, Users } from '@/models/index.js'; -import { User } from '@/models/entities/user.js'; -import Chart, { KVs } from '../core.js'; +import type { User } from '@/models/entities/user.js'; +import type { AppLockService } from '@/services/AppLockService.js'; +import Chart from '../core.js'; import { name, schema } from './entities/per-user-following.js'; +import type { KVs } from '../core.js'; +import type { DataSource } from 'typeorm'; /** * ユーザーごとのフォローに関するチャート @@ -11,8 +14,13 @@ import { name, schema } from './entities/per-user-following.js'; // eslint-disable-next-line import/no-default-export @Injectable() export default class PerUserFollowingChart extends Chart { - constructor() { - super(name, schema, true); + constructor( + @Inject('db') + private db: DataSource, + + private appLockService: AppLockService, + ) { + super(db, appLockService.getChartInsertLock, name, schema, true); } protected async tickMajor(group: string): Promise>> { diff --git a/packages/backend/src/services/chart/charts/per-user-notes.ts b/packages/backend/src/services/chart/charts/per-user-notes.ts index a666f05141f3..e5f64c84f213 100644 --- a/packages/backend/src/services/chart/charts/per-user-notes.ts +++ b/packages/backend/src/services/chart/charts/per-user-notes.ts @@ -1,9 +1,12 @@ import { Injectable, Inject } from '@nestjs/common'; -import { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/user.js'; import { Notes } from '@/models/index.js'; -import { Note } from '@/models/entities/note.js'; -import Chart, { KVs } from '../core.js'; +import type { Note } from '@/models/entities/note.js'; +import type { AppLockService } from '@/services/AppLockService.js'; +import Chart from '../core.js'; import { name, schema } from './entities/per-user-notes.js'; +import type { KVs } from '../core.js'; +import type { DataSource } from 'typeorm'; /** * ユーザーごとのノートに関するチャート @@ -11,8 +14,13 @@ import { name, schema } from './entities/per-user-notes.js'; // eslint-disable-next-line import/no-default-export @Injectable() export default class PerUserNotesChart extends Chart { - constructor() { - super(name, schema, true); + constructor( + @Inject('db') + private db: DataSource, + + private appLockService: AppLockService, + ) { + super(db, appLockService.getChartInsertLock, name, schema, true); } protected async tickMajor(group: string): Promise>> { diff --git a/packages/backend/src/services/chart/charts/per-user-reactions.ts b/packages/backend/src/services/chart/charts/per-user-reactions.ts index bc4cf7e71bf3..9eca3b383ab0 100644 --- a/packages/backend/src/services/chart/charts/per-user-reactions.ts +++ b/packages/backend/src/services/chart/charts/per-user-reactions.ts @@ -1,9 +1,12 @@ import { Injectable, Inject } from '@nestjs/common'; -import { User } from '@/models/entities/user.js'; -import { Note } from '@/models/entities/note.js'; +import type { User } from '@/models/entities/user.js'; +import type { Note } from '@/models/entities/note.js'; import { Users } from '@/models/index.js'; -import Chart, { KVs } from '../core.js'; +import type { AppLockService } from '@/services/AppLockService.js'; +import Chart from '../core.js'; import { name, schema } from './entities/per-user-reactions.js'; +import type { KVs } from '../core.js'; +import type { DataSource } from 'typeorm'; /** * ユーザーごとのリアクションに関するチャート @@ -11,8 +14,13 @@ import { name, schema } from './entities/per-user-reactions.js'; // eslint-disable-next-line import/no-default-export @Injectable() export default class PerUserReactionsChart extends Chart { - constructor() { - super(name, schema, true); + constructor( + @Inject('db') + private db: DataSource, + + private appLockService: AppLockService, + ) { + super(db, appLockService.getChartInsertLock, name, schema, true); } protected async tickMajor(group: string): Promise>> { diff --git a/packages/backend/src/services/chart/charts/test-grouped.ts b/packages/backend/src/services/chart/charts/test-grouped.ts index 03956a9c4c56..11af7ff44d8b 100644 --- a/packages/backend/src/services/chart/charts/test-grouped.ts +++ b/packages/backend/src/services/chart/charts/test-grouped.ts @@ -1,7 +1,9 @@ import { Injectable, Inject } from '@nestjs/common'; -import { DataSource } from 'typeorm'; -import Chart, { KVs } from '../core.js'; +import type { AppLockService } from '@/services/AppLockService.js'; +import Chart from '../core.js'; import { name, schema } from './entities/test-grouped.js'; +import type { DataSource , DataSource } from 'typeorm'; +import type { KVs } from '../core.js'; /** * For testing @@ -11,8 +13,13 @@ import { name, schema } from './entities/test-grouped.js'; export default class TestGroupedChart extends Chart { private total = {} as Record; - constructor(db: DataSource) { - super(db, name, schema, true); + constructor( + @Inject('db') + private db: DataSource, + + private appLockService: AppLockService, + ) { + super(db, appLockService.getChartInsertLock, name, schema, true); } protected async tickMajor(group: string): Promise>> { diff --git a/packages/backend/src/services/chart/charts/test-intersection.ts b/packages/backend/src/services/chart/charts/test-intersection.ts index ad2768a5875e..e7e5ac99f23c 100644 --- a/packages/backend/src/services/chart/charts/test-intersection.ts +++ b/packages/backend/src/services/chart/charts/test-intersection.ts @@ -1,7 +1,9 @@ import { Injectable, Inject } from '@nestjs/common'; -import { DataSource } from 'typeorm'; -import Chart, { KVs } from '../core.js'; +import type { AppLockService } from '@/services/AppLockService.js'; +import Chart from '../core.js'; import { name, schema } from './entities/test-intersection.js'; +import type { DataSource , DataSource } from 'typeorm'; +import type { KVs } from '../core.js'; /** * For testing @@ -9,8 +11,13 @@ import { name, schema } from './entities/test-intersection.js'; // eslint-disable-next-line import/no-default-export @Injectable() export default class TestIntersectionChart extends Chart { - constructor(db: DataSource) { - super(db, name, schema); + constructor( + @Inject('db') + private db: DataSource, + + private appLockService: AppLockService, + ) { + super(db, appLockService.getChartInsertLock, name, schema); } protected async tickMajor(): Promise>> { diff --git a/packages/backend/src/services/chart/charts/test-unique.ts b/packages/backend/src/services/chart/charts/test-unique.ts index 61624de632fc..b7a2ca6e0f2c 100644 --- a/packages/backend/src/services/chart/charts/test-unique.ts +++ b/packages/backend/src/services/chart/charts/test-unique.ts @@ -1,7 +1,9 @@ import { Injectable, Inject } from '@nestjs/common'; -import { DataSource } from 'typeorm'; -import Chart, { KVs } from '../core.js'; +import type { AppLockService } from '@/services/AppLockService.js'; +import Chart from '../core.js'; import { name, schema } from './entities/test-unique.js'; +import type { DataSource , DataSource } from 'typeorm'; +import type { KVs } from '../core.js'; /** * For testing @@ -9,8 +11,13 @@ import { name, schema } from './entities/test-unique.js'; // eslint-disable-next-line import/no-default-export @Injectable() export default class TestUniqueChart extends Chart { - constructor(db: DataSource) { - super(db, name, schema); + constructor( + @Inject('db') + private db: DataSource, + + private appLockService: AppLockService, + ) { + super(db, appLockService.getChartInsertLock, name, schema); } protected async tickMajor(): Promise>> { diff --git a/packages/backend/src/services/chart/charts/test.ts b/packages/backend/src/services/chart/charts/test.ts index d2229b97b8e5..a92e9cbf1cab 100644 --- a/packages/backend/src/services/chart/charts/test.ts +++ b/packages/backend/src/services/chart/charts/test.ts @@ -1,7 +1,9 @@ import { Injectable, Inject } from '@nestjs/common'; -import { DataSource } from 'typeorm'; -import Chart, { KVs } from '../core.js'; +import type { AppLockService } from '@/services/AppLockService.js'; +import Chart from '../core.js'; import { name, schema } from './entities/test.js'; +import type { DataSource , DataSource } from 'typeorm'; +import type { KVs } from '../core.js'; /** * For testing @@ -11,8 +13,13 @@ import { name, schema } from './entities/test.js'; export default class TestChart extends Chart { public total = 0; // publicにするのはテストのため - constructor(db: DataSource) { - super(db, name, schema); + constructor( + @Inject('db') + private db: DataSource, + + private appLockService: AppLockService, + ) { + super(db, appLockService.getChartInsertLock, name, schema); } protected async tickMajor(): Promise>> { diff --git a/packages/backend/src/services/chart/charts/users.ts b/packages/backend/src/services/chart/charts/users.ts index 10d4fb5e3e77..da8eb62ba0a9 100644 --- a/packages/backend/src/services/chart/charts/users.ts +++ b/packages/backend/src/services/chart/charts/users.ts @@ -1,9 +1,12 @@ import { Injectable, Inject } from '@nestjs/common'; import { Not, IsNull } from 'typeorm'; import { Users } from '@/models/index.js'; -import { User } from '@/models/entities/user.js'; -import Chart, { KVs } from '../core.js'; +import type { User } from '@/models/entities/user.js'; +import type { AppLockService } from '@/services/AppLockService.js'; +import Chart from '../core.js'; import { name, schema } from './entities/users.js'; +import type { KVs } from '../core.js'; +import type { DataSource } from 'typeorm'; /** * ユーザー数に関するチャート @@ -11,8 +14,13 @@ import { name, schema } from './entities/users.js'; // eslint-disable-next-line import/no-default-export @Injectable() export default class UsersChart extends Chart { - constructor() { - super(name, schema); + constructor( + @Inject('db') + private db: DataSource, + + private appLockService: AppLockService, + ) { + super(db, appLockService.getChartInsertLock, name, schema); } protected async tickMajor(): Promise>> { diff --git a/packages/backend/src/services/chart/core.ts b/packages/backend/src/services/chart/core.ts index 77b21ad6f8a1..ff2c9aa4dca4 100644 --- a/packages/backend/src/services/chart/core.ts +++ b/packages/backend/src/services/chart/core.ts @@ -5,10 +5,10 @@ */ import * as nestedProperty from 'nested-property'; -import { EntitySchema, Repository, LessThan, Between, DataSource } from 'typeorm'; +import { EntitySchema, LessThan, Between } from 'typeorm'; import { dateUTC, isTimeSame, isTimeBefore, subtractTime, addTime } from '@/prelude/time.js'; -import { getChartInsertLock } from '@/misc/app-lock.js'; import Logger from '../logger.js'; +import type { Repository, DataSource } from 'typeorm'; const logger = new Logger('chart', 'white', process.env.NODE_ENV !== 'test'); @@ -229,9 +229,12 @@ export default abstract class Chart { }; } - constructor(db: DataSource, name: string, schema: T, grouped = false) { + private lock: (key: string) => Promise<() => void>; + + constructor(db: DataSource, lock: (key: string) => Promise<() => void>, name: string, schema: T, grouped = false) { this.name = name; this.schema = schema; + this.lock = lock; const { hour, day } = Chart.schemaToEntity(name, schema, grouped); this.repositoryForHour = db.getRepository<{ id: number; group?: string | null; date: number; }>(hour); @@ -328,7 +331,7 @@ export default abstract class Chart { const date = Chart.dateToTimestamp(current); const lockKey = group ? `${this.name}:${date}:${span}:${group}` : `${this.name}:${date}:${span}`; - const unlock = await getChartInsertLock(lockKey); + const unlock = await this.lock(lockKey); try { // ロック内でもう1回チェックする const currentLog = await repository.findOneBy({ From f42ff470465001ae07532a1e60b7604c2c4a0bef Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 02:21:48 +0900 Subject: [PATCH 022/180] Update AppLockService.ts --- packages/backend/src/services/AppLockService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/src/services/AppLockService.ts b/packages/backend/src/services/AppLockService.ts index 50dce1132d4e..5bc4dd192354 100644 --- a/packages/backend/src/services/AppLockService.ts +++ b/packages/backend/src/services/AppLockService.ts @@ -16,7 +16,6 @@ export class AppLockService { @Inject('redis') private redisClient: Redis.Redis, ) { - console.warn(); this.#lock = promisify(redisLock(this.redisClient, retryDelay)); } From cd287ff8c7634a76ed283226d421201a9acb4356 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 02:58:10 +0900 Subject: [PATCH 023/180] wip --- .../src/services/DeleteAccountService.ts | 37 ++++++++++++++++ .../src/services/FederatedInstanceService.ts | 42 +++++++++++++++++++ .../backend/src/services/delete-account.ts | 23 ---------- .../register-or-fetch-instance-doc.ts | 31 -------------- 4 files changed, 79 insertions(+), 54 deletions(-) create mode 100644 packages/backend/src/services/DeleteAccountService.ts create mode 100644 packages/backend/src/services/FederatedInstanceService.ts delete mode 100644 packages/backend/src/services/delete-account.ts delete mode 100644 packages/backend/src/services/register-or-fetch-instance-doc.ts diff --git a/packages/backend/src/services/DeleteAccountService.ts b/packages/backend/src/services/DeleteAccountService.ts new file mode 100644 index 000000000000..228c67f75416 --- /dev/null +++ b/packages/backend/src/services/DeleteAccountService.ts @@ -0,0 +1,37 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { Users } from '@/models/index.js'; +import type { QueueService } from '@/queue/queue.service.js'; +import type { UserSuspendService } from '@/service/UserSuspendService.js'; +import type { GlobalEventService } from '@/service/GlobalEventService.js'; + +@Injectable() +export class DeleteAccountService { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + private userSuspendService: UserSuspendService, + private queueService: QueueService, + private globalEventServie: GlobalEventService, + ) { + } + + public async deleteAccount(user: { + id: string; + host: string | null; + }): Promise { + // 物理削除する前にDelete activityを送信する + await this.userSuspendService.doPostSuspend(user).catch(e => {}); + + this.queueService.createDeleteAccountJob(user, { + soft: false, + }); + + await this.usersRepository.update(user.id, { + isDeleted: true, + }); + + // Terminate streaming + this.globalEventServie.publishUserEvent(user.id, 'terminate', {}); + } +} diff --git a/packages/backend/src/services/FederatedInstanceService.ts b/packages/backend/src/services/FederatedInstanceService.ts new file mode 100644 index 000000000000..d9a15406c80c --- /dev/null +++ b/packages/backend/src/services/FederatedInstanceService.ts @@ -0,0 +1,42 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { Instances } from '@/models/index.js'; +import type { Instance } from '@/models/entities/instance'; +import { Cache } from '@/misc/cache.js'; +import { genId } from '@/misc/gen-id.js'; +import { toPuny } from '@/misc/convert-host.js'; + +@Injectable() +export class FederatedInstanceService { + #cache: Cache; + + constructor( + @Inject('instancesRepository') + private instancesRepository: typeof Instances, + ) { + this.#cache = new Cache(1000 * 60 * 60); + } + + public async registerOrFetchInstanceDoc(host: string): Promise { + host = toPuny(host); + + const cached = this.#cache.get(host); + if (cached) return cached; + + const index = await this.instancesRepository.findOneBy({ host }); + + if (index == null) { + const i = await this.instancesRepository.insert({ + id: genId(), + host, + caughtAt: new Date(), + lastCommunicatedAt: new Date(), + }).then(x => this.instancesRepository.findOneByOrFail(x.identifiers[0])); + + this.#cache.set(host, i); + return i; + } else { + this.#cache.set(host, index); + return index; + } + } +} diff --git a/packages/backend/src/services/delete-account.ts b/packages/backend/src/services/delete-account.ts deleted file mode 100644 index 0fdceb671b92..000000000000 --- a/packages/backend/src/services/delete-account.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Users } from '@/models/index.js'; -import { createDeleteAccountJob } from '@/queue/index.js'; -import { publishUserEvent } from './stream.js'; -import { doPostSuspend } from './suspend-user.js'; - -export async function deleteAccount(user: { - id: string; - host: string | null; -}): Promise { - // 物理削除する前にDelete activityを送信する - await doPostSuspend(user).catch(e => {}); - - createDeleteAccountJob(user, { - soft: false, - }); - - await Users.update(user.id, { - isDeleted: true, - }); - - // Terminate streaming - publishUserEvent(user.id, 'terminate', {}); -} diff --git a/packages/backend/src/services/register-or-fetch-instance-doc.ts b/packages/backend/src/services/register-or-fetch-instance-doc.ts deleted file mode 100644 index df7d125d0b0f..000000000000 --- a/packages/backend/src/services/register-or-fetch-instance-doc.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Instance } from '@/models/entities/instance.js'; -import { Instances } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; -import { toPuny } from '@/misc/convert-host.js'; -import { Cache } from '@/misc/cache.js'; - -const cache = new Cache(1000 * 60 * 60); - -export async function registerOrFetchInstanceDoc(host: string): Promise { - host = toPuny(host); - - const cached = cache.get(host); - if (cached) return cached; - - const index = await Instances.findOneBy({ host }); - - if (index == null) { - const i = await Instances.insert({ - id: genId(), - host, - caughtAt: new Date(), - lastCommunicatedAt: new Date(), - }).then(x => Instances.findOneByOrFail(x.identifiers[0])); - - cache.set(host, i); - return i; - } else { - cache.set(host, index); - return index; - } -} From 836be94fcaf4e771ece34bc63bfc1c4a0e4b25c9 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 03:15:59 +0900 Subject: [PATCH 024/180] wip --- .../src/services/DeleteAccountService.ts | 4 +- .../src/services/UserBlockingService.ts | 191 ++++++++++++++++++ .../backend/src/services/blocking/create.ts | 145 ------------- .../backend/src/services/blocking/delete.ts | 34 ---- 4 files changed, 193 insertions(+), 181 deletions(-) create mode 100644 packages/backend/src/services/UserBlockingService.ts delete mode 100644 packages/backend/src/services/blocking/create.ts delete mode 100644 packages/backend/src/services/blocking/delete.ts diff --git a/packages/backend/src/services/DeleteAccountService.ts b/packages/backend/src/services/DeleteAccountService.ts index 228c67f75416..203bec375e5d 100644 --- a/packages/backend/src/services/DeleteAccountService.ts +++ b/packages/backend/src/services/DeleteAccountService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users } from '@/models/index.js'; import type { QueueService } from '@/queue/queue.service.js'; -import type { UserSuspendService } from '@/service/UserSuspendService.js'; -import type { GlobalEventService } from '@/service/GlobalEventService.js'; +import type { UserSuspendService } from '@/services/UserSuspendService.js'; +import type { GlobalEventService } from '@/services/GlobalEventService.js'; @Injectable() export class DeleteAccountService { diff --git a/packages/backend/src/services/UserBlockingService.ts b/packages/backend/src/services/UserBlockingService.ts new file mode 100644 index 000000000000..8b0476ae2f56 --- /dev/null +++ b/packages/backend/src/services/UserBlockingService.ts @@ -0,0 +1,191 @@ + +import { Inject, Injectable } from '@nestjs/common'; +import type { FollowRequests , Followings , UserLists , UserListJoinings , Users , Blockings } from '@/models/index.js'; + +import { genId } from '@/misc/gen-id.js'; +import type { CacheableUser, User } from '@/models/entities/user.js'; +import type { Blocking } from '@/models/entities/blocking.js'; +import type { QueueService } from '@/queue/queue.service.js'; +import type { GlobalEventService } from '@/services/GlobalEventService.js'; + +@Injectable() +export class UserBlockingService { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + @Inject('followRequestsRepository') + private followRequestsRepository: typeof FollowRequests, + + @Inject('blockingRepository') + private blockingRepository: typeof Blockings, + + @Inject('userListsRepository') + private userListsRepository: typeof UserLists, + + @Inject('userListJoiningsRepository') + private userListJoiningsRepository: typeof UserListJoinings, + + private queueService: QueueService, + private globalEventServie: GlobalEventService, + ) { + } + + async block(blocker: User, blockee: User) { + await Promise.all([ + this.#cancelRequest(blocker, blockee), + this.#cancelRequest(blockee, blocker), + this.#unFollow(blocker, blockee), + this.#unFollow(blockee, blocker), + this.#removeFromList(blockee, blocker), + ]); + + const blocking = { + id: genId(), + createdAt: new Date(), + blocker, + blockerId: blocker.id, + blockee, + blockeeId: blockee.id, + } as Blocking; + + await this.blockingRepository.insert(blocking); + + if (this.usersRepository.isLocalUser(blocker) && this.usersRepository.isRemoteUser(blockee)) { + const content = renderActivity(renderBlock(blocking)); + this.queueService.deliver(blocker, content, blockee.inbox); + } + } + + async #cancelRequest(follower: User, followee: User) { + const request = await this.followRequestsRepository.findOneBy({ + followeeId: followee.id, + followerId: follower.id, + }); + + if (request == null) { + return; + } + + await this.followRequestsRepository.delete({ + followeeId: followee.id, + followerId: follower.id, + }); + + if (this.usersRepository.isLocalUser(followee)) { + this.usersRepository.pack(followee, followee, { + detail: true, + }).then(packed => this.globalEventServie.publishMainStream(followee.id, 'meUpdated', packed)); + } + + if (this.usersRepository.isLocalUser(follower)) { + this.usersRepository.pack(followee, follower, { + detail: true, + }).then(async packed => { + this.globalEventServie.publishUserEvent(follower.id, 'unfollow', packed); + this.globalEventServie.publishMainStream(follower.id, 'unfollow', packed); + + const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow')); + for (const webhook of webhooks) { + this.queueService.webhookDeliver(webhook, 'unfollow', { + user: packed, + }); + } + }); + } + + // リモートにフォローリクエストをしていたらUndoFollow送信 + if (this.usersRepository.isLocalUser(follower) && this.usersRepository.isRemoteUser(followee)) { + const content = renderActivity(renderUndo(renderFollow(follower, followee), follower)); + this.queueService.deliver(follower, content, followee.inbox); + } + + // リモートからフォローリクエストを受けていたらReject送信 + if (this.usersRepository.isRemoteUser(follower) && this.usersRepository.isLocalUser(followee)) { + const content = renderActivity(renderReject(renderFollow(follower, followee, request.requestId!), followee)); + this.queueService.deliver(followee, content, follower.inbox); + } + } + + async #unFollow(follower: User, followee: User) { + const following = await this.followingsRepository.findOneBy({ + followerId: follower.id, + followeeId: followee.id, + }); + + if (following == null) { + return; + } + + await Promise.all([ + this.followingsRepository.delete(following.id), + this.usersRepository.decrement({ id: follower.id }, 'followingCount', 1), + this.usersRepository.decrement({ id: followee.id }, 'followersCount', 1), + perUserFollowingChart.update(follower, followee, false), + ]); + + // Publish unfollow event + if (this.usersRepository.isLocalUser(follower)) { + this.usersRepository.pack(followee, follower, { + detail: true, + }).then(async packed => { + this.globalEventServie.publishUserEvent(follower.id, 'unfollow', packed); + this.globalEventServie.publishMainStream(follower.id, 'unfollow', packed); + + const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow')); + for (const webhook of webhooks) { + this.queueService.webhookDeliver(webhook, 'unfollow', { + user: packed, + }); + } + }); + } + + // リモートにフォローをしていたらUndoFollow送信 + if (this.usersRepository.isLocalUser(follower) && this.usersRepository.isRemoteUser(followee)) { + const content = renderActivity(renderUndo(renderFollow(follower, followee), follower)); + this.queueService.deliver(follower, content, followee.inbox); + } + } + + async #removeFromList(listOwner: User, user: User) { + const userLists = await this.userListsRepository.findBy({ + userId: listOwner.id, + }); + + for (const userList of userLists) { + await this.userListJoiningsRepository.delete({ + userListId: userList.id, + userId: user.id, + }); + } + } + + async unblock(blocker: CacheableUser, blockee: CacheableUser) { + const blocking = await this.blockingRepository.findOneBy({ + blockerId: blocker.id, + blockeeId: blockee.id, + }); + + if (blocking == null) { + logger.warn('ブロック解除がリクエストされましたがブロックしていませんでした'); + return; + } + + // Since we already have the blocker and blockee, we do not need to fetch + // them in the query above and can just manually insert them here. + blocking.blocker = blocker; + blocking.blockee = blockee; + + await this.blockingRepository.delete(blocking.id); + + // deliver if remote bloking + if (this.usersRepository.isLocalUser(blocker) && this.usersRepository.isRemoteUser(blockee)) { + const content = renderActivity(renderUndo(renderBlock(blocking), blocker)); + this.queueService.deliver(blocker, content, blockee.inbox); + } + } +} diff --git a/packages/backend/src/services/blocking/create.ts b/packages/backend/src/services/blocking/create.ts deleted file mode 100644 index a2c61cca229f..000000000000 --- a/packages/backend/src/services/blocking/create.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { publishMainStream, publishUserEvent } from '@/services/stream.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import renderFollow from '@/remote/activitypub/renderer/follow.js'; -import renderUndo from '@/remote/activitypub/renderer/undo.js'; -import { renderBlock } from '@/remote/activitypub/renderer/block.js'; -import { deliver } from '@/queue/index.js'; -import renderReject from '@/remote/activitypub/renderer/reject.js'; -import { Blocking } from '@/models/entities/blocking.js'; -import { User } from '@/models/entities/user.js'; -import { Blockings, Users, FollowRequests, Followings, UserListJoinings, UserLists } from '@/models/index.js'; -import { perUserFollowingChart } from '@/services/chart/index.js'; -import { genId } from '@/misc/gen-id.js'; -import { IdentifiableError } from '@/misc/identifiable-error.js'; -import { getActiveWebhooks } from '@/misc/webhook-cache.js'; -import { webhookDeliver } from '@/queue/index.js'; - -export default async function(blocker: User, blockee: User) { - await Promise.all([ - cancelRequest(blocker, blockee), - cancelRequest(blockee, blocker), - unFollow(blocker, blockee), - unFollow(blockee, blocker), - removeFromList(blockee, blocker), - ]); - - const blocking = { - id: genId(), - createdAt: new Date(), - blocker, - blockerId: blocker.id, - blockee, - blockeeId: blockee.id, - } as Blocking; - - await Blockings.insert(blocking); - - if (Users.isLocalUser(blocker) && Users.isRemoteUser(blockee)) { - const content = renderActivity(renderBlock(blocking)); - deliver(blocker, content, blockee.inbox); - } -} - -async function cancelRequest(follower: User, followee: User) { - const request = await FollowRequests.findOneBy({ - followeeId: followee.id, - followerId: follower.id, - }); - - if (request == null) { - return; - } - - await FollowRequests.delete({ - followeeId: followee.id, - followerId: follower.id, - }); - - if (Users.isLocalUser(followee)) { - Users.pack(followee, followee, { - detail: true, - }).then(packed => publishMainStream(followee.id, 'meUpdated', packed)); - } - - if (Users.isLocalUser(follower)) { - Users.pack(followee, follower, { - detail: true, - }).then(async packed => { - publishUserEvent(follower.id, 'unfollow', packed); - publishMainStream(follower.id, 'unfollow', packed); - - const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow')); - for (const webhook of webhooks) { - webhookDeliver(webhook, 'unfollow', { - user: packed, - }); - } - }); - } - - // リモートにフォローリクエストをしていたらUndoFollow送信 - if (Users.isLocalUser(follower) && Users.isRemoteUser(followee)) { - const content = renderActivity(renderUndo(renderFollow(follower, followee), follower)); - deliver(follower, content, followee.inbox); - } - - // リモートからフォローリクエストを受けていたらReject送信 - if (Users.isRemoteUser(follower) && Users.isLocalUser(followee)) { - const content = renderActivity(renderReject(renderFollow(follower, followee, request.requestId!), followee)); - deliver(followee, content, follower.inbox); - } -} - -async function unFollow(follower: User, followee: User) { - const following = await Followings.findOneBy({ - followerId: follower.id, - followeeId: followee.id, - }); - - if (following == null) { - return; - } - - await Promise.all([ - Followings.delete(following.id), - Users.decrement({ id: follower.id }, 'followingCount', 1), - Users.decrement({ id: followee.id }, 'followersCount', 1), - perUserFollowingChart.update(follower, followee, false), - ]); - - // Publish unfollow event - if (Users.isLocalUser(follower)) { - Users.pack(followee, follower, { - detail: true, - }).then(async packed => { - publishUserEvent(follower.id, 'unfollow', packed); - publishMainStream(follower.id, 'unfollow', packed); - - const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow')); - for (const webhook of webhooks) { - webhookDeliver(webhook, 'unfollow', { - user: packed, - }); - } - }); - } - - // リモートにフォローをしていたらUndoFollow送信 - if (Users.isLocalUser(follower) && Users.isRemoteUser(followee)) { - const content = renderActivity(renderUndo(renderFollow(follower, followee), follower)); - deliver(follower, content, followee.inbox); - } -} - -async function removeFromList(listOwner: User, user: User) { - const userLists = await UserLists.findBy({ - userId: listOwner.id, - }); - - for (const userList of userLists) { - await UserListJoinings.delete({ - userListId: userList.id, - userId: user.id, - }); - } -} diff --git a/packages/backend/src/services/blocking/delete.ts b/packages/backend/src/services/blocking/delete.ts deleted file mode 100644 index cb16651bc009..000000000000 --- a/packages/backend/src/services/blocking/delete.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import { renderBlock } from '@/remote/activitypub/renderer/block.js'; -import renderUndo from '@/remote/activitypub/renderer/undo.js'; -import { deliver } from '@/queue/index.js'; -import Logger from '../logger.js'; -import { CacheableUser, User } from '@/models/entities/user.js'; -import { Blockings, Users } from '@/models/index.js'; - -const logger = new Logger('blocking/delete'); - -export default async function(blocker: CacheableUser, blockee: CacheableUser) { - const blocking = await Blockings.findOneBy({ - blockerId: blocker.id, - blockeeId: blockee.id, - }); - - if (blocking == null) { - logger.warn('ブロック解除がリクエストされましたがブロックしていませんでした'); - return; - } - - // Since we already have the blocker and blockee, we do not need to fetch - // them in the query above and can just manually insert them here. - blocking.blocker = blocker; - blocking.blockee = blockee; - - Blockings.delete(blocking.id); - - // deliver if remote bloking - if (Users.isLocalUser(blocker) && Users.isRemoteUser(blockee)) { - const content = renderActivity(renderUndo(renderBlock(blocking), blocker)); - deliver(blocker, content, blockee.inbox); - } -} From a6130072e5805ed46b8b24163551d056a4a91e1a Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 03:44:40 +0900 Subject: [PATCH 025/180] wip --- packages/backend/src/misc/webhook-cache.ts | 49 --- .../src/services/UserBlockingService.ts | 19 +- .../backend/src/services/UserCacheService.ts | 2 +- .../src/services/UserFollowingService.ts | 302 ++++++++++++++++++ .../backend/src/services/WebhookService.ts | 69 ++++ .../backend/src/services/following/create.ts | 201 ------------ .../backend/src/services/following/delete.ts | 83 ----- 7 files changed, 386 insertions(+), 339 deletions(-) delete mode 100644 packages/backend/src/misc/webhook-cache.ts create mode 100644 packages/backend/src/services/UserFollowingService.ts create mode 100644 packages/backend/src/services/WebhookService.ts delete mode 100644 packages/backend/src/services/following/create.ts delete mode 100644 packages/backend/src/services/following/delete.ts diff --git a/packages/backend/src/misc/webhook-cache.ts b/packages/backend/src/misc/webhook-cache.ts deleted file mode 100644 index 4bd233366103..000000000000 --- a/packages/backend/src/misc/webhook-cache.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Webhooks } from '@/models/index.js'; -import { Webhook } from '@/models/entities/webhook.js'; -import { subsdcriber } from '../db/redis.js'; - -let webhooksFetched = false; -let webhooks: Webhook[] = []; - -export async function getActiveWebhooks() { - if (!webhooksFetched) { - webhooks = await Webhooks.findBy({ - active: true, - }); - webhooksFetched = true; - } - - return webhooks; -} - -subsdcriber.on('message', async (_, data) => { - const obj = JSON.parse(data); - - if (obj.channel === 'internal') { - const { type, body } = obj.message; - switch (type) { - case 'webhookCreated': - if (body.active) { - webhooks.push(body); - } - break; - case 'webhookUpdated': - if (body.active) { - const i = webhooks.findIndex(a => a.id === body.id); - if (i > -1) { - webhooks[i] = body; - } else { - webhooks.push(body); - } - } else { - webhooks = webhooks.filter(a => a.id !== body.id); - } - break; - case 'webhookDeleted': - webhooks = webhooks.filter(a => a.id !== body.id); - break; - default: - break; - } - } -}); diff --git a/packages/backend/src/services/UserBlockingService.ts b/packages/backend/src/services/UserBlockingService.ts index 8b0476ae2f56..84be9d77525f 100644 --- a/packages/backend/src/services/UserBlockingService.ts +++ b/packages/backend/src/services/UserBlockingService.ts @@ -7,6 +7,13 @@ import type { CacheableUser, User } from '@/models/entities/user.js'; import type { Blocking } from '@/models/entities/blocking.js'; import type { QueueService } from '@/queue/queue.service.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import { renderActivity } from '@/remote/activitypub/renderer'; +import { renderBlock } from '@/remote/activitypub/renderer/block'; +import renderFollow from '@/remote/activitypub/renderer/follow.js'; +import renderUndo from '@/remote/activitypub/renderer/undo.js'; +import renderReject from '@/remote/activitypub/renderer/reject.js'; +import type PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; +import type { WebhookService } from '@/services/webhookService.js'; @Injectable() export class UserBlockingService { @@ -31,10 +38,12 @@ export class UserBlockingService { private queueService: QueueService, private globalEventServie: GlobalEventService, + private webhookService: WebhookService, + private perUserFollowingChart: PerUserFollowingChart, ) { } - async block(blocker: User, blockee: User) { + public async block(blocker: User, blockee: User) { await Promise.all([ this.#cancelRequest(blocker, blockee), this.#cancelRequest(blockee, blocker), @@ -88,7 +97,7 @@ export class UserBlockingService { this.globalEventServie.publishUserEvent(follower.id, 'unfollow', packed); this.globalEventServie.publishMainStream(follower.id, 'unfollow', packed); - const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow')); + const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow')); for (const webhook of webhooks) { this.queueService.webhookDeliver(webhook, 'unfollow', { user: packed, @@ -124,7 +133,7 @@ export class UserBlockingService { this.followingsRepository.delete(following.id), this.usersRepository.decrement({ id: follower.id }, 'followingCount', 1), this.usersRepository.decrement({ id: followee.id }, 'followersCount', 1), - perUserFollowingChart.update(follower, followee, false), + this.perUserFollowingChart.update(follower, followee, false), ]); // Publish unfollow event @@ -135,7 +144,7 @@ export class UserBlockingService { this.globalEventServie.publishUserEvent(follower.id, 'unfollow', packed); this.globalEventServie.publishMainStream(follower.id, 'unfollow', packed); - const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow')); + const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow')); for (const webhook of webhooks) { this.queueService.webhookDeliver(webhook, 'unfollow', { user: packed, @@ -164,7 +173,7 @@ export class UserBlockingService { } } - async unblock(blocker: CacheableUser, blockee: CacheableUser) { + public async unblock(blocker: CacheableUser, blockee: CacheableUser) { const blocking = await this.blockingRepository.findOneBy({ blockerId: blocker.id, blockeeId: blockee.id, diff --git a/packages/backend/src/services/UserCacheService.ts b/packages/backend/src/services/UserCacheService.ts index 0a6738213754..81d9c2b0eb9b 100644 --- a/packages/backend/src/services/UserCacheService.ts +++ b/packages/backend/src/services/UserCacheService.ts @@ -6,7 +6,7 @@ import type Redis from 'ioredis'; import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() -export class UserSuspendService implements OnApplicationShutdown { +export class UserCacheService implements OnApplicationShutdown { public userByIdCache: Cache; public localUserByNativeTokenCache: Cache; public localUserByIdCache: Cache; diff --git a/packages/backend/src/services/UserFollowingService.ts b/packages/backend/src/services/UserFollowingService.ts new file mode 100644 index 000000000000..91ada19d9c38 --- /dev/null +++ b/packages/backend/src/services/UserFollowingService.ts @@ -0,0 +1,302 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { Users , Blockings, Followings, UserProfiles , FollowRequests, Instances } from '@/models/index.js'; + +import type { User } from '@/models/entities/user'; +import { renderActivity } from '@/remote/activitypub/renderer/index.js'; +import renderFollow from '@/remote/activitypub/renderer/follow.js'; +import renderAccept from '@/remote/activitypub/renderer/accept.js'; +import renderReject from '@/remote/activitypub/renderer/reject.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; +import type { QueueService } from '@/queue/queue.service.js'; +import type PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; +import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import { genId } from '@/misc/gen-id.js'; +import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; +import renderUndo from '@/remote/activitypub/renderer/undo.js'; +import type { Packed } from '@/misc/schema.js'; +import type InstanceChart from '@/services/chart/charts/instance.js'; +import type { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; +import type { WebhookService } from '@/services/webhookService.js'; +import Logger from './logger.js'; + +const logger = new Logger('following/create'); + +@Injectable() +export class UserFollowingService { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + @Inject('followRequestsRepository') + private followRequestsRepository: typeof FollowRequests, + + @Inject('blockingRepository') + private blockingRepository: typeof Blockings, + + @Inject('instancesRepository') + private instancesRepository: typeof Instances, + + private queueService: QueueService, + private globalEventServie: GlobalEventService, + private federatedInstanceService: FederatedInstanceService, + private webhookService: WebhookService, + private perUserFollowingChart: PerUserFollowingChart, + private instanceChart: InstanceChart, + ) { + } + + public async follow(_follower: { id: User['id'] }, _followee: { id: User['id'] }, requestId?: string) { + const [follower, followee] = await Promise.all([ + this.usersRepository.findOneByOrFail({ id: _follower.id }), + this.usersRepository.findOneByOrFail({ id: _followee.id }), + ]); + + // check blocking + const [blocking, blocked] = await Promise.all([ + this.blockingRepository.findOneBy({ + blockerId: follower.id, + blockeeId: followee.id, + }), + this.blockingRepository.findOneBy({ + blockerId: followee.id, + blockeeId: follower.id, + }), + ]); + + if (this.usersRepository.isRemoteUser(follower) && this.usersRepository.isLocalUser(followee) && blocked) { + // リモートフォローを受けてブロックしていた場合は、エラーにするのではなくRejectを送り返しておしまい。 + const content = renderActivity(renderReject(renderFollow(follower, followee, requestId), followee)); + this.queueService.deliver(followee , content, follower.inbox); + return; + } else if (this.usersRepository.isRemoteUser(follower) && this.usersRepository.isLocalUser(followee) && blocking) { + // リモートフォローを受けてブロックされているはずの場合だったら、ブロック解除しておく。 + await this.blockingRepository.delete(blocking.id); + } else { + // それ以外は単純に例外 + if (blocking != null) throw new IdentifiableError('710e8fb0-b8c3-4922-be49-d5d93d8e6a6e', 'blocking'); + if (blocked != null) throw new IdentifiableError('3338392a-f764-498d-8855-db939dcf8c48', 'blocked'); + } + + const followeeProfile = await this.userProfilesRepository.findOneByOrFail({ userId: followee.id }); + + // フォロー対象が鍵アカウントである or + // フォロワーがBotであり、フォロー対象がBotからのフォローに慎重である or + // フォロワーがローカルユーザーであり、フォロー対象がリモートユーザーである + // 上記のいずれかに当てはまる場合はすぐフォローせずにフォローリクエストを発行しておく + if (followee.isLocked || (followeeProfile.carefulBot && follower.isBot) || (this.usersRepository.isLocalUser(follower) && this.usersRepository.isRemoteUser(followee))) { + let autoAccept = false; + + // 鍵アカウントであっても、既にフォローされていた場合はスルー + const following = await this.followingsRepository.findOneBy({ + followerId: follower.id, + followeeId: followee.id, + }); + if (following) { + autoAccept = true; + } + + // フォローしているユーザーは自動承認オプション + if (!autoAccept && (this.usersRepository.isLocalUser(followee) && followeeProfile.autoAcceptFollowed)) { + const followed = await this.followingsRepository.findOneBy({ + followerId: followee.id, + followeeId: follower.id, + }); + + if (followed) autoAccept = true; + } + + if (!autoAccept) { + await createFollowRequest(follower, followee, requestId); + return; + } + } + + await this.insertFollowingDoc(followee, follower); + + if (this.usersRepository.isRemoteUser(follower) && this.usersRepository.isLocalUser(followee)) { + const content = renderActivity(renderAccept(renderFollow(follower, followee, requestId), followee)); + this.queueService.deliver(followee, content, follower.inbox); + } + } + + public async insertFollowingDoc(followee: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'] }, follower: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'] }) { + if (follower.id === followee.id) return; + + let alreadyFollowed = false; + + await this.followingsRepository.insert({ + id: genId(), + createdAt: new Date(), + followerId: follower.id, + followeeId: followee.id, + + // 非正規化 + followerHost: follower.host, + followerInbox: this.usersRepository.isRemoteUser(follower) ? follower.inbox : null, + followerSharedInbox: this.usersRepository.isRemoteUser(follower) ? follower.sharedInbox : null, + followeeHost: followee.host, + followeeInbox: this.usersRepository.isRemoteUser(followee) ? followee.inbox : null, + followeeSharedInbox: this.usersRepository.isRemoteUser(followee) ? followee.sharedInbox : null, + }).catch(e => { + if (isDuplicateKeyValueError(e) && this.usersRepository.isRemoteUser(follower) && this.usersRepository.isLocalUser(followee)) { + logger.info(`Insert duplicated ignore. ${follower.id} => ${followee.id}`); + alreadyFollowed = true; + } else { + throw e; + } + }); + + const req = await this.followRequestsRepository.findOneBy({ + followeeId: followee.id, + followerId: follower.id, + }); + + if (req) { + await this.followRequestsRepository.delete({ + followeeId: followee.id, + followerId: follower.id, + }); + + // 通知を作成 + createNotification(follower.id, 'followRequestAccepted', { + notifierId: followee.id, + }); + } + + if (alreadyFollowed) return; + + //#region Increment counts + await Promise.all([ + this.usersRepository.increment({ id: follower.id }, 'followingCount', 1), + this.usersRepository.increment({ id: followee.id }, 'followersCount', 1), + ]); + //#endregion + + //#region Update instance stats + if (this.usersRepository.isRemoteUser(follower) && this.usersRepository.isLocalUser(followee)) { + this.federatedInstanceService.registerOrFetchInstanceDoc(follower.host).then(i => { + this.instancesRepository.increment({ id: i.id }, 'followingCount', 1); + this.instanceChart.updateFollowing(i.host, true); + }); + } else if (this.usersRepository.isLocalUser(follower) && this.usersRepository.isRemoteUser(followee)) { + this.federatedInstanceService.registerOrFetchInstanceDoc(followee.host).then(i => { + this.instancesRepository.increment({ id: i.id }, 'followersCount', 1); + this.instanceChart.updateFollowers(i.host, true); + }); + } + //#endregion + + this.perUserFollowingChart.update(follower, followee, true); + + // Publish follow event + if (this.usersRepository.isLocalUser(follower)) { + this.usersRepository.pack(followee.id, follower, { + detail: true, + }).then(async packed => { + this.globalEventServie.publishUserEvent(follower.id, 'follow', packed as Packed<'UserDetailedNotMe'>); + this.globalEventServie.publishMainStream(follower.id, 'follow', packed as Packed<'UserDetailedNotMe'>); + + const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('follow')); + for (const webhook of webhooks) { + this.queueService.webhookDeliver(webhook, 'follow', { + user: packed, + }); + } + }); + } + + // Publish followed event + if (this.usersRepository.isLocalUser(followee)) { + this.usersRepository.pack(follower.id, followee).then(async packed => { + this.globalEventServie.publishMainStream(followee.id, 'followed', packed); + + const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === followee.id && x.on.includes('followed')); + for (const webhook of webhooks) { + this.queueService.webhookDeliver(webhook, 'followed', { + user: packed, + }); + } + }); + + // 通知を作成 + createNotification(followee.id, 'follow', { + notifierId: follower.id, + }); + } + } + + public async unfollow(follower: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; }, followee: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; }, silent = false) { + const following = await this.followingsRepository.findOneBy({ + followerId: follower.id, + followeeId: followee.id, + }); + + if (following == null) { + logger.warn('フォロー解除がリクエストされましたがフォローしていませんでした'); + return; + } + + await this.followingsRepository.delete(following.id); + + this.decrementFollowing(follower, followee); + + // Publish unfollow event + if (!silent && this.usersRepository.isLocalUser(follower)) { + this.usersRepository.pack(followee.id, follower, { + detail: true, + }).then(async packed => { + this.globalEventServie.publishUserEvent(follower.id, 'unfollow', packed); + this.globalEventServie.publishMainStream(follower.id, 'unfollow', packed); + + const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow')); + for (const webhook of webhooks) { + this.queueService.webhookDeliver(webhook, 'unfollow', { + user: packed, + }); + } + }); + } + + if (this.usersRepository.isLocalUser(follower) && this.usersRepository.isRemoteUser(followee)) { + const content = renderActivity(renderUndo(renderFollow(follower, followee), follower)); + this.queueService.deliver(follower, content, followee.inbox); + } + + if (this.usersRepository.isLocalUser(followee) && this.usersRepository.isRemoteUser(follower)) { + // local user has null host + const content = renderActivity(renderReject(renderFollow(follower, followee), followee)); + this.queueService.deliver(followee, content, follower.inbox); + } + } + + public async decrementFollowing(follower: { id: User['id']; host: User['host']; }, followee: { id: User['id']; host: User['host']; }) { + //#region Decrement following / followers counts + await Promise.all([ + this.usersRepository.decrement({ id: follower.id }, 'followingCount', 1), + this.usersRepository.decrement({ id: followee.id }, 'followersCount', 1), + ]); + //#endregion + + //#region Update instance stats + if (this.usersRepository.isRemoteUser(follower) && this.usersRepository.isLocalUser(followee)) { + this.federatedInstanceService.registerOrFetchInstanceDoc(follower.host).then(i => { + this.instancesRepository.decrement({ id: i.id }, 'followingCount', 1); + this.instanceChart.updateFollowing(i.host, false); + }); + } else if (this.usersRepository.isLocalUser(follower) && this.usersRepository.isRemoteUser(followee)) { + this.federatedInstanceService.registerOrFetchInstanceDoc(followee.host).then(i => { + this.instancesRepository.decrement({ id: i.id }, 'followersCount', 1); + this.instanceChart.updateFollowers(i.host, false); + }); + } + //#endregion + + this.perUserFollowingChart.update(follower, followee, false); + } +} diff --git a/packages/backend/src/services/WebhookService.ts b/packages/backend/src/services/WebhookService.ts new file mode 100644 index 000000000000..a2bc12b7c13f --- /dev/null +++ b/packages/backend/src/services/WebhookService.ts @@ -0,0 +1,69 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { Webhooks } from '@/models/index.js'; +import type { Webhook } from '@/models/entities/webhook'; +import type Redis from 'ioredis'; +import type { OnApplicationShutdown } from '@nestjs/common'; + +@Injectable() +export class WebhookService implements OnApplicationShutdown { + #webhooksFetched = false; + #webhooks: Webhook[] = []; + + constructor( + @Inject('redisSubscriber') + private redisSubscriber: Redis.Redis, + + @Inject('webhooksRepository') + private webhooksRepository: typeof Webhooks, + ) { + this.onMessage = this.onMessage.bind(this); + this.redisSubscriber.on('message', this.onMessage); + } + + public async getActiveWebhooks() { + if (!this.#webhooksFetched) { + this.#webhooks = await this.webhooksRepository.findBy({ + active: true, + }); + this.#webhooksFetched = true; + } + + return this.#webhooks; + } + + private async onMessage(_, data) { + const obj = JSON.parse(data); + + if (obj.channel === 'internal') { + const { type, body } = obj.message; + switch (type) { + case 'webhookCreated': + if (body.active) { + this.#webhooks.push(body); + } + break; + case 'webhookUpdated': + if (body.active) { + const i = this.#webhooks.findIndex(a => a.id === body.id); + if (i > -1) { + this.#webhooks[i] = body; + } else { + this.#webhooks.push(body); + } + } else { + this.#webhooks = this.#webhooks.filter(a => a.id !== body.id); + } + break; + case 'webhookDeleted': + this.#webhooks = this.#webhooks.filter(a => a.id !== body.id); + break; + default: + break; + } + } + } + + public onApplicationShutdown(signal?: string | undefined) { + this.redisSubscriber.off('message', this.onMessage); + } +} diff --git a/packages/backend/src/services/following/create.ts b/packages/backend/src/services/following/create.ts deleted file mode 100644 index 72c24676bb21..000000000000 --- a/packages/backend/src/services/following/create.ts +++ /dev/null @@ -1,201 +0,0 @@ -import { publishMainStream, publishUserEvent } from '@/services/stream.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import renderFollow from '@/remote/activitypub/renderer/follow.js'; -import renderAccept from '@/remote/activitypub/renderer/accept.js'; -import renderReject from '@/remote/activitypub/renderer/reject.js'; -import { deliver } from '@/queue/index.js'; -import createFollowRequest from './requests/create.js'; -import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js'; -import Logger from '../logger.js'; -import { IdentifiableError } from '@/misc/identifiable-error.js'; -import { User } from '@/models/entities/user.js'; -import { Followings, Users, FollowRequests, Blockings, Instances, UserProfiles } from '@/models/index.js'; -import { instanceChart, perUserFollowingChart } from '@/services/chart/index.js'; -import { genId } from '@/misc/gen-id.js'; -import { createNotification } from '../create-notification.js'; -import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; -import { Packed } from '@/misc/schema.js'; -import { getActiveWebhooks } from '@/misc/webhook-cache.js'; -import { webhookDeliver } from '@/queue/index.js'; - -const logger = new Logger('following/create'); - -export async function insertFollowingDoc(followee: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'] }, follower: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'] }) { - if (follower.id === followee.id) return; - - let alreadyFollowed = false; - - await Followings.insert({ - id: genId(), - createdAt: new Date(), - followerId: follower.id, - followeeId: followee.id, - - // 非正規化 - followerHost: follower.host, - followerInbox: Users.isRemoteUser(follower) ? follower.inbox : null, - followerSharedInbox: Users.isRemoteUser(follower) ? follower.sharedInbox : null, - followeeHost: followee.host, - followeeInbox: Users.isRemoteUser(followee) ? followee.inbox : null, - followeeSharedInbox: Users.isRemoteUser(followee) ? followee.sharedInbox : null, - }).catch(e => { - if (isDuplicateKeyValueError(e) && Users.isRemoteUser(follower) && Users.isLocalUser(followee)) { - logger.info(`Insert duplicated ignore. ${follower.id} => ${followee.id}`); - alreadyFollowed = true; - } else { - throw e; - } - }); - - const req = await FollowRequests.findOneBy({ - followeeId: followee.id, - followerId: follower.id, - }); - - if (req) { - await FollowRequests.delete({ - followeeId: followee.id, - followerId: follower.id, - }); - - // 通知を作成 - createNotification(follower.id, 'followRequestAccepted', { - notifierId: followee.id, - }); - } - - if (alreadyFollowed) return; - - //#region Increment counts - await Promise.all([ - Users.increment({ id: follower.id }, 'followingCount', 1), - Users.increment({ id: followee.id }, 'followersCount', 1), - ]); - //#endregion - - //#region Update instance stats - if (Users.isRemoteUser(follower) && Users.isLocalUser(followee)) { - registerOrFetchInstanceDoc(follower.host).then(i => { - Instances.increment({ id: i.id }, 'followingCount', 1); - instanceChart.updateFollowing(i.host, true); - }); - } else if (Users.isLocalUser(follower) && Users.isRemoteUser(followee)) { - registerOrFetchInstanceDoc(followee.host).then(i => { - Instances.increment({ id: i.id }, 'followersCount', 1); - instanceChart.updateFollowers(i.host, true); - }); - } - //#endregion - - perUserFollowingChart.update(follower, followee, true); - - // Publish follow event - if (Users.isLocalUser(follower)) { - Users.pack(followee.id, follower, { - detail: true, - }).then(async packed => { - publishUserEvent(follower.id, 'follow', packed as Packed<"UserDetailedNotMe">); - publishMainStream(follower.id, 'follow', packed as Packed<"UserDetailedNotMe">); - - const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('follow')); - for (const webhook of webhooks) { - webhookDeliver(webhook, 'follow', { - user: packed, - }); - } - }); - } - - // Publish followed event - if (Users.isLocalUser(followee)) { - Users.pack(follower.id, followee).then(async packed => { - publishMainStream(followee.id, 'followed', packed); - - const webhooks = (await getActiveWebhooks()).filter(x => x.userId === followee.id && x.on.includes('followed')); - for (const webhook of webhooks) { - webhookDeliver(webhook, 'followed', { - user: packed, - }); - } - }); - - // 通知を作成 - createNotification(followee.id, 'follow', { - notifierId: follower.id, - }); - } -} - -export default async function(_follower: { id: User['id'] }, _followee: { id: User['id'] }, requestId?: string) { - const [follower, followee] = await Promise.all([ - Users.findOneByOrFail({ id: _follower.id }), - Users.findOneByOrFail({ id: _followee.id }), - ]); - - // check blocking - const [blocking, blocked] = await Promise.all([ - Blockings.findOneBy({ - blockerId: follower.id, - blockeeId: followee.id, - }), - Blockings.findOneBy({ - blockerId: followee.id, - blockeeId: follower.id, - }), - ]); - - if (Users.isRemoteUser(follower) && Users.isLocalUser(followee) && blocked) { - // リモートフォローを受けてブロックしていた場合は、エラーにするのではなくRejectを送り返しておしまい。 - const content = renderActivity(renderReject(renderFollow(follower, followee, requestId), followee)); - deliver(followee , content, follower.inbox); - return; - } else if (Users.isRemoteUser(follower) && Users.isLocalUser(followee) && blocking) { - // リモートフォローを受けてブロックされているはずの場合だったら、ブロック解除しておく。 - await Blockings.delete(blocking.id); - } else { - // それ以外は単純に例外 - if (blocking != null) throw new IdentifiableError('710e8fb0-b8c3-4922-be49-d5d93d8e6a6e', 'blocking'); - if (blocked != null) throw new IdentifiableError('3338392a-f764-498d-8855-db939dcf8c48', 'blocked'); - } - - const followeeProfile = await UserProfiles.findOneByOrFail({ userId: followee.id }); - - // フォロー対象が鍵アカウントである or - // フォロワーがBotであり、フォロー対象がBotからのフォローに慎重である or - // フォロワーがローカルユーザーであり、フォロー対象がリモートユーザーである - // 上記のいずれかに当てはまる場合はすぐフォローせずにフォローリクエストを発行しておく - if (followee.isLocked || (followeeProfile.carefulBot && follower.isBot) || (Users.isLocalUser(follower) && Users.isRemoteUser(followee))) { - let autoAccept = false; - - // 鍵アカウントであっても、既にフォローされていた場合はスルー - const following = await Followings.findOneBy({ - followerId: follower.id, - followeeId: followee.id, - }); - if (following) { - autoAccept = true; - } - - // フォローしているユーザーは自動承認オプション - if (!autoAccept && (Users.isLocalUser(followee) && followeeProfile.autoAcceptFollowed)) { - const followed = await Followings.findOneBy({ - followerId: followee.id, - followeeId: follower.id, - }); - - if (followed) autoAccept = true; - } - - if (!autoAccept) { - await createFollowRequest(follower, followee, requestId); - return; - } - } - - await insertFollowingDoc(followee, follower); - - if (Users.isRemoteUser(follower) && Users.isLocalUser(followee)) { - const content = renderActivity(renderAccept(renderFollow(follower, followee, requestId), followee)); - deliver(followee, content, follower.inbox); - } -} diff --git a/packages/backend/src/services/following/delete.ts b/packages/backend/src/services/following/delete.ts deleted file mode 100644 index 91b5a3d61db9..000000000000 --- a/packages/backend/src/services/following/delete.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { publishMainStream, publishUserEvent } from '@/services/stream.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import renderFollow from '@/remote/activitypub/renderer/follow.js'; -import renderUndo from '@/remote/activitypub/renderer/undo.js'; -import renderReject from '@/remote/activitypub/renderer/reject.js'; -import { deliver, webhookDeliver } from '@/queue/index.js'; -import Logger from '../logger.js'; -import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js'; -import { User } from '@/models/entities/user.js'; -import { Followings, Users, Instances } from '@/models/index.js'; -import { instanceChart, perUserFollowingChart } from '@/services/chart/index.js'; -import { getActiveWebhooks } from '@/misc/webhook-cache.js'; - -const logger = new Logger('following/delete'); - -export default async function(follower: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; }, followee: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; }, silent = false) { - const following = await Followings.findOneBy({ - followerId: follower.id, - followeeId: followee.id, - }); - - if (following == null) { - logger.warn('フォロー解除がリクエストされましたがフォローしていませんでした'); - return; - } - - await Followings.delete(following.id); - - decrementFollowing(follower, followee); - - // Publish unfollow event - if (!silent && Users.isLocalUser(follower)) { - Users.pack(followee.id, follower, { - detail: true, - }).then(async packed => { - publishUserEvent(follower.id, 'unfollow', packed); - publishMainStream(follower.id, 'unfollow', packed); - - const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow')); - for (const webhook of webhooks) { - webhookDeliver(webhook, 'unfollow', { - user: packed, - }); - } - }); - } - - if (Users.isLocalUser(follower) && Users.isRemoteUser(followee)) { - const content = renderActivity(renderUndo(renderFollow(follower, followee), follower)); - deliver(follower, content, followee.inbox); - } - - if (Users.isLocalUser(followee) && Users.isRemoteUser(follower)) { - // local user has null host - const content = renderActivity(renderReject(renderFollow(follower, followee), followee)); - deliver(followee, content, follower.inbox); - } -} - -export async function decrementFollowing(follower: { id: User['id']; host: User['host']; }, followee: { id: User['id']; host: User['host']; }) { - //#region Decrement following / followers counts - await Promise.all([ - Users.decrement({ id: follower.id }, 'followingCount', 1), - Users.decrement({ id: followee.id }, 'followersCount', 1), - ]); - //#endregion - - //#region Update instance stats - if (Users.isRemoteUser(follower) && Users.isLocalUser(followee)) { - registerOrFetchInstanceDoc(follower.host).then(i => { - Instances.decrement({ id: i.id }, 'followingCount', 1); - instanceChart.updateFollowing(i.host, false); - }); - } else if (Users.isLocalUser(follower) && Users.isRemoteUser(followee)) { - registerOrFetchInstanceDoc(followee.host).then(i => { - Instances.decrement({ id: i.id }, 'followersCount', 1); - instanceChart.updateFollowers(i.host, false); - }); - } - //#endregion - - perUserFollowingChart.update(follower, followee, false); -} From a6d1bb0d351bf789d54b6d868235f259d2e1c986 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 07:17:38 +0900 Subject: [PATCH 026/180] wip --- packages/backend/src/app.module.ts | 5 + packages/backend/src/config/index.ts | 3 - packages/backend/src/config/load.ts | 4 +- packages/backend/src/di-symbols.ts | 3 + .../src/services/UserFollowingService.ts | 286 +++++++++++++++++- .../backend/src/services/following/reject.ts | 122 -------- .../services/following/requests/accept-all.ts | 18 -- .../src/services/following/requests/accept.ts | 31 -- .../src/services/following/requests/cancel.ts | 36 --- .../src/services/following/requests/create.ts | 63 ---- .../src/services/note/NoteCreateService.ts | 47 +-- 11 files changed, 313 insertions(+), 305 deletions(-) delete mode 100644 packages/backend/src/config/index.ts create mode 100644 packages/backend/src/di-symbols.ts delete mode 100644 packages/backend/src/services/following/reject.ts delete mode 100644 packages/backend/src/services/following/requests/accept-all.ts delete mode 100644 packages/backend/src/services/following/requests/accept.ts delete mode 100644 packages/backend/src/services/following/requests/cancel.ts delete mode 100644 packages/backend/src/services/following/requests/create.ts diff --git a/packages/backend/src/app.module.ts b/packages/backend/src/app.module.ts index 59b80b096552..0b0d66ee7b84 100644 --- a/packages/backend/src/app.module.ts +++ b/packages/backend/src/app.module.ts @@ -3,6 +3,8 @@ import { Notes, Users } from '@/models/index.js'; import { EndpointsModule } from '@/server/api/endpoints.module.js'; import { QueueModule } from '@/queue/queue.module.js'; import { ChartsModule } from './services/chart/charts.module'; +import { DI_SYMBOLS } from './di-symbols'; +import { loadConfig } from './config/load'; @Module({ imports: [ @@ -11,6 +13,9 @@ import { ChartsModule } from './services/chart/charts.module'; QueueModule, ], providers: [{ + provide: DI_SYMBOLS.config, + useValue: loadConfig(), + }, { provide: 'usersRepository', useValue: Users, }, { diff --git a/packages/backend/src/config/index.ts b/packages/backend/src/config/index.ts deleted file mode 100644 index 3e53b0003604..000000000000 --- a/packages/backend/src/config/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import load from './load.js'; - -export default load(); diff --git a/packages/backend/src/config/load.ts b/packages/backend/src/config/load.ts index 9654a4f3b078..d547c5af712c 100644 --- a/packages/backend/src/config/load.ts +++ b/packages/backend/src/config/load.ts @@ -6,7 +6,7 @@ import * as fs from 'node:fs'; import { fileURLToPath } from 'node:url'; import { dirname } from 'node:path'; import * as yaml from 'js-yaml'; -import { Source, Mixin } from './types.js'; +import type { Source, Mixin } from './types.js'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); @@ -23,7 +23,7 @@ const path = process.env.NODE_ENV === 'test' ? `${dir}/test.yml` : `${dir}/default.yml`; -export default function load() { +export function loadConfig() { const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/meta.json`, 'utf-8')); const clientManifest = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/_client_dist_/manifest.json`, 'utf-8')); const config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source; diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts new file mode 100644 index 000000000000..677cd910952e --- /dev/null +++ b/packages/backend/src/di-symbols.ts @@ -0,0 +1,3 @@ +export const DI_SYMBOLS = { + config: Symbol('config'), +}; diff --git a/packages/backend/src/services/UserFollowingService.ts b/packages/backend/src/services/UserFollowingService.ts index 91ada19d9c38..93ee0f08cbb9 100644 --- a/packages/backend/src/services/UserFollowingService.ts +++ b/packages/backend/src/services/UserFollowingService.ts @@ -1,7 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { Users , Blockings, Followings, UserProfiles , FollowRequests, Instances } from '@/models/index.js'; - -import type { User } from '@/models/entities/user'; +import type { Users , Followings, FollowRequests , UserProfiles , Instances , Blockings } from '@/models/index.js'; +import type { CacheableUser, ILocalUser, IRemoteUser, User } from '@/models/entities/user'; import { renderActivity } from '@/remote/activitypub/renderer/index.js'; import renderFollow from '@/remote/activitypub/renderer/follow.js'; import renderAccept from '@/remote/activitypub/renderer/accept.js'; @@ -21,6 +20,19 @@ import Logger from './logger.js'; const logger = new Logger('following/create'); +type Local = ILocalUser | { + id: ILocalUser['id']; + host: ILocalUser['host']; + uri: ILocalUser['uri'] +}; +type Remote = IRemoteUser | { + id: IRemoteUser['id']; + host: IRemoteUser['host']; + uri: IRemoteUser['uri']; + inbox: IRemoteUser['inbox']; +}; +type Both = Local | Remote; + @Injectable() export class UserFollowingService { constructor( @@ -51,7 +63,7 @@ export class UserFollowingService { ) { } - public async follow(_follower: { id: User['id'] }, _followee: { id: User['id'] }, requestId?: string) { + public async follow(_follower: { id: User['id'] }, _followee: { id: User['id'] }, requestId?: string): Promise { const [follower, followee] = await Promise.all([ this.usersRepository.findOneByOrFail({ id: _follower.id }), this.usersRepository.findOneByOrFail({ id: _followee.id }), @@ -125,10 +137,17 @@ export class UserFollowingService { } } - public async insertFollowingDoc(followee: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'] }, follower: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'] }) { + public async insertFollowingDoc( + followee: { + id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'] + }, + follower: { + id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'] + }, + ): Promise { if (follower.id === followee.id) return; - let alreadyFollowed = false; + let alreadyFollowed = false as boolean; await this.followingsRepository.insert({ id: genId(), @@ -231,7 +250,15 @@ export class UserFollowingService { } } - public async unfollow(follower: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; }, followee: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; }, silent = false) { + public async unfollow( + follower: { + id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; + }, + followee: { + id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; + }, + silent = false, + ): Promise { const following = await this.followingsRepository.findOneBy({ followerId: follower.id, followeeId: followee.id, @@ -275,7 +302,10 @@ export class UserFollowingService { } } - public async decrementFollowing(follower: { id: User['id']; host: User['host']; }, followee: { id: User['id']; host: User['host']; }) { + public async decrementFollowing( + follower: {id: User['id']; host: User['host']; }, + followee: { id: User['id']; host: User['host']; }, + ): Promise { //#region Decrement following / followers counts await Promise.all([ this.usersRepository.decrement({ id: follower.id }, 'followingCount', 1), @@ -299,4 +329,244 @@ export class UserFollowingService { this.perUserFollowingChart.update(follower, followee, false); } + + public async createFollowRequest( + follower: { + id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; + }, + followee: { + id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; + }, + requestId?: string, + ): Promise { + if (follower.id === followee.id) return; + + // check blocking + const [blocking, blocked] = await Promise.all([ + this.blockingRepository.findOneBy({ + blockerId: follower.id, + blockeeId: followee.id, + }), + this.blockingRepository.findOneBy({ + blockerId: followee.id, + blockeeId: follower.id, + }), + ]); + + if (blocking != null) throw new Error('blocking'); + if (blocked != null) throw new Error('blocked'); + + const followRequest = await this.followRequestsRepository.insert({ + id: genId(), + createdAt: new Date(), + followerId: follower.id, + followeeId: followee.id, + requestId, + + // 非正規化 + followerHost: follower.host, + followerInbox: this.usersRepository.isRemoteUser(follower) ? follower.inbox : undefined, + followerSharedInbox: this.usersRepository.isRemoteUser(follower) ? follower.sharedInbox : undefined, + followeeHost: followee.host, + followeeInbox: this.usersRepository.isRemoteUser(followee) ? followee.inbox : undefined, + followeeSharedInbox: this.usersRepository.isRemoteUser(followee) ? followee.sharedInbox : undefined, + }).then(x => this.followRequestsRepository.findOneByOrFail(x.identifiers[0])); + + // Publish receiveRequest event + if (this.usersRepository.isLocalUser(followee)) { + this.usersRepository.pack(follower.id, followee).then(packed => this.globalEventServie.publishMainStream(followee.id, 'receiveFollowRequest', packed)); + + this.usersRepository.pack(followee.id, followee, { + detail: true, + }).then(packed => this.globalEventServie.publishMainStream(followee.id, 'meUpdated', packed)); + + // 通知を作成 + createNotification(followee.id, 'receiveFollowRequest', { + notifierId: follower.id, + followRequestId: followRequest.id, + }); + } + + if (this.usersRepository.isLocalUser(follower) && this.usersRepository.isRemoteUser(followee)) { + const content = renderActivity(renderFollow(follower, followee)); + this.queueService.deliver(follower, content, followee.inbox); + } + } + + public async cancelFollowRequest( + followee: { + id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox'] + }, + follower: { + id: User['id']; host: User['host']; uri: User['host'] + }, + ): Promise { + if (this.usersRepository.isRemoteUser(followee)) { + const content = renderActivity(renderUndo(renderFollow(follower, followee), follower)); + + if (this.usersRepository.isLocalUser(follower)) { // 本来このチェックは不要だけどTSに怒られるので + this.queueService.deliver(follower, content, followee.inbox); + } + } + + const request = await this.followRequestsRepository.findOneBy({ + followeeId: followee.id, + followerId: follower.id, + }); + + if (request == null) { + throw new IdentifiableError('17447091-ce07-46dd-b331-c1fd4f15b1e7', 'request not found'); + } + + await this.followRequestsRepository.delete({ + followeeId: followee.id, + followerId: follower.id, + }); + + this.usersRepository.pack(followee.id, followee, { + detail: true, + }).then(packed => this.globalEventServie.publishMainStream(followee.id, 'meUpdated', packed)); + } + + public async acceptFollowRequest( + followee: { + id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; + }, + follower: CacheableUser, + ): Promise { + const request = await this.followRequestsRepository.findOneBy({ + followeeId: followee.id, + followerId: follower.id, + }); + + if (request == null) { + throw new IdentifiableError('8884c2dd-5795-4ac9-b27e-6a01d38190f9', 'No follow request.'); + } + + await this.insertFollowingDoc(followee, follower); + + if (this.usersRepository.isRemoteUser(follower) && this.usersRepository.isLocalUser(followee)) { + const content = renderActivity(renderAccept(renderFollow(follower, followee, request.requestId!), followee)); + this.queueService.deliver(followee, content, follower.inbox); + } + + this.usersRepository.pack(followee.id, followee, { + detail: true, + }).then(packed => this.globalEventServie.publishMainStream(followee.id, 'meUpdated', packed)); + } + + public async acceptAllFollowRequest( + user: { + id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; + }, + ): Promise { + const requests = await this.followRequestsRepository.findBy({ + followeeId: user.id, + }); + + for (const request of requests) { + const follower = await this.usersRepository.findOneByOrFail({ id: request.followerId }); + this.acceptFollowRequest(user, follower); + } + } + + /** + * API following/request/reject + */ + public async rejectFollowRequest(user: Local, follower: Both): Promise { + if (this.usersRepository.isRemoteUser(follower)) { + this.#deliverReject(user, follower); + } + + await this.#removeFollowRequest(user, follower); + + if (this.usersRepository.isLocalUser(follower)) { + this.#publishUnfollow(user, follower); + } + } + + /** + * API following/reject + */ + public async rejectFollow(user: Local, follower: Both): Promise { + if (this.usersRepository.isRemoteUser(follower)) { + this.#deliverReject(user, follower); + } + + await this.#removeFollow(user, follower); + + if (this.usersRepository.isLocalUser(follower)) { + this.#publishUnfollow(user, follower); + } + } + + /** + * AP Reject/Follow + */ + public async remoteReject(actor: Remote, follower: Local): Promise { + await this.#removeFollowRequest(actor, follower); + await this.#removeFollow(actor, follower); + this.#publishUnfollow(actor, follower); + } + + /** + * Remove follow request record + */ + async #removeFollowRequest(followee: Both, follower: Both): Promise { + const request = await this.followRequestsRepository.findOneBy({ + followeeId: followee.id, + followerId: follower.id, + }); + + if (!request) return; + + await this.followRequestsRepository.delete(request.id); + } + + /** + * Remove follow record + */ + async #removeFollow(followee: Both, follower: Both): Promise { + const following = await this.followingsRepository.findOneBy({ + followeeId: followee.id, + followerId: follower.id, + }); + + if (!following) return; + + await this.followingsRepository.delete(following.id); + this.decrementFollowing(follower, followee); + } + + /** + * Deliver Reject to remote + */ + async #deliverReject(followee: Local, follower: Remote): Promise { + const request = await this.followRequestsRepository.findOneBy({ + followeeId: followee.id, + followerId: follower.id, + }); + + const content = renderActivity(renderReject(renderFollow(follower, followee, request?.requestId || undefined), followee)); + this.queueService.deliver(followee, content, follower.inbox); + } + + /** + * Publish unfollow to local + */ + async #publishUnfollow(followee: Both, follower: Local): Promise { + const packedFollowee = await this.usersRepository.pack(followee.id, follower, { + detail: true, + }); + + this.globalEventServie.publishUserEvent(follower.id, 'unfollow', packedFollowee); + this.globalEventServie.publishMainStream(follower.id, 'unfollow', packedFollowee); + + const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow')); + for (const webhook of webhooks) { + this.queueService.webhookDeliver(webhook, 'unfollow', { + user: packedFollowee, + }); + } + } } diff --git a/packages/backend/src/services/following/reject.ts b/packages/backend/src/services/following/reject.ts deleted file mode 100644 index 691fca245678..000000000000 --- a/packages/backend/src/services/following/reject.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import renderFollow from '@/remote/activitypub/renderer/follow.js'; -import renderReject from '@/remote/activitypub/renderer/reject.js'; -import { deliver, webhookDeliver } from '@/queue/index.js'; -import { publishMainStream, publishUserEvent } from '@/services/stream.js'; -import { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; -import { Users, FollowRequests, Followings } from '@/models/index.js'; -import { decrementFollowing } from './delete.js'; -import { getActiveWebhooks } from '@/misc/webhook-cache.js'; - -type Local = ILocalUser | { - id: ILocalUser['id']; - host: ILocalUser['host']; - uri: ILocalUser['uri'] -}; -type Remote = IRemoteUser | { - id: IRemoteUser['id']; - host: IRemoteUser['host']; - uri: IRemoteUser['uri']; - inbox: IRemoteUser['inbox']; -}; -type Both = Local | Remote; - -/** - * API following/request/reject - */ -export async function rejectFollowRequest(user: Local, follower: Both) { - if (Users.isRemoteUser(follower)) { - deliverReject(user, follower); - } - - await removeFollowRequest(user, follower); - - if (Users.isLocalUser(follower)) { - publishUnfollow(user, follower); - } -} - -/** - * API following/reject - */ -export async function rejectFollow(user: Local, follower: Both) { - if (Users.isRemoteUser(follower)) { - deliverReject(user, follower); - } - - await removeFollow(user, follower); - - if (Users.isLocalUser(follower)) { - publishUnfollow(user, follower); - } -} - -/** - * AP Reject/Follow - */ -export async function remoteReject(actor: Remote, follower: Local) { - await removeFollowRequest(actor, follower); - await removeFollow(actor, follower); - publishUnfollow(actor, follower); -} - -/** - * Remove follow request record - */ -async function removeFollowRequest(followee: Both, follower: Both) { - const request = await FollowRequests.findOneBy({ - followeeId: followee.id, - followerId: follower.id, - }); - - if (!request) return; - - await FollowRequests.delete(request.id); -} - -/** - * Remove follow record - */ -async function removeFollow(followee: Both, follower: Both) { - const following = await Followings.findOneBy({ - followeeId: followee.id, - followerId: follower.id, - }); - - if (!following) return; - - await Followings.delete(following.id); - decrementFollowing(follower, followee); -} - -/** - * Deliver Reject to remote - */ -async function deliverReject(followee: Local, follower: Remote) { - const request = await FollowRequests.findOneBy({ - followeeId: followee.id, - followerId: follower.id, - }); - - const content = renderActivity(renderReject(renderFollow(follower, followee, request?.requestId || undefined), followee)); - deliver(followee, content, follower.inbox); -} - -/** - * Publish unfollow to local - */ -async function publishUnfollow(followee: Both, follower: Local) { - const packedFollowee = await Users.pack(followee.id, follower, { - detail: true, - }); - - publishUserEvent(follower.id, 'unfollow', packedFollowee); - publishMainStream(follower.id, 'unfollow', packedFollowee); - - const webhooks = (await getActiveWebhooks()).filter(x => x.userId === follower.id && x.on.includes('unfollow')); - for (const webhook of webhooks) { - webhookDeliver(webhook, 'unfollow', { - user: packedFollowee, - }); - } -} diff --git a/packages/backend/src/services/following/requests/accept-all.ts b/packages/backend/src/services/following/requests/accept-all.ts deleted file mode 100644 index 5fbb549e01a6..000000000000 --- a/packages/backend/src/services/following/requests/accept-all.ts +++ /dev/null @@ -1,18 +0,0 @@ -import accept from './accept.js'; -import { User } from '@/models/entities/user.js'; -import { FollowRequests, Users } from '@/models/index.js'; - -/** - * 指定したユーザー宛てのフォローリクエストをすべて承認 - * @param user ユーザー - */ -export default async function(user: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; }) { - const requests = await FollowRequests.findBy({ - followeeId: user.id, - }); - - for (const request of requests) { - const follower = await Users.findOneByOrFail({ id: request.followerId }); - accept(user, follower); - } -} diff --git a/packages/backend/src/services/following/requests/accept.ts b/packages/backend/src/services/following/requests/accept.ts deleted file mode 100644 index 20829f70c7f3..000000000000 --- a/packages/backend/src/services/following/requests/accept.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import renderFollow from '@/remote/activitypub/renderer/follow.js'; -import renderAccept from '@/remote/activitypub/renderer/accept.js'; -import { deliver } from '@/queue/index.js'; -import { publishMainStream } from '@/services/stream.js'; -import { insertFollowingDoc } from '../create.js'; -import { User, ILocalUser, CacheableUser } from '@/models/entities/user.js'; -import { FollowRequests, Users } from '@/models/index.js'; -import { IdentifiableError } from '@/misc/identifiable-error.js'; - -export default async function(followee: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; }, follower: CacheableUser) { - const request = await FollowRequests.findOneBy({ - followeeId: followee.id, - followerId: follower.id, - }); - - if (request == null) { - throw new IdentifiableError('8884c2dd-5795-4ac9-b27e-6a01d38190f9', 'No follow request.'); - } - - await insertFollowingDoc(followee, follower); - - if (Users.isRemoteUser(follower) && Users.isLocalUser(followee)) { - const content = renderActivity(renderAccept(renderFollow(follower, followee, request.requestId!), followee)); - deliver(followee, content, follower.inbox); - } - - Users.pack(followee.id, followee, { - detail: true, - }).then(packed => publishMainStream(followee.id, 'meUpdated', packed)); -} diff --git a/packages/backend/src/services/following/requests/cancel.ts b/packages/backend/src/services/following/requests/cancel.ts deleted file mode 100644 index 56531fa1fdcb..000000000000 --- a/packages/backend/src/services/following/requests/cancel.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import renderFollow from '@/remote/activitypub/renderer/follow.js'; -import renderUndo from '@/remote/activitypub/renderer/undo.js'; -import { deliver } from '@/queue/index.js'; -import { publishMainStream } from '@/services/stream.js'; -import { IdentifiableError } from '@/misc/identifiable-error.js'; -import { User, ILocalUser } from '@/models/entities/user.js'; -import { Users, FollowRequests } from '@/models/index.js'; - -export default async function(followee: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox'] }, follower: { id: User['id']; host: User['host']; uri: User['host'] }) { - if (Users.isRemoteUser(followee)) { - const content = renderActivity(renderUndo(renderFollow(follower, followee), follower)); - - if (Users.isLocalUser(follower)) { // 本来このチェックは不要だけどTSに怒られるので - deliver(follower, content, followee.inbox); - } - } - - const request = await FollowRequests.findOneBy({ - followeeId: followee.id, - followerId: follower.id, - }); - - if (request == null) { - throw new IdentifiableError('17447091-ce07-46dd-b331-c1fd4f15b1e7', 'request not found'); - } - - await FollowRequests.delete({ - followeeId: followee.id, - followerId: follower.id, - }); - - Users.pack(followee.id, followee, { - detail: true, - }).then(packed => publishMainStream(followee.id, 'meUpdated', packed)); -} diff --git a/packages/backend/src/services/following/requests/create.ts b/packages/backend/src/services/following/requests/create.ts deleted file mode 100644 index bda2f8f92d03..000000000000 --- a/packages/backend/src/services/following/requests/create.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { publishMainStream } from '@/services/stream.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import renderFollow from '@/remote/activitypub/renderer/follow.js'; -import { deliver } from '@/queue/index.js'; -import { User } from '@/models/entities/user.js'; -import { Blockings, FollowRequests, Users } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; -import { createNotification } from '../../create-notification.js'; - -export default async function(follower: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; }, followee: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; }, requestId?: string) { - if (follower.id === followee.id) return; - - // check blocking - const [blocking, blocked] = await Promise.all([ - Blockings.findOneBy({ - blockerId: follower.id, - blockeeId: followee.id, - }), - Blockings.findOneBy({ - blockerId: followee.id, - blockeeId: follower.id, - }), - ]); - - if (blocking != null) throw new Error('blocking'); - if (blocked != null) throw new Error('blocked'); - - const followRequest = await FollowRequests.insert({ - id: genId(), - createdAt: new Date(), - followerId: follower.id, - followeeId: followee.id, - requestId, - - // 非正規化 - followerHost: follower.host, - followerInbox: Users.isRemoteUser(follower) ? follower.inbox : undefined, - followerSharedInbox: Users.isRemoteUser(follower) ? follower.sharedInbox : undefined, - followeeHost: followee.host, - followeeInbox: Users.isRemoteUser(followee) ? followee.inbox : undefined, - followeeSharedInbox: Users.isRemoteUser(followee) ? followee.sharedInbox : undefined, - }).then(x => FollowRequests.findOneByOrFail(x.identifiers[0])); - - // Publish receiveRequest event - if (Users.isLocalUser(followee)) { - Users.pack(follower.id, followee).then(packed => publishMainStream(followee.id, 'receiveFollowRequest', packed)); - - Users.pack(followee.id, followee, { - detail: true, - }).then(packed => publishMainStream(followee.id, 'meUpdated', packed)); - - // 通知を作成 - createNotification(followee.id, 'receiveFollowRequest', { - notifierId: follower.id, - followRequestId: followRequest.id, - }); - } - - if (Users.isLocalUser(follower) && Users.isRemoteUser(followee)) { - const content = renderActivity(renderFollow(follower, followee)); - deliver(follower, content, followee.inbox); - } -} diff --git a/packages/backend/src/services/note/NoteCreateService.ts b/packages/backend/src/services/note/NoteCreateService.ts index 198428a0d9f0..e628c1589124 100644 --- a/packages/backend/src/services/note/NoteCreateService.ts +++ b/packages/backend/src/services/note/NoteCreateService.ts @@ -4,10 +4,12 @@ import { Inject, Injectable } from '@nestjs/common'; import { extractMentions } from '@/misc/extract-mentions.js'; import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js'; import { extractHashtags } from '@/misc/extract-hashtags.js'; -import { Note, IMentionedRemoteUsers } from '@/models/entities/note.js'; -import { Mutings, Users, NoteWatchings, Notes, Instances, UserProfiles, MutedNotes, Channels, ChannelFollowings, NoteThreadMutings } from '@/models/index.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; -import { App } from '@/models/entities/app.js'; +import type { IMentionedRemoteUsers } from '@/models/entities/note.js'; +import { Note } from '@/models/entities/note.js'; +import type { Notes } from '@/models/index.js'; +import { Mutings, Users, NoteWatchings, Instances, UserProfiles, MutedNotes, Channels, ChannelFollowings, NoteThreadMutings } from '@/models/index.js'; +import type { DriveFile } from '@/models/entities/drive-file.js'; +import type { App } from '@/models/entities/app.js'; import { insertNoteUnread } from '@/services/note/unread.js'; import { concat } from '@/prelude/array.js'; import config from '@/config/index.js'; @@ -19,28 +21,29 @@ import renderNote from '@/remote/activitypub/renderer/note.js'; import DeliverManager from '@/remote/activitypub/deliver-manager.js'; import { publishMainStream, publishNotesStream } from '@/services/stream.js'; import { genId } from '@/misc/gen-id.js'; -import { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; -import { Poll, IPoll } from '@/models/entities/poll.js'; +import type { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; +import type { IPoll } from '@/models/entities/poll.js'; +import { Poll } from '@/models/entities/poll.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import { checkHitAntenna } from '@/misc/check-hit-antenna.js'; import { checkWordMute } from '@/misc/check-word-mute.js'; import { countSameRenotes } from '@/misc/count-same-renotes.js'; -import { Channel } from '@/models/entities/channel.js'; +import type { Channel } from '@/models/entities/channel.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { getAntennas } from '@/misc/antenna-cache.js'; import { endedPollNotificationQueue } from '@/queue/queues.js'; import { webhookDeliver } from '@/queue/index.js'; import { Cache } from '@/misc/cache.js'; -import { UserProfile } from '@/models/entities/user-profile.js'; +import type { UserProfile } from '@/models/entities/user-profile.js'; import { db } from '@/db/postgre.js'; -import { getActiveWebhooks } from '@/misc/webhook-cache.js'; +import { getActiveWebhooks } from '@/services/webhook-cache.js'; import es from '../../db/elasticsearch.js'; import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js'; import { updateHashtags } from '../update-hashtag.js'; import { deliverToRelays } from '../relay.js'; import { addNoteToAntenna } from '../add-note-to-antenna.js'; import { createNotification } from '../create-notification.js'; -import { WebhookService } from '../webhookService.js'; +import type { WebhookService } from '../webhookService.js'; import type NotesChart from '../chart/charts/notes.js'; import type PerUserNotesChart from '../chart/charts/per-user-notes.js'; import type ActiveUsersChart from '../chart/charts/active-users.js'; @@ -612,12 +615,12 @@ export class NoteCreateService { #incRenoteCount(renote: Note) { this.notesRepository.createQueryBuilder().update() - .set({ - renoteCount: () => '"renoteCount" + 1', - score: () => '"score" + 1', - }) - .where('id = :id', { id: renote.id }) - .execute(); + .set({ + renoteCount: () => '"renoteCount" + 1', + score: () => '"score" + 1', + }) + .where('id = :id', { id: renote.id }) + .execute(); } async #createMentionedEvents(mentionedUsers: MinimumUser[], note: Note, nm: NotificationManager) { @@ -701,12 +704,12 @@ export class NoteCreateService { #incNotesCountOfUser(user: { id: User['id']; }) { Users.createQueryBuilder().update() - .set({ - updatedAt: new Date(), - notesCount: () => '"notesCount" + 1', - }) - .where('id = :id', { id: user.id }) - .execute(); + .set({ + updatedAt: new Date(), + notesCount: () => '"notesCount" + 1', + }) + .where('id = :id', { id: user.id }) + .execute(); } async #extractMentionedUsers(user: { host: User['host']; }, tokens: mfm.MfmNode[]): Promise { From b8c50ece97feaf317a9f3e4bdedb844a9b736f57 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 07:25:36 +0900 Subject: [PATCH 027/180] wip --- .../src/services/CreateNotificationService.ts | 82 +++++++++++++++++++ .../src/services/UserFollowingService.ts | 10 ++- .../src/services/create-notification.ts | 62 -------------- 3 files changed, 88 insertions(+), 66 deletions(-) create mode 100644 packages/backend/src/services/CreateNotificationService.ts delete mode 100644 packages/backend/src/services/create-notification.ts diff --git a/packages/backend/src/services/CreateNotificationService.ts b/packages/backend/src/services/CreateNotificationService.ts new file mode 100644 index 000000000000..3de940ed4c5b --- /dev/null +++ b/packages/backend/src/services/CreateNotificationService.ts @@ -0,0 +1,82 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { Mutings, Notifications, UserProfiles } from '@/models/index.js'; +import { Users } from '@/models/index.js'; +import type { User } from '@/models/entities/user'; +import type { Notification } from '@/models/entities/notification.js'; +import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import { genId } from '@/misc/gen-id.js'; + +@Injectable() +export class CreateNotificationService { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + @Inject('notificationsRepository') + private notificationsRepository: typeof Notifications, + + @Inject('mutingsRepository') + private mutingsRepository: typeof Mutings, + + private globalEventServie: GlobalEventService, + ) { + } + + public async createNotification( + notifieeId: User['id'], + type: Notification['type'], + data: Partial, + ): Promise { + if (data.notifierId && (notifieeId === data.notifierId)) { + return null; + } + + const profile = await this.userProfilesRepository.findOneBy({ userId: notifieeId }); + + const isMuted = profile?.mutingNotificationTypes.includes(type); + + // Create notification + const notification = await this.notificationsRepository.insert({ + id: genId(), + createdAt: new Date(), + notifieeId: notifieeId, + type: type, + // 相手がこの通知をミュートしているようなら、既読を予めつけておく + isRead: isMuted, + ...data, + } as Partial) + .then(x => this.notificationsRepository.findOneByOrFail(x.identifiers[0])); + + const packed = await this.notificationsRepository.pack(notification, {}); + + // Publish notification event + this.globalEventServie.publishMainStream(notifieeId, 'notification', packed); + + // 2秒経っても(今回作成した)通知が既読にならなかったら「未読の通知がありますよ」イベントを発行する + setTimeout(async () => { + const fresh = await this.notificationsRepository.findOneBy({ id: notification.id }); + if (fresh == null) return; // 既に削除されているかもしれない + if (fresh.isRead) return; + + //#region ただしミュートしているユーザーからの通知なら無視 + const mutings = await this.mutingsRepository.findBy({ + muterId: notifieeId, + }); + if (data.notifierId && mutings.map(m => m.muteeId).includes(data.notifierId)) { + return; + } + //#endregion + + this.globalEventServie.publishMainStream(notifieeId, 'unreadNotification', packed); + pushNotification(notifieeId, 'notification', packed); + + if (type === 'follow') sendEmailNotification.follow(notifieeId, await Users.findOneByOrFail({ id: data.notifierId! })); + if (type === 'receiveFollowRequest') sendEmailNotification.receiveFollowRequest(notifieeId, await Users.findOneByOrFail({ id: data.notifierId! })); + }, 2000); + + return notification; + } +} diff --git a/packages/backend/src/services/UserFollowingService.ts b/packages/backend/src/services/UserFollowingService.ts index 93ee0f08cbb9..3ea47d217aa9 100644 --- a/packages/backend/src/services/UserFollowingService.ts +++ b/packages/backend/src/services/UserFollowingService.ts @@ -17,6 +17,7 @@ import type InstanceChart from '@/services/chart/charts/instance.js'; import type { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; import type { WebhookService } from '@/services/webhookService.js'; import Logger from './logger.js'; +import type { CreateNotificationService } from './CreateNotificationService.js'; const logger = new Logger('following/create'); @@ -56,6 +57,7 @@ export class UserFollowingService { private queueService: QueueService, private globalEventServie: GlobalEventService, + private createNotificationService: CreateNotificationService, private federatedInstanceService: FederatedInstanceService, private webhookService: WebhookService, private perUserFollowingChart: PerUserFollowingChart, @@ -124,7 +126,7 @@ export class UserFollowingService { } if (!autoAccept) { - await createFollowRequest(follower, followee, requestId); + await this.createFollowRequest(follower, followee, requestId); return; } } @@ -183,7 +185,7 @@ export class UserFollowingService { }); // 通知を作成 - createNotification(follower.id, 'followRequestAccepted', { + this.createNotificationService.createNotification(follower.id, 'followRequestAccepted', { notifierId: followee.id, }); } @@ -244,7 +246,7 @@ export class UserFollowingService { }); // 通知を作成 - createNotification(followee.id, 'follow', { + this.createNotificationService.createNotification(followee.id, 'follow', { notifierId: follower.id, }); } @@ -381,7 +383,7 @@ export class UserFollowingService { }).then(packed => this.globalEventServie.publishMainStream(followee.id, 'meUpdated', packed)); // 通知を作成 - createNotification(followee.id, 'receiveFollowRequest', { + this.createNotificationService.createNotification(followee.id, 'receiveFollowRequest', { notifierId: follower.id, followRequestId: followRequest.id, }); diff --git a/packages/backend/src/services/create-notification.ts b/packages/backend/src/services/create-notification.ts deleted file mode 100644 index d53a4235b848..000000000000 --- a/packages/backend/src/services/create-notification.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { publishMainStream } from '@/services/stream.js'; -import { pushNotification } from '@/services/push-notification.js'; -import { Notifications, Mutings, UserProfiles, Users } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; -import { User } from '@/models/entities/user.js'; -import { Notification } from '@/models/entities/notification.js'; -import { sendEmailNotification } from './send-email-notification.js'; - -export async function createNotification( - notifieeId: User['id'], - type: Notification['type'], - data: Partial -) { - if (data.notifierId && (notifieeId === data.notifierId)) { - return null; - } - - const profile = await UserProfiles.findOneBy({ userId: notifieeId }); - - const isMuted = profile?.mutingNotificationTypes.includes(type); - - // Create notification - const notification = await Notifications.insert({ - id: genId(), - createdAt: new Date(), - notifieeId: notifieeId, - type: type, - // 相手がこの通知をミュートしているようなら、既読を予めつけておく - isRead: isMuted, - ...data, - } as Partial) - .then(x => Notifications.findOneByOrFail(x.identifiers[0])); - - const packed = await Notifications.pack(notification, {}); - - // Publish notification event - publishMainStream(notifieeId, 'notification', packed); - - // 2秒経っても(今回作成した)通知が既読にならなかったら「未読の通知がありますよ」イベントを発行する - setTimeout(async () => { - const fresh = await Notifications.findOneBy({ id: notification.id }); - if (fresh == null) return; // 既に削除されているかもしれない - if (fresh.isRead) return; - - //#region ただしミュートしているユーザーからの通知なら無視 - const mutings = await Mutings.findBy({ - muterId: notifieeId, - }); - if (data.notifierId && mutings.map(m => m.muteeId).includes(data.notifierId)) { - return; - } - //#endregion - - publishMainStream(notifieeId, 'unreadNotification', packed); - pushNotification(notifieeId, 'notification', packed); - - if (type === 'follow') sendEmailNotification.follow(notifieeId, await Users.findOneByOrFail({ id: data.notifierId! })); - if (type === 'receiveFollowRequest') sendEmailNotification.receiveFollowRequest(notifieeId, await Users.findOneByOrFail({ id: data.notifierId! })); - }, 2000); - - return notification; -} From d2d593b8b5e35a0599c0896e297a64aa799329f4 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 07:29:13 +0900 Subject: [PATCH 028/180] wip --- packages/backend/src/app.module.ts | 4 ++++ packages/backend/src/di-symbols.ts | 1 + packages/backend/src/services/CreateSystemUserService.ts | 3 ++- packages/backend/src/services/chart/charts/active-users.ts | 3 ++- packages/backend/src/services/chart/charts/ap-request.ts | 3 ++- packages/backend/src/services/chart/charts/drive.ts | 3 ++- packages/backend/src/services/chart/charts/federation.ts | 3 ++- packages/backend/src/services/chart/charts/hashtag.ts | 3 ++- packages/backend/src/services/chart/charts/instance.ts | 3 ++- packages/backend/src/services/chart/charts/notes.ts | 3 ++- packages/backend/src/services/chart/charts/per-user-drive.ts | 3 ++- .../backend/src/services/chart/charts/per-user-following.ts | 3 ++- packages/backend/src/services/chart/charts/per-user-notes.ts | 3 ++- .../backend/src/services/chart/charts/per-user-reactions.ts | 3 ++- packages/backend/src/services/chart/charts/test-grouped.ts | 3 ++- .../backend/src/services/chart/charts/test-intersection.ts | 3 ++- packages/backend/src/services/chart/charts/test-unique.ts | 3 ++- packages/backend/src/services/chart/charts/test.ts | 3 ++- packages/backend/src/services/chart/charts/users.ts | 3 ++- 19 files changed, 39 insertions(+), 17 deletions(-) diff --git a/packages/backend/src/app.module.ts b/packages/backend/src/app.module.ts index 0b0d66ee7b84..b42955dd3352 100644 --- a/packages/backend/src/app.module.ts +++ b/packages/backend/src/app.module.ts @@ -5,6 +5,7 @@ import { QueueModule } from '@/queue/queue.module.js'; import { ChartsModule } from './services/chart/charts.module'; import { DI_SYMBOLS } from './di-symbols'; import { loadConfig } from './config/load'; +import { db } from './db/postgre'; @Module({ imports: [ @@ -16,6 +17,9 @@ import { loadConfig } from './config/load'; provide: DI_SYMBOLS.config, useValue: loadConfig(), }, { + provide: DI_SYMBOLS.db, + useValue: db, + }, { provide: 'usersRepository', useValue: Users, }, { diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts index 677cd910952e..533e449b4b98 100644 --- a/packages/backend/src/di-symbols.ts +++ b/packages/backend/src/di-symbols.ts @@ -1,3 +1,4 @@ export const DI_SYMBOLS = { config: Symbol('config'), + db: Symbol('db'), }; diff --git a/packages/backend/src/services/CreateSystemUserService.ts b/packages/backend/src/services/CreateSystemUserService.ts index ade251c4d892..3321fdd88e53 100644 --- a/packages/backend/src/services/CreateSystemUserService.ts +++ b/packages/backend/src/services/CreateSystemUserService.ts @@ -9,13 +9,14 @@ import { UserProfile } from '@/models/entities/user-profile.js'; import { genId } from '@/misc/gen-id.js'; import { UserKeypair } from '@/models/entities/user-keypair.js'; import { UsedUsername } from '@/models/entities/used-username.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import generateNativeUserToken from '../server/api/common/generate-native-user-token.js'; import type { DataSource } from 'typeorm'; @Injectable() export class CreateSystemUserService { constructor( - @Inject('db') + @Inject(DI_SYMBOLS.db) private db: DataSource, ) { } diff --git a/packages/backend/src/services/chart/charts/active-users.ts b/packages/backend/src/services/chart/charts/active-users.ts index 40fe20e731da..cc0013210be4 100644 --- a/packages/backend/src/services/chart/charts/active-users.ts +++ b/packages/backend/src/services/chart/charts/active-users.ts @@ -1,6 +1,7 @@ import { Injectable, Inject } from '@nestjs/common'; import type { AppLockService } from '@/services/AppLockService.js'; import type { User } from '@/models/entities/user.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/active-users.js'; import type { DataSource } from 'typeorm'; @@ -17,7 +18,7 @@ const year = 1000 * 60 * 60 * 24 * 365; @Injectable() export default class ActiveUsersChart extends Chart { constructor( - @Inject('db') + @Inject(DI_SYMBOLS.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/ap-request.ts b/packages/backend/src/services/chart/charts/ap-request.ts index 83c6fa89c88c..e10e46f0e75f 100644 --- a/packages/backend/src/services/chart/charts/ap-request.ts +++ b/packages/backend/src/services/chart/charts/ap-request.ts @@ -1,5 +1,6 @@ import { Injectable, Inject } from '@nestjs/common'; import type { AppLockService } from '@/services/AppLockService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/ap-request.js'; import type { KVs } from '../core.js'; @@ -12,7 +13,7 @@ import type { DataSource } from 'typeorm'; @Injectable() export default class ApRequestChart extends Chart { constructor( - @Inject('db') + @Inject(DI_SYMBOLS.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/drive.ts b/packages/backend/src/services/chart/charts/drive.ts index 77c7ed7a8fd6..3aaaae1ee312 100644 --- a/packages/backend/src/services/chart/charts/drive.ts +++ b/packages/backend/src/services/chart/charts/drive.ts @@ -3,6 +3,7 @@ import { Not, IsNull } from 'typeorm'; import { DriveFiles } from '@/models/index.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import type { AppLockService } from '@/services/AppLockService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/drive.js'; import type { KVs } from '../core.js'; @@ -15,7 +16,7 @@ import type { DataSource } from 'typeorm'; @Injectable() export default class DriveChart extends Chart { constructor( - @Inject('db') + @Inject(DI_SYMBOLS.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/federation.ts b/packages/backend/src/services/chart/charts/federation.ts index 1a6fe617c5dc..aa6710bb003b 100644 --- a/packages/backend/src/services/chart/charts/federation.ts +++ b/packages/backend/src/services/chart/charts/federation.ts @@ -2,6 +2,7 @@ import { Injectable, Inject } from '@nestjs/common'; import { Followings, Instances } from '@/models/index.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import type { AppLockService } from '@/services/AppLockService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/federation.js'; import type { KVs } from '../core.js'; @@ -14,7 +15,7 @@ import type { DataSource } from 'typeorm'; @Injectable() export default class FederationChart extends Chart { constructor( - @Inject('db') + @Inject(DI_SYMBOLS.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/hashtag.ts b/packages/backend/src/services/chart/charts/hashtag.ts index b388de667073..cf5d7ea0610b 100644 --- a/packages/backend/src/services/chart/charts/hashtag.ts +++ b/packages/backend/src/services/chart/charts/hashtag.ts @@ -2,6 +2,7 @@ import { Injectable, Inject } from '@nestjs/common'; import type { User } from '@/models/entities/user.js'; import { Users } from '@/models/index.js'; import type { AppLockService } from '@/services/AppLockService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/hashtag.js'; import type { KVs } from '../core.js'; @@ -14,7 +15,7 @@ import type { DataSource } from 'typeorm'; @Injectable() export default class HashtagChart extends Chart { constructor( - @Inject('db') + @Inject(DI_SYMBOLS.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/instance.ts b/packages/backend/src/services/chart/charts/instance.ts index c0d45ab028ab..254bb04448b1 100644 --- a/packages/backend/src/services/chart/charts/instance.ts +++ b/packages/backend/src/services/chart/charts/instance.ts @@ -4,6 +4,7 @@ import type { DriveFile } from '@/models/entities/drive-file.js'; import type { Note } from '@/models/entities/note.js'; import { toPuny } from '@/misc/convert-host.js'; import type { AppLockService } from '@/services/AppLockService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/instance.js'; import type { KVs } from '../core.js'; @@ -16,7 +17,7 @@ import type { DataSource } from 'typeorm'; @Injectable() export default class InstanceChart extends Chart { constructor( - @Inject('db') + @Inject(DI_SYMBOLS.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/notes.ts b/packages/backend/src/services/chart/charts/notes.ts index c234d4a00352..43f18e9cd303 100644 --- a/packages/backend/src/services/chart/charts/notes.ts +++ b/packages/backend/src/services/chart/charts/notes.ts @@ -3,6 +3,7 @@ import { Not, IsNull } from 'typeorm'; import { Notes } from '@/models/index.js'; import type { Note } from '@/models/entities/note.js'; import type { AppLockService } from '@/services/AppLockService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/notes.js'; import type { KVs } from '../core.js'; @@ -15,7 +16,7 @@ import type { DataSource } from 'typeorm'; @Injectable() export default class NotesChart extends Chart { constructor( - @Inject('db') + @Inject(DI_SYMBOLS.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/per-user-drive.ts b/packages/backend/src/services/chart/charts/per-user-drive.ts index 5a2cb3db42c0..2c8325dcf3f9 100644 --- a/packages/backend/src/services/chart/charts/per-user-drive.ts +++ b/packages/backend/src/services/chart/charts/per-user-drive.ts @@ -2,6 +2,7 @@ import { Injectable, Inject } from '@nestjs/common'; import { DriveFiles } from '@/models/index.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import type { AppLockService } from '@/services/AppLockService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/per-user-drive.js'; import type { KVs } from '../core.js'; @@ -14,7 +15,7 @@ import type { DataSource } from 'typeorm'; @Injectable() export default class PerUserDriveChart extends Chart { constructor( - @Inject('db') + @Inject(DI_SYMBOLS.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/per-user-following.ts b/packages/backend/src/services/chart/charts/per-user-following.ts index a262c0d83a07..c6767b074fd5 100644 --- a/packages/backend/src/services/chart/charts/per-user-following.ts +++ b/packages/backend/src/services/chart/charts/per-user-following.ts @@ -3,6 +3,7 @@ import { Not, IsNull } from 'typeorm'; import { Followings, Users } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; import type { AppLockService } from '@/services/AppLockService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/per-user-following.js'; import type { KVs } from '../core.js'; @@ -15,7 +16,7 @@ import type { DataSource } from 'typeorm'; @Injectable() export default class PerUserFollowingChart extends Chart { constructor( - @Inject('db') + @Inject(DI_SYMBOLS.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/per-user-notes.ts b/packages/backend/src/services/chart/charts/per-user-notes.ts index e5f64c84f213..1caf5fe1d814 100644 --- a/packages/backend/src/services/chart/charts/per-user-notes.ts +++ b/packages/backend/src/services/chart/charts/per-user-notes.ts @@ -3,6 +3,7 @@ import type { User } from '@/models/entities/user.js'; import { Notes } from '@/models/index.js'; import type { Note } from '@/models/entities/note.js'; import type { AppLockService } from '@/services/AppLockService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/per-user-notes.js'; import type { KVs } from '../core.js'; @@ -15,7 +16,7 @@ import type { DataSource } from 'typeorm'; @Injectable() export default class PerUserNotesChart extends Chart { constructor( - @Inject('db') + @Inject(DI_SYMBOLS.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/per-user-reactions.ts b/packages/backend/src/services/chart/charts/per-user-reactions.ts index 9eca3b383ab0..e865bec639cb 100644 --- a/packages/backend/src/services/chart/charts/per-user-reactions.ts +++ b/packages/backend/src/services/chart/charts/per-user-reactions.ts @@ -3,6 +3,7 @@ import type { User } from '@/models/entities/user.js'; import type { Note } from '@/models/entities/note.js'; import { Users } from '@/models/index.js'; import type { AppLockService } from '@/services/AppLockService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/per-user-reactions.js'; import type { KVs } from '../core.js'; @@ -15,7 +16,7 @@ import type { DataSource } from 'typeorm'; @Injectable() export default class PerUserReactionsChart extends Chart { constructor( - @Inject('db') + @Inject(DI_SYMBOLS.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/test-grouped.ts b/packages/backend/src/services/chart/charts/test-grouped.ts index 11af7ff44d8b..3851fac7239e 100644 --- a/packages/backend/src/services/chart/charts/test-grouped.ts +++ b/packages/backend/src/services/chart/charts/test-grouped.ts @@ -1,5 +1,6 @@ import { Injectable, Inject } from '@nestjs/common'; import type { AppLockService } from '@/services/AppLockService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/test-grouped.js'; import type { DataSource , DataSource } from 'typeorm'; @@ -14,7 +15,7 @@ export default class TestGroupedChart extends Chart { private total = {} as Record; constructor( - @Inject('db') + @Inject(DI_SYMBOLS.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/test-intersection.ts b/packages/backend/src/services/chart/charts/test-intersection.ts index e7e5ac99f23c..94511eba9a09 100644 --- a/packages/backend/src/services/chart/charts/test-intersection.ts +++ b/packages/backend/src/services/chart/charts/test-intersection.ts @@ -1,5 +1,6 @@ import { Injectable, Inject } from '@nestjs/common'; import type { AppLockService } from '@/services/AppLockService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/test-intersection.js'; import type { DataSource , DataSource } from 'typeorm'; @@ -12,7 +13,7 @@ import type { KVs } from '../core.js'; @Injectable() export default class TestIntersectionChart extends Chart { constructor( - @Inject('db') + @Inject(DI_SYMBOLS.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/test-unique.ts b/packages/backend/src/services/chart/charts/test-unique.ts index b7a2ca6e0f2c..47906fb6b038 100644 --- a/packages/backend/src/services/chart/charts/test-unique.ts +++ b/packages/backend/src/services/chart/charts/test-unique.ts @@ -1,5 +1,6 @@ import { Injectable, Inject } from '@nestjs/common'; import type { AppLockService } from '@/services/AppLockService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/test-unique.js'; import type { DataSource , DataSource } from 'typeorm'; @@ -12,7 +13,7 @@ import type { KVs } from '../core.js'; @Injectable() export default class TestUniqueChart extends Chart { constructor( - @Inject('db') + @Inject(DI_SYMBOLS.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/test.ts b/packages/backend/src/services/chart/charts/test.ts index a92e9cbf1cab..10323649e307 100644 --- a/packages/backend/src/services/chart/charts/test.ts +++ b/packages/backend/src/services/chart/charts/test.ts @@ -1,5 +1,6 @@ import { Injectable, Inject } from '@nestjs/common'; import type { AppLockService } from '@/services/AppLockService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/test.js'; import type { DataSource , DataSource } from 'typeorm'; @@ -14,7 +15,7 @@ export default class TestChart extends Chart { public total = 0; // publicにするのはテストのため constructor( - @Inject('db') + @Inject(DI_SYMBOLS.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/users.ts b/packages/backend/src/services/chart/charts/users.ts index da8eb62ba0a9..5db35451c5c5 100644 --- a/packages/backend/src/services/chart/charts/users.ts +++ b/packages/backend/src/services/chart/charts/users.ts @@ -3,6 +3,7 @@ import { Not, IsNull } from 'typeorm'; import { Users } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; import type { AppLockService } from '@/services/AppLockService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/users.js'; import type { KVs } from '../core.js'; @@ -15,7 +16,7 @@ import type { DataSource } from 'typeorm'; @Injectable() export default class UsersChart extends Chart { constructor( - @Inject('db') + @Inject(DI_SYMBOLS.db) private db: DataSource, private appLockService: AppLockService, From e8ce817b46f3c6c32fab1b5ff1bb2def653db7e8 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 07:34:02 +0900 Subject: [PATCH 029/180] wip --- .../backend/src/services/AntennaService.ts | 72 +++++++++++++++++++ .../src/services/CreateNotificationService.ts | 7 +- .../src/services/add-note-to-antenna.ts | 54 -------------- 3 files changed, 75 insertions(+), 58 deletions(-) create mode 100644 packages/backend/src/services/AntennaService.ts delete mode 100644 packages/backend/src/services/add-note-to-antenna.ts diff --git a/packages/backend/src/services/AntennaService.ts b/packages/backend/src/services/AntennaService.ts new file mode 100644 index 000000000000..8a83057b4f53 --- /dev/null +++ b/packages/backend/src/services/AntennaService.ts @@ -0,0 +1,72 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { AntennaNotes, Mutings, Notes, Users } from '@/models/index.js'; +import type { Antenna } from '@/models/entities/antenna'; +import type { Note } from '@/models/entities/note'; +import type { User } from '@/models/entities/user'; +import { genId } from '@/misc/gen-id.js'; +import { isUserRelated } from '@/misc/is-user-related.js'; +import type { GlobalEventService } from '@/services/GlobalEventService.js'; + +@Injectable() +export class AntennaService { + constructor( + @Inject('mutingsRepository') + private mutingsRepository: typeof Mutings, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + + @Inject('antennaNotesRepository') + private antennaNotesRepository: typeof AntennaNotes, + + private globalEventServie: GlobalEventService, + ) { + } + + public async addNoteToAntenna(antenna: Antenna, note: Note, noteUser: { id: User['id']; }): Promise { + // 通知しない設定になっているか、自分自身の投稿なら既読にする + const read = !antenna.notify || (antenna.userId === noteUser.id); + + this.antennaNotesRepository.insert({ + id: genId(), + antennaId: antenna.id, + noteId: note.id, + read: read, + }); + + this.globalEventServie.publishAntennaStream(antenna.id, 'note', note); + + if (!read) { + const mutings = await this.mutingsRepository.find({ + where: { + muterId: antenna.userId, + }, + select: ['muteeId'], + }); + + // Copy + const _note: Note = { + ...note, + }; + + if (note.replyId != null) { + _note.reply = await this.notesRepository.findOneByOrFail({ id: note.replyId }); + } + if (note.renoteId != null) { + _note.renote = await this.notesRepository.findOneByOrFail({ id: note.renoteId }); + } + + if (isUserRelated(_note, new Set(mutings.map(x => x.muteeId)))) { + return; + } + + // 2秒経っても既読にならなかったら通知 + setTimeout(async () => { + const unread = await this.antennaNotesRepository.findOneBy({ antennaId: antenna.id, read: false }); + if (unread) { + this.globalEventServie.publishMainStream(antenna.userId, 'unreadAntenna', antenna); + } + }, 2000); + } + } +} diff --git a/packages/backend/src/services/CreateNotificationService.ts b/packages/backend/src/services/CreateNotificationService.ts index 3de940ed4c5b..80d20db46957 100644 --- a/packages/backend/src/services/CreateNotificationService.ts +++ b/packages/backend/src/services/CreateNotificationService.ts @@ -1,6 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { Mutings, Notifications, UserProfiles } from '@/models/index.js'; -import { Users } from '@/models/index.js'; +import type { Mutings, Notifications, UserProfiles , Users } from '@/models/index.js'; import type { User } from '@/models/entities/user'; import type { Notification } from '@/models/entities/notification.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; @@ -73,8 +72,8 @@ export class CreateNotificationService { this.globalEventServie.publishMainStream(notifieeId, 'unreadNotification', packed); pushNotification(notifieeId, 'notification', packed); - if (type === 'follow') sendEmailNotification.follow(notifieeId, await Users.findOneByOrFail({ id: data.notifierId! })); - if (type === 'receiveFollowRequest') sendEmailNotification.receiveFollowRequest(notifieeId, await Users.findOneByOrFail({ id: data.notifierId! })); + if (type === 'follow') sendEmailNotification.follow(notifieeId, await this.usersRepository.findOneByOrFail({ id: data.notifierId! })); + if (type === 'receiveFollowRequest') sendEmailNotification.receiveFollowRequest(notifieeId, await this.usersRepository.findOneByOrFail({ id: data.notifierId! })); }, 2000); return notification; diff --git a/packages/backend/src/services/add-note-to-antenna.ts b/packages/backend/src/services/add-note-to-antenna.ts deleted file mode 100644 index 1f344222e165..000000000000 --- a/packages/backend/src/services/add-note-to-antenna.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Antenna } from '@/models/entities/antenna.js'; -import { Note } from '@/models/entities/note.js'; -import { AntennaNotes, Mutings, Notes } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; -import { isUserRelated } from '@/misc/is-user-related.js'; -import { publishAntennaStream, publishMainStream } from '@/services/stream.js'; -import { User } from '@/models/entities/user.js'; - -export async function addNoteToAntenna(antenna: Antenna, note: Note, noteUser: { id: User['id']; }) { - // 通知しない設定になっているか、自分自身の投稿なら既読にする - const read = !antenna.notify || (antenna.userId === noteUser.id); - - AntennaNotes.insert({ - id: genId(), - antennaId: antenna.id, - noteId: note.id, - read: read, - }); - - publishAntennaStream(antenna.id, 'note', note); - - if (!read) { - const mutings = await Mutings.find({ - where: { - muterId: antenna.userId, - }, - select: ['muteeId'], - }); - - // Copy - const _note: Note = { - ...note, - }; - - if (note.replyId != null) { - _note.reply = await Notes.findOneByOrFail({ id: note.replyId }); - } - if (note.renoteId != null) { - _note.renote = await Notes.findOneByOrFail({ id: note.renoteId }); - } - - if (isUserRelated(_note, new Set(mutings.map(x => x.muteeId)))) { - return; - } - - // 2秒経っても既読にならなかったら通知 - setTimeout(async () => { - const unread = await AntennaNotes.findOneBy({ antennaId: antenna.id, read: false }); - if (unread) { - publishMainStream(antenna.userId, 'unreadAntenna', antenna); - } - }, 2000); - } -} From 41349a79cba1803114f19c98c3234bbd2afcaecc Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 07:44:38 +0900 Subject: [PATCH 030/180] wip --- packages/backend/src/misc/fetch-meta.ts | 46 ---------- .../{send-email.ts => EmailService.ts} | 83 +++++++++++-------- packages/backend/src/services/MetaService.ts | 66 +++++++++++++++ 3 files changed, 114 insertions(+), 81 deletions(-) delete mode 100644 packages/backend/src/misc/fetch-meta.ts rename packages/backend/src/services/{send-email.ts => EmailService.ts} (50%) create mode 100644 packages/backend/src/services/MetaService.ts diff --git a/packages/backend/src/misc/fetch-meta.ts b/packages/backend/src/misc/fetch-meta.ts deleted file mode 100644 index 10a554da2843..000000000000 --- a/packages/backend/src/misc/fetch-meta.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { Meta } from '@/models/entities/meta.js'; - -let cache: Meta; - -export async function fetchMeta(noCache = false): Promise { - if (!noCache && cache) return cache; - - return await db.transaction(async transactionalEntityManager => { - // 過去のバグでレコードが複数出来てしまっている可能性があるので新しいIDを優先する - const metas = await transactionalEntityManager.find(Meta, { - order: { - id: 'DESC', - }, - }); - - const meta = metas[0]; - - if (meta) { - cache = meta; - return meta; - } else { - // metaが空のときfetchMetaが同時に呼ばれるとここが同時に呼ばれてしまうことがあるのでフェイルセーフなupsertを使う - const saved = await transactionalEntityManager - .upsert( - Meta, - { - id: 'x', - }, - ['id'], - ) - .then((x) => transactionalEntityManager.findOneByOrFail(Meta, x.identifiers[0])); - - cache = saved; - return saved; - } - }); -} - -if (process.env.NODE_ENV !== 'test') { - setInterval(() => { - fetchMeta(true).then(meta => { - cache = meta; - }); - }, 1000 * 10); -} diff --git a/packages/backend/src/services/send-email.ts b/packages/backend/src/services/EmailService.ts similarity index 50% rename from packages/backend/src/services/send-email.ts rename to packages/backend/src/services/EmailService.ts index b35d22548cd3..0fa8079683bd 100644 --- a/packages/backend/src/services/send-email.ts +++ b/packages/backend/src/services/EmailService.ts @@ -1,38 +1,50 @@ import * as nodemailer from 'nodemailer'; -import { fetchMeta } from '@/misc/fetch-meta.js'; +import { Inject, Injectable } from '@nestjs/common'; +import type { MetaService } from '@/services/MetaService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; import Logger from './logger.js'; -import config from '@/config/index.js'; export const logger = new Logger('email'); -export async function sendEmail(to: string, subject: string, html: string, text: string) { - const meta = await fetchMeta(true); +@Injectable() +export class EmailService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, - const iconUrl = `${config.url}/static-assets/mi-white.png`; - const emailSettingUrl = `${config.url}/settings/email`; - - const enableAuth = meta.smtpUser != null && meta.smtpUser !== ''; - - const transporter = nodemailer.createTransport({ - host: meta.smtpHost, - port: meta.smtpPort, - secure: meta.smtpSecure, - ignoreTLS: !enableAuth, - proxy: config.proxySmtp, - auth: enableAuth ? { - user: meta.smtpUser, - pass: meta.smtpPass, - } : undefined, - } as any); + private metaService: MetaService, + ) { + } - try { - // TODO: htmlサニタイズ - const info = await transporter.sendMail({ - from: meta.email!, - to: to, - subject: subject, - text: text, - html: ` + public async sendEmail(to: string, subject: string, html: string, text: string) { + const meta = await this.metaService.fetch(true); + + const iconUrl = `${this.config.url}/static-assets/mi-white.png`; + const emailSettingUrl = `${this.config.url}/settings/email`; + + const enableAuth = meta.smtpUser != null && meta.smtpUser !== ''; + + const transporter = nodemailer.createTransport({ + host: meta.smtpHost, + port: meta.smtpPort, + secure: meta.smtpSecure, + ignoreTLS: !enableAuth, + proxy: this.config.proxySmtp, + auth: enableAuth ? { + user: meta.smtpUser, + pass: meta.smtpPass, + } : undefined, + } as any); + + try { + // TODO: htmlサニタイズ + const info = await transporter.sendMail({ + from: meta.email!, + to: to, + subject: subject, + text: text, + html: ` @@ -108,15 +120,16 @@ export async function sendEmail(to: string, subject: string, html: string, text: `, - }); - - logger.info(`Message sent: ${info.messageId}`); - } catch (err) { - logger.error(err as Error); - throw err; + }); + + logger.info(`Message sent: ${info.messageId}`); + } catch (err) { + logger.error(err as Error); + throw err; + } } } diff --git a/packages/backend/src/services/MetaService.ts b/packages/backend/src/services/MetaService.ts new file mode 100644 index 000000000000..5d9ca4ba2c83 --- /dev/null +++ b/packages/backend/src/services/MetaService.ts @@ -0,0 +1,66 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { Users } from '@/models/index.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { Meta } from '@/models/entities/meta'; +import type { OnApplicationShutdown } from '@nestjs/common'; +import type { DataSource } from 'typeorm'; + +@Injectable() +export class MetaService implements OnApplicationShutdown { + #cache: Meta | undefined; + #intervalId: NodeJS.Timer; + + constructor( + @Inject(DI_SYMBOLS.db) + private db: DataSource, + + @Inject('usersRepository') + private usersRepository: typeof Users, + ) { + if (process.env.NODE_ENV !== 'test') { + this.#intervalId = setInterval(() => { + this.fetch(true).then(meta => { + this.#cache = meta; + }); + }, 1000 * 10); + } + } + + async fetch(noCache = false): Promise { + if (!noCache && this.#cache) return this.#cache; + + return await this.db.transaction(async transactionalEntityManager => { + // 過去のバグでレコードが複数出来てしまっている可能性があるので新しいIDを優先する + const metas = await transactionalEntityManager.find(Meta, { + order: { + id: 'DESC', + }, + }); + + const meta = metas[0]; + + if (meta) { + this.#cache = meta; + return meta; + } else { + // metaが空のときfetchMetaが同時に呼ばれるとここが同時に呼ばれてしまうことがあるのでフェイルセーフなupsertを使う + const saved = await transactionalEntityManager + .upsert( + Meta, + { + id: 'x', + }, + ['id'], + ) + .then((x) => transactionalEntityManager.findOneByOrFail(Meta, x.identifiers[0])); + + this.#cache = saved; + return saved; + } + }); + } + + public onApplicationShutdown(signal?: string | undefined) { + clearInterval(this.#intervalId); + } +} From ca619480c03fa3696921c046f7d717a63c4c1ac7 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 07:55:13 +0900 Subject: [PATCH 031/180] wip --- .../backend/src/services/AntennaService.ts | 6 +- .../src/services/CreateNotificationService.ts | 2 +- .../src/services/GlobalEventService.ts | 8 +- .../backend/src/services/HashtagService.ts | 144 ++++++++++++++++++ .../src/services/UserFollowingService.ts | 2 +- .../backend/src/services/UserListService.ts | 43 ++++++ .../src/services/UserSuspendService.ts | 15 +- .../backend/src/services/update-hashtag.ts | 128 ---------------- .../backend/src/services/user-list/push.ts | 27 ---- 9 files changed, 207 insertions(+), 168 deletions(-) create mode 100644 packages/backend/src/services/HashtagService.ts create mode 100644 packages/backend/src/services/UserListService.ts delete mode 100644 packages/backend/src/services/update-hashtag.ts delete mode 100644 packages/backend/src/services/user-list/push.ts diff --git a/packages/backend/src/services/AntennaService.ts b/packages/backend/src/services/AntennaService.ts index 8a83057b4f53..4b88432c1f6d 100644 --- a/packages/backend/src/services/AntennaService.ts +++ b/packages/backend/src/services/AntennaService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import type { AntennaNotes, Mutings, Notes, Users } from '@/models/index.js'; -import type { Antenna } from '@/models/entities/antenna'; -import type { Note } from '@/models/entities/note'; -import type { User } from '@/models/entities/user'; +import type { Antenna } from '@/models/entities/antenna.js'; +import type { Note } from '@/models/entities/note.js'; +import type { User } from '@/models/entities/user.js'; import { genId } from '@/misc/gen-id.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; diff --git a/packages/backend/src/services/CreateNotificationService.ts b/packages/backend/src/services/CreateNotificationService.ts index 80d20db46957..22be5f44467c 100644 --- a/packages/backend/src/services/CreateNotificationService.ts +++ b/packages/backend/src/services/CreateNotificationService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Mutings, Notifications, UserProfiles , Users } from '@/models/index.js'; -import type { User } from '@/models/entities/user'; +import type { User } from '@/models/entities/user.js'; import type { Notification } from '@/models/entities/notification.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; import { genId } from '@/misc/gen-id.js'; diff --git a/packages/backend/src/services/GlobalEventService.ts b/packages/backend/src/services/GlobalEventService.ts index f59d1d4f4aa5..340b857e7f30 100644 --- a/packages/backend/src/services/GlobalEventService.ts +++ b/packages/backend/src/services/GlobalEventService.ts @@ -3,7 +3,6 @@ import type { User } from '@/models/entities/user.js'; import type { Note } from '@/models/entities/note.js'; import type { UserList } from '@/models/entities/user-list.js'; import type { UserGroup } from '@/models/entities/user-group.js'; -import config from '@/config/index.js'; import type { Antenna } from '@/models/entities/antenna.js'; import type { Channel } from '@/models/entities/channel.js'; import type { @@ -23,11 +22,16 @@ import type { UserStreamTypes, } from '@/server/api/stream/types.js'; import type { Packed } from '@/misc/schema.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; import type Redis from 'ioredis'; @Injectable() export class GlobalEventService { constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + @Inject('redis') private redisClient: Redis.Redis, ) { @@ -38,7 +42,7 @@ export class GlobalEventService { { type: type, body: null } : { type: type, body: value }; - this.redisClient.publish(config.host, JSON.stringify({ + this.redisClient.publish(this.config.host, JSON.stringify({ channel: channel, message: message, })); diff --git a/packages/backend/src/services/HashtagService.ts b/packages/backend/src/services/HashtagService.ts new file mode 100644 index 000000000000..bb826729a2ab --- /dev/null +++ b/packages/backend/src/services/HashtagService.ts @@ -0,0 +1,144 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { Hashtags, Users } from '@/models/index.js'; +import type { User } from '@/models/entities/user.js'; +import { normalizeForSearch } from '@/misc/normalize-for-search.js'; +import { genId } from '@/misc/gen-id.js'; +import type { Hashtag } from '@/models/entities/hashtag.js'; +import type HashtagChart from '@/services/chart/charts/hashtag.js'; + +@Injectable() +export class HashtagService { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('hashtagsRepository') + private hashtagsRepository: typeof Hashtags, + + private hashtagChart: HashtagChart, + ) { + } + + public async updateHashtags(user: { id: User['id']; host: User['host']; }, tags: string[]) { + for (const tag of tags) { + await this.updateHashtag(user, tag); + } + } + + public async updateUsertags(user: User, tags: string[]) { + for (const tag of tags) { + await this.updateHashtag(user, tag, true, true); + } + + for (const tag of (user.tags || []).filter(x => !tags.includes(x))) { + await this.updateHashtag(user, tag, true, false); + } + } + + public async updateHashtag(user: { id: User['id']; host: User['host']; }, tag: string, isUserAttached = false, inc = true) { + tag = normalizeForSearch(tag); + + const index = await this.hashtagsRepository.findOneBy({ name: tag }); + + if (index == null && !inc) return; + + if (index != null) { + const q = Hashtags.createQueryBuilder('tag').update() + .where('name = :name', { name: tag }); + + const set = {} as any; + + if (isUserAttached) { + if (inc) { + // 自分が初めてこのタグを使ったなら + if (!index.attachedUserIds.some(id => id === user.id)) { + set.attachedUserIds = () => `array_append("attachedUserIds", '${user.id}')`; + set.attachedUsersCount = () => '"attachedUsersCount" + 1'; + } + // 自分が(ローカル内で)初めてこのタグを使ったなら + if (Users.isLocalUser(user) && !index.attachedLocalUserIds.some(id => id === user.id)) { + set.attachedLocalUserIds = () => `array_append("attachedLocalUserIds", '${user.id}')`; + set.attachedLocalUsersCount = () => '"attachedLocalUsersCount" + 1'; + } + // 自分が(リモートで)初めてこのタグを使ったなら + if (Users.isRemoteUser(user) && !index.attachedRemoteUserIds.some(id => id === user.id)) { + set.attachedRemoteUserIds = () => `array_append("attachedRemoteUserIds", '${user.id}')`; + set.attachedRemoteUsersCount = () => '"attachedRemoteUsersCount" + 1'; + } + } else { + set.attachedUserIds = () => `array_remove("attachedUserIds", '${user.id}')`; + set.attachedUsersCount = () => '"attachedUsersCount" - 1'; + if (Users.isLocalUser(user)) { + set.attachedLocalUserIds = () => `array_remove("attachedLocalUserIds", '${user.id}')`; + set.attachedLocalUsersCount = () => '"attachedLocalUsersCount" - 1'; + } else { + set.attachedRemoteUserIds = () => `array_remove("attachedRemoteUserIds", '${user.id}')`; + set.attachedRemoteUsersCount = () => '"attachedRemoteUsersCount" - 1'; + } + } + } else { + // 自分が初めてこのタグを使ったなら + if (!index.mentionedUserIds.some(id => id === user.id)) { + set.mentionedUserIds = () => `array_append("mentionedUserIds", '${user.id}')`; + set.mentionedUsersCount = () => '"mentionedUsersCount" + 1'; + } + // 自分が(ローカル内で)初めてこのタグを使ったなら + if (Users.isLocalUser(user) && !index.mentionedLocalUserIds.some(id => id === user.id)) { + set.mentionedLocalUserIds = () => `array_append("mentionedLocalUserIds", '${user.id}')`; + set.mentionedLocalUsersCount = () => '"mentionedLocalUsersCount" + 1'; + } + // 自分が(リモートで)初めてこのタグを使ったなら + if (Users.isRemoteUser(user) && !index.mentionedRemoteUserIds.some(id => id === user.id)) { + set.mentionedRemoteUserIds = () => `array_append("mentionedRemoteUserIds", '${user.id}')`; + set.mentionedRemoteUsersCount = () => '"mentionedRemoteUsersCount" + 1'; + } + } + + if (Object.keys(set).length > 0) { + q.set(set); + q.execute(); + } + } else { + if (isUserAttached) { + Hashtags.insert({ + id: genId(), + name: tag, + mentionedUserIds: [], + mentionedUsersCount: 0, + mentionedLocalUserIds: [], + mentionedLocalUsersCount: 0, + mentionedRemoteUserIds: [], + mentionedRemoteUsersCount: 0, + attachedUserIds: [user.id], + attachedUsersCount: 1, + attachedLocalUserIds: Users.isLocalUser(user) ? [user.id] : [], + attachedLocalUsersCount: Users.isLocalUser(user) ? 1 : 0, + attachedRemoteUserIds: Users.isRemoteUser(user) ? [user.id] : [], + attachedRemoteUsersCount: Users.isRemoteUser(user) ? 1 : 0, + } as Hashtag); + } else { + Hashtags.insert({ + id: genId(), + name: tag, + mentionedUserIds: [user.id], + mentionedUsersCount: 1, + mentionedLocalUserIds: Users.isLocalUser(user) ? [user.id] : [], + mentionedLocalUsersCount: Users.isLocalUser(user) ? 1 : 0, + mentionedRemoteUserIds: Users.isRemoteUser(user) ? [user.id] : [], + mentionedRemoteUsersCount: Users.isRemoteUser(user) ? 1 : 0, + attachedUserIds: [], + attachedUsersCount: 0, + attachedLocalUserIds: [], + attachedLocalUsersCount: 0, + attachedRemoteUserIds: [], + attachedRemoteUsersCount: 0, + } as Hashtag); + } + } + + if (!isUserAttached) { + this.hashtagChart.update(tag, user); + } + } +} diff --git a/packages/backend/src/services/UserFollowingService.ts b/packages/backend/src/services/UserFollowingService.ts index 3ea47d217aa9..44b9ddd0b76f 100644 --- a/packages/backend/src/services/UserFollowingService.ts +++ b/packages/backend/src/services/UserFollowingService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users , Followings, FollowRequests , UserProfiles , Instances , Blockings } from '@/models/index.js'; -import type { CacheableUser, ILocalUser, IRemoteUser, User } from '@/models/entities/user'; +import type { CacheableUser, ILocalUser, IRemoteUser, User } from '@/models/entities/user.js'; import { renderActivity } from '@/remote/activitypub/renderer/index.js'; import renderFollow from '@/remote/activitypub/renderer/follow.js'; import renderAccept from '@/remote/activitypub/renderer/accept.js'; diff --git a/packages/backend/src/services/UserListService.ts b/packages/backend/src/services/UserListService.ts new file mode 100644 index 000000000000..285fd18bb3c8 --- /dev/null +++ b/packages/backend/src/services/UserListService.ts @@ -0,0 +1,43 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { UserListJoinings } from '@/models/index.js'; +import { Users } from '@/models/index.js'; +import type { User } from '@/models/entities/user.js'; +import type { UserList } from '@/models/entities/user-list.js'; +import type { UserListJoining } from '@/models/entities/user-list-joining.js'; +import { genId } from '@/misc/gen-id.js'; +import type { UserFollowingService } from '@/services/UserFollowingService.js'; +import type { GlobalEventService } from '@/services/GlobalEventService.js'; + +@Injectable() +export class UserListService { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('userListJoiningsRepository') + private userListJoiningsRepository: typeof UserListJoinings, + + private userFollowingService: UserFollowingService, + private globalEventServie: GlobalEventService, + ) { + } + + public async push(target: User, list: UserList) { + await this.userListJoiningsRepository.insert({ + id: genId(), + createdAt: new Date(), + userId: target.id, + userListId: list.id, + } as UserListJoining); + + this.globalEventServie.publishUserListStream(list.id, 'userAdded', await this.usersRepository.pack(target)); + + // このインスタンス内にこのリモートユーザーをフォローしているユーザーがいなくても投稿を受け取るためにダミーのユーザーがフォローしたということにする + if (Users.isRemoteUser(target)) { + const proxy = await fetchProxyAccount(); + if (proxy) { + this.userFollowingService.follow(proxy, target); + } + } + } +} diff --git a/packages/backend/src/services/UserSuspendService.ts b/packages/backend/src/services/UserSuspendService.ts index 0a6872bbf1e2..fa5f3b5f7d29 100644 --- a/packages/backend/src/services/UserSuspendService.ts +++ b/packages/backend/src/services/UserSuspendService.ts @@ -1,18 +1,21 @@ import { Inject, Injectable } from '@nestjs/common'; import { Not, IsNull } from 'typeorm'; import type { Followings , Users } from '@/models/index.js'; - -import type { User } from '@/models/entities/user'; -import type { QueueService } from '@/queue/queue.service'; +import type { User } from '@/models/entities/user.js'; +import type { QueueService } from '@/queue/queue.service.js'; import renderDelete from '@/remote/activitypub/renderer/delete.js'; import renderUndo from '@/remote/activitypub/renderer/undo.js'; import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import config from '@/config/index.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; @Injectable() export class UserSuspendService { constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + @Inject('usersRepository') private usersRepository: typeof Users, @@ -29,7 +32,7 @@ export class UserSuspendService { if (this.usersRepository.isLocalUser(user)) { // 知り得る全SharedInboxにDelete配信 - const content = renderActivity(renderDelete(`${config.url}/users/${user.id}`, user)); + const content = renderActivity(renderDelete(`${this.config.url}/users/${user.id}`, user)); const queue: string[] = []; @@ -58,7 +61,7 @@ export class UserSuspendService { if (this.usersRepository.isLocalUser(user)) { // 知り得る全SharedInboxにUndo Delete配信 - const content = renderActivity(renderUndo(renderDelete(`${config.url}/users/${user.id}`, user), user)); + const content = renderActivity(renderUndo(renderDelete(`${this.config.url}/users/${user.id}`, user), user)); const queue: string[] = []; diff --git a/packages/backend/src/services/update-hashtag.ts b/packages/backend/src/services/update-hashtag.ts deleted file mode 100644 index 23b210b7a900..000000000000 --- a/packages/backend/src/services/update-hashtag.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { User } from '@/models/entities/user.js'; -import { Hashtags, Users } from '@/models/index.js'; -import { hashtagChart } from '@/services/chart/index.js'; -import { genId } from '@/misc/gen-id.js'; -import { Hashtag } from '@/models/entities/hashtag.js'; -import { normalizeForSearch } from '@/misc/normalize-for-search.js'; - -export async function updateHashtags(user: { id: User['id']; host: User['host']; }, tags: string[]) { - for (const tag of tags) { - await updateHashtag(user, tag); - } -} - -export async function updateUsertags(user: User, tags: string[]) { - for (const tag of tags) { - await updateHashtag(user, tag, true, true); - } - - for (const tag of (user.tags || []).filter(x => !tags.includes(x))) { - await updateHashtag(user, tag, true, false); - } -} - -export async function updateHashtag(user: { id: User['id']; host: User['host']; }, tag: string, isUserAttached = false, inc = true) { - tag = normalizeForSearch(tag); - - const index = await Hashtags.findOneBy({ name: tag }); - - if (index == null && !inc) return; - - if (index != null) { - const q = Hashtags.createQueryBuilder('tag').update() - .where('name = :name', { name: tag }); - - const set = {} as any; - - if (isUserAttached) { - if (inc) { - // 自分が初めてこのタグを使ったなら - if (!index.attachedUserIds.some(id => id === user.id)) { - set.attachedUserIds = () => `array_append("attachedUserIds", '${user.id}')`; - set.attachedUsersCount = () => `"attachedUsersCount" + 1`; - } - // 自分が(ローカル内で)初めてこのタグを使ったなら - if (Users.isLocalUser(user) && !index.attachedLocalUserIds.some(id => id === user.id)) { - set.attachedLocalUserIds = () => `array_append("attachedLocalUserIds", '${user.id}')`; - set.attachedLocalUsersCount = () => `"attachedLocalUsersCount" + 1`; - } - // 自分が(リモートで)初めてこのタグを使ったなら - if (Users.isRemoteUser(user) && !index.attachedRemoteUserIds.some(id => id === user.id)) { - set.attachedRemoteUserIds = () => `array_append("attachedRemoteUserIds", '${user.id}')`; - set.attachedRemoteUsersCount = () => `"attachedRemoteUsersCount" + 1`; - } - } else { - set.attachedUserIds = () => `array_remove("attachedUserIds", '${user.id}')`; - set.attachedUsersCount = () => `"attachedUsersCount" - 1`; - if (Users.isLocalUser(user)) { - set.attachedLocalUserIds = () => `array_remove("attachedLocalUserIds", '${user.id}')`; - set.attachedLocalUsersCount = () => `"attachedLocalUsersCount" - 1`; - } else { - set.attachedRemoteUserIds = () => `array_remove("attachedRemoteUserIds", '${user.id}')`; - set.attachedRemoteUsersCount = () => `"attachedRemoteUsersCount" - 1`; - } - } - } else { - // 自分が初めてこのタグを使ったなら - if (!index.mentionedUserIds.some(id => id === user.id)) { - set.mentionedUserIds = () => `array_append("mentionedUserIds", '${user.id}')`; - set.mentionedUsersCount = () => `"mentionedUsersCount" + 1`; - } - // 自分が(ローカル内で)初めてこのタグを使ったなら - if (Users.isLocalUser(user) && !index.mentionedLocalUserIds.some(id => id === user.id)) { - set.mentionedLocalUserIds = () => `array_append("mentionedLocalUserIds", '${user.id}')`; - set.mentionedLocalUsersCount = () => `"mentionedLocalUsersCount" + 1`; - } - // 自分が(リモートで)初めてこのタグを使ったなら - if (Users.isRemoteUser(user) && !index.mentionedRemoteUserIds.some(id => id === user.id)) { - set.mentionedRemoteUserIds = () => `array_append("mentionedRemoteUserIds", '${user.id}')`; - set.mentionedRemoteUsersCount = () => `"mentionedRemoteUsersCount" + 1`; - } - } - - if (Object.keys(set).length > 0) { - q.set(set); - q.execute(); - } - } else { - if (isUserAttached) { - Hashtags.insert({ - id: genId(), - name: tag, - mentionedUserIds: [], - mentionedUsersCount: 0, - mentionedLocalUserIds: [], - mentionedLocalUsersCount: 0, - mentionedRemoteUserIds: [], - mentionedRemoteUsersCount: 0, - attachedUserIds: [user.id], - attachedUsersCount: 1, - attachedLocalUserIds: Users.isLocalUser(user) ? [user.id] : [], - attachedLocalUsersCount: Users.isLocalUser(user) ? 1 : 0, - attachedRemoteUserIds: Users.isRemoteUser(user) ? [user.id] : [], - attachedRemoteUsersCount: Users.isRemoteUser(user) ? 1 : 0, - } as Hashtag); - } else { - Hashtags.insert({ - id: genId(), - name: tag, - mentionedUserIds: [user.id], - mentionedUsersCount: 1, - mentionedLocalUserIds: Users.isLocalUser(user) ? [user.id] : [], - mentionedLocalUsersCount: Users.isLocalUser(user) ? 1 : 0, - mentionedRemoteUserIds: Users.isRemoteUser(user) ? [user.id] : [], - mentionedRemoteUsersCount: Users.isRemoteUser(user) ? 1 : 0, - attachedUserIds: [], - attachedUsersCount: 0, - attachedLocalUserIds: [], - attachedLocalUsersCount: 0, - attachedRemoteUserIds: [], - attachedRemoteUsersCount: 0, - } as Hashtag); - } - } - - if (!isUserAttached) { - hashtagChart.update(tag, user); - } -} diff --git a/packages/backend/src/services/user-list/push.ts b/packages/backend/src/services/user-list/push.ts deleted file mode 100644 index d073afcd3a84..000000000000 --- a/packages/backend/src/services/user-list/push.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { publishUserListStream } from '@/services/stream.js'; -import { User } from '@/models/entities/user.js'; -import { UserList } from '@/models/entities/user-list.js'; -import { UserListJoinings, Users } from '@/models/index.js'; -import { UserListJoining } from '@/models/entities/user-list-joining.js'; -import { genId } from '@/misc/gen-id.js'; -import { fetchProxyAccount } from '@/misc/fetch-proxy-account.js'; -import createFollowing from '../following/create.js'; - -export async function pushUserToUserList(target: User, list: UserList) { - await UserListJoinings.insert({ - id: genId(), - createdAt: new Date(), - userId: target.id, - userListId: list.id, - } as UserListJoining); - - publishUserListStream(list.id, 'userAdded', await Users.pack(target)); - - // このインスタンス内にこのリモートユーザーをフォローしているユーザーがいなくても投稿を受け取るためにダミーのユーザーがフォローしたということにする - if (Users.isRemoteUser(target)) { - const proxy = await fetchProxyAccount(); - if (proxy) { - createFollowing(proxy, target); - } - } -} From cdc44d501039c829c82fa240ca38ca85733960d4 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 09:15:25 +0900 Subject: [PATCH 032/180] wip --- .../backend/src/services/ReactionService.ts | 204 ++++++++++++++++++ .../src/services/UserBlockingService.ts | 10 +- .../src/services/UserFollowingService.ts | 16 +- .../src/services/note/reaction/create.ts | 145 ------------- .../src/services/note/reaction/delete.ts | 58 ----- packages/backend/src/services/note/unwatch.ts | 10 - packages/backend/src/services/note/watch.ts | 20 -- 7 files changed, 217 insertions(+), 246 deletions(-) create mode 100644 packages/backend/src/services/ReactionService.ts delete mode 100644 packages/backend/src/services/note/reaction/create.ts delete mode 100644 packages/backend/src/services/note/reaction/delete.ts delete mode 100644 packages/backend/src/services/note/unwatch.ts delete mode 100644 packages/backend/src/services/note/watch.ts diff --git a/packages/backend/src/services/ReactionService.ts b/packages/backend/src/services/ReactionService.ts new file mode 100644 index 000000000000..3c21d2d811ae --- /dev/null +++ b/packages/backend/src/services/ReactionService.ts @@ -0,0 +1,204 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { IsNull } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Blockings, Emojis, NoteReactions , Users , Notes } from '@/models/index.js'; + +import { IdentifiableError } from '@/misc/identifiable-error.js'; +import type { IRemoteUser, User } from '@/models/entities/user.js'; +import type { Note } from '@/models/entities/note.js'; +import { genId } from '@/misc/gen-id.js'; +import type { NoteReaction } from '@/models/entities/note-reaction.js'; +import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; +import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import type { CreateNotificationService } from '@/services/CreateNotificationService.js'; +import DeliverManager from '@/remote/activitypub/deliver-manager.js'; +import type PerUserReactionsChart from '@/services/chart/charts/per-user-reactions.js'; +import { toDbReaction } from '@/misc/reaction-lib.js'; + +@Injectable() +export class ReactionService { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('blockingsRepository') + private blockingsRepository: typeof Blockings, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + + @Inject('noteReactionsRepository') + private noteReactionsRepository: typeof NoteReactions, + + @Inject('emojisRepository') + private emojisRepository: typeof Emojis, + + private globalEventServie: GlobalEventService, + private createNotificationService: CreateNotificationService, + private perUserReactionsChart: PerUserReactionsChart, + ) { + } + + public async create(user: { id: User['id']; host: User['host']; }, note: Note, reaction?: string) { + // Check blocking + if (note.userId !== user.id) { + const block = await this.blockingsRepository.findOneBy({ + blockerId: note.userId, + blockeeId: user.id, + }); + if (block) { + throw new IdentifiableError('e70412a4-7197-4726-8e74-f3e0deb92aa7'); + } + } + + // check visibility + if (!await this.notesRepository.isVisibleForMe(note, user.id)) { + throw new IdentifiableError('68e9d2d1-48bf-42c2-b90a-b20e09fd3d48', 'Note not accessible for you.'); + } + + // TODO: cache + reaction = await toDbReaction(reaction, user.host); + + const record: NoteReaction = { + id: genId(), + createdAt: new Date(), + noteId: note.id, + userId: user.id, + reaction, + }; + + // Create reaction + try { + await this.noteReactionsRepository.insert(record); + } catch (e) { + if (isDuplicateKeyValueError(e)) { + const exists = await this.noteReactionsRepository.findOneByOrFail({ + noteId: note.id, + userId: user.id, + }); + + if (exists.reaction !== reaction) { + // 別のリアクションがすでにされていたら置き換える + await deleteReaction(user, note); + await this.noteReactionsRepository.insert(record); + } else { + // 同じリアクションがすでにされていたらエラー + throw new IdentifiableError('51c42bb4-931a-456b-bff7-e5a8a70dd298'); + } + } else { + throw e; + } + } + + // Increment reactions count + const sql = `jsonb_set("reactions", '{${reaction}}', (COALESCE("reactions"->>'${reaction}', '0')::int + 1)::text::jsonb)`; + await this.notesRepository.createQueryBuilder().update() + .set({ + reactions: () => sql, + score: () => '"score" + 1', + }) + .where('id = :id', { id: note.id }) + .execute(); + + this.perUserReactionsChart.update(user, note); + + // カスタム絵文字リアクションだったら絵文字情報も送る + const decodedReaction = decodeReaction(reaction); + + const emoji = await this.emojisRepository.findOne({ + where: { + name: decodedReaction.name, + host: decodedReaction.host ?? IsNull(), + }, + select: ['name', 'host', 'originalUrl', 'publicUrl'], + }); + + this.globalEventServie.publishNoteStream(note.id, 'reacted', { + reaction: decodedReaction.reaction, + emoji: emoji != null ? { + name: emoji.host ? `${emoji.name}@${emoji.host}` : `${emoji.name}@.`, + url: emoji.publicUrl || emoji.originalUrl, // || emoji.originalUrl してるのは後方互換性のため + } : null, + userId: user.id, + }); + + // リアクションされたユーザーがローカルユーザーなら通知を作成 + if (note.userHost === null) { + this.createNotificationService.createNotification(note.userId, 'reaction', { + notifierId: user.id, + noteId: note.id, + reaction: reaction, + }); + } + + //#region 配信 + if (this.usersRepository.isLocalUser(user) && !note.localOnly) { + const content = renderActivity(await renderLike(record, note)); + const dm = new DeliverManager(user, content); + if (note.userHost !== null) { + const reactee = await this.usersRepository.findOneBy({ id: note.userId }); + dm.addDirectRecipe(reactee as IRemoteUser); + } + + if (['public', 'home', 'followers'].includes(note.visibility)) { + dm.addFollowersRecipe(); + } else if (note.visibility === 'specified') { + const visibleUsers = await Promise.all(note.visibleUserIds.map(id => this.usersRepository.findOneBy({ id }))); + for (const u of visibleUsers.filter(u => u && this.usersRepository.isRemoteUser(u))) { + dm.addDirectRecipe(u as IRemoteUser); + } + } + + dm.execute(); + } + //#endregion + } + + public async delete(user: { id: User['id']; host: User['host']; }, note: Note) { + // if already unreacted + const exist = await this.noteReactionsRepository.findOneBy({ + noteId: note.id, + userId: user.id, + }); + + if (exist == null) { + throw new IdentifiableError('60527ec9-b4cb-4a88-a6bd-32d3ad26817d', 'not reacted'); + } + + // Delete reaction + const result = await this.noteReactionsRepository.delete(exist.id); + + if (result.affected !== 1) { + throw new IdentifiableError('60527ec9-b4cb-4a88-a6bd-32d3ad26817d', 'not reacted'); + } + + // Decrement reactions count + const sql = `jsonb_set("reactions", '{${exist.reaction}}', (COALESCE("reactions"->>'${exist.reaction}', '0')::int - 1)::text::jsonb)`; + await this.notesRepository.createQueryBuilder().update() + .set({ + reactions: () => sql, + }) + .where('id = :id', { id: note.id }) + .execute(); + + this.notesRepository.decrement({ id: note.id }, 'score', 1); + + this.globalEventServie.publishNoteStream(note.id, 'unreacted', { + reaction: decodeReaction(exist.reaction).reaction, + userId: user.id, + }); + + //#region 配信 + if (this.usersRepository.isLocalUser(user) && !note.localOnly) { + const content = renderActivity(renderUndo(await renderLike(exist, note), user)); + const dm = new DeliverManager(user, content); + if (note.userHost !== null) { + const reactee = await this.usersRepository.findOneBy({ id: note.userId }); + dm.addDirectRecipe(reactee as IRemoteUser); + } + dm.addFollowersRecipe(); + dm.execute(); + } + //#endregion + } +} diff --git a/packages/backend/src/services/UserBlockingService.ts b/packages/backend/src/services/UserBlockingService.ts index 84be9d77525f..258f4ad42593 100644 --- a/packages/backend/src/services/UserBlockingService.ts +++ b/packages/backend/src/services/UserBlockingService.ts @@ -27,8 +27,8 @@ export class UserBlockingService { @Inject('followRequestsRepository') private followRequestsRepository: typeof FollowRequests, - @Inject('blockingRepository') - private blockingRepository: typeof Blockings, + @Inject('blockingsRepository') + private blockingsRepository: typeof Blockings, @Inject('userListsRepository') private userListsRepository: typeof UserLists, @@ -61,7 +61,7 @@ export class UserBlockingService { blockeeId: blockee.id, } as Blocking; - await this.blockingRepository.insert(blocking); + await this.blockingsRepository.insert(blocking); if (this.usersRepository.isLocalUser(blocker) && this.usersRepository.isRemoteUser(blockee)) { const content = renderActivity(renderBlock(blocking)); @@ -174,7 +174,7 @@ export class UserBlockingService { } public async unblock(blocker: CacheableUser, blockee: CacheableUser) { - const blocking = await this.blockingRepository.findOneBy({ + const blocking = await this.blockingsRepository.findOneBy({ blockerId: blocker.id, blockeeId: blockee.id, }); @@ -189,7 +189,7 @@ export class UserBlockingService { blocking.blocker = blocker; blocking.blockee = blockee; - await this.blockingRepository.delete(blocking.id); + await this.blockingsRepository.delete(blocking.id); // deliver if remote bloking if (this.usersRepository.isLocalUser(blocker) && this.usersRepository.isRemoteUser(blockee)) { diff --git a/packages/backend/src/services/UserFollowingService.ts b/packages/backend/src/services/UserFollowingService.ts index 44b9ddd0b76f..ac6162be0701 100644 --- a/packages/backend/src/services/UserFollowingService.ts +++ b/packages/backend/src/services/UserFollowingService.ts @@ -16,8 +16,8 @@ import type { Packed } from '@/misc/schema.js'; import type InstanceChart from '@/services/chart/charts/instance.js'; import type { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; import type { WebhookService } from '@/services/webhookService.js'; +import type { CreateNotificationService } from '@/services/CreateNotificationService.js'; import Logger from './logger.js'; -import type { CreateNotificationService } from './CreateNotificationService.js'; const logger = new Logger('following/create'); @@ -49,8 +49,8 @@ export class UserFollowingService { @Inject('followRequestsRepository') private followRequestsRepository: typeof FollowRequests, - @Inject('blockingRepository') - private blockingRepository: typeof Blockings, + @Inject('blockingsRepository') + private blockingsRepository: typeof Blockings, @Inject('instancesRepository') private instancesRepository: typeof Instances, @@ -73,11 +73,11 @@ export class UserFollowingService { // check blocking const [blocking, blocked] = await Promise.all([ - this.blockingRepository.findOneBy({ + this.blockingsRepository.findOneBy({ blockerId: follower.id, blockeeId: followee.id, }), - this.blockingRepository.findOneBy({ + this.blockingsRepository.findOneBy({ blockerId: followee.id, blockeeId: follower.id, }), @@ -90,7 +90,7 @@ export class UserFollowingService { return; } else if (this.usersRepository.isRemoteUser(follower) && this.usersRepository.isLocalUser(followee) && blocking) { // リモートフォローを受けてブロックされているはずの場合だったら、ブロック解除しておく。 - await this.blockingRepository.delete(blocking.id); + await this.blockingsRepository.delete(blocking.id); } else { // それ以外は単純に例外 if (blocking != null) throw new IdentifiableError('710e8fb0-b8c3-4922-be49-d5d93d8e6a6e', 'blocking'); @@ -345,11 +345,11 @@ export class UserFollowingService { // check blocking const [blocking, blocked] = await Promise.all([ - this.blockingRepository.findOneBy({ + this.blockingsRepository.findOneBy({ blockerId: follower.id, blockeeId: followee.id, }), - this.blockingRepository.findOneBy({ + this.blockingsRepository.findOneBy({ blockerId: followee.id, blockeeId: follower.id, }), diff --git a/packages/backend/src/services/note/reaction/create.ts b/packages/backend/src/services/note/reaction/create.ts deleted file mode 100644 index 83d302826abc..000000000000 --- a/packages/backend/src/services/note/reaction/create.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { publishNoteStream } from '@/services/stream.js'; -import { renderLike } from '@/remote/activitypub/renderer/like.js'; -import DeliverManager from '@/remote/activitypub/deliver-manager.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import { toDbReaction, decodeReaction } from '@/misc/reaction-lib.js'; -import { User, IRemoteUser } from '@/models/entities/user.js'; -import { Note } from '@/models/entities/note.js'; -import { NoteReactions, Users, NoteWatchings, Notes, Emojis, Blockings } from '@/models/index.js'; -import { IsNull, Not } from 'typeorm'; -import { perUserReactionsChart } from '@/services/chart/index.js'; -import { genId } from '@/misc/gen-id.js'; -import { createNotification } from '../../create-notification.js'; -import deleteReaction from './delete.js'; -import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; -import { NoteReaction } from '@/models/entities/note-reaction.js'; -import { IdentifiableError } from '@/misc/identifiable-error.js'; - -export default async (user: { id: User['id']; host: User['host']; }, note: Note, reaction?: string) => { - // Check blocking - if (note.userId !== user.id) { - const block = await Blockings.findOneBy({ - blockerId: note.userId, - blockeeId: user.id, - }); - if (block) { - throw new IdentifiableError('e70412a4-7197-4726-8e74-f3e0deb92aa7'); - } - } - - // check visibility - if (!await Notes.isVisibleForMe(note, user.id)) { - throw new IdentifiableError('68e9d2d1-48bf-42c2-b90a-b20e09fd3d48', 'Note not accessible for you.'); - } - - // TODO: cache - reaction = await toDbReaction(reaction, user.host); - - const record: NoteReaction = { - id: genId(), - createdAt: new Date(), - noteId: note.id, - userId: user.id, - reaction, - }; - - // Create reaction - try { - await NoteReactions.insert(record); - } catch (e) { - if (isDuplicateKeyValueError(e)) { - const exists = await NoteReactions.findOneByOrFail({ - noteId: note.id, - userId: user.id, - }); - - if (exists.reaction !== reaction) { - // 別のリアクションがすでにされていたら置き換える - await deleteReaction(user, note); - await NoteReactions.insert(record); - } else { - // 同じリアクションがすでにされていたらエラー - throw new IdentifiableError('51c42bb4-931a-456b-bff7-e5a8a70dd298'); - } - } else { - throw e; - } - } - - // Increment reactions count - const sql = `jsonb_set("reactions", '{${reaction}}', (COALESCE("reactions"->>'${reaction}', '0')::int + 1)::text::jsonb)`; - await Notes.createQueryBuilder().update() - .set({ - reactions: () => sql, - score: () => '"score" + 1', - }) - .where('id = :id', { id: note.id }) - .execute(); - - perUserReactionsChart.update(user, note); - - // カスタム絵文字リアクションだったら絵文字情報も送る - const decodedReaction = decodeReaction(reaction); - - const emoji = await Emojis.findOne({ - where: { - name: decodedReaction.name, - host: decodedReaction.host ?? IsNull(), - }, - select: ['name', 'host', 'originalUrl', 'publicUrl'], - }); - - publishNoteStream(note.id, 'reacted', { - reaction: decodedReaction.reaction, - emoji: emoji != null ? { - name: emoji.host ? `${emoji.name}@${emoji.host}` : `${emoji.name}@.`, - url: emoji.publicUrl || emoji.originalUrl, // || emoji.originalUrl してるのは後方互換性のため - } : null, - userId: user.id, - }); - - // リアクションされたユーザーがローカルユーザーなら通知を作成 - if (note.userHost === null) { - createNotification(note.userId, 'reaction', { - notifierId: user.id, - noteId: note.id, - reaction: reaction, - }); - } - - // Fetch watchers - NoteWatchings.findBy({ - noteId: note.id, - userId: Not(user.id), - }).then(watchers => { - for (const watcher of watchers) { - createNotification(watcher.userId, 'reaction', { - notifierId: user.id, - noteId: note.id, - reaction: reaction, - }); - } - }); - - //#region 配信 - if (Users.isLocalUser(user) && !note.localOnly) { - const content = renderActivity(await renderLike(record, note)); - const dm = new DeliverManager(user, content); - if (note.userHost !== null) { - const reactee = await Users.findOneBy({ id: note.userId }); - dm.addDirectRecipe(reactee as IRemoteUser); - } - - if (['public', 'home', 'followers'].includes(note.visibility)) { - dm.addFollowersRecipe(); - } else if (note.visibility === 'specified') { - const visibleUsers = await Promise.all(note.visibleUserIds.map(id => Users.findOneBy({ id }))); - for (const u of visibleUsers.filter(u => u && Users.isRemoteUser(u))) { - dm.addDirectRecipe(u as IRemoteUser); - } - } - - dm.execute(); - } - //#endregion -}; diff --git a/packages/backend/src/services/note/reaction/delete.ts b/packages/backend/src/services/note/reaction/delete.ts deleted file mode 100644 index a7cbcb1c17fa..000000000000 --- a/packages/backend/src/services/note/reaction/delete.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { publishNoteStream } from '@/services/stream.js'; -import { renderLike } from '@/remote/activitypub/renderer/like.js'; -import renderUndo from '@/remote/activitypub/renderer/undo.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import DeliverManager from '@/remote/activitypub/deliver-manager.js'; -import { IdentifiableError } from '@/misc/identifiable-error.js'; -import { User, IRemoteUser } from '@/models/entities/user.js'; -import { Note } from '@/models/entities/note.js'; -import { NoteReactions, Users, Notes } from '@/models/index.js'; -import { decodeReaction } from '@/misc/reaction-lib.js'; - -export default async (user: { id: User['id']; host: User['host']; }, note: Note) => { - // if already unreacted - const exist = await NoteReactions.findOneBy({ - noteId: note.id, - userId: user.id, - }); - - if (exist == null) { - throw new IdentifiableError('60527ec9-b4cb-4a88-a6bd-32d3ad26817d', 'not reacted'); - } - - // Delete reaction - const result = await NoteReactions.delete(exist.id); - - if (result.affected !== 1) { - throw new IdentifiableError('60527ec9-b4cb-4a88-a6bd-32d3ad26817d', 'not reacted'); - } - - // Decrement reactions count - const sql = `jsonb_set("reactions", '{${exist.reaction}}', (COALESCE("reactions"->>'${exist.reaction}', '0')::int - 1)::text::jsonb)`; - await Notes.createQueryBuilder().update() - .set({ - reactions: () => sql, - }) - .where('id = :id', { id: note.id }) - .execute(); - - Notes.decrement({ id: note.id }, 'score', 1); - - publishNoteStream(note.id, 'unreacted', { - reaction: decodeReaction(exist.reaction).reaction, - userId: user.id, - }); - - //#region 配信 - if (Users.isLocalUser(user) && !note.localOnly) { - const content = renderActivity(renderUndo(await renderLike(exist, note), user)); - const dm = new DeliverManager(user, content); - if (note.userHost !== null) { - const reactee = await Users.findOneBy({ id: note.userId }); - dm.addDirectRecipe(reactee as IRemoteUser); - } - dm.addFollowersRecipe(); - dm.execute(); - } - //#endregion -}; diff --git a/packages/backend/src/services/note/unwatch.ts b/packages/backend/src/services/note/unwatch.ts deleted file mode 100644 index 3964b2ba5f91..000000000000 --- a/packages/backend/src/services/note/unwatch.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { User } from '@/models/entities/user.js'; -import { NoteWatchings } from '@/models/index.js'; -import { Note } from '@/models/entities/note.js'; - -export default async (me: User['id'], note: Note) => { - await NoteWatchings.delete({ - noteId: note.id, - userId: me, - }); -}; diff --git a/packages/backend/src/services/note/watch.ts b/packages/backend/src/services/note/watch.ts deleted file mode 100644 index 2210c44a7514..000000000000 --- a/packages/backend/src/services/note/watch.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { User } from '@/models/entities/user.js'; -import { Note } from '@/models/entities/note.js'; -import { NoteWatchings } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; -import { NoteWatching } from '@/models/entities/note-watching.js'; - -export default async (me: User['id'], note: Note) => { - // 自分の投稿はwatchできない - if (me === note.userId) { - return; - } - - await NoteWatchings.insert({ - id: genId(), - createdAt: new Date(), - noteId: note.id, - userId: me, - noteUserId: note.userId, - } as NoteWatching); -}; From e96029c21298e71e1c143e0652db1b68170b382d Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 09:33:44 +0900 Subject: [PATCH 033/180] wip --- .../services/{note => }/NoteCreateService.ts | 123 ++++++++++-------- .../services/{note => }/NoteDeleteService.ts | 62 +++++---- packages/backend/src/services/RelayService.ts | 2 +- 3 files changed, 106 insertions(+), 81 deletions(-) rename packages/backend/src/services/{note => }/NoteCreateService.ts (82%) rename packages/backend/src/services/{note => }/NoteDeleteService.ts (66%) diff --git a/packages/backend/src/services/note/NoteCreateService.ts b/packages/backend/src/services/NoteCreateService.ts similarity index 82% rename from packages/backend/src/services/note/NoteCreateService.ts rename to packages/backend/src/services/NoteCreateService.ts index e628c1589124..62be0516bc1e 100644 --- a/packages/backend/src/services/note/NoteCreateService.ts +++ b/packages/backend/src/services/NoteCreateService.ts @@ -12,14 +12,12 @@ import type { DriveFile } from '@/models/entities/drive-file.js'; import type { App } from '@/models/entities/app.js'; import { insertNoteUnread } from '@/services/note/unread.js'; import { concat } from '@/prelude/array.js'; -import config from '@/config/index.js'; import { resolveUser } from '@/remote/resolve-user.js'; import { renderActivity } from '@/remote/activitypub/renderer/index.js'; import renderAnnounce from '@/remote/activitypub/renderer/announce.js'; import renderCreate from '@/remote/activitypub/renderer/create.js'; import renderNote from '@/remote/activitypub/renderer/note.js'; import DeliverManager from '@/remote/activitypub/deliver-manager.js'; -import { publishMainStream, publishNotesStream } from '@/services/stream.js'; import { genId } from '@/misc/gen-id.js'; import type { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; import type { IPoll } from '@/models/entities/poll.js'; @@ -31,23 +29,24 @@ import { countSameRenotes } from '@/misc/count-same-renotes.js'; import type { Channel } from '@/models/entities/channel.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { getAntennas } from '@/misc/antenna-cache.js'; -import { endedPollNotificationQueue } from '@/queue/queues.js'; -import { webhookDeliver } from '@/queue/index.js'; import { Cache } from '@/misc/cache.js'; import type { UserProfile } from '@/models/entities/user-profile.js'; import { db } from '@/db/postgre.js'; -import { getActiveWebhooks } from '@/services/webhook-cache.js'; -import es from '../../db/elasticsearch.js'; -import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js'; -import { updateHashtags } from '../update-hashtag.js'; -import { deliverToRelays } from '../relay.js'; -import { addNoteToAntenna } from '../add-note-to-antenna.js'; -import { createNotification } from '../create-notification.js'; -import type { WebhookService } from '../webhookService.js'; -import type NotesChart from '../chart/charts/notes.js'; -import type PerUserNotesChart from '../chart/charts/per-user-notes.js'; -import type ActiveUsersChart from '../chart/charts/active-users.js'; -import type InstanceChart from '../chart/charts/instance.js'; +import type { RelayService } from '@/services/RelayService.js'; +import type { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; +import type NotesChart from '@/services/chart/charts/notes.js'; +import type PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; +import type InstanceChart from '@/services/chart/charts/instance.js'; +import type ActiveUsersChart from '@/services/chart/charts/active-users.js'; +import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import type { CreateNotificationService } from '@/services/CreateNotificationService.js'; +import type { WebhookService } from '@/services/WebhookService.js'; +import type { HashtagService } from '@/services/HashtagService.js'; +import type { AntennaService } from '@/services/AntennaService.js'; +import type { QueueService } from '@/queue/queue.service.js'; +import es from '../db/elasticsearch.js'; const mutedWordsCache = new Cache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5); @@ -61,7 +60,7 @@ class NotificationManager { reason: NotificationType; }[]; - constructor(notifier: { id: User['id']; }, note: Note) { + constructor(private createNotificationService: CreateNotificationService, notifier: { id: User['id']; }, note: Note) { this.notifier = notifier; this.note = note; this.queue = []; @@ -97,7 +96,7 @@ class NotificationManager { // 通知される側のユーザーが通知する側のユーザーをミュートしていない限りは通知する if (!mentioneesMutedUserIds.includes(this.notifier.id)) { - createNotification(x.target, x.reason, { + this.createNotificationService.createNotification(x.target, x.reason, { notifierId: this.notifier.id, noteId: this.note.id, }); @@ -137,15 +136,27 @@ type Option = { @Injectable() export class NoteCreateService { constructor( - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + @Inject('notesRepository') + private notesRepository: typeof Notes, + + private globalEventServie: GlobalEventService, + private queueService: QueueService, + private createNotificationService: CreateNotificationService, + private relayService: RelayService, + private federatedInstanceService: FederatedInstanceService, + private hashtagService: HashtagService, + private antennaService: AntennaService, + private webhookService: WebhookService, private notesChart: NotesChart, private perUserNotesChart: PerUserNotesChart, private activeUsersChart: ActiveUsersChart, private instanceChart: InstanceChart, - - private webhookService: WebhookService, ) {} public async create(user: { @@ -243,7 +254,7 @@ export class NoteCreateService { 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 Users.findOneByOrFail({ id: data.reply!.userId })); + mentionedUsers.push(await this.usersRepository.findOneByOrFail({ id: data.reply!.userId })); } if (data.visibility === 'specified') { @@ -256,7 +267,7 @@ export class NoteCreateService { } if (data.reply && !data.visibleUsers.some(x => x.id === data.reply!.userId)) { - data.visibleUsers.push(await Users.findOneByOrFail({ id: data.reply!.userId })); + data.visibleUsers.push(await this.usersRepository.findOneByOrFail({ id: data.reply!.userId })); } } @@ -375,8 +386,8 @@ export class NoteCreateService { this.perUserNotesChart.update(user, note, true); // Register host - if (Users.isRemoteUser(user)) { - registerOrFetchInstanceDoc(user.host).then(i => { + if (this.usersRepository.isRemoteUser(user)) { + this.federatedInstanceService.registerOrFetchInstanceDoc(user.host).then(i => { Instances.increment({ id: i.id }, 'notesCount', 1); this.instanceChart.updateNote(i.host, note, true); }); @@ -384,7 +395,7 @@ export class NoteCreateService { // ハッシュタグ更新 if (data.visibility === 'public' || data.visibility === 'home') { - updateHashtags(user, tags); + this.hashtagService.updateHashtags(user, tags); } // Increment notes count (user) @@ -415,7 +426,7 @@ export class NoteCreateService { for (const antenna of (await getAntennas())) { checkHitAntenna(antenna, note, user).then(hit => { if (hit) { - addNoteToAntenna(antenna, note, user); + this.antennaService.addNoteToAntenna(antenna, note, user); } }); } @@ -452,7 +463,7 @@ export class NoteCreateService { } if (!silent) { - if (Users.isLocalUser(user)) this.activeUsersChart.write(user); + if (this.usersRepository.isLocalUser(user)) this.activeUsersChart.write(user); // 未読通知を作成 if (data.visibility === 'specified') { @@ -460,7 +471,7 @@ export class NoteCreateService { for (const u of data.visibleUsers) { // ローカルユーザーのみ - if (!Users.isLocalUser(u)) continue; + if (!this.usersRepository.isLocalUser(u)) continue; insertNoteUnread(u.id, note, { isSpecified: true, @@ -470,7 +481,7 @@ export class NoteCreateService { } else { for (const u of mentionedUsers) { // ローカルユーザーのみ - if (!Users.isLocalUser(u)) continue; + if (!this.usersRepository.isLocalUser(u)) continue; insertNoteUnread(u.id, note, { isSpecified: false, @@ -482,18 +493,18 @@ export class NoteCreateService { // Pack the note const noteObj = await this.notesRepository.pack(note); - publishNotesStream(noteObj); + this.globalEventServie.publishNotesStream(noteObj); - getActiveWebhooks().then(webhooks => { + this.webhookService.getActiveWebhooks().then(webhooks => { webhooks = webhooks.filter(x => x.userId === user.id && x.on.includes('note')); for (const webhook of webhooks) { - webhookDeliver(webhook, 'note', { + this.queueService.webhookDeliver(webhook, 'note', { note: noteObj, }); } }); - const nm = new NotificationManager(user, note); + const nm = new NotificationManager(this.createNotificationService, user, note); const nmRelatedPromises = []; await this.#createMentionedEvents(mentionedUsers, note, nm); @@ -512,11 +523,11 @@ export class NoteCreateService { if (!threadMuted) { nm.push(data.reply.userId, 'reply'); - publishMainStream(data.reply.userId, 'reply', noteObj); + this.globalEventServie.publishMainStream(data.reply.userId, 'reply', noteObj); - const webhooks = (await getActiveWebhooks()).filter(x => x.userId === data.reply!.userId && x.on.includes('reply')); + const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === data.reply!.userId && x.on.includes('reply')); for (const webhook of webhooks) { - webhookDeliver(webhook, 'reply', { + this.queueService.webhookDeliver(webhook, 'reply', { note: noteObj, }); } @@ -538,11 +549,11 @@ export class NoteCreateService { // Publish event if ((user.id !== data.renote.userId) && data.renote.userHost === null) { - publishMainStream(data.renote.userId, 'renote', noteObj); + this.globalEventServie.publishMainStream(data.renote.userId, 'renote', noteObj); - const webhooks = (await getActiveWebhooks()).filter(x => x.userId === data.renote!.userId && x.on.includes('renote')); + const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === data.renote!.userId && x.on.includes('renote')); for (const webhook of webhooks) { - webhookDeliver(webhook, 'renote', { + this.queueService.webhookDeliver(webhook, 'renote', { note: noteObj, }); } @@ -554,26 +565,26 @@ export class NoteCreateService { }); //#region AP deliver - if (Users.isLocalUser(user)) { + if (this.usersRepository.isLocalUser(user)) { (async () => { const noteActivity = await this.#renderNoteOrRenoteActivity(data, note); const dm = new DeliverManager(user, noteActivity); // メンションされたリモートユーザーに配送 - for (const u of mentionedUsers.filter(u => Users.isRemoteUser(u))) { + for (const u of mentionedUsers.filter(u => this.usersRepository.isRemoteUser(u))) { dm.addDirectRecipe(u as IRemoteUser); } // 投稿がリプライかつ投稿者がローカルユーザーかつリプライ先の投稿の投稿者がリモートユーザーなら配送 if (data.reply && data.reply.userHost !== null) { - const u = await Users.findOneBy({ id: data.reply.userId }); - if (u && Users.isRemoteUser(u)) dm.addDirectRecipe(u); + const u = await this.usersRepository.findOneBy({ id: data.reply.userId }); + if (u && this.usersRepository.isRemoteUser(u)) dm.addDirectRecipe(u); } // 投稿がRenoteかつ投稿者がローカルユーザーかつRenote元の投稿の投稿者がリモートユーザーなら配送 if (data.renote && data.renote.userHost !== null) { - const u = await Users.findOneBy({ id: data.renote.userId }); - if (u && Users.isRemoteUser(u)) dm.addDirectRecipe(u); + const u = await this.usersRepository.findOneBy({ id: data.renote.userId }); + if (u && this.usersRepository.isRemoteUser(u)) dm.addDirectRecipe(u); } // フォロワーに配送 @@ -582,7 +593,7 @@ export class NoteCreateService { } if (['public'].includes(note.visibility)) { - deliverToRelays(user, noteActivity); + this.relayService.deliverToRelays(user, noteActivity); } dm.execute(); @@ -624,7 +635,7 @@ export class NoteCreateService { } async #createMentionedEvents(mentionedUsers: MinimumUser[], note: Note, nm: NotificationManager) { - for (const u of mentionedUsers.filter(u => Users.isLocalUser(u))) { + for (const u of mentionedUsers.filter(u => this.usersRepository.isLocalUser(u))) { const threadMuted = await NoteThreadMutings.findOneBy({ userId: u.id, threadId: note.threadId || note.id, @@ -638,11 +649,11 @@ export class NoteCreateService { detail: true, }); - publishMainStream(u.id, 'mention', detailPackedNote); + this.globalEventServie.publishMainStream(u.id, 'mention', detailPackedNote); - const webhooks = (await getActiveWebhooks()).filter(x => x.userId === u.id && x.on.includes('mention')); + const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === u.id && x.on.includes('mention')); for (const webhook of webhooks) { - webhookDeliver(webhook, 'mention', { + this.queueService.webhookDeliver(webhook, 'mention', { note: detailPackedNote, }); } @@ -660,17 +671,17 @@ export class NoteCreateService { if (data.localOnly) return null; const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length === 0) - ? renderAnnounce(data.renote.uri ? data.renote.uri : `${config.url}/notes/${data.renote.id}`, note) + ? renderAnnounce(data.renote.uri ? data.renote.uri : `${this.config.url}/notes/${data.renote.id}`, note) : renderCreate(await renderNote(note, false), note); return renderActivity(content); } #index(note: Note) { - if (note.text == null || config.elasticsearch == null) return; + if (note.text == null || this.config.elasticsearch == null) return; es!.index({ - index: config.elasticsearch.index || 'misskey_note', + index: this.config.elasticsearch.index || 'misskey_note', id: note.id.toString(), body: { text: normalizeForSearch(note.text), @@ -703,7 +714,7 @@ export class NoteCreateService { } #incNotesCountOfUser(user: { id: User['id']; }) { - Users.createQueryBuilder().update() + this.usersRepository.createQueryBuilder().update() .set({ updatedAt: new Date(), notesCount: () => '"notesCount" + 1', diff --git a/packages/backend/src/services/note/NoteDeleteService.ts b/packages/backend/src/services/NoteDeleteService.ts similarity index 66% rename from packages/backend/src/services/note/NoteDeleteService.ts rename to packages/backend/src/services/NoteDeleteService.ts index 72b6f1ba6355..abf5fd087f2a 100644 --- a/packages/backend/src/services/note/NoteDeleteService.ts +++ b/packages/backend/src/services/NoteDeleteService.ts @@ -1,26 +1,40 @@ import { Brackets, In } from 'typeorm'; import { Injectable, Inject } from '@nestjs/common'; -import { publishNoteStream } from '@/services/stream.js'; import renderDelete from '@/remote/activitypub/renderer/delete.js'; import renderAnnounce from '@/remote/activitypub/renderer/announce.js'; import renderUndo from '@/remote/activitypub/renderer/undo.js'; import { renderActivity } from '@/remote/activitypub/renderer/index.js'; import renderTombstone from '@/remote/activitypub/renderer/tombstone.js'; -import config from '@/config/index.js'; -import { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; -import { Note, IMentionedRemoteUsers } from '@/models/entities/note.js'; -import { Notes, Users, Instances } from '@/models/index.js'; -import { notesChart, perUserNotesChart, instanceChart } from '@/services/chart/index.js'; +import type { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; +import type { Note, IMentionedRemoteUsers } from '@/models/entities/note.js'; +import type { Notes } from '@/models/index.js'; +import { Users, Instances } from '@/models/index.js'; import { deliverToFollowers, deliverToUser } from '@/remote/activitypub/deliver-manager.js'; import { countSameRenotes } from '@/misc/count-same-renotes.js'; -import { registerOrFetchInstanceDoc } from '../register-or-fetch-instance-doc.js'; -import { deliverToRelays } from '../relay.js'; +import type { RelayService } from '@/services/RelayService.js'; +import type { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; +import type NotesChart from '@/services/chart/charts/notes.js'; +import type PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; +import type InstanceChart from '@/services/chart/charts/instance.js'; +import type { GlobalEventService } from '@/services/GlobalEventService.js'; @Injectable() export class NoteCreateService { constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, + + private globalEventServie: GlobalEventService, + private relayService: RelayService, + private federatedInstanceService: FederatedInstanceService, + private notesChart: NotesChart, + private perUserNotesChart: PerUserNotesChart, + private instanceChart: InstanceChart, ) {} /** @@ -42,7 +56,7 @@ export class NoteCreateService { } if (!quiet) { - publishNoteStream(note.id, 'deleted', { + this.globalEventServie.publishNoteStream(note.id, 'deleted', { deletedAt: deletedAt, }); @@ -58,8 +72,8 @@ export class NoteCreateService { } const content = renderActivity(renote - ? renderUndo(renderAnnounce(renote.uri || `${config.url}/notes/${renote.id}`, note), user) - : renderDelete(renderTombstone(`${config.url}/notes/${note.id}`), user)); + ? renderUndo(renderAnnounce(renote.uri || `${this.config.url}/notes/${renote.id}`, note), user) + : renderDelete(renderTombstone(`${this.config.url}/notes/${note.id}`), user)); this.#deliverToConcerned(user, note, content); } @@ -69,19 +83,19 @@ export class NoteCreateService { for (const cascadingNote of cascadingNotes) { if (!cascadingNote.user) continue; if (!Users.isLocalUser(cascadingNote.user)) continue; - const content = renderActivity(renderDelete(renderTombstone(`${config.url}/notes/${cascadingNote.id}`), cascadingNote.user)); + const content = renderActivity(renderDelete(renderTombstone(`${this.config.url}/notes/${cascadingNote.id}`), cascadingNote.user)); this.#deliverToConcerned(cascadingNote.user, cascadingNote, content); } //#endregion // 統計を更新 - notesChart.update(note, false); - perUserNotesChart.update(user, note, false); + this.notesChart.update(note, false); + this.perUserNotesChart.update(user, note, false); if (Users.isRemoteUser(user)) { - registerOrFetchInstanceDoc(user.host).then(i => { + this.federatedInstanceService.registerOrFetchInstanceDoc(user.host).then(i => { Instances.decrement({ id: i.id }, 'notesCount', 1); - instanceChart.updateNote(i.host, note, false); + this.instanceChart.updateNote(i.host, note, false); }); } } @@ -97,12 +111,12 @@ export class NoteCreateService { const recursive = async (noteId: string) => { const query = this.notesRepository.createQueryBuilder('note') - .where('note.replyId = :noteId', { noteId }) - .orWhere(new Brackets(q => { - q.where('note.renoteId = :noteId', { noteId }) - .andWhere('note.text IS NOT NULL'); - })) - .leftJoinAndSelect('note.user', 'user'); + .where('note.replyId = :noteId', { noteId }) + .orWhere(new Brackets(q => { + q.where('note.renoteId = :noteId', { noteId }) + .andWhere('note.text IS NOT NULL'); + })) + .leftJoinAndSelect('note.user', 'user'); const replies = await query.getMany(); for (const reply of replies) { cascadingNotes.push(reply); @@ -141,7 +155,7 @@ export class NoteCreateService { async #deliverToConcerned(user: { id: ILocalUser['id']; host: null; }, note: Note, content: any) { deliverToFollowers(user, content); - deliverToRelays(user, content); + this.relayService.deliverToRelays(user, content); const remoteUsers = await this.#getMentionedRemoteUsers(note); for (const remoteUser of remoteUsers) { deliverToUser(user, content, remoteUser); diff --git a/packages/backend/src/services/RelayService.ts b/packages/backend/src/services/RelayService.ts index 1b66a834d15b..b8db30424f42 100644 --- a/packages/backend/src/services/RelayService.ts +++ b/packages/backend/src/services/RelayService.ts @@ -9,7 +9,7 @@ import { genId } from '@/misc/gen-id.js'; import { Cache } from '@/misc/cache.js'; import type { Relay } from '@/models/entities/relay.js'; import type { QueueService } from '@/queue/queue.service.js'; -import type { CreateSystemUserService } from './CreateSystemUserService.js'; +import type { CreateSystemUserService } from '@/services/CreateSystemUserService.js'; const ACTOR_USERNAME = 'relay.actor' as const; From 946a2a94ddb63b9085cfc7c3fe1d2b1009ff3100 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 09:42:19 +0900 Subject: [PATCH 034/180] wip --- packages/backend/src/services/PollService.ts | 112 ++++++++++++++++++ .../backend/src/services/note/polls/update.ts | 21 ---- .../backend/src/services/note/polls/vote.ts | 81 ------------- 3 files changed, 112 insertions(+), 102 deletions(-) create mode 100644 packages/backend/src/services/PollService.ts delete mode 100644 packages/backend/src/services/note/polls/update.ts delete mode 100644 packages/backend/src/services/note/polls/vote.ts diff --git a/packages/backend/src/services/PollService.ts b/packages/backend/src/services/PollService.ts new file mode 100644 index 000000000000..8b04f7676b76 --- /dev/null +++ b/packages/backend/src/services/PollService.ts @@ -0,0 +1,112 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Not } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Notes, Users , Blockings } from '@/models/index.js'; +import { Polls , PollVotes } from '@/models/index.js'; +import type { Note } from '@/models/entities/note.js'; +import type { RelayService } from '@/services/RelayService.js'; +import type { CacheableUser } from '@/models/entities/user.js'; +import { genId } from '@/misc/gen-id.js'; +import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import type { CreateNotificationService } from '@/services/CreateNotificationService.js'; +import renderUpdate from '@/remote/activitypub/renderer/update.js'; +import { renderActivity } from '@/remote/activitypub/renderer/index.js'; +import renderNote from '@/remote/activitypub/renderer/note.js'; + +@Injectable() +export class PollService { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + + @Inject('pollsRepository') + private pollsRepository: typeof Polls, + + @Inject('pollVotesRepository') + private pollVotesRepository: typeof PollVotes, + + @Inject('blockingsRepository') + private blockingsRepository: typeof Blockings, + + private relayService: RelayService, + private globalEventServie: GlobalEventService, + private createNotificationService: CreateNotificationService, + ) { + } + + public async vote(user: CacheableUser, note: Note, choice: number) { + const poll = await this.pollsRepository.findOneBy({ noteId: note.id }); + + if (poll == null) throw new Error('poll not found'); + + // Check whether is valid choice + if (poll.choices[choice] == null) throw new Error('invalid choice param'); + + // Check blocking + if (note.userId !== user.id) { + const block = await this.blockingsRepository.findOneBy({ + blockerId: note.userId, + blockeeId: user.id, + }); + if (block) { + throw new Error('blocked'); + } + } + + // if already voted + const exist = await this.pollVotesRepository.findBy({ + noteId: note.id, + userId: user.id, + }); + + if (poll.multiple) { + if (exist.some(x => x.choice === choice)) { + throw new Error('already voted'); + } + } else if (exist.length !== 0) { + throw new Error('already voted'); + } + + // Create vote + await PollVotes.insert({ + id: genId(), + createdAt: new Date(), + noteId: note.id, + userId: user.id, + choice: choice, + }); + + // Increment votes count + const index = choice + 1; // In SQL, array index is 1 based + await Polls.query(`UPDATE poll SET votes[${index}] = votes[${index}] + 1 WHERE "noteId" = '${poll.noteId}'`); + + this.globalEventServie.publishNoteStream(note.id, 'pollVoted', { + choice: choice, + userId: user.id, + }); + + // Notify + this.createNotificationService.createNotification(note.userId, 'pollVote', { + notifierId: user.id, + noteId: note.id, + choice: choice, + }); + } + + public async deliverQuestionUpdate(noteId: Note['id']) { + const note = await this.notesRepository.findOneBy({ id: noteId }); + if (note == null) throw new Error('note not found'); + + const user = await this.usersRepository.findOneBy({ id: note.userId }); + if (user == null) throw new Error('note not found'); + + if (this.usersRepository.isLocalUser(user)) { + const content = renderActivity(renderUpdate(await renderNote(note, false), user)); + deliverToFollowers(user, content); + this.relayService.deliverToRelays(user, content); + } + } +} diff --git a/packages/backend/src/services/note/polls/update.ts b/packages/backend/src/services/note/polls/update.ts deleted file mode 100644 index 68cbb9835a51..000000000000 --- a/packages/backend/src/services/note/polls/update.ts +++ /dev/null @@ -1,21 +0,0 @@ -import renderUpdate from '@/remote/activitypub/renderer/update.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import renderNote from '@/remote/activitypub/renderer/note.js'; -import { Users, Notes } from '@/models/index.js'; -import { Note } from '@/models/entities/note.js'; -import { deliverToFollowers } from '@/remote/activitypub/deliver-manager.js'; -import { deliverToRelays } from '../../relay.js'; - -export async function deliverQuestionUpdate(noteId: Note['id']) { - const note = await Notes.findOneBy({ id: noteId }); - if (note == null) throw new Error('note not found'); - - const user = await Users.findOneBy({ id: note.userId }); - if (user == null) throw new Error('note not found'); - - if (Users.isLocalUser(user)) { - const content = renderActivity(renderUpdate(await renderNote(note, false), user)); - deliverToFollowers(user, content); - deliverToRelays(user, content); - } -} diff --git a/packages/backend/src/services/note/polls/vote.ts b/packages/backend/src/services/note/polls/vote.ts deleted file mode 100644 index 84d98769d94b..000000000000 --- a/packages/backend/src/services/note/polls/vote.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { publishNoteStream } from '@/services/stream.js'; -import { CacheableUser, User } from '@/models/entities/user.js'; -import { Note } from '@/models/entities/note.js'; -import { PollVotes, NoteWatchings, Polls, Blockings } from '@/models/index.js'; -import { Not } from 'typeorm'; -import { genId } from '@/misc/gen-id.js'; -import { createNotification } from '../../create-notification.js'; - -export default async function(user: CacheableUser, note: Note, choice: number) { - const poll = await Polls.findOneBy({ noteId: note.id }); - - if (poll == null) throw new Error('poll not found'); - - // Check whether is valid choice - if (poll.choices[choice] == null) throw new Error('invalid choice param'); - - // Check blocking - if (note.userId !== user.id) { - const block = await Blockings.findOneBy({ - blockerId: note.userId, - blockeeId: user.id, - }); - if (block) { - throw new Error('blocked'); - } - } - - // if already voted - const exist = await PollVotes.findBy({ - noteId: note.id, - userId: user.id, - }); - - if (poll.multiple) { - if (exist.some(x => x.choice === choice)) { - throw new Error('already voted'); - } - } else if (exist.length !== 0) { - throw new Error('already voted'); - } - - // Create vote - await PollVotes.insert({ - id: genId(), - createdAt: new Date(), - noteId: note.id, - userId: user.id, - choice: choice, - }); - - // Increment votes count - const index = choice + 1; // In SQL, array index is 1 based - await Polls.query(`UPDATE poll SET votes[${index}] = votes[${index}] + 1 WHERE "noteId" = '${poll.noteId}'`); - - publishNoteStream(note.id, 'pollVoted', { - choice: choice, - userId: user.id, - }); - - // Notify - createNotification(note.userId, 'pollVote', { - notifierId: user.id, - noteId: note.id, - choice: choice, - }); - - // Fetch watchers - NoteWatchings.findBy({ - noteId: note.id, - userId: Not(user.id), - }) - .then(watchers => { - for (const watcher of watchers) { - createNotification(watcher.userId, 'pollVote', { - notifierId: user.id, - noteId: note.id, - choice: choice, - }); - } - }); -} From 5a7fcc8d7a689a076663dcba6374543df2631ea9 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 09:45:55 +0900 Subject: [PATCH 035/180] wip --- .../src/services/ModerationLogService.ts | 24 +++++++++++++++++++ .../src/services/insert-moderation-log.ts | 13 ---------- 2 files changed, 24 insertions(+), 13 deletions(-) create mode 100644 packages/backend/src/services/ModerationLogService.ts delete mode 100644 packages/backend/src/services/insert-moderation-log.ts diff --git a/packages/backend/src/services/ModerationLogService.ts b/packages/backend/src/services/ModerationLogService.ts new file mode 100644 index 000000000000..83f4ce6c47f4 --- /dev/null +++ b/packages/backend/src/services/ModerationLogService.ts @@ -0,0 +1,24 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { ModerationLogs } from '@/models/index.js'; +import type { User } from '@/models/entities/user.js'; +import { genId } from '@/misc/gen-id.js'; + +@Injectable() +export class ModerationLogService { + constructor( + @Inject('moderationLogsRepository') + private moderationLogsRepository: typeof ModerationLogs, + ) { + } + + public async insertModerationLog(moderator: { id: User['id'] }, type: string, info?: Record) { + await this.moderationLogsRepository.insert({ + id: genId(), + createdAt: new Date(), + userId: moderator.id, + type: type, + info: info || {}, + }); + } +} diff --git a/packages/backend/src/services/insert-moderation-log.ts b/packages/backend/src/services/insert-moderation-log.ts deleted file mode 100644 index 0a7c472d8d1a..000000000000 --- a/packages/backend/src/services/insert-moderation-log.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ModerationLogs } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; -import { User } from '@/models/entities/user.js'; - -export async function insertModerationLog(moderator: { id: User['id'] }, type: string, info?: Record) { - await ModerationLogs.insert({ - id: genId(), - createdAt: new Date(), - userId: moderator.id, - type: type, - info: info || {}, - }); -} From 10e143126aae655a70392b7167ef04254ab24eee Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 09:51:25 +0900 Subject: [PATCH 036/180] wip --- .../backend/src/services/NoteReadService.ts | 193 ++++++++++++++++++ packages/backend/src/services/note/read.ts | 132 ------------ packages/backend/src/services/note/unread.ts | 55 ----- 3 files changed, 193 insertions(+), 187 deletions(-) create mode 100644 packages/backend/src/services/NoteReadService.ts delete mode 100644 packages/backend/src/services/note/read.ts delete mode 100644 packages/backend/src/services/note/unread.ts diff --git a/packages/backend/src/services/NoteReadService.ts b/packages/backend/src/services/NoteReadService.ts new file mode 100644 index 000000000000..c329d1cac86a --- /dev/null +++ b/packages/backend/src/services/NoteReadService.ts @@ -0,0 +1,193 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { NoteUnreads , Users } from '@/models/index.js'; + +import type { User } from '@/models/entities/user.js'; +import type { Channel } from '@/models/entities/channel.js'; +import type { Packed } from '@/misc/schema.js'; +import type { Note } from '@/models/entities/note.js'; +import { genId } from '@/misc/gen-id.js'; +import type { GlobalEventService } from '@/services/GlobalEventService.js'; + +@Injectable() +export class NoteReadService { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('noteUnreadsRepository') + private noteUnreadsRepository: typeof NoteUnreads, + + private globalEventServie: GlobalEventService, + ) { + } + + public async insertNoteUnread(userId: User['id'], note: Note, params: { + // NOTE: isSpecifiedがtrueならisMentionedは必ずfalse + isSpecified: boolean; + isMentioned: boolean; + }): Promise { + //#region ミュートしているなら無視 + // TODO: 現在の仕様ではChannelにミュートは適用されないのでよしなにケアする + const mute = await Mutings.findBy({ + muterId: userId, + }); + if (mute.map(m => m.muteeId).includes(note.userId)) return; + //#endregion + + // スレッドミュート + const threadMute = await NoteThreadMutings.findOneBy({ + userId: userId, + threadId: note.threadId || note.id, + }); + if (threadMute) return; + + const unread = { + id: genId(), + noteId: note.id, + userId: userId, + isSpecified: params.isSpecified, + isMentioned: params.isMentioned, + noteChannelId: note.channelId, + noteUserId: note.userId, + }; + + await this.noteUnreadsRepository.insert(unread); + + // 2秒経っても既読にならなかったら「未読の投稿がありますよ」イベントを発行する + setTimeout(async () => { + const exist = await this.noteUnreadsRepository.findOneBy({ id: unread.id }); + + if (exist == null) return; + + if (params.isMentioned) { + this.globalEventServie.publishMainStream(userId, 'unreadMention', note.id); + } + if (params.isSpecified) { + this.globalEventServie.publishMainStream(userId, 'unreadSpecifiedNote', note.id); + } + if (note.channelId) { + this.globalEventServie.publishMainStream(userId, 'unreadChannel', note.id); + } + }, 2000); + } + + public async read( + userId: User['id'], + notes: (Note | Packed<'Note'>)[], + info?: { + following: Set; + followingChannels: Set; + }, + ): Promise { + const following = info?.following ? info.following : new Set((await Followings.find({ + where: { + followerId: userId, + }, + select: ['followeeId'], + })).map(x => x.followeeId)); + const followingChannels = info?.followingChannels ? info.followingChannels : new Set((await ChannelFollowings.find({ + where: { + followerId: userId, + }, + select: ['followeeId'], + })).map(x => x.followeeId)); + + const myAntennas = (await getAntennas()).filter(a => a.userId === userId); + const readMentions: (Note | Packed<'Note'>)[] = []; + const readSpecifiedNotes: (Note | Packed<'Note'>)[] = []; + const readChannelNotes: (Note | Packed<'Note'>)[] = []; + const readAntennaNotes: (Note | Packed<'Note'>)[] = []; + + for (const note of notes) { + if (note.mentions && note.mentions.includes(userId)) { + readMentions.push(note); + } else if (note.visibleUserIds && note.visibleUserIds.includes(userId)) { + readSpecifiedNotes.push(note); + } + + if (note.channelId && followingChannels.has(note.channelId)) { + readChannelNotes.push(note); + } + + if (note.user != null) { // たぶんnullになることは無いはずだけど一応 + for (const antenna of myAntennas) { + if (await checkHitAntenna(antenna, note, note.user, undefined, Array.from(following))) { + readAntennaNotes.push(note); + } + } + } + } + + if ((readMentions.length > 0) || (readSpecifiedNotes.length > 0) || (readChannelNotes.length > 0)) { + // Remove the record + await this.noteUnreadsRepository.delete({ + userId: userId, + noteId: In([...readMentions.map(n => n.id), ...readSpecifiedNotes.map(n => n.id), ...readChannelNotes.map(n => n.id)]), + }); + + // TODO: ↓まとめてクエリしたい + + this.noteUnreadsRepository.countBy({ + userId: userId, + isMentioned: true, + }).then(mentionsCount => { + if (mentionsCount === 0) { + // 全て既読になったイベントを発行 + this.globalEventServie.publishMainStream(userId, 'readAllUnreadMentions'); + } + }); + + this.noteUnreadsRepository.countBy({ + userId: userId, + isSpecified: true, + }).then(specifiedCount => { + if (specifiedCount === 0) { + // 全て既読になったイベントを発行 + this.globalEventServie.publishMainStream(userId, 'readAllUnreadSpecifiedNotes'); + } + }); + + this.noteUnreadsRepository.countBy({ + userId: userId, + noteChannelId: Not(IsNull()), + }).then(channelNoteCount => { + if (channelNoteCount === 0) { + // 全て既読になったイベントを発行 + this.globalEventServie.publishMainStream(userId, 'readAllChannels'); + } + }); + + readNotificationByQuery(userId, { + noteId: In([...readMentions.map(n => n.id), ...readSpecifiedNotes.map(n => n.id)]), + }); + } + + if (readAntennaNotes.length > 0) { + await AntennaNotes.update({ + antennaId: In(myAntennas.map(a => a.id)), + noteId: In(readAntennaNotes.map(n => n.id)), + }, { + read: true, + }); + + // TODO: まとめてクエリしたい + for (const antenna of myAntennas) { + const count = await AntennaNotes.countBy({ + antennaId: antenna.id, + read: false, + }); + + if (count === 0) { + this.globalEventServie.publishMainStream(userId, 'readAntenna', antenna); + } + } + + this.usersRepository.getHasUnreadAntenna(userId).then(unread => { + if (!unread) { + this.globalEventServie.publishMainStream(userId, 'readAllAntennas'); + } + }); + } + } +} diff --git a/packages/backend/src/services/note/read.ts b/packages/backend/src/services/note/read.ts deleted file mode 100644 index 915a9e9eeff1..000000000000 --- a/packages/backend/src/services/note/read.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { publishMainStream } from '@/services/stream.js'; -import { Note } from '@/models/entities/note.js'; -import { User } from '@/models/entities/user.js'; -import { NoteUnreads, AntennaNotes, Users, Followings, ChannelFollowings } from '@/models/index.js'; -import { Not, IsNull, In } from 'typeorm'; -import { Channel } from '@/models/entities/channel.js'; -import { checkHitAntenna } from '@/misc/check-hit-antenna.js'; -import { getAntennas } from '@/misc/antenna-cache.js'; -import { readNotificationByQuery } from '@/server/api/common/read-notification.js'; -import { Packed } from '@/misc/schema.js'; - -/** - * Mark notes as read - */ -export default async function( - userId: User['id'], - notes: (Note | Packed<'Note'>)[], - info?: { - following: Set; - followingChannels: Set; - } -) { - const following = info?.following ? info.following : new Set((await Followings.find({ - where: { - followerId: userId, - }, - select: ['followeeId'], - })).map(x => x.followeeId)); - const followingChannels = info?.followingChannels ? info.followingChannels : new Set((await ChannelFollowings.find({ - where: { - followerId: userId, - }, - select: ['followeeId'], - })).map(x => x.followeeId)); - - const myAntennas = (await getAntennas()).filter(a => a.userId === userId); - const readMentions: (Note | Packed<'Note'>)[] = []; - const readSpecifiedNotes: (Note | Packed<'Note'>)[] = []; - const readChannelNotes: (Note | Packed<'Note'>)[] = []; - const readAntennaNotes: (Note | Packed<'Note'>)[] = []; - - for (const note of notes) { - if (note.mentions && note.mentions.includes(userId)) { - readMentions.push(note); - } else if (note.visibleUserIds && note.visibleUserIds.includes(userId)) { - readSpecifiedNotes.push(note); - } - - if (note.channelId && followingChannels.has(note.channelId)) { - readChannelNotes.push(note); - } - - if (note.user != null) { // たぶんnullになることは無いはずだけど一応 - for (const antenna of myAntennas) { - if (await checkHitAntenna(antenna, note, note.user, undefined, Array.from(following))) { - readAntennaNotes.push(note); - } - } - } - } - - if ((readMentions.length > 0) || (readSpecifiedNotes.length > 0) || (readChannelNotes.length > 0)) { - // Remove the record - await NoteUnreads.delete({ - userId: userId, - noteId: In([...readMentions.map(n => n.id), ...readSpecifiedNotes.map(n => n.id), ...readChannelNotes.map(n => n.id)]), - }); - - // TODO: ↓まとめてクエリしたい - - NoteUnreads.countBy({ - userId: userId, - isMentioned: true, - }).then(mentionsCount => { - if (mentionsCount === 0) { - // 全て既読になったイベントを発行 - publishMainStream(userId, 'readAllUnreadMentions'); - } - }); - - NoteUnreads.countBy({ - userId: userId, - isSpecified: true, - }).then(specifiedCount => { - if (specifiedCount === 0) { - // 全て既読になったイベントを発行 - publishMainStream(userId, 'readAllUnreadSpecifiedNotes'); - } - }); - - NoteUnreads.countBy({ - userId: userId, - noteChannelId: Not(IsNull()), - }).then(channelNoteCount => { - if (channelNoteCount === 0) { - // 全て既読になったイベントを発行 - publishMainStream(userId, 'readAllChannels'); - } - }); - - readNotificationByQuery(userId, { - noteId: In([...readMentions.map(n => n.id), ...readSpecifiedNotes.map(n => n.id)]), - }); - } - - if (readAntennaNotes.length > 0) { - await AntennaNotes.update({ - antennaId: In(myAntennas.map(a => a.id)), - noteId: In(readAntennaNotes.map(n => n.id)), - }, { - read: true, - }); - - // TODO: まとめてクエリしたい - for (const antenna of myAntennas) { - const count = await AntennaNotes.countBy({ - antennaId: antenna.id, - read: false, - }); - - if (count === 0) { - publishMainStream(userId, 'readAntenna', antenna); - } - } - - Users.getHasUnreadAntenna(userId).then(unread => { - if (!unread) { - publishMainStream(userId, 'readAllAntennas'); - } - }); - } -} diff --git a/packages/backend/src/services/note/unread.ts b/packages/backend/src/services/note/unread.ts deleted file mode 100644 index d9ed711e03f3..000000000000 --- a/packages/backend/src/services/note/unread.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Note } from '@/models/entities/note.js'; -import { publishMainStream } from '@/services/stream.js'; -import { User } from '@/models/entities/user.js'; -import { Mutings, NoteThreadMutings, NoteUnreads } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; - -export async function insertNoteUnread(userId: User['id'], note: Note, params: { - // NOTE: isSpecifiedがtrueならisMentionedは必ずfalse - isSpecified: boolean; - isMentioned: boolean; -}) { - //#region ミュートしているなら無視 - // TODO: 現在の仕様ではChannelにミュートは適用されないのでよしなにケアする - const mute = await Mutings.findBy({ - muterId: userId, - }); - if (mute.map(m => m.muteeId).includes(note.userId)) return; - //#endregion - - // スレッドミュート - const threadMute = await NoteThreadMutings.findOneBy({ - userId: userId, - threadId: note.threadId || note.id, - }); - if (threadMute) return; - - const unread = { - id: genId(), - noteId: note.id, - userId: userId, - isSpecified: params.isSpecified, - isMentioned: params.isMentioned, - noteChannelId: note.channelId, - noteUserId: note.userId, - }; - - await NoteUnreads.insert(unread); - - // 2秒経っても既読にならなかったら「未読の投稿がありますよ」イベントを発行する - setTimeout(async () => { - const exist = await NoteUnreads.findOneBy({ id: unread.id }); - - if (exist == null) return; - - if (params.isMentioned) { - publishMainStream(userId, 'unreadMention', note.id); - } - if (params.isSpecified) { - publishMainStream(userId, 'unreadSpecifiedNote', note.id); - } - if (note.channelId) { - publishMainStream(userId, 'unreadChannel', note.id); - } - }, 2000); -} From e7541904c4451b82502fc5e0fa5f6bf50c8b198b Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 09:59:28 +0900 Subject: [PATCH 037/180] wip --- .../backend/src/services/NotePiningService.ts | 111 ++++++++++++++++++ .../backend/src/services/NoteReadService.ts | 2 +- packages/backend/src/services/i/pin.ts | 92 --------------- 3 files changed, 112 insertions(+), 93 deletions(-) create mode 100644 packages/backend/src/services/NotePiningService.ts delete mode 100644 packages/backend/src/services/i/pin.ts diff --git a/packages/backend/src/services/NotePiningService.ts b/packages/backend/src/services/NotePiningService.ts new file mode 100644 index 000000000000..91202f621cfa --- /dev/null +++ b/packages/backend/src/services/NotePiningService.ts @@ -0,0 +1,111 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Users } from '@/models/index.js'; +import { Notes, UserNotePinings } from '@/models/index.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; +import type { User } from '@/models/entities/user.js'; +import type { Note } from '@/models/entities/note.js'; +import { genId } from '@/misc/gen-id.js'; +import type { UserNotePining } from '@/models/entities/user-note-pining.js'; +import type { RelayService } from '@/services/RelayService.js'; +import type { Config } from '@/config/types.js'; + +@Injectable() +export class NotePiningService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + + @Inject('userNotePiningsRepository') + private userNotePiningsRepository: typeof UserNotePinings, + + private relayService: RelayService, + ) { + } + + /** + * 指定した投稿をピン留めします + * @param user + * @param noteId + */ + public async addPinned(user: { id: User['id']; host: User['host']; }, noteId: Note['id']) { + // Fetch pinee + const note = await Notes.findOneBy({ + id: noteId, + userId: user.id, + }); + + if (note == null) { + throw new IdentifiableError('70c4e51f-5bea-449c-a030-53bee3cce202', 'No such note.'); + } + + const pinings = await this.userNotePiningsRepository.findBy({ userId: user.id }); + + if (pinings.length >= 5) { + throw new IdentifiableError('15a018eb-58e5-4da1-93be-330fcc5e4e1a', 'You can not pin notes any more.'); + } + + if (pinings.some(pining => pining.noteId === note.id)) { + throw new IdentifiableError('23f0cf4e-59a3-4276-a91d-61a5891c1514', 'That note has already been pinned.'); + } + + await this.userNotePiningsRepository.insert({ + id: genId(), + createdAt: new Date(), + userId: user.id, + noteId: note.id, + } as UserNotePining); + + // Deliver to remote followers + if (this.usersRepository.isLocalUser(user)) { + deliverPinnedChange(user.id, note.id, true); + } + } + + /** + * 指定した投稿のピン留めを解除します + * @param user + * @param noteId + */ + public async removePinned(user: { id: User['id']; host: User['host']; }, noteId: Note['id']) { + // Fetch unpinee + const note = await Notes.findOneBy({ + id: noteId, + userId: user.id, + }); + + if (note == null) { + throw new IdentifiableError('b302d4cf-c050-400a-bbb3-be208681f40c', 'No such note.'); + } + + UserNotePinings.delete({ + userId: user.id, + noteId: note.id, + }); + + // Deliver to remote followers + if (this.usersRepository.isLocalUser(user)) { + deliverPinnedChange(user.id, noteId, false); + } + } + + public async deliverPinnedChange(userId: User['id'], noteId: Note['id'], isAddition: boolean) { + const user = await this.usersRepository.findOneBy({ id: userId }); + if (user == null) throw new Error('user not found'); + + if (!this.usersRepository.isLocalUser(user)) return; + + const target = `${this.config.url}/users/${user.id}/collections/featured`; + const item = `${this.config.url}/notes/${noteId}`; + const content = renderActivity(isAddition ? renderAdd(user, target, item) : renderRemove(user, target, item)); + + deliverToFollowers(user, content); + this.relayService.deliverToRelays(user, content); + } +} diff --git a/packages/backend/src/services/NoteReadService.ts b/packages/backend/src/services/NoteReadService.ts index c329d1cac86a..740a5511a4d4 100644 --- a/packages/backend/src/services/NoteReadService.ts +++ b/packages/backend/src/services/NoteReadService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; +import { In, IsNull, Not } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { NoteUnreads , Users } from '@/models/index.js'; - import type { User } from '@/models/entities/user.js'; import type { Channel } from '@/models/entities/channel.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/i/pin.ts b/packages/backend/src/services/i/pin.ts deleted file mode 100644 index f35392a34bf3..000000000000 --- a/packages/backend/src/services/i/pin.ts +++ /dev/null @@ -1,92 +0,0 @@ -import config from '@/config/index.js'; -import renderAdd from '@/remote/activitypub/renderer/add.js'; -import renderRemove from '@/remote/activitypub/renderer/remove.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import { IdentifiableError } from '@/misc/identifiable-error.js'; -import { User } from '@/models/entities/user.js'; -import { Note } from '@/models/entities/note.js'; -import { Notes, UserNotePinings, Users } from '@/models/index.js'; -import { UserNotePining } from '@/models/entities/user-note-pining.js'; -import { genId } from '@/misc/gen-id.js'; -import { deliverToFollowers } from '@/remote/activitypub/deliver-manager.js'; -import { deliverToRelays } from '../relay.js'; - -/** - * 指定した投稿をピン留めします - * @param user - * @param noteId - */ -export async function addPinned(user: { id: User['id']; host: User['host']; }, noteId: Note['id']) { - // Fetch pinee - const note = await Notes.findOneBy({ - id: noteId, - userId: user.id, - }); - - if (note == null) { - throw new IdentifiableError('70c4e51f-5bea-449c-a030-53bee3cce202', 'No such note.'); - } - - const pinings = await UserNotePinings.findBy({ userId: user.id }); - - if (pinings.length >= 5) { - throw new IdentifiableError('15a018eb-58e5-4da1-93be-330fcc5e4e1a', 'You can not pin notes any more.'); - } - - if (pinings.some(pining => pining.noteId === note.id)) { - throw new IdentifiableError('23f0cf4e-59a3-4276-a91d-61a5891c1514', 'That note has already been pinned.'); - } - - await UserNotePinings.insert({ - id: genId(), - createdAt: new Date(), - userId: user.id, - noteId: note.id, - } as UserNotePining); - - // Deliver to remote followers - if (Users.isLocalUser(user)) { - deliverPinnedChange(user.id, note.id, true); - } -} - -/** - * 指定した投稿のピン留めを解除します - * @param user - * @param noteId - */ -export async function removePinned(user: { id: User['id']; host: User['host']; }, noteId: Note['id']) { - // Fetch unpinee - const note = await Notes.findOneBy({ - id: noteId, - userId: user.id, - }); - - if (note == null) { - throw new IdentifiableError('b302d4cf-c050-400a-bbb3-be208681f40c', 'No such note.'); - } - - UserNotePinings.delete({ - userId: user.id, - noteId: note.id, - }); - - // Deliver to remote followers - if (Users.isLocalUser(user)) { - deliverPinnedChange(user.id, noteId, false); - } -} - -export async function deliverPinnedChange(userId: User['id'], noteId: Note['id'], isAddition: boolean) { - const user = await Users.findOneBy({ id: userId }); - if (user == null) throw new Error('user not found'); - - if (!Users.isLocalUser(user)) return; - - const target = `${config.url}/users/${user.id}/collections/featured`; - const item = `${config.url}/notes/${noteId}`; - const content = renderActivity(isAddition ? renderAdd(user, target, item) : renderRemove(user, target, item)); - - deliverToFollowers(user, content); - deliverToRelays(user, content); -} From 9fd72cbe7cf59e000d5556d4c3d66ef48e4c5b0a Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 11:08:04 +0900 Subject: [PATCH 038/180] wip --- .../backend/src/{ => services}/remote/activitypub/ap-request.ts | 0 .../backend/src/{ => services}/remote/activitypub/audience.ts | 0 .../backend/src/{ => services}/remote/activitypub/db-resolver.ts | 0 .../src/{ => services}/remote/activitypub/deliver-manager.ts | 0 .../src/{ => services}/remote/activitypub/kernel/accept/follow.ts | 0 .../src/{ => services}/remote/activitypub/kernel/accept/index.ts | 0 .../src/{ => services}/remote/activitypub/kernel/add/index.ts | 0 .../{ => services}/remote/activitypub/kernel/announce/index.ts | 0 .../src/{ => services}/remote/activitypub/kernel/announce/note.ts | 0 .../src/{ => services}/remote/activitypub/kernel/block/index.ts | 0 .../src/{ => services}/remote/activitypub/kernel/create/index.ts | 0 .../src/{ => services}/remote/activitypub/kernel/create/note.ts | 0 .../src/{ => services}/remote/activitypub/kernel/delete/actor.ts | 0 .../src/{ => services}/remote/activitypub/kernel/delete/index.ts | 0 .../src/{ => services}/remote/activitypub/kernel/delete/note.ts | 0 .../src/{ => services}/remote/activitypub/kernel/flag/index.ts | 0 .../src/{ => services}/remote/activitypub/kernel/follow.ts | 0 .../backend/src/{ => services}/remote/activitypub/kernel/index.ts | 0 .../backend/src/{ => services}/remote/activitypub/kernel/like.ts | 0 .../backend/src/{ => services}/remote/activitypub/kernel/read.ts | 0 .../src/{ => services}/remote/activitypub/kernel/reject/follow.ts | 0 .../src/{ => services}/remote/activitypub/kernel/reject/index.ts | 0 .../src/{ => services}/remote/activitypub/kernel/remove/index.ts | 0 .../src/{ => services}/remote/activitypub/kernel/undo/accept.ts | 0 .../src/{ => services}/remote/activitypub/kernel/undo/announce.ts | 0 .../src/{ => services}/remote/activitypub/kernel/undo/block.ts | 0 .../src/{ => services}/remote/activitypub/kernel/undo/follow.ts | 0 .../src/{ => services}/remote/activitypub/kernel/undo/index.ts | 0 .../src/{ => services}/remote/activitypub/kernel/undo/like.ts | 0 .../src/{ => services}/remote/activitypub/kernel/update/index.ts | 0 packages/backend/src/{ => services}/remote/activitypub/logger.ts | 0 .../src/{ => services}/remote/activitypub/misc/contexts.ts | 0 .../src/{ => services}/remote/activitypub/misc/get-note-html.ts | 0 .../src/{ => services}/remote/activitypub/misc/html-to-mfm.ts | 0 .../src/{ => services}/remote/activitypub/misc/ld-signature.ts | 0 .../backend/src/{ => services}/remote/activitypub/models/icon.ts | 0 .../src/{ => services}/remote/activitypub/models/identifier.ts | 0 .../backend/src/{ => services}/remote/activitypub/models/image.ts | 0 .../src/{ => services}/remote/activitypub/models/mention.ts | 0 .../backend/src/{ => services}/remote/activitypub/models/note.ts | 0 .../src/{ => services}/remote/activitypub/models/person.ts | 0 .../src/{ => services}/remote/activitypub/models/question.ts | 0 .../backend/src/{ => services}/remote/activitypub/models/tag.ts | 0 packages/backend/src/{ => services}/remote/activitypub/perform.ts | 0 .../src/{ => services}/remote/activitypub/renderer/accept.ts | 0 .../backend/src/{ => services}/remote/activitypub/renderer/add.ts | 0 .../src/{ => services}/remote/activitypub/renderer/announce.ts | 0 .../src/{ => services}/remote/activitypub/renderer/block.ts | 0 .../src/{ => services}/remote/activitypub/renderer/create.ts | 0 .../src/{ => services}/remote/activitypub/renderer/delete.ts | 0 .../src/{ => services}/remote/activitypub/renderer/document.ts | 0 .../src/{ => services}/remote/activitypub/renderer/emoji.ts | 0 .../src/{ => services}/remote/activitypub/renderer/flag.ts | 0 .../{ => services}/remote/activitypub/renderer/follow-relay.ts | 0 .../src/{ => services}/remote/activitypub/renderer/follow-user.ts | 0 .../src/{ => services}/remote/activitypub/renderer/follow.ts | 0 .../src/{ => services}/remote/activitypub/renderer/hashtag.ts | 0 .../src/{ => services}/remote/activitypub/renderer/image.ts | 0 .../src/{ => services}/remote/activitypub/renderer/index.ts | 0 .../backend/src/{ => services}/remote/activitypub/renderer/key.ts | 0 .../src/{ => services}/remote/activitypub/renderer/like.ts | 0 .../src/{ => services}/remote/activitypub/renderer/mention.ts | 0 .../src/{ => services}/remote/activitypub/renderer/note.ts | 0 .../remote/activitypub/renderer/ordered-collection-page.ts | 0 .../remote/activitypub/renderer/ordered-collection.ts | 0 .../src/{ => services}/remote/activitypub/renderer/person.ts | 0 .../src/{ => services}/remote/activitypub/renderer/question.ts | 0 .../src/{ => services}/remote/activitypub/renderer/read.ts | 0 .../src/{ => services}/remote/activitypub/renderer/reject.ts | 0 .../src/{ => services}/remote/activitypub/renderer/remove.ts | 0 .../src/{ => services}/remote/activitypub/renderer/tombstone.ts | 0 .../src/{ => services}/remote/activitypub/renderer/undo.ts | 0 .../src/{ => services}/remote/activitypub/renderer/update.ts | 0 .../src/{ => services}/remote/activitypub/renderer/vote.ts | 0 packages/backend/src/{ => services}/remote/activitypub/request.ts | 0 .../backend/src/{ => services}/remote/activitypub/resolver.ts | 0 packages/backend/src/{ => services}/remote/activitypub/type.ts | 0 packages/backend/src/{ => services}/remote/logger.ts | 0 packages/backend/src/{ => services}/remote/resolve-user.ts | 0 packages/backend/src/{ => services}/remote/webfinger.ts | 0 80 files changed, 0 insertions(+), 0 deletions(-) rename packages/backend/src/{ => services}/remote/activitypub/ap-request.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/audience.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/db-resolver.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/deliver-manager.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/accept/follow.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/accept/index.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/add/index.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/announce/index.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/announce/note.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/block/index.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/create/index.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/create/note.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/delete/actor.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/delete/index.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/delete/note.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/flag/index.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/follow.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/index.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/like.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/read.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/reject/follow.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/reject/index.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/remove/index.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/undo/accept.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/undo/announce.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/undo/block.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/undo/follow.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/undo/index.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/undo/like.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/kernel/update/index.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/logger.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/misc/contexts.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/misc/get-note-html.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/misc/html-to-mfm.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/misc/ld-signature.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/models/icon.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/models/identifier.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/models/image.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/models/mention.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/models/note.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/models/person.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/models/question.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/models/tag.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/perform.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/accept.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/add.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/announce.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/block.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/create.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/delete.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/document.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/emoji.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/flag.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/follow-relay.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/follow-user.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/follow.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/hashtag.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/image.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/index.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/key.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/like.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/mention.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/note.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/ordered-collection-page.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/ordered-collection.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/person.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/question.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/read.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/reject.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/remove.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/tombstone.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/undo.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/update.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/renderer/vote.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/request.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/resolver.ts (100%) rename packages/backend/src/{ => services}/remote/activitypub/type.ts (100%) rename packages/backend/src/{ => services}/remote/logger.ts (100%) rename packages/backend/src/{ => services}/remote/resolve-user.ts (100%) rename packages/backend/src/{ => services}/remote/webfinger.ts (100%) diff --git a/packages/backend/src/remote/activitypub/ap-request.ts b/packages/backend/src/services/remote/activitypub/ap-request.ts similarity index 100% rename from packages/backend/src/remote/activitypub/ap-request.ts rename to packages/backend/src/services/remote/activitypub/ap-request.ts diff --git a/packages/backend/src/remote/activitypub/audience.ts b/packages/backend/src/services/remote/activitypub/audience.ts similarity index 100% rename from packages/backend/src/remote/activitypub/audience.ts rename to packages/backend/src/services/remote/activitypub/audience.ts diff --git a/packages/backend/src/remote/activitypub/db-resolver.ts b/packages/backend/src/services/remote/activitypub/db-resolver.ts similarity index 100% rename from packages/backend/src/remote/activitypub/db-resolver.ts rename to packages/backend/src/services/remote/activitypub/db-resolver.ts diff --git a/packages/backend/src/remote/activitypub/deliver-manager.ts b/packages/backend/src/services/remote/activitypub/deliver-manager.ts similarity index 100% rename from packages/backend/src/remote/activitypub/deliver-manager.ts rename to packages/backend/src/services/remote/activitypub/deliver-manager.ts diff --git a/packages/backend/src/remote/activitypub/kernel/accept/follow.ts b/packages/backend/src/services/remote/activitypub/kernel/accept/follow.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/accept/follow.ts rename to packages/backend/src/services/remote/activitypub/kernel/accept/follow.ts diff --git a/packages/backend/src/remote/activitypub/kernel/accept/index.ts b/packages/backend/src/services/remote/activitypub/kernel/accept/index.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/accept/index.ts rename to packages/backend/src/services/remote/activitypub/kernel/accept/index.ts diff --git a/packages/backend/src/remote/activitypub/kernel/add/index.ts b/packages/backend/src/services/remote/activitypub/kernel/add/index.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/add/index.ts rename to packages/backend/src/services/remote/activitypub/kernel/add/index.ts diff --git a/packages/backend/src/remote/activitypub/kernel/announce/index.ts b/packages/backend/src/services/remote/activitypub/kernel/announce/index.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/announce/index.ts rename to packages/backend/src/services/remote/activitypub/kernel/announce/index.ts diff --git a/packages/backend/src/remote/activitypub/kernel/announce/note.ts b/packages/backend/src/services/remote/activitypub/kernel/announce/note.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/announce/note.ts rename to packages/backend/src/services/remote/activitypub/kernel/announce/note.ts diff --git a/packages/backend/src/remote/activitypub/kernel/block/index.ts b/packages/backend/src/services/remote/activitypub/kernel/block/index.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/block/index.ts rename to packages/backend/src/services/remote/activitypub/kernel/block/index.ts diff --git a/packages/backend/src/remote/activitypub/kernel/create/index.ts b/packages/backend/src/services/remote/activitypub/kernel/create/index.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/create/index.ts rename to packages/backend/src/services/remote/activitypub/kernel/create/index.ts diff --git a/packages/backend/src/remote/activitypub/kernel/create/note.ts b/packages/backend/src/services/remote/activitypub/kernel/create/note.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/create/note.ts rename to packages/backend/src/services/remote/activitypub/kernel/create/note.ts diff --git a/packages/backend/src/remote/activitypub/kernel/delete/actor.ts b/packages/backend/src/services/remote/activitypub/kernel/delete/actor.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/delete/actor.ts rename to packages/backend/src/services/remote/activitypub/kernel/delete/actor.ts diff --git a/packages/backend/src/remote/activitypub/kernel/delete/index.ts b/packages/backend/src/services/remote/activitypub/kernel/delete/index.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/delete/index.ts rename to packages/backend/src/services/remote/activitypub/kernel/delete/index.ts diff --git a/packages/backend/src/remote/activitypub/kernel/delete/note.ts b/packages/backend/src/services/remote/activitypub/kernel/delete/note.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/delete/note.ts rename to packages/backend/src/services/remote/activitypub/kernel/delete/note.ts diff --git a/packages/backend/src/remote/activitypub/kernel/flag/index.ts b/packages/backend/src/services/remote/activitypub/kernel/flag/index.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/flag/index.ts rename to packages/backend/src/services/remote/activitypub/kernel/flag/index.ts diff --git a/packages/backend/src/remote/activitypub/kernel/follow.ts b/packages/backend/src/services/remote/activitypub/kernel/follow.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/follow.ts rename to packages/backend/src/services/remote/activitypub/kernel/follow.ts diff --git a/packages/backend/src/remote/activitypub/kernel/index.ts b/packages/backend/src/services/remote/activitypub/kernel/index.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/index.ts rename to packages/backend/src/services/remote/activitypub/kernel/index.ts diff --git a/packages/backend/src/remote/activitypub/kernel/like.ts b/packages/backend/src/services/remote/activitypub/kernel/like.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/like.ts rename to packages/backend/src/services/remote/activitypub/kernel/like.ts diff --git a/packages/backend/src/remote/activitypub/kernel/read.ts b/packages/backend/src/services/remote/activitypub/kernel/read.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/read.ts rename to packages/backend/src/services/remote/activitypub/kernel/read.ts diff --git a/packages/backend/src/remote/activitypub/kernel/reject/follow.ts b/packages/backend/src/services/remote/activitypub/kernel/reject/follow.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/reject/follow.ts rename to packages/backend/src/services/remote/activitypub/kernel/reject/follow.ts diff --git a/packages/backend/src/remote/activitypub/kernel/reject/index.ts b/packages/backend/src/services/remote/activitypub/kernel/reject/index.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/reject/index.ts rename to packages/backend/src/services/remote/activitypub/kernel/reject/index.ts diff --git a/packages/backend/src/remote/activitypub/kernel/remove/index.ts b/packages/backend/src/services/remote/activitypub/kernel/remove/index.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/remove/index.ts rename to packages/backend/src/services/remote/activitypub/kernel/remove/index.ts diff --git a/packages/backend/src/remote/activitypub/kernel/undo/accept.ts b/packages/backend/src/services/remote/activitypub/kernel/undo/accept.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/undo/accept.ts rename to packages/backend/src/services/remote/activitypub/kernel/undo/accept.ts diff --git a/packages/backend/src/remote/activitypub/kernel/undo/announce.ts b/packages/backend/src/services/remote/activitypub/kernel/undo/announce.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/undo/announce.ts rename to packages/backend/src/services/remote/activitypub/kernel/undo/announce.ts diff --git a/packages/backend/src/remote/activitypub/kernel/undo/block.ts b/packages/backend/src/services/remote/activitypub/kernel/undo/block.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/undo/block.ts rename to packages/backend/src/services/remote/activitypub/kernel/undo/block.ts diff --git a/packages/backend/src/remote/activitypub/kernel/undo/follow.ts b/packages/backend/src/services/remote/activitypub/kernel/undo/follow.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/undo/follow.ts rename to packages/backend/src/services/remote/activitypub/kernel/undo/follow.ts diff --git a/packages/backend/src/remote/activitypub/kernel/undo/index.ts b/packages/backend/src/services/remote/activitypub/kernel/undo/index.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/undo/index.ts rename to packages/backend/src/services/remote/activitypub/kernel/undo/index.ts diff --git a/packages/backend/src/remote/activitypub/kernel/undo/like.ts b/packages/backend/src/services/remote/activitypub/kernel/undo/like.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/undo/like.ts rename to packages/backend/src/services/remote/activitypub/kernel/undo/like.ts diff --git a/packages/backend/src/remote/activitypub/kernel/update/index.ts b/packages/backend/src/services/remote/activitypub/kernel/update/index.ts similarity index 100% rename from packages/backend/src/remote/activitypub/kernel/update/index.ts rename to packages/backend/src/services/remote/activitypub/kernel/update/index.ts diff --git a/packages/backend/src/remote/activitypub/logger.ts b/packages/backend/src/services/remote/activitypub/logger.ts similarity index 100% rename from packages/backend/src/remote/activitypub/logger.ts rename to packages/backend/src/services/remote/activitypub/logger.ts diff --git a/packages/backend/src/remote/activitypub/misc/contexts.ts b/packages/backend/src/services/remote/activitypub/misc/contexts.ts similarity index 100% rename from packages/backend/src/remote/activitypub/misc/contexts.ts rename to packages/backend/src/services/remote/activitypub/misc/contexts.ts diff --git a/packages/backend/src/remote/activitypub/misc/get-note-html.ts b/packages/backend/src/services/remote/activitypub/misc/get-note-html.ts similarity index 100% rename from packages/backend/src/remote/activitypub/misc/get-note-html.ts rename to packages/backend/src/services/remote/activitypub/misc/get-note-html.ts diff --git a/packages/backend/src/remote/activitypub/misc/html-to-mfm.ts b/packages/backend/src/services/remote/activitypub/misc/html-to-mfm.ts similarity index 100% rename from packages/backend/src/remote/activitypub/misc/html-to-mfm.ts rename to packages/backend/src/services/remote/activitypub/misc/html-to-mfm.ts diff --git a/packages/backend/src/remote/activitypub/misc/ld-signature.ts b/packages/backend/src/services/remote/activitypub/misc/ld-signature.ts similarity index 100% rename from packages/backend/src/remote/activitypub/misc/ld-signature.ts rename to packages/backend/src/services/remote/activitypub/misc/ld-signature.ts diff --git a/packages/backend/src/remote/activitypub/models/icon.ts b/packages/backend/src/services/remote/activitypub/models/icon.ts similarity index 100% rename from packages/backend/src/remote/activitypub/models/icon.ts rename to packages/backend/src/services/remote/activitypub/models/icon.ts diff --git a/packages/backend/src/remote/activitypub/models/identifier.ts b/packages/backend/src/services/remote/activitypub/models/identifier.ts similarity index 100% rename from packages/backend/src/remote/activitypub/models/identifier.ts rename to packages/backend/src/services/remote/activitypub/models/identifier.ts diff --git a/packages/backend/src/remote/activitypub/models/image.ts b/packages/backend/src/services/remote/activitypub/models/image.ts similarity index 100% rename from packages/backend/src/remote/activitypub/models/image.ts rename to packages/backend/src/services/remote/activitypub/models/image.ts diff --git a/packages/backend/src/remote/activitypub/models/mention.ts b/packages/backend/src/services/remote/activitypub/models/mention.ts similarity index 100% rename from packages/backend/src/remote/activitypub/models/mention.ts rename to packages/backend/src/services/remote/activitypub/models/mention.ts diff --git a/packages/backend/src/remote/activitypub/models/note.ts b/packages/backend/src/services/remote/activitypub/models/note.ts similarity index 100% rename from packages/backend/src/remote/activitypub/models/note.ts rename to packages/backend/src/services/remote/activitypub/models/note.ts diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/services/remote/activitypub/models/person.ts similarity index 100% rename from packages/backend/src/remote/activitypub/models/person.ts rename to packages/backend/src/services/remote/activitypub/models/person.ts diff --git a/packages/backend/src/remote/activitypub/models/question.ts b/packages/backend/src/services/remote/activitypub/models/question.ts similarity index 100% rename from packages/backend/src/remote/activitypub/models/question.ts rename to packages/backend/src/services/remote/activitypub/models/question.ts diff --git a/packages/backend/src/remote/activitypub/models/tag.ts b/packages/backend/src/services/remote/activitypub/models/tag.ts similarity index 100% rename from packages/backend/src/remote/activitypub/models/tag.ts rename to packages/backend/src/services/remote/activitypub/models/tag.ts diff --git a/packages/backend/src/remote/activitypub/perform.ts b/packages/backend/src/services/remote/activitypub/perform.ts similarity index 100% rename from packages/backend/src/remote/activitypub/perform.ts rename to packages/backend/src/services/remote/activitypub/perform.ts diff --git a/packages/backend/src/remote/activitypub/renderer/accept.ts b/packages/backend/src/services/remote/activitypub/renderer/accept.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/accept.ts rename to packages/backend/src/services/remote/activitypub/renderer/accept.ts diff --git a/packages/backend/src/remote/activitypub/renderer/add.ts b/packages/backend/src/services/remote/activitypub/renderer/add.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/add.ts rename to packages/backend/src/services/remote/activitypub/renderer/add.ts diff --git a/packages/backend/src/remote/activitypub/renderer/announce.ts b/packages/backend/src/services/remote/activitypub/renderer/announce.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/announce.ts rename to packages/backend/src/services/remote/activitypub/renderer/announce.ts diff --git a/packages/backend/src/remote/activitypub/renderer/block.ts b/packages/backend/src/services/remote/activitypub/renderer/block.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/block.ts rename to packages/backend/src/services/remote/activitypub/renderer/block.ts diff --git a/packages/backend/src/remote/activitypub/renderer/create.ts b/packages/backend/src/services/remote/activitypub/renderer/create.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/create.ts rename to packages/backend/src/services/remote/activitypub/renderer/create.ts diff --git a/packages/backend/src/remote/activitypub/renderer/delete.ts b/packages/backend/src/services/remote/activitypub/renderer/delete.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/delete.ts rename to packages/backend/src/services/remote/activitypub/renderer/delete.ts diff --git a/packages/backend/src/remote/activitypub/renderer/document.ts b/packages/backend/src/services/remote/activitypub/renderer/document.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/document.ts rename to packages/backend/src/services/remote/activitypub/renderer/document.ts diff --git a/packages/backend/src/remote/activitypub/renderer/emoji.ts b/packages/backend/src/services/remote/activitypub/renderer/emoji.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/emoji.ts rename to packages/backend/src/services/remote/activitypub/renderer/emoji.ts diff --git a/packages/backend/src/remote/activitypub/renderer/flag.ts b/packages/backend/src/services/remote/activitypub/renderer/flag.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/flag.ts rename to packages/backend/src/services/remote/activitypub/renderer/flag.ts diff --git a/packages/backend/src/remote/activitypub/renderer/follow-relay.ts b/packages/backend/src/services/remote/activitypub/renderer/follow-relay.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/follow-relay.ts rename to packages/backend/src/services/remote/activitypub/renderer/follow-relay.ts diff --git a/packages/backend/src/remote/activitypub/renderer/follow-user.ts b/packages/backend/src/services/remote/activitypub/renderer/follow-user.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/follow-user.ts rename to packages/backend/src/services/remote/activitypub/renderer/follow-user.ts diff --git a/packages/backend/src/remote/activitypub/renderer/follow.ts b/packages/backend/src/services/remote/activitypub/renderer/follow.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/follow.ts rename to packages/backend/src/services/remote/activitypub/renderer/follow.ts diff --git a/packages/backend/src/remote/activitypub/renderer/hashtag.ts b/packages/backend/src/services/remote/activitypub/renderer/hashtag.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/hashtag.ts rename to packages/backend/src/services/remote/activitypub/renderer/hashtag.ts diff --git a/packages/backend/src/remote/activitypub/renderer/image.ts b/packages/backend/src/services/remote/activitypub/renderer/image.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/image.ts rename to packages/backend/src/services/remote/activitypub/renderer/image.ts diff --git a/packages/backend/src/remote/activitypub/renderer/index.ts b/packages/backend/src/services/remote/activitypub/renderer/index.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/index.ts rename to packages/backend/src/services/remote/activitypub/renderer/index.ts diff --git a/packages/backend/src/remote/activitypub/renderer/key.ts b/packages/backend/src/services/remote/activitypub/renderer/key.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/key.ts rename to packages/backend/src/services/remote/activitypub/renderer/key.ts diff --git a/packages/backend/src/remote/activitypub/renderer/like.ts b/packages/backend/src/services/remote/activitypub/renderer/like.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/like.ts rename to packages/backend/src/services/remote/activitypub/renderer/like.ts diff --git a/packages/backend/src/remote/activitypub/renderer/mention.ts b/packages/backend/src/services/remote/activitypub/renderer/mention.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/mention.ts rename to packages/backend/src/services/remote/activitypub/renderer/mention.ts diff --git a/packages/backend/src/remote/activitypub/renderer/note.ts b/packages/backend/src/services/remote/activitypub/renderer/note.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/note.ts rename to packages/backend/src/services/remote/activitypub/renderer/note.ts diff --git a/packages/backend/src/remote/activitypub/renderer/ordered-collection-page.ts b/packages/backend/src/services/remote/activitypub/renderer/ordered-collection-page.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/ordered-collection-page.ts rename to packages/backend/src/services/remote/activitypub/renderer/ordered-collection-page.ts diff --git a/packages/backend/src/remote/activitypub/renderer/ordered-collection.ts b/packages/backend/src/services/remote/activitypub/renderer/ordered-collection.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/ordered-collection.ts rename to packages/backend/src/services/remote/activitypub/renderer/ordered-collection.ts diff --git a/packages/backend/src/remote/activitypub/renderer/person.ts b/packages/backend/src/services/remote/activitypub/renderer/person.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/person.ts rename to packages/backend/src/services/remote/activitypub/renderer/person.ts diff --git a/packages/backend/src/remote/activitypub/renderer/question.ts b/packages/backend/src/services/remote/activitypub/renderer/question.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/question.ts rename to packages/backend/src/services/remote/activitypub/renderer/question.ts diff --git a/packages/backend/src/remote/activitypub/renderer/read.ts b/packages/backend/src/services/remote/activitypub/renderer/read.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/read.ts rename to packages/backend/src/services/remote/activitypub/renderer/read.ts diff --git a/packages/backend/src/remote/activitypub/renderer/reject.ts b/packages/backend/src/services/remote/activitypub/renderer/reject.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/reject.ts rename to packages/backend/src/services/remote/activitypub/renderer/reject.ts diff --git a/packages/backend/src/remote/activitypub/renderer/remove.ts b/packages/backend/src/services/remote/activitypub/renderer/remove.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/remove.ts rename to packages/backend/src/services/remote/activitypub/renderer/remove.ts diff --git a/packages/backend/src/remote/activitypub/renderer/tombstone.ts b/packages/backend/src/services/remote/activitypub/renderer/tombstone.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/tombstone.ts rename to packages/backend/src/services/remote/activitypub/renderer/tombstone.ts diff --git a/packages/backend/src/remote/activitypub/renderer/undo.ts b/packages/backend/src/services/remote/activitypub/renderer/undo.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/undo.ts rename to packages/backend/src/services/remote/activitypub/renderer/undo.ts diff --git a/packages/backend/src/remote/activitypub/renderer/update.ts b/packages/backend/src/services/remote/activitypub/renderer/update.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/update.ts rename to packages/backend/src/services/remote/activitypub/renderer/update.ts diff --git a/packages/backend/src/remote/activitypub/renderer/vote.ts b/packages/backend/src/services/remote/activitypub/renderer/vote.ts similarity index 100% rename from packages/backend/src/remote/activitypub/renderer/vote.ts rename to packages/backend/src/services/remote/activitypub/renderer/vote.ts diff --git a/packages/backend/src/remote/activitypub/request.ts b/packages/backend/src/services/remote/activitypub/request.ts similarity index 100% rename from packages/backend/src/remote/activitypub/request.ts rename to packages/backend/src/services/remote/activitypub/request.ts diff --git a/packages/backend/src/remote/activitypub/resolver.ts b/packages/backend/src/services/remote/activitypub/resolver.ts similarity index 100% rename from packages/backend/src/remote/activitypub/resolver.ts rename to packages/backend/src/services/remote/activitypub/resolver.ts diff --git a/packages/backend/src/remote/activitypub/type.ts b/packages/backend/src/services/remote/activitypub/type.ts similarity index 100% rename from packages/backend/src/remote/activitypub/type.ts rename to packages/backend/src/services/remote/activitypub/type.ts diff --git a/packages/backend/src/remote/logger.ts b/packages/backend/src/services/remote/logger.ts similarity index 100% rename from packages/backend/src/remote/logger.ts rename to packages/backend/src/services/remote/logger.ts diff --git a/packages/backend/src/remote/resolve-user.ts b/packages/backend/src/services/remote/resolve-user.ts similarity index 100% rename from packages/backend/src/remote/resolve-user.ts rename to packages/backend/src/services/remote/resolve-user.ts diff --git a/packages/backend/src/remote/webfinger.ts b/packages/backend/src/services/remote/webfinger.ts similarity index 100% rename from packages/backend/src/remote/webfinger.ts rename to packages/backend/src/services/remote/webfinger.ts From ecb0e6b6c87444a8b1e4e3a6a36109c10019f44b Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 11:17:05 +0900 Subject: [PATCH 039/180] wip --- .../queue/processors/db/import-blocking.ts | 12 ++++---- .../queue/processors/db/import-following.ts | 12 ++++---- .../src/queue/processors/db/import-muting.ts | 14 ++++----- .../queue/processors/db/import-user-lists.ts | 12 ++++---- .../backend/src/queue/processors/deliver.ts | 8 ++--- .../backend/src/queue/processors/inbox.ts | 30 +++++++++---------- packages/backend/src/queue/queue.service.ts | 2 +- packages/backend/src/queue/types.ts | 12 ++++---- packages/backend/src/server/activitypub.ts | 26 ++++++++-------- .../src/server/activitypub/featured.ts | 12 ++++---- .../src/server/activitypub/followers.ts | 15 +++++----- .../src/server/activitypub/following.ts | 15 +++++----- .../backend/src/server/activitypub/outbox.ts | 16 +++++----- .../api/common/read-messaging-message.ts | 26 ++++++++-------- .../admin/resolve-abuse-user-report.ts | 4 +-- .../src/server/api/endpoints/ap/get.ts | 2 +- .../src/server/api/endpoints/ap/show.ts | 13 ++++---- .../federation/update-remote-user.ts | 2 +- .../server/api/endpoints/notes/polls/vote.ts | 4 +-- .../src/server/api/endpoints/users/show.ts | 2 +- .../backend/src/services/NoteCreateService.ts | 12 ++++---- .../backend/src/services/NoteDeleteService.ts | 12 ++++---- packages/backend/src/services/PollService.ts | 6 ++-- .../backend/src/services/ReactionService.ts | 2 +- packages/backend/src/services/RelayService.ts | 6 ++-- .../src/services/UserBlockingService.ts | 10 +++---- .../src/services/UserFollowingService.ts | 10 +++---- .../src/services/UserSuspendService.ts | 6 ++-- packages/backend/src/services/i/update.ts | 10 +++---- .../backend/src/services/messages/create.ts | 18 +++++------ .../backend/src/services/messages/delete.ts | 8 ++--- .../remote/activitypub/kernel/read.ts | 11 +++---- .../remote/activitypub/misc/get-note-html.ts | 4 +-- .../remote/activitypub/misc/html-to-mfm.ts | 4 +-- .../remote/activitypub/models/person.ts | 12 ++++---- .../remote/activitypub/renderer/flag.ts | 2 +- .../remote/activitypub/renderer/person.ts | 18 +++++------ .../services/remote/activitypub/request.ts | 4 +-- .../services/remote/activitypub/resolver.ts | 14 ++++----- 39 files changed, 206 insertions(+), 202 deletions(-) diff --git a/packages/backend/src/queue/processors/db/import-blocking.ts b/packages/backend/src/queue/processors/db/import-blocking.ts index 8bddf34bc2ce..a216720bb861 100644 --- a/packages/backend/src/queue/processors/db/import-blocking.ts +++ b/packages/backend/src/queue/processors/db/import-blocking.ts @@ -1,14 +1,14 @@ -import Bull from 'bull'; - -import { queueLogger } from '../../logger.js'; +import { IsNull } from 'typeorm'; import * as Acct from '@/misc/acct.js'; -import { resolveUser } from '@/remote/resolve-user.js'; + +import { resolveUser } from '@/services/remote/resolve-user.js'; import { downloadTextFile } from '@/misc/download-text-file.js'; import { isSelfHost, toPuny } from '@/misc/convert-host.js'; import { Users, DriveFiles, Blockings } from '@/models/index.js'; -import { DbUserImportJobData } from '@/queue/types.js'; +import type { DbUserImportJobData } from '@/queue/types.js'; import block from '@/services/blocking/create.js'; -import { IsNull } from 'typeorm'; +import { queueLogger } from '../../logger.js'; +import type Bull from 'bull'; const logger = queueLogger.createSubLogger('import-blocking'); diff --git a/packages/backend/src/queue/processors/db/import-following.ts b/packages/backend/src/queue/processors/db/import-following.ts index 8ce2c367d6c8..09a3c49085be 100644 --- a/packages/backend/src/queue/processors/db/import-following.ts +++ b/packages/backend/src/queue/processors/db/import-following.ts @@ -1,14 +1,14 @@ -import Bull from 'bull'; - -import { queueLogger } from '../../logger.js'; +import { IsNull } from 'typeorm'; import follow from '@/services/following/create.js'; + import * as Acct from '@/misc/acct.js'; -import { resolveUser } from '@/remote/resolve-user.js'; +import { resolveUser } from '@/services/remote/resolve-user.js'; import { downloadTextFile } from '@/misc/download-text-file.js'; import { isSelfHost, toPuny } from '@/misc/convert-host.js'; import { Users, DriveFiles } from '@/models/index.js'; -import { DbUserImportJobData } from '@/queue/types.js'; -import { IsNull } from 'typeorm'; +import type { DbUserImportJobData } from '@/queue/types.js'; +import { queueLogger } from '../../logger.js'; +import type Bull from 'bull'; const logger = queueLogger.createSubLogger('import-following'); diff --git a/packages/backend/src/queue/processors/db/import-muting.ts b/packages/backend/src/queue/processors/db/import-muting.ts index 8552b797be21..ca966aa49245 100644 --- a/packages/backend/src/queue/processors/db/import-muting.ts +++ b/packages/backend/src/queue/processors/db/import-muting.ts @@ -1,15 +1,15 @@ -import Bull from 'bull'; - -import { queueLogger } from '../../logger.js'; +import { IsNull } from 'typeorm'; import * as Acct from '@/misc/acct.js'; -import { resolveUser } from '@/remote/resolve-user.js'; + +import { resolveUser } from '@/services/remote/resolve-user.js'; import { downloadTextFile } from '@/misc/download-text-file.js'; import { isSelfHost, toPuny } from '@/misc/convert-host.js'; import { Users, DriveFiles, Mutings } from '@/models/index.js'; -import { DbUserImportJobData } from '@/queue/types.js'; -import { User } from '@/models/entities/user.js'; +import type { DbUserImportJobData } from '@/queue/types.js'; +import type { User } from '@/models/entities/user.js'; import { genId } from '@/misc/gen-id.js'; -import { IsNull } from 'typeorm'; +import { queueLogger } from '../../logger.js'; +import type Bull from 'bull'; const logger = queueLogger.createSubLogger('import-muting'); diff --git a/packages/backend/src/queue/processors/db/import-user-lists.ts b/packages/backend/src/queue/processors/db/import-user-lists.ts index 9919b7c53c4b..3ea5cca57bcb 100644 --- a/packages/backend/src/queue/processors/db/import-user-lists.ts +++ b/packages/backend/src/queue/processors/db/import-user-lists.ts @@ -1,15 +1,15 @@ -import Bull from 'bull'; - -import { queueLogger } from '../../logger.js'; +import { IsNull } from 'typeorm'; import * as Acct from '@/misc/acct.js'; -import { resolveUser } from '@/remote/resolve-user.js'; + +import { resolveUser } from '@/services/remote/resolve-user.js'; import { pushUserToUserList } from '@/services/user-list/push.js'; import { downloadTextFile } from '@/misc/download-text-file.js'; import { isSelfHost, toPuny } from '@/misc/convert-host.js'; import { DriveFiles, Users, UserLists, UserListJoinings } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; -import { DbUserImportJobData } from '@/queue/types.js'; -import { IsNull } from 'typeorm'; +import type { DbUserImportJobData } from '@/queue/types.js'; +import { queueLogger } from '../../logger.js'; +import type Bull from 'bull'; const logger = queueLogger.createSubLogger('import-user-lists'); diff --git a/packages/backend/src/queue/processors/deliver.ts b/packages/backend/src/queue/processors/deliver.ts index 291c05766ed1..935bec837273 100644 --- a/packages/backend/src/queue/processors/deliver.ts +++ b/packages/backend/src/queue/processors/deliver.ts @@ -1,6 +1,5 @@ import { URL } from 'node:url'; -import Bull from 'bull'; -import request from '@/remote/activitypub/request.js'; +import request from '@/services/remote/activitypub/request.js'; import { registerOrFetchInstanceDoc } from '@/services/register-or-fetch-instance-doc.js'; import Logger from '@/services/logger.js'; import { Instances } from '@/models/index.js'; @@ -9,9 +8,10 @@ import { fetchInstanceMetadata } from '@/services/fetch-instance-metadata.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { toPuny } from '@/misc/convert-host.js'; import { Cache } from '@/misc/cache.js'; -import { Instance } from '@/models/entities/instance.js'; -import { DeliverJobData } from '../types.js'; +import type { Instance } from '@/models/entities/instance.js'; import { StatusError } from '@/misc/fetch.js'; +import type { DeliverJobData } from '../types.js'; +import type Bull from 'bull'; const logger = new Logger('deliver'); diff --git a/packages/backend/src/queue/processors/inbox.ts b/packages/backend/src/queue/processors/inbox.ts index 198dde60507b..3f7125ca598a 100644 --- a/packages/backend/src/queue/processors/inbox.ts +++ b/packages/backend/src/queue/processors/inbox.ts @@ -1,22 +1,22 @@ import { URL } from 'node:url'; -import Bull from 'bull'; import httpSignature from '@peertube/http-signature'; -import perform from '@/remote/activitypub/perform.js'; +import perform from '@/services/remote/activitypub/perform.js'; import Logger from '@/services/logger.js'; import { registerOrFetchInstanceDoc } from '@/services/register-or-fetch-instance-doc.js'; import { Instances } from '@/models/index.js'; import { apRequestChart, federationChart, instanceChart } from '@/services/chart/index.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { toPuny, extractDbHost } from '@/misc/convert-host.js'; -import { getApId } from '@/remote/activitypub/type.js'; +import { getApId } from '@/services/remote/activitypub/type.js'; import { fetchInstanceMetadata } from '@/services/fetch-instance-metadata.js'; -import { InboxJobData } from '../types.js'; -import DbResolver from '@/remote/activitypub/db-resolver.js'; -import { resolvePerson } from '@/remote/activitypub/models/person.js'; -import { LdSignature } from '@/remote/activitypub/misc/ld-signature.js'; +import DbResolver from '@/services/remote/activitypub/db-resolver.js'; +import { resolvePerson } from '@/services/remote/activitypub/models/person.js'; +import { LdSignature } from '@/services/remote/activitypub/misc/ld-signature.js'; import { StatusError } from '@/misc/fetch.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import { UserPublickey } from '@/models/entities/user-publickey.js'; +import type { CacheableRemoteUser } from '@/models/entities/user.js'; +import type { UserPublickey } from '@/models/entities/user-publickey.js'; +import type { InboxJobData } from '../types.js'; +import type Bull from 'bull'; const logger = new Logger('inbox'); @@ -69,12 +69,12 @@ export default async (job: Bull.Job): Promise => { // それでもわからなければ終了 if (authUser == null) { - return `skip: failed to resolve user`; + return 'skip: failed to resolve user'; } // publicKey がなくても終了 if (authUser.key == null) { - return `skip: failed to resolve user publicKey`; + return 'skip: failed to resolve user publicKey'; } // HTTP-Signatureの検証 @@ -98,18 +98,18 @@ export default async (job: Bull.Job): Promise => { // keyIdからLD-Signatureのユーザーを取得 authUser = await dbResolver.getAuthUserFromKeyId(activity.signature.creator); if (authUser == null) { - return `skip: LD-Signatureのユーザーが取得できませんでした`; + return 'skip: LD-Signatureのユーザーが取得できませんでした'; } if (authUser.key == null) { - return `skip: LD-SignatureのユーザーはpublicKeyを持っていませんでした`; + return 'skip: LD-SignatureのユーザーはpublicKeyを持っていませんでした'; } // LD-Signature検証 const ldSignature = new LdSignature(); const verified = await ldSignature.verifyRsaSignature2017(activity, authUser.key.keyPem).catch(() => false); if (!verified) { - return `skip: LD-Signatureの検証に失敗しました`; + return 'skip: LD-Signatureの検証に失敗しました'; } // もう一度actorチェック @@ -153,5 +153,5 @@ export default async (job: Bull.Job): Promise => { // アクティビティを処理 await perform(authUser.user, activity); - return `ok`; + return 'ok'; }; diff --git a/packages/backend/src/queue/queue.service.ts b/packages/backend/src/queue/queue.service.ts index 8a6585f0f31b..42a8f4f5fa8e 100644 --- a/packages/backend/src/queue/queue.service.ts +++ b/packages/backend/src/queue/queue.service.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { v4 as uuid } from 'uuid'; import config from '@/config/index.js'; -import type { IActivity } from '@/remote/activitypub/type.js'; +import type { IActivity } from '@/services/remote/activitypub/type.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import type { Webhook, webhookEventTypes } from '@/models/entities/webhook.js'; diff --git a/packages/backend/src/queue/types.ts b/packages/backend/src/queue/types.ts index 5ea472556199..cc47e7b376fa 100644 --- a/packages/backend/src/queue/types.ts +++ b/packages/backend/src/queue/types.ts @@ -1,9 +1,9 @@ -import { DriveFile } from '@/models/entities/drive-file.js'; -import { Note } from '@/models/entities/note'; -import { User } from '@/models/entities/user.js'; -import { Webhook } from '@/models/entities/webhook'; -import { IActivity } from '@/remote/activitypub/type.js'; -import httpSignature from '@peertube/http-signature'; +import type { DriveFile } from '@/models/entities/drive-file.js'; +import type { Note } from '@/models/entities/note'; +import type { User } from '@/models/entities/user.js'; +import type { Webhook } from '@/models/entities/webhook'; +import type { IActivity } from '@/services/remote/activitypub/type.js'; +import type httpSignature from '@peertube/http-signature'; export type DeliverJobData = { /** Actor */ diff --git a/packages/backend/src/server/activitypub.ts b/packages/backend/src/server/activitypub.ts index cd5f917c400e..eed6eed7ee78 100644 --- a/packages/backend/src/server/activitypub.ts +++ b/packages/backend/src/server/activitypub.ts @@ -2,23 +2,23 @@ import Router from '@koa/router'; import json from 'koa-json-body'; import httpSignature from '@peertube/http-signature'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import renderNote from '@/remote/activitypub/renderer/note.js'; -import renderKey from '@/remote/activitypub/renderer/key.js'; -import { renderPerson } from '@/remote/activitypub/renderer/person.js'; -import renderEmoji from '@/remote/activitypub/renderer/emoji.js'; -import Outbox, { packActivity } from './activitypub/outbox.js'; -import Followers from './activitypub/followers.js'; -import Following from './activitypub/following.js'; -import Featured from './activitypub/featured.js'; +import { In, IsNull, Not } from 'typeorm'; +import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; +import renderNote from '@/services/remote/activitypub/renderer/note.js'; +import renderKey from '@/services/remote/activitypub/renderer/key.js'; +import { renderPerson } from '@/services/remote/activitypub/renderer/person.js'; +import renderEmoji from '@/services/remote/activitypub/renderer/emoji.js'; import { inbox as processInbox } from '@/queue/index.js'; import { isSelfHost } from '@/misc/convert-host.js'; import { Notes, Users, Emojis, NoteReactions } from '@/models/index.js'; -import { ILocalUser, User } from '@/models/entities/user.js'; -import { In, IsNull, Not } from 'typeorm'; -import { renderLike } from '@/remote/activitypub/renderer/like.js'; +import type { ILocalUser, User } from '@/models/entities/user.js'; +import { renderLike } from '@/services/remote/activitypub/renderer/like.js'; import { getUserKeypair } from '@/misc/keypair-store.js'; -import renderFollow from '@/remote/activitypub/renderer/follow.js'; +import renderFollow from '@/services/remote/activitypub/renderer/follow.js'; +import Featured from './activitypub/featured.js'; +import Following from './activitypub/following.js'; +import Followers from './activitypub/followers.js'; +import Outbox, { packActivity } from './activitypub/outbox.js'; // Init router const router = new Router(); diff --git a/packages/backend/src/server/activitypub/featured.ts b/packages/backend/src/server/activitypub/featured.ts index c03fd1049f9e..35916cccea6c 100644 --- a/packages/backend/src/server/activitypub/featured.ts +++ b/packages/backend/src/server/activitypub/featured.ts @@ -1,11 +1,11 @@ -import Router from '@koa/router'; +import { IsNull } from 'typeorm'; import config from '@/config/index.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import renderOrderedCollection from '@/remote/activitypub/renderer/ordered-collection.js'; -import { setResponseType } from '../activitypub.js'; -import renderNote from '@/remote/activitypub/renderer/note.js'; +import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; +import renderOrderedCollection from '@/services/remote/activitypub/renderer/ordered-collection.js'; +import renderNote from '@/services/remote/activitypub/renderer/note.js'; import { Users, Notes, UserNotePinings } from '@/models/index.js'; -import { IsNull } from 'typeorm'; +import { setResponseType } from '../activitypub.js'; +import type Router from '@koa/router'; export default async (ctx: Router.RouterContext) => { const userId = ctx.params.user; diff --git a/packages/backend/src/server/activitypub/followers.ts b/packages/backend/src/server/activitypub/followers.ts index beb48713a6e3..47c078d5c077 100644 --- a/packages/backend/src/server/activitypub/followers.ts +++ b/packages/backend/src/server/activitypub/followers.ts @@ -1,14 +1,15 @@ -import Router from '@koa/router'; -import { FindOptionsWhere, IsNull, LessThan } from 'typeorm'; +import { IsNull, LessThan } from 'typeorm'; import config from '@/config/index.js'; import * as url from '@/prelude/url.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import renderOrderedCollection from '@/remote/activitypub/renderer/ordered-collection.js'; -import renderOrderedCollectionPage from '@/remote/activitypub/renderer/ordered-collection-page.js'; -import renderFollowUser from '@/remote/activitypub/renderer/follow-user.js'; +import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; +import renderOrderedCollection from '@/services/remote/activitypub/renderer/ordered-collection.js'; +import renderOrderedCollectionPage from '@/services/remote/activitypub/renderer/ordered-collection-page.js'; +import renderFollowUser from '@/services/remote/activitypub/renderer/follow-user.js'; import { Users, Followings, UserProfiles } from '@/models/index.js'; -import { Following } from '@/models/entities/following.js'; +import type { Following } from '@/models/entities/following.js'; import { setResponseType } from '../activitypub.js'; +import type { FindOptionsWhere } from 'typeorm'; +import type Router from '@koa/router'; export default async (ctx: Router.RouterContext) => { const userId = ctx.params.user; diff --git a/packages/backend/src/server/activitypub/following.ts b/packages/backend/src/server/activitypub/following.ts index 3a25a6316c36..72d004f7e782 100644 --- a/packages/backend/src/server/activitypub/following.ts +++ b/packages/backend/src/server/activitypub/following.ts @@ -1,14 +1,15 @@ -import Router from '@koa/router'; -import { LessThan, IsNull, FindOptionsWhere } from 'typeorm'; +import { LessThan, IsNull } from 'typeorm'; import config from '@/config/index.js'; import * as url from '@/prelude/url.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import renderOrderedCollection from '@/remote/activitypub/renderer/ordered-collection.js'; -import renderOrderedCollectionPage from '@/remote/activitypub/renderer/ordered-collection-page.js'; -import renderFollowUser from '@/remote/activitypub/renderer/follow-user.js'; +import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; +import renderOrderedCollection from '@/services/remote/activitypub/renderer/ordered-collection.js'; +import renderOrderedCollectionPage from '@/services/remote/activitypub/renderer/ordered-collection-page.js'; +import renderFollowUser from '@/services/remote/activitypub/renderer/follow-user.js'; import { Users, Followings, UserProfiles } from '@/models/index.js'; -import { Following } from '@/models/entities/following.js'; +import type { Following } from '@/models/entities/following.js'; import { setResponseType } from '../activitypub.js'; +import type { FindOptionsWhere } from 'typeorm'; +import type Router from '@koa/router'; export default async (ctx: Router.RouterContext) => { const userId = ctx.params.user; diff --git a/packages/backend/src/server/activitypub/outbox.ts b/packages/backend/src/server/activitypub/outbox.ts index 7a2586998a8b..70eded0b277a 100644 --- a/packages/backend/src/server/activitypub/outbox.ts +++ b/packages/backend/src/server/activitypub/outbox.ts @@ -1,18 +1,18 @@ -import Router from '@koa/router'; import { Brackets, IsNull } from 'typeorm'; import config from '@/config/index.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import renderOrderedCollection from '@/remote/activitypub/renderer/ordered-collection.js'; -import renderOrderedCollectionPage from '@/remote/activitypub/renderer/ordered-collection-page.js'; -import renderNote from '@/remote/activitypub/renderer/note.js'; -import renderCreate from '@/remote/activitypub/renderer/create.js'; -import renderAnnounce from '@/remote/activitypub/renderer/announce.js'; +import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; +import renderOrderedCollection from '@/services/remote/activitypub/renderer/ordered-collection.js'; +import renderOrderedCollectionPage from '@/services/remote/activitypub/renderer/ordered-collection-page.js'; +import renderNote from '@/services/remote/activitypub/renderer/note.js'; +import renderCreate from '@/services/remote/activitypub/renderer/create.js'; +import renderAnnounce from '@/services/remote/activitypub/renderer/announce.js'; import { countIf } from '@/prelude/array.js'; import * as url from '@/prelude/url.js'; import { Users, Notes } from '@/models/index.js'; -import { Note } from '@/models/entities/note.js'; +import type { Note } from '@/models/entities/note.js'; import { makePaginationQuery } from '../api/common/make-pagination-query.js'; import { setResponseType } from '../activitypub.js'; +import type Router from '@koa/router'; export default async (ctx: Router.RouterContext) => { const userId = ctx.params.user; diff --git a/packages/backend/src/server/api/common/read-messaging-message.ts b/packages/backend/src/server/api/common/read-messaging-message.ts index c4c18ffa0617..39d1290ad7d2 100644 --- a/packages/backend/src/server/api/common/read-messaging-message.ts +++ b/packages/backend/src/server/api/common/read-messaging-message.ts @@ -1,18 +1,16 @@ -import { publishMainStream, publishGroupMessagingStream } from '@/services/stream.js'; -import { publishMessagingStream } from '@/services/stream.js'; -import { publishMessagingIndexStream } from '@/services/stream.js'; +import { In } from 'typeorm'; +import { publishMainStream, publishGroupMessagingStream , publishMessagingStream , publishMessagingIndexStream } from '@/services/stream.js'; import { pushNotification } from '@/services/push-notification.js'; -import { User, IRemoteUser } from '@/models/entities/user.js'; -import { MessagingMessage } from '@/models/entities/messaging-message.js'; +import type { User, IRemoteUser } from '@/models/entities/user.js'; +import type { MessagingMessage } from '@/models/entities/messaging-message.js'; import { MessagingMessages, UserGroupJoinings, Users } from '@/models/index.js'; -import { In } from 'typeorm'; import { IdentifiableError } from '@/misc/identifiable-error.js'; -import { UserGroup } from '@/models/entities/user-group.js'; +import type { UserGroup } from '@/models/entities/user-group.js'; import { toArray } from '@/prelude/array.js'; -import { renderReadActivity } from '@/remote/activitypub/renderer/read.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; +import { renderReadActivity } from '@/services/remote/activitypub/renderer/read.js'; +import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; import { deliver } from '@/queue/index.js'; -import orderedCollection from '@/remote/activitypub/renderer/ordered-collection.js'; +import orderedCollection from '@/services/remote/activitypub/renderer/ordered-collection.js'; /** * Mark messages as read @@ -20,7 +18,7 @@ import orderedCollection from '@/remote/activitypub/renderer/ordered-collection. export async function readUserMessagingMessage( userId: User['id'], otherpartyId: User['id'], - messageIds: MessagingMessage['id'][] + messageIds: MessagingMessage['id'][], ) { if (messageIds.length === 0) return; @@ -60,7 +58,7 @@ export async function readUserMessagingMessage( recipientId: userId, isRead: false, }, - take: 1 + take: 1, }); if (!count) { @@ -75,7 +73,7 @@ export async function readUserMessagingMessage( export async function readGroupMessagingMessage( userId: User['id'], groupId: UserGroup['id'], - messageIds: MessagingMessage['id'][] + messageIds: MessagingMessage['id'][], ) { if (messageIds.length === 0) return; @@ -124,7 +122,7 @@ export async function readGroupMessagingMessage( } else { // そのグループにおいて未読がなければイベント発行 const unreadExist = await MessagingMessages.createQueryBuilder('message') - .where(`message.groupId = :groupId`, { groupId: groupId }) + .where('message.groupId = :groupId', { groupId: groupId }) .andWhere('message.userId != :userId', { userId: userId }) .andWhere('NOT (:userId = ANY(message.reads))', { userId: userId }) .andWhere('message.createdAt > :joinedAt', { joinedAt: joining.createdAt }) // 自分が加入する前の会話については、未読扱いしない diff --git a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts index 9740c6b62766..2f6fcc785d50 100644 --- a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts +++ b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts @@ -2,8 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; import { AbuseUserReports } from '@/models/index.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import { renderFlag } from '@/remote/activitypub/renderer/flag.js'; +import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; +import { renderFlag } from '@/services/remote/activitypub/renderer/flag.js'; import type { InstanceActorService } from '@/services/InstanceActorService'; import type { QueueService } from '@/queue/queue.service'; diff --git a/packages/backend/src/server/api/endpoints/ap/get.ts b/packages/backend/src/server/api/endpoints/ap/get.ts index ebea57a83000..48d66a7d055e 100644 --- a/packages/backend/src/server/api/endpoints/ap/get.ts +++ b/packages/backend/src/server/api/endpoints/ap/get.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import Resolver from '@/remote/activitypub/resolver.js'; +import Resolver from '@/services/remote/activitypub/resolver.js'; import { ApiError } from '../../error.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index 00dbba4ba09f..3f3d1142882a 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -2,16 +2,17 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import config from '@/config/index.js'; -import { createPerson } from '@/remote/activitypub/models/person.js'; -import { createNote } from '@/remote/activitypub/models/note.js'; -import DbResolver from '@/remote/activitypub/db-resolver.js'; -import Resolver from '@/remote/activitypub/resolver.js'; +import { createPerson } from '@/services/remote/activitypub/models/person.js'; +import { createNote } from '@/services/remote/activitypub/models/note.js'; +import DbResolver from '@/services/remote/activitypub/db-resolver.js'; +import Resolver from '@/services/remote/activitypub/resolver.js'; import { extractDbHost } from '@/misc/convert-host.js'; -import { Users, Notes } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { Notes } from '@/models/index.js'; import type { Note } from '@/models/entities/note.js'; import type { CacheableLocalUser, User } from '@/models/entities/user.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; -import { isActor, isPost, getApId } from '@/remote/activitypub/type.js'; +import { isActor, isPost, getApId } from '@/services/remote/activitypub/type.js'; import type { SchemaType } from '@/misc/schema.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts index c4e59dac63a7..a19e4fa83440 100644 --- a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts +++ b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { updatePerson } from '@/remote/activitypub/models/person.js'; +import { updatePerson } from '@/services/remote/activitypub/models/person.js'; import { getRemoteUser } from '../../common/getters.js'; export const meta = { 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 848cf1210e05..a433b66741fb 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -3,8 +3,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { publishNoteStream } from '@/services/stream.js'; import { createNotification } from '@/services/create-notification.js'; import { deliver } from '@/queue/index.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import renderVote from '@/remote/activitypub/renderer/vote.js'; +import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; +import renderVote from '@/services/remote/activitypub/renderer/vote.js'; import { deliverQuestionUpdate } from '@/services/note/polls/update.js'; import type { Users } from '@/models/index.js'; import { PollVotes, NoteWatchings, Polls, Blockings } from '@/models/index.js'; diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts index 5cd4961c4855..59a9476c1ef3 100644 --- a/packages/backend/src/server/api/endpoints/users/show.ts +++ b/packages/backend/src/server/api/endpoints/users/show.ts @@ -1,6 +1,6 @@ import { In, IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { resolveUser } from '@/remote/resolve-user.js'; +import { resolveUser } from '@/services/remote/resolve-user.js'; import type { Users } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; diff --git a/packages/backend/src/services/NoteCreateService.ts b/packages/backend/src/services/NoteCreateService.ts index 62be0516bc1e..19a0d87eb33c 100644 --- a/packages/backend/src/services/NoteCreateService.ts +++ b/packages/backend/src/services/NoteCreateService.ts @@ -12,12 +12,12 @@ import type { DriveFile } from '@/models/entities/drive-file.js'; import type { App } from '@/models/entities/app.js'; import { insertNoteUnread } from '@/services/note/unread.js'; import { concat } from '@/prelude/array.js'; -import { resolveUser } from '@/remote/resolve-user.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import renderAnnounce from '@/remote/activitypub/renderer/announce.js'; -import renderCreate from '@/remote/activitypub/renderer/create.js'; -import renderNote from '@/remote/activitypub/renderer/note.js'; -import DeliverManager from '@/remote/activitypub/deliver-manager.js'; +import { resolveUser } from '@/services/remote/resolve-user.js'; +import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; +import renderAnnounce from '@/services/remote/activitypub/renderer/announce.js'; +import renderCreate from '@/services/remote/activitypub/renderer/create.js'; +import renderNote from '@/services/remote/activitypub/renderer/note.js'; +import DeliverManager from '@/services/remote/activitypub/deliver-manager.js'; import { genId } from '@/misc/gen-id.js'; import type { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; import type { IPoll } from '@/models/entities/poll.js'; diff --git a/packages/backend/src/services/NoteDeleteService.ts b/packages/backend/src/services/NoteDeleteService.ts index abf5fd087f2a..ff63e6545204 100644 --- a/packages/backend/src/services/NoteDeleteService.ts +++ b/packages/backend/src/services/NoteDeleteService.ts @@ -1,15 +1,15 @@ import { Brackets, In } from 'typeorm'; import { Injectable, Inject } from '@nestjs/common'; -import renderDelete from '@/remote/activitypub/renderer/delete.js'; -import renderAnnounce from '@/remote/activitypub/renderer/announce.js'; -import renderUndo from '@/remote/activitypub/renderer/undo.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import renderTombstone from '@/remote/activitypub/renderer/tombstone.js'; +import renderDelete from '@/services/remote/activitypub/renderer/delete.js'; +import renderAnnounce from '@/services/remote/activitypub/renderer/announce.js'; +import renderUndo from '@/services/remote/activitypub/renderer/undo.js'; +import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; +import renderTombstone from '@/services/remote/activitypub/renderer/tombstone.js'; import type { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; import type { Note, IMentionedRemoteUsers } from '@/models/entities/note.js'; import type { Notes } from '@/models/index.js'; import { Users, Instances } from '@/models/index.js'; -import { deliverToFollowers, deliverToUser } from '@/remote/activitypub/deliver-manager.js'; +import { deliverToFollowers, deliverToUser } from '@/services/remote/activitypub/deliver-manager.js'; import { countSameRenotes } from '@/misc/count-same-renotes.js'; import type { RelayService } from '@/services/RelayService.js'; import type { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; diff --git a/packages/backend/src/services/PollService.ts b/packages/backend/src/services/PollService.ts index 8b04f7676b76..a72b7065f7c0 100644 --- a/packages/backend/src/services/PollService.ts +++ b/packages/backend/src/services/PollService.ts @@ -9,9 +9,9 @@ import type { CacheableUser } from '@/models/entities/user.js'; import { genId } from '@/misc/gen-id.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; import type { CreateNotificationService } from '@/services/CreateNotificationService.js'; -import renderUpdate from '@/remote/activitypub/renderer/update.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import renderNote from '@/remote/activitypub/renderer/note.js'; +import renderUpdate from '@/services/remote/activitypub/renderer/update.js'; +import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; +import renderNote from '@/services/remote/activitypub/renderer/note.js'; @Injectable() export class PollService { diff --git a/packages/backend/src/services/ReactionService.ts b/packages/backend/src/services/ReactionService.ts index 3c21d2d811ae..0b90e6d689dc 100644 --- a/packages/backend/src/services/ReactionService.ts +++ b/packages/backend/src/services/ReactionService.ts @@ -11,7 +11,7 @@ import type { NoteReaction } from '@/models/entities/note-reaction.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; import type { CreateNotificationService } from '@/services/CreateNotificationService.js'; -import DeliverManager from '@/remote/activitypub/deliver-manager.js'; +import DeliverManager from '@/services/remote/activitypub/deliver-manager.js'; import type PerUserReactionsChart from '@/services/chart/charts/per-user-reactions.js'; import { toDbReaction } from '@/misc/reaction-lib.js'; diff --git a/packages/backend/src/services/RelayService.ts b/packages/backend/src/services/RelayService.ts index b8db30424f42..acf2c3964020 100644 --- a/packages/backend/src/services/RelayService.ts +++ b/packages/backend/src/services/RelayService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; -import { renderFollowRelay } from '@/remote/activitypub/renderer/follow-relay.js'; -import { renderActivity, attachLdSignature } from '@/remote/activitypub/renderer/index.js'; -import renderUndo from '@/remote/activitypub/renderer/undo.js'; +import { renderFollowRelay } from '@/services/remote/activitypub/renderer/follow-relay.js'; +import { renderActivity, attachLdSignature } from '@/services/remote/activitypub/renderer/index.js'; +import renderUndo from '@/services/remote/activitypub/renderer/undo.js'; import type { ILocalUser, User } from '@/models/entities/user.js'; import type { Relays, Users } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; diff --git a/packages/backend/src/services/UserBlockingService.ts b/packages/backend/src/services/UserBlockingService.ts index 258f4ad42593..061a14caac78 100644 --- a/packages/backend/src/services/UserBlockingService.ts +++ b/packages/backend/src/services/UserBlockingService.ts @@ -7,11 +7,11 @@ import type { CacheableUser, User } from '@/models/entities/user.js'; import type { Blocking } from '@/models/entities/blocking.js'; import type { QueueService } from '@/queue/queue.service.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; -import { renderActivity } from '@/remote/activitypub/renderer'; -import { renderBlock } from '@/remote/activitypub/renderer/block'; -import renderFollow from '@/remote/activitypub/renderer/follow.js'; -import renderUndo from '@/remote/activitypub/renderer/undo.js'; -import renderReject from '@/remote/activitypub/renderer/reject.js'; +import { renderActivity } from '@/services/remote/activitypub/renderer'; +import { renderBlock } from '@/services/remote/activitypub/renderer/block'; +import renderFollow from '@/services/remote/activitypub/renderer/follow.js'; +import renderUndo from '@/services/remote/activitypub/renderer/undo.js'; +import renderReject from '@/services/remote/activitypub/renderer/reject.js'; import type PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; import type { WebhookService } from '@/services/webhookService.js'; diff --git a/packages/backend/src/services/UserFollowingService.ts b/packages/backend/src/services/UserFollowingService.ts index ac6162be0701..3231d2e1323b 100644 --- a/packages/backend/src/services/UserFollowingService.ts +++ b/packages/backend/src/services/UserFollowingService.ts @@ -1,17 +1,17 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users , Followings, FollowRequests , UserProfiles , Instances , Blockings } from '@/models/index.js'; import type { CacheableUser, ILocalUser, IRemoteUser, User } from '@/models/entities/user.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import renderFollow from '@/remote/activitypub/renderer/follow.js'; -import renderAccept from '@/remote/activitypub/renderer/accept.js'; -import renderReject from '@/remote/activitypub/renderer/reject.js'; +import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; +import renderFollow from '@/services/remote/activitypub/renderer/follow.js'; +import renderAccept from '@/services/remote/activitypub/renderer/accept.js'; +import renderReject from '@/services/remote/activitypub/renderer/reject.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import type { QueueService } from '@/queue/queue.service.js'; import type PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; import { genId } from '@/misc/gen-id.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; -import renderUndo from '@/remote/activitypub/renderer/undo.js'; +import renderUndo from '@/services/remote/activitypub/renderer/undo.js'; import type { Packed } from '@/misc/schema.js'; import type InstanceChart from '@/services/chart/charts/instance.js'; import type { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; diff --git a/packages/backend/src/services/UserSuspendService.ts b/packages/backend/src/services/UserSuspendService.ts index fa5f3b5f7d29..66706af62c76 100644 --- a/packages/backend/src/services/UserSuspendService.ts +++ b/packages/backend/src/services/UserSuspendService.ts @@ -3,9 +3,9 @@ import { Not, IsNull } from 'typeorm'; import type { Followings , Users } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; import type { QueueService } from '@/queue/queue.service.js'; -import renderDelete from '@/remote/activitypub/renderer/delete.js'; -import renderUndo from '@/remote/activitypub/renderer/undo.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; +import renderDelete from '@/services/remote/activitypub/renderer/delete.js'; +import renderUndo from '@/services/remote/activitypub/renderer/undo.js'; +import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Config } from '@/config/types.js'; diff --git a/packages/backend/src/services/i/update.ts b/packages/backend/src/services/i/update.ts index 27bd38bd39f4..257db678113a 100644 --- a/packages/backend/src/services/i/update.ts +++ b/packages/backend/src/services/i/update.ts @@ -1,9 +1,9 @@ -import renderUpdate from '@/remote/activitypub/renderer/update.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; +import renderUpdate from '@/services/remote/activitypub/renderer/update.js'; +import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; import { Users } from '@/models/index.js'; -import { User } from '@/models/entities/user.js'; -import { renderPerson } from '@/remote/activitypub/renderer/person.js'; -import { deliverToFollowers } from '@/remote/activitypub/deliver-manager.js'; +import type { User } from '@/models/entities/user.js'; +import { renderPerson } from '@/services/remote/activitypub/renderer/person.js'; +import { deliverToFollowers } from '@/services/remote/activitypub/deliver-manager.js'; import { deliverToRelays } from '../relay.js'; export async function publishToFollowers(userId: User['id']) { diff --git a/packages/backend/src/services/messages/create.ts b/packages/backend/src/services/messages/create.ts index e6b3204922b4..1732ef715bbb 100644 --- a/packages/backend/src/services/messages/create.ts +++ b/packages/backend/src/services/messages/create.ts @@ -1,16 +1,16 @@ -import { CacheableUser, User } from '@/models/entities/user.js'; -import { UserGroup } from '@/models/entities/user-group.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; +import { Not } from 'typeorm'; +import type { CacheableUser, User } from '@/models/entities/user.js'; +import type { UserGroup } from '@/models/entities/user-group.js'; +import type { DriveFile } from '@/models/entities/drive-file.js'; import { MessagingMessages, UserGroupJoinings, Mutings, Users } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; -import { MessagingMessage } from '@/models/entities/messaging-message.js'; +import type { MessagingMessage } from '@/models/entities/messaging-message.js'; import { publishMessagingStream, publishMessagingIndexStream, publishMainStream, publishGroupMessagingStream } from '@/services/stream.js'; import { pushNotification } from '@/services/push-notification.js'; -import { Not } from 'typeorm'; -import { Note } from '@/models/entities/note.js'; -import renderNote from '@/remote/activitypub/renderer/note.js'; -import renderCreate from '@/remote/activitypub/renderer/create.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; +import type { Note } from '@/models/entities/note.js'; +import renderNote from '@/services/remote/activitypub/renderer/note.js'; +import renderCreate from '@/services/remote/activitypub/renderer/create.js'; +import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; import { deliver } from '@/queue/index.js'; export async function createMessage(user: { id: User['id']; host: User['host']; }, recipientUser: CacheableUser | undefined, recipientGroup: UserGroup | undefined, text: string | null | undefined, file: DriveFile | null, uri?: string) { diff --git a/packages/backend/src/services/messages/delete.ts b/packages/backend/src/services/messages/delete.ts index 1e7ce1981cc8..6c3a407be873 100644 --- a/packages/backend/src/services/messages/delete.ts +++ b/packages/backend/src/services/messages/delete.ts @@ -1,10 +1,10 @@ import config from '@/config/index.js'; import { MessagingMessages, Users } from '@/models/index.js'; -import { MessagingMessage } from '@/models/entities/messaging-message.js'; +import type { MessagingMessage } from '@/models/entities/messaging-message.js'; import { publishGroupMessagingStream, publishMessagingStream } from '@/services/stream.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import renderDelete from '@/remote/activitypub/renderer/delete.js'; -import renderTombstone from '@/remote/activitypub/renderer/tombstone.js'; +import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; +import renderDelete from '@/services/remote/activitypub/renderer/delete.js'; +import renderTombstone from '@/services/remote/activitypub/renderer/tombstone.js'; import { deliver } from '@/queue/index.js'; export async function deleteMessage(message: MessagingMessage) { diff --git a/packages/backend/src/services/remote/activitypub/kernel/read.ts b/packages/backend/src/services/remote/activitypub/kernel/read.ts index f7b0bcecdfd2..1f21dd5fdd2e 100644 --- a/packages/backend/src/services/remote/activitypub/kernel/read.ts +++ b/packages/backend/src/services/remote/activitypub/kernel/read.ts @@ -1,8 +1,9 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import { IRead, getApId } from '../type.js'; +import type { CacheableRemoteUser } from '@/models/entities/user.js'; import { isSelfHost, extractDbHost } from '@/misc/convert-host.js'; import { MessagingMessages } from '@/models/index.js'; -import { readUserMessagingMessage } from '../../../server/api/common/read-messaging-message.js'; +import { getApId } from '../type.js'; +import { readUserMessagingMessage } from '../../../../server/api/common/read-messaging-message.js'; +import type { IRead } from '../type.js'; export const performReadActivity = async (actor: CacheableRemoteUser, activity: IRead): Promise => { const id = await getApId(activity.object); @@ -15,11 +16,11 @@ export const performReadActivity = async (actor: CacheableRemoteUser, activity: const message = await MessagingMessages.findOneBy({ id: messageId }); if (message == null) { - return `skip: message not found`; + return 'skip: message not found'; } if (actor.id !== message.recipientId) { - return `skip: actor is not a message recipient`; + return 'skip: actor is not a message recipient'; } await readUserMessagingMessage(message.recipientId!, message.userId, [message.id]); diff --git a/packages/backend/src/services/remote/activitypub/misc/get-note-html.ts b/packages/backend/src/services/remote/activitypub/misc/get-note-html.ts index 389039ebedec..e6c38745b161 100644 --- a/packages/backend/src/services/remote/activitypub/misc/get-note-html.ts +++ b/packages/backend/src/services/remote/activitypub/misc/get-note-html.ts @@ -1,6 +1,6 @@ import * as mfm from 'mfm-js'; -import { Note } from '@/models/entities/note.js'; -import { toHtml } from '../../../mfm/to-html.js'; +import type { Note } from '@/models/entities/note.js'; +import { toHtml } from '../../../../mfm/to-html.js'; export default function(note: Note) { if (!note.text) return ''; diff --git a/packages/backend/src/services/remote/activitypub/misc/html-to-mfm.ts b/packages/backend/src/services/remote/activitypub/misc/html-to-mfm.ts index bb1ba7925c0f..829c5d781b45 100644 --- a/packages/backend/src/services/remote/activitypub/misc/html-to-mfm.ts +++ b/packages/backend/src/services/remote/activitypub/misc/html-to-mfm.ts @@ -1,6 +1,6 @@ -import { IObject } from '../type.js'; import { extractApHashtagObjects } from '../models/tag.js'; -import { fromHtml } from '../../../mfm/from-html.js'; +import { fromHtml } from '../../../../mfm/from-html.js'; +import type { IObject } from '../type.js'; export function htmlToMfm(html: string, tag?: IObject | IObject[]) { const hashtagNames = extractApHashtagObjects(tag).map(x => x.name).filter((x): x is string => x != null); diff --git a/packages/backend/src/services/remote/activitypub/models/person.ts b/packages/backend/src/services/remote/activitypub/models/person.ts index 6097e3b6edbe..74e5eeb6da98 100644 --- a/packages/backend/src/services/remote/activitypub/models/person.ts +++ b/packages/backend/src/services/remote/activitypub/models/person.ts @@ -3,11 +3,12 @@ import promiseLimit from 'promise-limit'; import config from '@/config/index.js'; import { registerOrFetchInstanceDoc } from '@/services/register-or-fetch-instance-doc.js'; -import { Note } from '@/models/entities/note.js'; +import type { Note } from '@/models/entities/note.js'; import { updateUsertags } from '@/services/update-hashtag.js'; import { Users, Instances, DriveFiles, Followings, UserProfiles, UserPublickeys } from '@/models/index.js'; -import { User, IRemoteUser, CacheableUser } from '@/models/entities/user.js'; -import { Emoji } from '@/models/entities/emoji.js'; +import type { IRemoteUser, CacheableUser } from '@/models/entities/user.js'; +import { User } from '@/models/entities/user.js'; +import type { Emoji } from '@/models/entities/emoji.js'; import { UserNotePining } from '@/models/entities/user-note-pining.js'; import { genId } from '@/misc/gen-id.js'; import { instanceChart, usersChart } from '@/services/chart/index.js'; @@ -25,12 +26,13 @@ import { publishInternalEvent } from '@/services/stream.js'; import { db } from '@/db/postgre.js'; import { apLogger } from '../logger.js'; import { htmlToMfm } from '../misc/html-to-mfm.js'; -import { fromHtml } from '../../../mfm/from-html.js'; -import { isCollectionOrOrderedCollection, isCollection, IActor, getApId, getOneApHrefNullable, IObject, isPropertyValue, IApPropertyValue, getApType, isActor } from '../type.js'; +import { fromHtml } from '../../../../mfm/from-html.js'; +import { isCollectionOrOrderedCollection, isCollection, getApId, getOneApHrefNullable, isPropertyValue, getApType, isActor } from '../type.js'; import Resolver from '../resolver.js'; import { extractApHashtags } from './tag.js'; import { resolveNote, extractEmojis } from './note.js'; import { resolveImage } from './image.js'; +import type { IActor, IObject, IApPropertyValue } from '../type.js'; const logger = apLogger; diff --git a/packages/backend/src/services/remote/activitypub/renderer/flag.ts b/packages/backend/src/services/remote/activitypub/renderer/flag.ts index 91a65c1db0af..c3607dc279f8 100644 --- a/packages/backend/src/services/remote/activitypub/renderer/flag.ts +++ b/packages/backend/src/services/remote/activitypub/renderer/flag.ts @@ -1,5 +1,5 @@ import config from '@/config/index.js'; -import { IObject, IActivity } from '@/remote/activitypub/type.js'; +import { IObject, IActivity } from '@/services/remote/activitypub/type.js'; import type { ILocalUser } from '@/models/entities/user.js'; import { IRemoteUser } from '@/models/entities/user.js'; diff --git a/packages/backend/src/services/remote/activitypub/renderer/person.ts b/packages/backend/src/services/remote/activitypub/renderer/person.ts index cd2fd74d47f1..f301158eb985 100644 --- a/packages/backend/src/services/remote/activitypub/renderer/person.ts +++ b/packages/backend/src/services/remote/activitypub/renderer/person.ts @@ -1,16 +1,16 @@ import { URL } from 'node:url'; import * as mfm from 'mfm-js'; +import config from '@/config/index.js'; +import type { ILocalUser } from '@/models/entities/user.js'; +import { DriveFiles, UserProfiles } from '@/models/index.js'; +import { getUserKeypair } from '@/misc/keypair-store.js'; +import { toHtml } from '../../../../mfm/to-html.js'; import renderImage from './image.js'; import renderKey from './key.js'; -import config from '@/config/index.js'; -import { ILocalUser } from '@/models/entities/user.js'; -import { toHtml } from '../../../mfm/to-html.js'; import { getEmojis } from './note.js'; import renderEmoji from './emoji.js'; -import { IIdentifier } from '../models/identifier.js'; import renderHashtag from './hashtag.js'; -import { DriveFiles, UserProfiles } from '@/models/index.js'; -import { getUserKeypair } from '@/misc/keypair-store.js'; +import type { IIdentifier } from '../models/identifier.js'; export async function renderPerson(user: ILocalUser) { const id = `${config.url}/users/${user.id}`; @@ -72,16 +72,16 @@ export async function renderPerson(user: ILocalUser) { tag, manuallyApprovesFollowers: user.isLocked, discoverable: !!user.isExplorable, - publicKey: renderKey(user, keypair, `#main-key`), + publicKey: renderKey(user, keypair, '#main-key'), isCat: user.isCat, attachment: attachment.length ? attachment : undefined, } as any; - if (profile?.birthday) { + if (profile.birthday) { person['vcard:bday'] = profile.birthday; } - if (profile?.location) { + if (profile.location) { person['vcard:Address'] = profile.location; } diff --git a/packages/backend/src/services/remote/activitypub/request.ts b/packages/backend/src/services/remote/activitypub/request.ts index 5cbfd8c259e7..00c58a019c75 100644 --- a/packages/backend/src/services/remote/activitypub/request.ts +++ b/packages/backend/src/services/remote/activitypub/request.ts @@ -1,7 +1,7 @@ import config from '@/config/index.js'; import { getUserKeypair } from '@/misc/keypair-store.js'; -import { User } from '@/models/entities/user.js'; -import { getResponse } from '../../misc/fetch.js'; +import type { User } from '@/models/entities/user.js'; +import { getResponse } from '../../../misc/fetch.js'; import { createSignedPost, createSignedGet } from './ap-request.js'; export default async (user: { id: User['id'] }, url: string, object: any) => { diff --git a/packages/backend/src/services/remote/activitypub/resolver.ts b/packages/backend/src/services/remote/activitypub/resolver.ts index dc134e52576b..a8c97b9d9800 100644 --- a/packages/backend/src/services/remote/activitypub/resolver.ts +++ b/packages/backend/src/services/remote/activitypub/resolver.ts @@ -5,13 +5,13 @@ import type { InstanceActorService } from '@/services/InstanceActorService.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { extractDbHost, isSelfHost } from '@/misc/convert-host.js'; import { FollowRequests, Notes, NoteReactions, Polls, Users } from '@/models/index.js'; -import renderNote from '@/remote/activitypub/renderer/note.js'; -import { renderLike } from '@/remote/activitypub/renderer/like.js'; -import { renderPerson } from '@/remote/activitypub/renderer/person.js'; -import renderQuestion from '@/remote/activitypub/renderer/question.js'; -import renderCreate from '@/remote/activitypub/renderer/create.js'; -import { renderActivity } from '@/remote/activitypub/renderer/index.js'; -import renderFollow from '@/remote/activitypub/renderer/follow.js'; +import renderNote from '@/services/remote/activitypub/renderer/note.js'; +import { renderLike } from '@/services/remote/activitypub/renderer/like.js'; +import { renderPerson } from '@/services/remote/activitypub/renderer/person.js'; +import renderQuestion from '@/services/remote/activitypub/renderer/question.js'; +import renderCreate from '@/services/remote/activitypub/renderer/create.js'; +import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; +import renderFollow from '@/services/remote/activitypub/renderer/follow.js'; import { isCollectionOrOrderedCollection } from './type.js'; import { parseUri } from './db-resolver.js'; import { signedGet } from './request.js'; From 0fb812ef310a2b083607d868d53b8f5b7f19f9f9 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 12 Sep 2022 11:37:06 +0900 Subject: [PATCH 040/180] wip --- .../src/services/remote/ResolveUserService.ts | 125 ++++++++++++++++++ .../src/services/remote/resolve-user.ts | 111 ---------------- 2 files changed, 125 insertions(+), 111 deletions(-) create mode 100644 packages/backend/src/services/remote/ResolveUserService.ts delete mode 100644 packages/backend/src/services/remote/resolve-user.ts diff --git a/packages/backend/src/services/remote/ResolveUserService.ts b/packages/backend/src/services/remote/ResolveUserService.ts new file mode 100644 index 000000000000..2ddc15486d41 --- /dev/null +++ b/packages/backend/src/services/remote/ResolveUserService.ts @@ -0,0 +1,125 @@ +import { URL } from 'node:url'; +import { Inject, Injectable } from '@nestjs/common'; +import chalk from 'chalk'; +import { IsNull } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Users } from '@/models/index.js'; +import type { IRemoteUser, User } from '@/models/entities/user.js'; +import type { Config } from '@/config/types.js'; +import { toPuny } from '@/misc/convert-host.js'; +import { remoteLogger } from './logger.js'; +import { createPerson, updatePerson } from './activitypub/models/person.js'; +import webFinger from './webfinger.js'; + +const logger = remoteLogger.createSubLogger('resolve-user'); + +@Injectable() +export class ResolveUserService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + ) { + } + + public async resolveUser(username: string, host: string | null): Promise { + const usernameLower = username.toLowerCase(); + + if (host == null) { + logger.info(`return local user: ${usernameLower}`); + return await this.usersRepository.findOneBy({ usernameLower, host: IsNull() }).then(u => { + if (u == null) { + throw new Error('user not found'); + } else { + return u; + } + }); + } + + host = toPuny(host); + + if (this.config.host === host) { + logger.info(`return local user: ${usernameLower}`); + return await this.usersRepository.findOneBy({ usernameLower, host: IsNull() }).then(u => { + if (u == null) { + throw new Error('user not found'); + } else { + return u; + } + }); + } + + const user = await this.usersRepository.findOneBy({ usernameLower, host }) as IRemoteUser | null; + + const acctLower = `${usernameLower}@${host}`; + + if (user == null) { + const self = await this.#resolveSelf(acctLower); + + logger.succ(`return new remote user: ${chalk.magenta(acctLower)}`); + return await createPerson(self.href); + } + + // ユーザー情報が古い場合は、WebFilgerからやりなおして返す + if (user.lastFetchedAt == null || Date.now() - user.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) { + // 繋がらないインスタンスに何回も試行するのを防ぐ, 後続の同様処理の連続試行を防ぐ ため 試行前にも更新する + await this.usersRepository.update(user.id, { + lastFetchedAt: new Date(), + }); + + logger.info(`try resync: ${acctLower}`); + const self = await this.#resolveSelf(acctLower); + + if (user.uri !== self.href) { + // if uri mismatch, Fix (user@host <=> AP's Person id(IRemoteUser.uri)) mapping. + logger.info(`uri missmatch: ${acctLower}`); + logger.info(`recovery missmatch uri for (username=${username}, host=${host}) from ${user.uri} to ${self.href}`); + + // validate uri + const uri = new URL(self.href); + if (uri.hostname !== host) { + throw new Error('Invalid uri'); + } + + await this.usersRepository.update({ + usernameLower, + host: host, + }, { + uri: self.href, + }); + } else { + logger.info(`uri is fine: ${acctLower}`); + } + + await updatePerson(self.href); + + logger.info(`return resynced remote user: ${acctLower}`); + return await this.usersRepository.findOneBy({ uri: self.href }).then(u => { + if (u == null) { + throw new Error('user not found'); + } else { + return u; + } + }); + } + + logger.info(`return existing remote user: ${acctLower}`); + return user; + } + + async #resolveSelf(acctLower: string) { + logger.info(`WebFinger for ${chalk.yellow(acctLower)}`); + const finger = await webFinger(acctLower).catch(e => { + logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: ${ e.statusCode || e.message }`); + throw new Error(`Failed to WebFinger for ${acctLower}: ${ e.statusCode || e.message }`); + }); + const self = finger.links.find(link => link.rel != null && link.rel.toLowerCase() === 'self'); + if (!self) { + logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: self link not found`); + throw new Error('self link not found'); + } + return self; + } +} diff --git a/packages/backend/src/services/remote/resolve-user.ts b/packages/backend/src/services/remote/resolve-user.ts deleted file mode 100644 index 6fc6f2c4d3fa..000000000000 --- a/packages/backend/src/services/remote/resolve-user.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { URL } from 'node:url'; -import webFinger from './webfinger.js'; -import config from '@/config/index.js'; -import { createPerson, updatePerson } from './activitypub/models/person.js'; -import { remoteLogger } from './logger.js'; -import chalk from 'chalk'; -import { User, IRemoteUser } from '@/models/entities/user.js'; -import { Users } from '@/models/index.js'; -import { toPuny } from '@/misc/convert-host.js'; -import { IsNull } from 'typeorm'; - -const logger = remoteLogger.createSubLogger('resolve-user'); - -export async function resolveUser(username: string, host: string | null): Promise { - const usernameLower = username.toLowerCase(); - - if (host == null) { - logger.info(`return local user: ${usernameLower}`); - return await Users.findOneBy({ usernameLower, host: IsNull() }).then(u => { - if (u == null) { - throw new Error('user not found'); - } else { - return u; - } - }); - } - - host = toPuny(host); - - if (config.host === host) { - logger.info(`return local user: ${usernameLower}`); - return await Users.findOneBy({ usernameLower, host: IsNull() }).then(u => { - if (u == null) { - throw new Error('user not found'); - } else { - return u; - } - }); - } - - const user = await Users.findOneBy({ usernameLower, host }) as IRemoteUser | null; - - const acctLower = `${usernameLower}@${host}`; - - if (user == null) { - const self = await resolveSelf(acctLower); - - logger.succ(`return new remote user: ${chalk.magenta(acctLower)}`); - return await createPerson(self.href); - } - - // ユーザー情報が古い場合は、WebFilgerからやりなおして返す - if (user.lastFetchedAt == null || Date.now() - user.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) { - // 繋がらないインスタンスに何回も試行するのを防ぐ, 後続の同様処理の連続試行を防ぐ ため 試行前にも更新する - await Users.update(user.id, { - lastFetchedAt: new Date(), - }); - - logger.info(`try resync: ${acctLower}`); - const self = await resolveSelf(acctLower); - - if (user.uri !== self.href) { - // if uri mismatch, Fix (user@host <=> AP's Person id(IRemoteUser.uri)) mapping. - logger.info(`uri missmatch: ${acctLower}`); - logger.info(`recovery missmatch uri for (username=${username}, host=${host}) from ${user.uri} to ${self.href}`); - - // validate uri - const uri = new URL(self.href); - if (uri.hostname !== host) { - throw new Error(`Invalid uri`); - } - - await Users.update({ - usernameLower, - host: host, - }, { - uri: self.href, - }); - } else { - logger.info(`uri is fine: ${acctLower}`); - } - - await updatePerson(self.href); - - logger.info(`return resynced remote user: ${acctLower}`); - return await Users.findOneBy({ uri: self.href }).then(u => { - if (u == null) { - throw new Error('user not found'); - } else { - return u; - } - }); - } - - logger.info(`return existing remote user: ${acctLower}`); - return user; -} - -async function resolveSelf(acctLower: string) { - logger.info(`WebFinger for ${chalk.yellow(acctLower)}`); - const finger = await webFinger(acctLower).catch(e => { - logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: ${ e.statusCode || e.message }`); - throw new Error(`Failed to WebFinger for ${acctLower}: ${ e.statusCode || e.message }`); - }); - const self = finger.links.find(link => link.rel != null && link.rel.toLowerCase() === 'self'); - if (!self) { - logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: self link not found`); - throw new Error('self link not found'); - } - return self; -} From c64966b576fa27817fe2a5e1fb08d606121f407d Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 00:39:35 +0900 Subject: [PATCH 041/180] wip --- packages/backend/src/misc/keypair-store.ts | 10 - .../src/services/UserKeypairStoreService.ts | 22 +++ .../remote/activitypub/ApRequestService.ts | 181 ++++++++++++++++++ .../services/remote/activitypub/ap-request.ts | 104 ---------- .../services/remote/activitypub/request.ts | 58 ------ 5 files changed, 203 insertions(+), 172 deletions(-) delete mode 100644 packages/backend/src/misc/keypair-store.ts create mode 100644 packages/backend/src/services/UserKeypairStoreService.ts create mode 100644 packages/backend/src/services/remote/activitypub/ApRequestService.ts delete mode 100644 packages/backend/src/services/remote/activitypub/ap-request.ts delete mode 100644 packages/backend/src/services/remote/activitypub/request.ts diff --git a/packages/backend/src/misc/keypair-store.ts b/packages/backend/src/misc/keypair-store.ts deleted file mode 100644 index 1183b9a78166..000000000000 --- a/packages/backend/src/misc/keypair-store.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { UserKeypairs } from '@/models/index.js'; -import { User } from '@/models/entities/user.js'; -import { UserKeypair } from '@/models/entities/user-keypair.js'; -import { Cache } from './cache.js'; - -const cache = new Cache(Infinity); - -export async function getUserKeypair(userId: User['id']): Promise { - return await cache.fetch(userId, () => UserKeypairs.findOneByOrFail({ userId: userId })); -} diff --git a/packages/backend/src/services/UserKeypairStoreService.ts b/packages/backend/src/services/UserKeypairStoreService.ts new file mode 100644 index 000000000000..257d2172d4d5 --- /dev/null +++ b/packages/backend/src/services/UserKeypairStoreService.ts @@ -0,0 +1,22 @@ +import { Inject, Injectable } from '@nestjs/common'; + +import type { User } from '@/models/entities/user.js'; +import type { UserKeypairs } from '@/models/index.js'; +import { Cache } from '@/misc/cache.js'; +import type { UserKeypair } from '@/models/entities/user-keypair.js'; + +@Injectable() +export class UserKeypairStoreService { + #cache: Cache; + + constructor( + @Inject('userKeypairsRepository') + private userKeypairsRepository: typeof UserKeypairs, + ) { + this.#cache = new Cache(Infinity); + } + + public async getUserKeypair(userId: User['id']): Promise { + return await this.#cache.fetch(userId, () => this.userKeypairsRepository.findOneByOrFail({ userId: userId })); + } +} diff --git a/packages/backend/src/services/remote/activitypub/ApRequestService.ts b/packages/backend/src/services/remote/activitypub/ApRequestService.ts new file mode 100644 index 000000000000..1e5dedf416c3 --- /dev/null +++ b/packages/backend/src/services/remote/activitypub/ApRequestService.ts @@ -0,0 +1,181 @@ +import * as crypto from 'node:crypto'; +import { URL } from 'node:url'; +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; +import type { User } from '@/models/entities/user.js'; +import type { UserKeypairStoreService } from '@/services/UserKeypairStoreService.js'; +import { getResponse } from '@/misc/fetch.js'; + +type Request = { + url: string; + method: string; + headers: Record; +}; + +type Signed = { + request: Request; + signingString: string; + signature: string; + signatureHeader: string; +}; + +type PrivateKey = { + privateKeyPem: string; + keyId: string; +}; + +@Injectable() +export class ApRequestService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + private userKeypairStoreService: UserKeypairStoreService, + ) { + } + + #createSignedPost(args: { key: PrivateKey, url: string, body: string, additionalHeaders: Record }): Signed { + const u = new URL(args.url); + const digestHeader = `SHA-256=${crypto.createHash('sha256').update(args.body).digest('base64')}`; + + const request: Request = { + url: u.href, + method: 'POST', + headers: this.#objectAssignWithLcKey({ + 'Date': new Date().toUTCString(), + 'Host': u.hostname, + 'Content-Type': 'application/activity+json', + 'Digest': digestHeader, + }, args.additionalHeaders), + }; + + const result = this.#signToRequest(request, args.key, ['(request-target)', 'date', 'host', 'digest']); + + return { + request, + signingString: result.signingString, + signature: result.signature, + signatureHeader: result.signatureHeader, + }; + } + + #createSignedGet(args: { key: PrivateKey, url: string, additionalHeaders: Record }): Signed { + const u = new URL(args.url); + + const request: Request = { + url: u.href, + method: 'GET', + headers: this.#objectAssignWithLcKey({ + 'Accept': 'application/activity+json, application/ld+json', + 'Date': new Date().toUTCString(), + 'Host': new URL(args.url).hostname, + }, args.additionalHeaders), + }; + + const result = this.#signToRequest(request, args.key, ['(request-target)', 'date', 'host', 'accept']); + + return { + request, + signingString: result.signingString, + signature: result.signature, + signatureHeader: result.signatureHeader, + }; + } + + #signToRequest(request: Request, key: PrivateKey, includeHeaders: string[]): Signed { + const signingString = this.#genSigningString(request, includeHeaders); + const signature = crypto.sign('sha256', Buffer.from(signingString), key.privateKeyPem).toString('base64'); + const signatureHeader = `keyId="${key.keyId}",algorithm="rsa-sha256",headers="${includeHeaders.join(' ')}",signature="${signature}"`; + + request.headers = this.#objectAssignWithLcKey(request.headers, { + Signature: signatureHeader, + }); + + return { + request, + signingString, + signature, + signatureHeader, + }; + } + + #genSigningString(request: Request, includeHeaders: string[]): string { + request.headers = this.#lcObjectKey(request.headers); + + const results: string[] = []; + + for (const key of includeHeaders.map(x => x.toLowerCase())) { + if (key === '(request-target)') { + results.push(`(request-target): ${request.method.toLowerCase()} ${new URL(request.url).pathname}`); + } else { + results.push(`${key}: ${request.headers[key]}`); + } + } + + return results.join('\n'); + } + + #lcObjectKey(src: Record): Record { + const dst: Record = {}; + for (const key of Object.keys(src).filter(x => x !== '__proto__' && typeof src[x] === 'string')) dst[key.toLowerCase()] = src[key]; + return dst; + } + + #objectAssignWithLcKey(a: Record, b: Record): Record { + return Object.assign(this.#lcObjectKey(a), this.#lcObjectKey(b)); + } + + public async signedPost(user: { id: User['id'] }, url: string, object: any) { + const body = JSON.stringify(object); + + const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); + + const req = this.#createSignedPost({ + key: { + privateKeyPem: keypair.privateKey, + keyId: `${this.config.url}/users/${user.id}#main-key`, + }, + url, + body, + additionalHeaders: { + 'User-Agent': this.config.userAgent, + }, + }); + + await getResponse({ + url, + method: req.request.method, + headers: req.request.headers, + body, + }); + } + + /** + * Get AP object with http-signature + * @param user http-signature user + * @param url URL to fetch + */ + public async signedGet(url: string, user: { id: User['id'] }) { + const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); + + const req = this.#createSignedGet({ + key: { + privateKeyPem: keypair.privateKey, + keyId: `${this.config.url}/users/${user.id}#main-key`, + }, + url, + additionalHeaders: { + 'User-Agent': this.config.userAgent, + }, + }); + + const res = await getResponse({ + url, + method: req.request.method, + headers: req.request.headers, + }); + + return await res.json(); + } +} diff --git a/packages/backend/src/services/remote/activitypub/ap-request.ts b/packages/backend/src/services/remote/activitypub/ap-request.ts deleted file mode 100644 index 8b55f2247742..000000000000 --- a/packages/backend/src/services/remote/activitypub/ap-request.ts +++ /dev/null @@ -1,104 +0,0 @@ -import * as crypto from 'node:crypto'; -import { URL } from 'node:url'; - -type Request = { - url: string; - method: string; - headers: Record; -}; - -type PrivateKey = { - privateKeyPem: string; - keyId: string; -}; - -export function createSignedPost(args: { key: PrivateKey, url: string, body: string, additionalHeaders: Record }) { - const u = new URL(args.url); - const digestHeader = `SHA-256=${crypto.createHash('sha256').update(args.body).digest('base64')}`; - - const request: Request = { - url: u.href, - method: 'POST', - headers: objectAssignWithLcKey({ - 'Date': new Date().toUTCString(), - 'Host': u.hostname, - 'Content-Type': 'application/activity+json', - 'Digest': digestHeader, - }, args.additionalHeaders), - }; - - const result = signToRequest(request, args.key, ['(request-target)', 'date', 'host', 'digest']); - - return { - request, - signingString: result.signingString, - signature: result.signature, - signatureHeader: result.signatureHeader, - }; -} - -export function createSignedGet(args: { key: PrivateKey, url: string, additionalHeaders: Record }) { - const u = new URL(args.url); - - const request: Request = { - url: u.href, - method: 'GET', - headers: objectAssignWithLcKey({ - 'Accept': 'application/activity+json, application/ld+json', - 'Date': new Date().toUTCString(), - 'Host': new URL(args.url).hostname, - }, args.additionalHeaders), - }; - - const result = signToRequest(request, args.key, ['(request-target)', 'date', 'host', 'accept']); - - return { - request, - signingString: result.signingString, - signature: result.signature, - signatureHeader: result.signatureHeader, - }; -} - -function signToRequest(request: Request, key: PrivateKey, includeHeaders: string[]) { - const signingString = genSigningString(request, includeHeaders); - const signature = crypto.sign('sha256', Buffer.from(signingString), key.privateKeyPem).toString('base64'); - const signatureHeader = `keyId="${key.keyId}",algorithm="rsa-sha256",headers="${includeHeaders.join(' ')}",signature="${signature}"`; - - request.headers = objectAssignWithLcKey(request.headers, { - Signature: signatureHeader, - }); - - return { - request, - signingString, - signature, - signatureHeader, - }; -} - -function genSigningString(request: Request, includeHeaders: string[]) { - request.headers = lcObjectKey(request.headers); - - const results: string[] = []; - - for (const key of includeHeaders.map(x => x.toLowerCase())) { - if (key === '(request-target)') { - results.push(`(request-target): ${request.method.toLowerCase()} ${new URL(request.url).pathname}`); - } else { - results.push(`${key}: ${request.headers[key]}`); - } - } - - return results.join('\n'); -} - -function lcObjectKey(src: Record) { - const dst: Record = {}; - for (const key of Object.keys(src).filter(x => x !== '__proto__' && typeof src[x] === 'string')) dst[key.toLowerCase()] = src[key]; - return dst; -} - -function objectAssignWithLcKey(a: Record, b: Record) { - return Object.assign(lcObjectKey(a), lcObjectKey(b)); -} diff --git a/packages/backend/src/services/remote/activitypub/request.ts b/packages/backend/src/services/remote/activitypub/request.ts deleted file mode 100644 index 00c58a019c75..000000000000 --- a/packages/backend/src/services/remote/activitypub/request.ts +++ /dev/null @@ -1,58 +0,0 @@ -import config from '@/config/index.js'; -import { getUserKeypair } from '@/misc/keypair-store.js'; -import type { User } from '@/models/entities/user.js'; -import { getResponse } from '../../../misc/fetch.js'; -import { createSignedPost, createSignedGet } from './ap-request.js'; - -export default async (user: { id: User['id'] }, url: string, object: any) => { - const body = JSON.stringify(object); - - const keypair = await getUserKeypair(user.id); - - const req = createSignedPost({ - key: { - privateKeyPem: keypair.privateKey, - keyId: `${config.url}/users/${user.id}#main-key`, - }, - url, - body, - additionalHeaders: { - 'User-Agent': config.userAgent, - }, - }); - - await getResponse({ - url, - method: req.request.method, - headers: req.request.headers, - body, - }); -}; - -/** - * Get AP object with http-signature - * @param user http-signature user - * @param url URL to fetch - */ -export async function signedGet(url: string, user: { id: User['id'] }) { - const keypair = await getUserKeypair(user.id); - - const req = createSignedGet({ - key: { - privateKeyPem: keypair.privateKey, - keyId: `${config.url}/users/${user.id}#main-key`, - }, - url, - additionalHeaders: { - 'User-Agent': config.userAgent, - }, - }); - - const res = await getResponse({ - url, - method: req.request.method, - headers: req.request.headers, - }); - - return await res.json(); -} From dd1a1082120941cd3ca0f339d116eb25f122b24d Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 00:49:32 +0900 Subject: [PATCH 042/180] wip --- packages/backend/src/misc/fetch.ts | 141 ---------------- .../src/services/HttpRequestService.ts | 159 ++++++++++++++++++ 2 files changed, 159 insertions(+), 141 deletions(-) delete mode 100644 packages/backend/src/misc/fetch.ts create mode 100644 packages/backend/src/services/HttpRequestService.ts diff --git a/packages/backend/src/misc/fetch.ts b/packages/backend/src/misc/fetch.ts deleted file mode 100644 index af6bf2fca76f..000000000000 --- a/packages/backend/src/misc/fetch.ts +++ /dev/null @@ -1,141 +0,0 @@ -import * as http from 'node:http'; -import * as https from 'node:https'; -import { URL } from 'node:url'; -import CacheableLookup from 'cacheable-lookup'; -import fetch from 'node-fetch'; -import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent'; -import config from '@/config/index.js'; - -export async function getJson(url: string, accept = 'application/json, */*', timeout = 10000, headers?: Record) { - const res = await getResponse({ - url, - method: 'GET', - headers: Object.assign({ - 'User-Agent': config.userAgent, - Accept: accept, - }, headers || {}), - timeout, - }); - - return await res.json(); -} - -export async function getHtml(url: string, accept = 'text/html, */*', timeout = 10000, headers?: Record) { - const res = await getResponse({ - url, - method: 'GET', - headers: Object.assign({ - 'User-Agent': config.userAgent, - Accept: accept, - }, headers || {}), - timeout, - }); - - return await res.text(); -} - -export async function getResponse(args: { url: string, method: string, body?: string, headers: Record, timeout?: number, size?: number }) { - const timeout = args.timeout || 10 * 1000; - - const controller = new AbortController(); - setTimeout(() => { - controller.abort(); - }, timeout * 6); - - const res = await fetch(args.url, { - method: args.method, - headers: args.headers, - body: args.body, - timeout, - size: args.size || 10 * 1024 * 1024, - agent: getAgentByUrl, - signal: controller.signal, - }); - - if (!res.ok) { - throw new StatusError(`${res.status} ${res.statusText}`, res.status, res.statusText); - } - - return res; -} - -const cache = new CacheableLookup({ - maxTtl: 3600, // 1hours - errorTtl: 30, // 30secs - lookup: false, // nativeのdns.lookupにfallbackしない -}); - -/** - * Get http non-proxy agent - */ -const _http = new http.Agent({ - keepAlive: true, - keepAliveMsecs: 30 * 1000, - lookup: cache.lookup, -} as http.AgentOptions); - -/** - * Get https non-proxy agent - */ -const _https = new https.Agent({ - keepAlive: true, - keepAliveMsecs: 30 * 1000, - lookup: cache.lookup, -} as https.AgentOptions); - -const maxSockets = Math.max(256, config.deliverJobConcurrency || 128); - -/** - * Get http proxy or non-proxy agent - */ -export const httpAgent = config.proxy - ? new HttpProxyAgent({ - keepAlive: true, - keepAliveMsecs: 30 * 1000, - maxSockets, - maxFreeSockets: 256, - scheduling: 'lifo', - proxy: config.proxy, - }) - : _http; - -/** - * Get https proxy or non-proxy agent - */ -export const httpsAgent = config.proxy - ? new HttpsProxyAgent({ - keepAlive: true, - keepAliveMsecs: 30 * 1000, - maxSockets, - maxFreeSockets: 256, - scheduling: 'lifo', - proxy: config.proxy, - }) - : _https; - -/** - * Get agent by URL - * @param url URL - * @param bypassProxy Allways bypass proxy - */ -export function getAgentByUrl(url: URL, bypassProxy = false) { - if (bypassProxy || (config.proxyBypassHosts || []).includes(url.hostname)) { - return url.protocol === 'http:' ? _http : _https; - } else { - return url.protocol === 'http:' ? httpAgent : httpsAgent; - } -} - -export class StatusError extends Error { - public statusCode: number; - public statusMessage?: string; - public isClientError: boolean; - - constructor(message: string, statusCode: number, statusMessage?: string) { - super(message); - this.name = 'StatusError'; - this.statusCode = statusCode; - this.statusMessage = statusMessage; - this.isClientError = typeof this.statusCode === 'number' && this.statusCode >= 400 && this.statusCode < 500; - } -} diff --git a/packages/backend/src/services/HttpRequestService.ts b/packages/backend/src/services/HttpRequestService.ts new file mode 100644 index 000000000000..ea096bec6faf --- /dev/null +++ b/packages/backend/src/services/HttpRequestService.ts @@ -0,0 +1,159 @@ +import * as http from 'node:http'; +import * as https from 'node:https'; +import CacheableLookup from 'cacheable-lookup'; +import fetch from 'node-fetch'; +import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent'; +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types'; +import type { URL } from 'node:url'; + +@Injectable() +export class HttpRequestService { + /** + * Get http non-proxy agent + */ + #_http: http.Agent; + + /** + * Get https non-proxy agent + */ + #_https: https.Agent; + + /** + * Get http proxy or non-proxy agent + */ + public httpAgent: http.Agent; + + /** + * Get https proxy or non-proxy agent + */ + public httpsAgent: https.Agent; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + ) { + const cache = new CacheableLookup({ + maxTtl: 3600, // 1hours + errorTtl: 30, // 30secs + lookup: false, // nativeのdns.lookupにfallbackしない + }); + + this.#_http = new http.Agent({ + keepAlive: true, + keepAliveMsecs: 30 * 1000, + lookup: cache.lookup, + } as http.AgentOptions); + + this.#_https = new https.Agent({ + keepAlive: true, + keepAliveMsecs: 30 * 1000, + lookup: cache.lookup, + } as https.AgentOptions); + + const maxSockets = Math.max(256, config.deliverJobConcurrency || 128); + + this.httpAgent = config.proxy + ? new HttpProxyAgent({ + keepAlive: true, + keepAliveMsecs: 30 * 1000, + maxSockets, + maxFreeSockets: 256, + scheduling: 'lifo', + proxy: config.proxy, + }) + : this.#_http; + + this.httpsAgent = config.proxy + ? new HttpsProxyAgent({ + keepAlive: true, + keepAliveMsecs: 30 * 1000, + maxSockets, + maxFreeSockets: 256, + scheduling: 'lifo', + proxy: config.proxy, + }) + : this.#_https; + } + + /** + * Get agent by URL + * @param url URL + * @param bypassProxy Allways bypass proxy + */ + public getAgentByUrl(url: URL, bypassProxy = false): http.Agent | https.Agent { + if (bypassProxy || (this.config.proxyBypassHosts || []).includes(url.hostname)) { + return url.protocol === 'http:' ? this.#_http : this.#_https; + } else { + return url.protocol === 'http:' ? this.httpAgent : this.httpsAgent; + } + } + + public async getJson(url: string, accept = 'application/json, */*', timeout = 10000, headers?: Record) { + const res = await this.getResponse({ + url, + method: 'GET', + headers: Object.assign({ + 'User-Agent': this.config.userAgent, + Accept: accept, + }, headers || {}), + timeout, + }); + + return await res.json(); + } + + public async getHtml(url: string, accept = 'text/html, */*', timeout = 10000, headers?: Record) { + const res = await this.getResponse({ + url, + method: 'GET', + headers: Object.assign({ + 'User-Agent': this.config.userAgent, + Accept: accept, + }, headers || {}), + timeout, + }); + + return await res.text(); + } + + public async getResponse(args: { url: string, method: string, body?: string, headers: Record, timeout?: number, size?: number }) { + const timeout = args.timeout || 10 * 1000; + + const controller = new AbortController(); + setTimeout(() => { + controller.abort(); + }, timeout * 6); + + const res = await fetch(args.url, { + method: args.method, + headers: args.headers, + body: args.body, + timeout, + size: args.size || 10 * 1024 * 1024, + agent: this.getAgentByUrl, + signal: controller.signal, + }); + + if (!res.ok) { + throw new StatusError(`${res.status} ${res.statusText}`, res.status, res.statusText); + } + + return res; + } +} + +export class StatusError extends Error { + public statusCode: number; + public statusMessage?: string; + public isClientError: boolean; + + constructor(message: string, statusCode: number, statusMessage?: string) { + super(message); + this.name = 'StatusError'; + this.statusCode = statusCode; + this.statusMessage = statusMessage; + this.isClientError = typeof this.statusCode === 'number' && this.statusCode >= 400 && this.statusCode < 500; + } +} From e5c64dc50ae1ae1aa44d7ef25121c5a489a76365 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 00:50:19 +0900 Subject: [PATCH 043/180] Update ApRequestService.ts --- .../src/services/remote/activitypub/ApRequestService.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/services/remote/activitypub/ApRequestService.ts b/packages/backend/src/services/remote/activitypub/ApRequestService.ts index 1e5dedf416c3..836948661db6 100644 --- a/packages/backend/src/services/remote/activitypub/ApRequestService.ts +++ b/packages/backend/src/services/remote/activitypub/ApRequestService.ts @@ -5,7 +5,7 @@ import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Config } from '@/config/types.js'; import type { User } from '@/models/entities/user.js'; import type { UserKeypairStoreService } from '@/services/UserKeypairStoreService.js'; -import { getResponse } from '@/misc/fetch.js'; +import type { HttpRequestService } from '@/services/HttpRequestService.js'; type Request = { url: string; @@ -32,6 +32,7 @@ export class ApRequestService { private config: Config, private userKeypairStoreService: UserKeypairStoreService, + private httpRequestService: HttpRequestService, ) { } @@ -143,7 +144,7 @@ export class ApRequestService { }, }); - await getResponse({ + await this.httpRequestService.getResponse({ url, method: req.request.method, headers: req.request.headers, @@ -170,7 +171,7 @@ export class ApRequestService { }, }); - const res = await getResponse({ + const res = await this.httpRequestService.getResponse({ url, method: req.request.method, headers: req.request.headers, From b7c156f80ee45b1d603b5d259c88d44c0e572644 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 00:53:58 +0900 Subject: [PATCH 044/180] Update HttpRequestService.ts --- .../src/services/HttpRequestService.ts | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/backend/src/services/HttpRequestService.ts b/packages/backend/src/services/HttpRequestService.ts index ea096bec6faf..e26becc95641 100644 --- a/packages/backend/src/services/HttpRequestService.ts +++ b/packages/backend/src/services/HttpRequestService.ts @@ -6,6 +6,7 @@ import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent'; import { Inject, Injectable } from '@nestjs/common'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Config } from '@/config/types'; +import type { Response } from 'node-fetch'; import type { URL } from 'node:url'; @Injectable() @@ -13,12 +14,12 @@ export class HttpRequestService { /** * Get http non-proxy agent */ - #_http: http.Agent; + #http: http.Agent; /** * Get https non-proxy agent */ - #_https: https.Agent; + #https: https.Agent; /** * Get http proxy or non-proxy agent @@ -40,13 +41,13 @@ export class HttpRequestService { lookup: false, // nativeのdns.lookupにfallbackしない }); - this.#_http = new http.Agent({ + this.#http = new http.Agent({ keepAlive: true, keepAliveMsecs: 30 * 1000, lookup: cache.lookup, } as http.AgentOptions); - this.#_https = new https.Agent({ + this.#https = new https.Agent({ keepAlive: true, keepAliveMsecs: 30 * 1000, lookup: cache.lookup, @@ -63,7 +64,7 @@ export class HttpRequestService { scheduling: 'lifo', proxy: config.proxy, }) - : this.#_http; + : this.#http; this.httpsAgent = config.proxy ? new HttpsProxyAgent({ @@ -74,7 +75,7 @@ export class HttpRequestService { scheduling: 'lifo', proxy: config.proxy, }) - : this.#_https; + : this.#https; } /** @@ -84,13 +85,13 @@ export class HttpRequestService { */ public getAgentByUrl(url: URL, bypassProxy = false): http.Agent | https.Agent { if (bypassProxy || (this.config.proxyBypassHosts || []).includes(url.hostname)) { - return url.protocol === 'http:' ? this.#_http : this.#_https; + return url.protocol === 'http:' ? this.#http : this.#https; } else { return url.protocol === 'http:' ? this.httpAgent : this.httpsAgent; } } - public async getJson(url: string, accept = 'application/json, */*', timeout = 10000, headers?: Record) { + public async getJson(url: string, accept = 'application/json, */*', timeout = 10000, headers?: Record): Promise { const res = await this.getResponse({ url, method: 'GET', @@ -104,7 +105,7 @@ export class HttpRequestService { return await res.json(); } - public async getHtml(url: string, accept = 'text/html, */*', timeout = 10000, headers?: Record) { + public async getHtml(url: string, accept = 'text/html, */*', timeout = 10000, headers?: Record): Promise { const res = await this.getResponse({ url, method: 'GET', @@ -118,7 +119,14 @@ export class HttpRequestService { return await res.text(); } - public async getResponse(args: { url: string, method: string, body?: string, headers: Record, timeout?: number, size?: number }) { + public async getResponse(args: { + url: string, + method: string, + body?: string, + headers: Record, + timeout?: number, + size?: number, + }): Promise { const timeout = args.timeout || 10 * 1000; const controller = new AbortController(); From 924849458fe7cf74cad2eb9c662863a20b3c8295 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 01:25:58 +0900 Subject: [PATCH 045/180] wip --- .../remote/activitypub/ApRendererService.ts | 691 ++++++++++++++++++ .../remote/activitypub/renderer/accept.ts | 8 - .../remote/activitypub/renderer/add.ts | 9 - .../remote/activitypub/renderer/announce.ts | 29 - .../remote/activitypub/renderer/block.ts | 20 - .../remote/activitypub/renderer/create.ts | 17 - .../remote/activitypub/renderer/delete.ts | 9 - .../remote/activitypub/renderer/document.ts | 9 - .../remote/activitypub/renderer/emoji.ts | 14 - .../remote/activitypub/renderer/flag.ts | 15 - .../activitypub/renderer/follow-relay.ts | 14 - .../activitypub/renderer/follow-user.ts | 12 - .../remote/activitypub/renderer/follow.ts | 14 - .../remote/activitypub/renderer/hashtag.ts | 7 - .../remote/activitypub/renderer/image.ts | 9 - .../remote/activitypub/renderer/index.ts | 59 -- .../remote/activitypub/renderer/key.ts | 14 - .../remote/activitypub/renderer/like.ts | 31 - .../remote/activitypub/renderer/mention.ts | 9 - .../remote/activitypub/renderer/note.ts | 169 ----- .../renderer/ordered-collection-page.ts | 23 - .../renderer/ordered-collection.ts | 28 - .../remote/activitypub/renderer/person.ts | 89 --- .../remote/activitypub/renderer/question.ts | 23 - .../remote/activitypub/renderer/read.ts | 9 - .../remote/activitypub/renderer/reject.ts | 8 - .../remote/activitypub/renderer/remove.ts | 9 - .../remote/activitypub/renderer/tombstone.ts | 4 - .../remote/activitypub/renderer/undo.ts | 15 - .../remote/activitypub/renderer/update.ts | 15 - .../remote/activitypub/renderer/vote.ts | 23 - 31 files changed, 691 insertions(+), 714 deletions(-) create mode 100644 packages/backend/src/services/remote/activitypub/ApRendererService.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/accept.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/add.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/announce.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/block.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/create.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/delete.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/document.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/emoji.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/flag.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/follow-relay.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/follow-user.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/follow.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/hashtag.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/image.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/index.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/key.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/like.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/mention.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/note.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/ordered-collection-page.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/ordered-collection.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/person.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/question.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/read.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/reject.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/remove.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/tombstone.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/undo.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/update.ts delete mode 100644 packages/backend/src/services/remote/activitypub/renderer/vote.ts diff --git a/packages/backend/src/services/remote/activitypub/ApRendererService.ts b/packages/backend/src/services/remote/activitypub/ApRendererService.ts new file mode 100644 index 000000000000..304ccf4c4b01 --- /dev/null +++ b/packages/backend/src/services/remote/activitypub/ApRendererService.ts @@ -0,0 +1,691 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { In, IsNull } from 'typeorm'; +import { v4 as uuid } from 'uuid'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Polls , DriveFiles, Emojis, Notes, Users } from '@/models/index.js'; + +import type { Config } from '@/config/types.js'; +import type { ILocalUser, IRemoteUser, User } from '@/models/entities/user.js'; +import type { IMentionedRemoteUsers, Note } from '@/models/entities/note.js'; +import type { Blocking } from '@/models/entities/blocking.js'; +import type { Relay } from '@/models/entities/relay.js'; +import type { DriveFile } from '@/models/entities/drive-file.js'; +import type { NoteReaction } from '@/models/entities/note-reaction.js'; +import type { Emoji } from '@/models/entities/emoji.js'; +import type { Poll } from '@/models/entities/poll.js'; +import type { MessagingMessage } from '@/models/entities/messaging-message.js'; +import type { PollVote } from '@/models/entities/poll-vote.js'; +import type { UserKeypairStoreService } from '@/services/UserKeypairStoreService.js'; +import { LdSignature } from './misc/ld-signature.js'; +import type { IActivity } from './type.js'; +import type { IIdentifier } from './models/identifier.js'; + +@Injectable() +export class ApRendererService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + @Inject('emojisRepository') + private emojisRepository: typeof Emojis, + + @Inject('pollsRepository') + private pollsRepository: typeof Polls, + + private userKeypairStoreService: UserKeypairStoreService, + ) { + } + + public renderAccept(object: any, user: { id: User['id']; host: null }) { + return { + type: 'Accept', + actor: `${this.config.url}/users/${user.id}`, + object, + }; + } + + public renderAdd(user: ILocalUser, target: any, object: any) { + return { + type: 'Add', + actor: `${this.config.url}/users/${user.id}`, + target, + object, + }; + } + + public renderAnnounce(object: any, note: Note) { + const attributedTo = `${this.config.url}/users/${note.userId}`; + + let to: string[] = []; + let cc: string[] = []; + + if (note.visibility === 'public') { + to = ['https://www.w3.org/ns/activitystreams#Public']; + cc = [`${attributedTo}/followers`]; + } else if (note.visibility === 'home') { + to = [`${attributedTo}/followers`]; + cc = ['https://www.w3.org/ns/activitystreams#Public']; + } else { + return null; + } + + return { + id: `${this.config.url}/notes/${note.id}/activity`, + actor: `${this.config.url}/users/${note.userId}`, + type: 'Announce', + published: note.createdAt.toISOString(), + to, + cc, + object, + }; + } + + /** + * Renders a block into its ActivityPub representation. + * + * @param block The block to be rendered. The blockee relation must be loaded. + */ + public renderBlock(block: Blocking) { + if (block.blockee?.uri == null) { + throw new Error('renderBlock: missing blockee uri'); + } + + return { + type: 'Block', + id: `${this.config.url}/blocks/${block.id}`, + actor: `${this.config.url}/users/${block.blockerId}`, + object: block.blockee.uri, + }; + } + + public renderCreate(object: any, note: Note) { + const activity = { + id: `${this.config.url}/notes/${note.id}/activity`, + actor: `${this.config.url}/users/${note.userId}`, + type: 'Create', + published: note.createdAt.toISOString(), + object, + } as any; + + if (object.to) activity.to = object.to; + if (object.cc) activity.cc = object.cc; + + return activity; + } + + public renderDelete(object: any, user: { id: User['id']; host: null }) { + return { + type: 'Delete', + actor: `${this.config.url}/users/${user.id}`, + object, + published: new Date().toISOString(), + }; + } + + public renderDocument(file: DriveFile) { + return { + type: 'Document', + mediaType: file.type, + url: this.driveFilesRepository.getPublicUrl(file), + name: file.comment, + }; + } + + public renderEmoji(emoji: Emoji) { + return { + id: `${this.config.url}/emojis/${emoji.name}`, + type: 'Emoji', + name: `:${emoji.name}:`, + updated: emoji.updatedAt != null ? emoji.updatedAt.toISOString() : new Date().toISOString, + icon: { + type: 'Image', + mediaType: emoji.type || 'image/png', + url: emoji.publicUrl || emoji.originalUrl, // || emoji.originalUrl してるのは後方互換性のため + }, + }; + } + + // to anonymise reporters, the reporting actor must be a system user + // object has to be a uri or array of uris + public renderFlag(user: ILocalUser, object: [string], content: string) { + return { + type: 'Flag', + actor: `${this.config.url}/users/${user.id}`, + content, + object, + }; + } + + public renderFollowRelay(relay: Relay, relayActor: ILocalUser) { + const follow = { + id: `${this.config.url}/activities/follow-relay/${relay.id}`, + type: 'Follow', + actor: `${this.config.url}/users/${relayActor.id}`, + object: 'https://www.w3.org/ns/activitystreams#Public', + }; + + return follow; + } + + /** + * Convert (local|remote)(Follower|Followee)ID to URL + * @param id Follower|Followee ID + */ + public async renderFollowUser(id: User['id']) { + const user = await this.usersRepository.findOneByOrFail({ id: id }); + return this.usersRepository.isLocalUser(user) ? `${this.config.url}/users/${user.id}` : user.uri; + } + + public renderFollow( + follower: { id: User['id']; host: User['host']; uri: User['host'] }, + followee: { id: User['id']; host: User['host']; uri: User['host'] }, + requestId?: string, + ) { + const follow = { + id: requestId ?? `${this.config.url}/follows/${follower.id}/${followee.id}`, + type: 'Follow', + actor: this.usersRepository.isLocalUser(follower) ? `${this.config.url}/users/${follower.id}` : follower.uri, + object: this.usersRepository.isLocalUser(followee) ? `${this.config.url}/users/${followee.id}` : followee.uri, + } as any; + + return follow; + } + + public renderHashtag(tag: string) { + return { + type: 'Hashtag', + href: `${this.config.url}/tags/${encodeURIComponent(tag)}`, + name: `#${tag}`, + }; + } + + public renderImage(file: DriveFile) { + return { + type: 'Image', + url: this.driveFilesRepository.getPublicUrl(file), + sensitive: file.isSensitive, + name: file.comment, + }; + } + + public renderKey(user: ILocalUser, key: UserKeypair, postfix?: string) { + return { + id: `${this.config.url}/users/${user.id}${postfix || '/publickey'}`, + type: 'Key', + owner: `${this.config.url}/users/${user.id}`, + publicKeyPem: createPublicKey(key.publicKey).export({ + type: 'spki', + format: 'pem', + }), + }; + } + + public async renderLike(noteReaction: NoteReaction, note: Note) { + const reaction = noteReaction.reaction; + + const object = { + type: 'Like', + id: `${this.config.url}/likes/${noteReaction.id}`, + actor: `${this.config.url}/users/${noteReaction.userId}`, + object: note.uri ? note.uri : `${this.config.url}/notes/${noteReaction.noteId}`, + content: reaction, + _misskey_reaction: reaction, + } as any; + + if (reaction.startsWith(':')) { + const name = reaction.replace(/:/g, ''); + const emoji = await this.emojisRepository.findOneBy({ + name, + host: IsNull(), + }); + + if (emoji) object.tag = [this.renderEmoji(emoji)]; + } + + return object; + } + + public renderMention(mention: User) { + return { + type: 'Mention', + href: this.usersRepository.isRemoteUser(mention) ? mention.uri : `${this.config.url}/users/${(mention as ILocalUser).id}`, + name: this.usersRepository.isRemoteUser(mention) ? `@${mention.username}@${mention.host}` : `@${(mention as ILocalUser).username}`, + }; + } + + public async renderNote(note: Note, dive = true, isTalk = false): Promise> { + const getPromisedFiles = async (ids: string[]) => { + if (!ids || ids.length === 0) return []; + const items = await this.driveFilesRepository.findBy({ id: In(ids) }); + return ids.map(id => items.find(item => item.id === id)).filter(item => item != null) as DriveFile[]; + }; + + let inReplyTo; + let inReplyToNote: Note | null; + + if (note.replyId) { + inReplyToNote = await this.notesRepository.findOneBy({ id: note.replyId }); + + if (inReplyToNote != null) { + const inReplyToUser = await this.usersRepository.findOneBy({ id: inReplyToNote.userId }); + + if (inReplyToUser != null) { + if (inReplyToNote.uri) { + inReplyTo = inReplyToNote.uri; + } else { + if (dive) { + inReplyTo = await this.renderNote(inReplyToNote, false); + } else { + inReplyTo = `${this.config.url}/notes/${inReplyToNote.id}`; + } + } + } + } + } else { + inReplyTo = null; + } + + let quote; + + if (note.renoteId) { + const renote = await this.notesRepository.findOneBy({ id: note.renoteId }); + + if (renote) { + quote = renote.uri ? renote.uri : `${this.config.url}/notes/${renote.id}`; + } + } + + const attributedTo = `${this.config.url}/users/${note.userId}`; + + const mentions = (JSON.parse(note.mentionedRemoteUsers) as IMentionedRemoteUsers).map(x => x.uri); + + let to: string[] = []; + let cc: string[] = []; + + if (note.visibility === 'public') { + to = ['https://www.w3.org/ns/activitystreams#Public']; + cc = [`${attributedTo}/followers`].concat(mentions); + } else if (note.visibility === 'home') { + to = [`${attributedTo}/followers`]; + cc = ['https://www.w3.org/ns/activitystreams#Public'].concat(mentions); + } else if (note.visibility === 'followers') { + to = [`${attributedTo}/followers`]; + cc = mentions; + } else { + to = mentions; + } + + const mentionedUsers = note.mentions.length > 0 ? await this.usersRepository.findBy({ + id: In(note.mentions), + }) : []; + + const hashtagTags = (note.tags || []).map(tag => this.renderHashtag(tag)); + const mentionTags = mentionedUsers.map(u => this.renderMention(u)); + + const files = await getPromisedFiles(note.fileIds); + + const text = note.text ?? ''; + let poll: Poll | null = null; + + if (note.hasPoll) { + poll = await this.pollsRepository.findOneBy({ noteId: note.id }); + } + + let apText = text; + + if (quote) { + apText += `\n\nRE: ${quote}`; + } + + const summary = note.cw === '' ? String.fromCharCode(0x200B) : note.cw; + + const content = toHtml(Object.assign({}, note, { + text: apText, + })); + + const emojis = await this.#getEmojis(note.emojis); + const apemojis = emojis.map(emoji => this.renderEmoji(emoji)); + + const tag = [ + ...hashtagTags, + ...mentionTags, + ...apemojis, + ]; + + const asPoll = poll ? { + type: 'Question', + content: toHtml(Object.assign({}, note, { + text: text, + })), + [poll.expiresAt && poll.expiresAt < new Date() ? 'closed' : 'endTime']: poll.expiresAt, + [poll.multiple ? 'anyOf' : 'oneOf']: poll.choices.map((text, i) => ({ + type: 'Note', + name: text, + replies: { + type: 'Collection', + totalItems: poll!.votes[i], + }, + })), + } : {}; + + const asTalk = isTalk ? { + _misskey_talk: true, + } : {}; + + return { + id: `${this.config.url}/notes/${note.id}`, + type: 'Note', + attributedTo, + summary, + content, + _misskey_content: text, + source: { + content: text, + mediaType: 'text/x.misskeymarkdown', + }, + _misskey_quote: quote, + quoteUrl: quote, + published: note.createdAt.toISOString(), + to, + cc, + inReplyTo, + attachment: files.map(x => this.renderDocument(x)), + sensitive: note.cw != null || files.some(file => file.isSensitive), + tag, + ...asPoll, + ...asTalk, + }; + } + + public async renderPerson(user: ILocalUser) { + const id = `${this.config.url}/users/${user.id}`; + const isSystem = !!user.username.match(/\./); + + const [avatar, banner, profile] = await Promise.all([ + user.avatarId ? this.driveFilesRepository.findOneBy({ id: user.avatarId }) : Promise.resolve(undefined), + user.bannerId ? this.driveFilesRepository.findOneBy({ id: user.bannerId }) : Promise.resolve(undefined), + UserProfiles.findOneByOrFail({ userId: user.id }), + ]); + + const attachment: { + type: 'PropertyValue', + name: string, + value: string, + identifier?: IIdentifier, + }[] = []; + + if (profile.fields) { + for (const field of profile.fields) { + attachment.push({ + type: 'PropertyValue', + name: field.name, + value: (field.value != null && field.value.match(/^https?:/)) + ? `${new URL(field.value).href}` + : field.value, + }); + } + } + + const emojis = await this.#getEmojis(user.emojis); + const apemojis = emojis.map(emoji => this.renderEmoji(emoji)); + + const hashtagTags = (user.tags || []).map(tag => this.renderHashtag(tag)); + + const tag = [ + ...apemojis, + ...hashtagTags, + ]; + + const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); + + const person = { + type: isSystem ? 'Application' : user.isBot ? 'Service' : 'Person', + id, + inbox: `${id}/inbox`, + outbox: `${id}/outbox`, + followers: `${id}/followers`, + following: `${id}/following`, + featured: `${id}/collections/featured`, + sharedInbox: `${this.config.url}/inbox`, + endpoints: { sharedInbox: `${this.config.url}/inbox` }, + url: `${this.config.url}/@${user.username}`, + preferredUsername: user.username, + name: user.name, + summary: profile.description ? toHtml(mfm.parse(profile.description)) : null, + icon: avatar ? this.renderImage(avatar) : null, + image: banner ? this.renderImage(banner) : null, + tag, + manuallyApprovesFollowers: user.isLocked, + discoverable: !!user.isExplorable, + publicKey: this.renderKey(user, keypair, '#main-key'), + isCat: user.isCat, + attachment: attachment.length ? attachment : undefined, + } as any; + + if (profile.birthday) { + person['vcard:bday'] = profile.birthday; + } + + if (profile.location) { + person['vcard:Address'] = profile.location; + } + + return person; + } + + public async renderQuestion(user: { id: User['id'] }, note: Note, poll: Poll) { + const question = { + type: 'Question', + id: `${this.config.url}/questions/${note.id}`, + actor: `${this.config.url}/users/${user.id}`, + content: note.text || '', + [poll.multiple ? 'anyOf' : 'oneOf']: poll.choices.map((text, i) => ({ + name: text, + _misskey_votes: poll.votes[i], + replies: { + type: 'Collection', + totalItems: poll.votes[i], + }, + })), + }; + + return question; + } + + public renderRead(user: { id: User['id'] }, message: MessagingMessage) { + return { + type: 'Read', + actor: `${this.config.url}/users/${user.id}`, + object: message.uri, + }; + } + + public renderReject(object: any, user: { id: User['id'] }) { + return { + type: 'Reject', + actor: `${this.config.url}/users/${user.id}`, + object, + }; + } + + public renderRemove(user: { id: User['id'] }, target: any, object: any) { + return { + type: 'Remove', + actor: `${this.config.url}/users/${user.id}`, + target, + object, + }; + } + + public renderTombstone(id: string) { + return { + id, + type: 'Tombstone', + }; + } + + public renderUndo(object: any, user: { id: User['id'] }) { + if (object == null) return null; + const id = typeof object.id === 'string' && object.id.startsWith(this.config.url) ? `${object.id}/undo` : undefined; + + return { + type: 'Undo', + ...(id ? { id } : {}), + actor: `${this.config.url}/users/${user.id}`, + object, + published: new Date().toISOString(), + }; + } + + public renderUpdate(object: any, user: { id: User['id'] }) { + const activity = { + id: `${this.config.url}/users/${user.id}#updates/${new Date().getTime()}`, + actor: `${this.config.url}/users/${user.id}`, + type: 'Update', + to: [ 'https://www.w3.org/ns/activitystreams#Public' ], + object, + published: new Date().toISOString(), + } as any; + + return activity; + } + + public renderVote(user: { id: User['id'] }, vote: PollVote, note: Note, poll: Poll, pollOwner: IRemoteUser) { + return { + id: `${this.config.url}/users/${user.id}#votes/${vote.id}/activity`, + actor: `${this.config.url}/users/${user.id}`, + type: 'Create', + to: [pollOwner.uri], + published: new Date().toISOString(), + object: { + id: `${this.config.url}/users/${user.id}#votes/${vote.id}`, + type: 'Note', + attributedTo: `${this.config.url}/users/${user.id}`, + to: [pollOwner.uri], + inReplyTo: note.uri, + name: poll.choices[vote.choice], + }, + }; + } + + public renderActivity(x: any): IActivity | null { + if (x == null) return null; + + if (typeof x === 'object' && x.id == null) { + x.id = `${this.config.url}/${uuid()}`; + } + + return Object.assign({ + '@context': [ + 'https://www.w3.org/ns/activitystreams', + 'https://w3id.org/security/v1', + { + // as non-standards + manuallyApprovesFollowers: 'as:manuallyApprovesFollowers', + sensitive: 'as:sensitive', + Hashtag: 'as:Hashtag', + quoteUrl: 'as:quoteUrl', + // Mastodon + toot: 'http://joinmastodon.org/ns#', + Emoji: 'toot:Emoji', + featured: 'toot:featured', + discoverable: 'toot:discoverable', + // schema + schema: 'http://schema.org#', + PropertyValue: 'schema:PropertyValue', + value: 'schema:value', + // Misskey + misskey: 'https://misskey-hub.net/ns#', + '_misskey_content': 'misskey:_misskey_content', + '_misskey_quote': 'misskey:_misskey_quote', + '_misskey_reaction': 'misskey:_misskey_reaction', + '_misskey_votes': 'misskey:_misskey_votes', + '_misskey_talk': 'misskey:_misskey_talk', + 'isCat': 'misskey:isCat', + // vcard + vcard: 'http://www.w3.org/2006/vcard/ns#', + }, + ], + }, x); + } + + public async attachLdSignature(activity: any, user: { id: User['id']; host: null; }): Promise { + if (activity == null) return null; + + const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); + + const ldSignature = new LdSignature(); + ldSignature.debug = false; + activity = await ldSignature.signRsaSignature2017(activity, keypair.privateKey, `${this.config.url}/users/${user.id}#main-key`); + + return activity; + } + + /** + * Render OrderedCollectionPage + * @param id URL of self + * @param totalItems Number of total items + * @param orderedItems Items + * @param partOf URL of base + * @param prev URL of prev page (optional) + * @param next URL of next page (optional) + */ + public renderOrderedCollectionPage(id: string, totalItems: any, orderedItems: any, partOf: string, prev?: string, next?: string) { + const page = { + id, + partOf, + type: 'OrderedCollectionPage', + totalItems, + orderedItems, + } as any; + + if (prev) page.prev = prev; + if (next) page.next = next; + + return page; + } + + /** + * Render OrderedCollection + * @param id URL of self + * @param totalItems Total number of items + * @param first URL of first page (optional) + * @param last URL of last page (optional) + * @param orderedItems attached objects (optional) + */ + public renderOrderedCollection(id: string | null, totalItems: any, first?: string, last?: string, orderedItems?: Record[]) { + const page: any = { + id, + type: 'OrderedCollection', + totalItems, + }; + + if (first) page.first = first; + if (last) page.last = last; + if (orderedItems) page.orderedItems = orderedItems; + + return page; + } + + async #getEmojis(names: string[]): Promise { + if (names == null || names.length === 0) return []; + + const emojis = await Promise.all( + names.map(name => this.emojisRepository.findOneBy({ + name, + host: IsNull(), + })), + ); + + return emojis.filter(emoji => emoji != null) as Emoji[]; + } +} diff --git a/packages/backend/src/services/remote/activitypub/renderer/accept.ts b/packages/backend/src/services/remote/activitypub/renderer/accept.ts deleted file mode 100644 index cb01f6a91bf3..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/accept.ts +++ /dev/null @@ -1,8 +0,0 @@ -import config from '@/config/index.js'; -import { User } from '@/models/entities/user.js'; - -export default (object: any, user: { id: User['id']; host: null }) => ({ - type: 'Accept', - actor: `${config.url}/users/${user.id}`, - object, -}); diff --git a/packages/backend/src/services/remote/activitypub/renderer/add.ts b/packages/backend/src/services/remote/activitypub/renderer/add.ts deleted file mode 100644 index ec478842916c..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/add.ts +++ /dev/null @@ -1,9 +0,0 @@ -import config from '@/config/index.js'; -import { ILocalUser } from '@/models/entities/user.js'; - -export default (user: ILocalUser, target: any, object: any) => ({ - type: 'Add', - actor: `${config.url}/users/${user.id}`, - target, - object, -}); diff --git a/packages/backend/src/services/remote/activitypub/renderer/announce.ts b/packages/backend/src/services/remote/activitypub/renderer/announce.ts deleted file mode 100644 index 2709fea51de4..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/announce.ts +++ /dev/null @@ -1,29 +0,0 @@ -import config from '@/config/index.js'; -import { Note } from '@/models/entities/note.js'; - -export default (object: any, note: Note) => { - const attributedTo = `${config.url}/users/${note.userId}`; - - let to: string[] = []; - let cc: string[] = []; - - if (note.visibility === 'public') { - to = ['https://www.w3.org/ns/activitystreams#Public']; - cc = [`${attributedTo}/followers`]; - } else if (note.visibility === 'home') { - to = [`${attributedTo}/followers`]; - cc = ['https://www.w3.org/ns/activitystreams#Public']; - } else { - return null; - } - - return { - id: `${config.url}/notes/${note.id}/activity`, - actor: `${config.url}/users/${note.userId}`, - type: 'Announce', - published: note.createdAt.toISOString(), - to, - cc, - object, - }; -}; diff --git a/packages/backend/src/services/remote/activitypub/renderer/block.ts b/packages/backend/src/services/remote/activitypub/renderer/block.ts deleted file mode 100644 index 802d7280b119..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/block.ts +++ /dev/null @@ -1,20 +0,0 @@ -import config from '@/config/index.js'; -import { Blocking } from '@/models/entities/blocking.js'; - -/** - * Renders a block into its ActivityPub representation. - * - * @param block The block to be rendered. The blockee relation must be loaded. - */ -export function renderBlock(block: Blocking) { - if (block.blockee?.uri == null) { - throw new Error('renderBlock: missing blockee uri'); - } - - return { - type: 'Block', - id: `${config.url}/blocks/${block.id}`, - actor: `${config.url}/users/${block.blockerId}`, - object: block.blockee.uri, - }; -} diff --git a/packages/backend/src/services/remote/activitypub/renderer/create.ts b/packages/backend/src/services/remote/activitypub/renderer/create.ts deleted file mode 100644 index 281a3cb2af90..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/create.ts +++ /dev/null @@ -1,17 +0,0 @@ -import config from '@/config/index.js'; -import { Note } from '@/models/entities/note.js'; - -export default (object: any, note: Note) => { - const activity = { - id: `${config.url}/notes/${note.id}/activity`, - actor: `${config.url}/users/${note.userId}`, - type: 'Create', - published: note.createdAt.toISOString(), - object, - } as any; - - if (object.to) activity.to = object.to; - if (object.cc) activity.cc = object.cc; - - return activity; -}; diff --git a/packages/backend/src/services/remote/activitypub/renderer/delete.ts b/packages/backend/src/services/remote/activitypub/renderer/delete.ts deleted file mode 100644 index 4edd3a8807f5..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/delete.ts +++ /dev/null @@ -1,9 +0,0 @@ -import config from '@/config/index.js'; -import { User } from '@/models/entities/user.js'; - -export default (object: any, user: { id: User['id']; host: null }) => ({ - type: 'Delete', - actor: `${config.url}/users/${user.id}`, - object, - published: new Date().toISOString(), -}); diff --git a/packages/backend/src/services/remote/activitypub/renderer/document.ts b/packages/backend/src/services/remote/activitypub/renderer/document.ts deleted file mode 100644 index c973de4c4c59..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/document.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { DriveFile } from '@/models/entities/drive-file.js'; -import { DriveFiles } from '@/models/index.js'; - -export default (file: DriveFile) => ({ - type: 'Document', - mediaType: file.type, - url: DriveFiles.getPublicUrl(file), - name: file.comment, -}); diff --git a/packages/backend/src/services/remote/activitypub/renderer/emoji.ts b/packages/backend/src/services/remote/activitypub/renderer/emoji.ts deleted file mode 100644 index 0bf15eefd974..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/emoji.ts +++ /dev/null @@ -1,14 +0,0 @@ -import config from '@/config/index.js'; -import { Emoji } from '@/models/entities/emoji.js'; - -export default (emoji: Emoji) => ({ - id: `${config.url}/emojis/${emoji.name}`, - type: 'Emoji', - name: `:${emoji.name}:`, - updated: emoji.updatedAt != null ? emoji.updatedAt.toISOString() : new Date().toISOString, - icon: { - type: 'Image', - mediaType: emoji.type || 'image/png', - url: emoji.publicUrl || emoji.originalUrl, // || emoji.originalUrl してるのは後方互換性のため - }, -}); diff --git a/packages/backend/src/services/remote/activitypub/renderer/flag.ts b/packages/backend/src/services/remote/activitypub/renderer/flag.ts deleted file mode 100644 index c3607dc279f8..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/flag.ts +++ /dev/null @@ -1,15 +0,0 @@ -import config from '@/config/index.js'; -import { IObject, IActivity } from '@/services/remote/activitypub/type.js'; -import type { ILocalUser } from '@/models/entities/user.js'; -import { IRemoteUser } from '@/models/entities/user.js'; - -// to anonymise reporters, the reporting actor must be a system user -// object has to be a uri or array of uris -export const renderFlag = (user: ILocalUser, object: [string], content: string) => { - return { - type: 'Flag', - actor: `${config.url}/users/${user.id}`, - content, - object, - }; -}; diff --git a/packages/backend/src/services/remote/activitypub/renderer/follow-relay.ts b/packages/backend/src/services/remote/activitypub/renderer/follow-relay.ts deleted file mode 100644 index 2c9678090f98..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/follow-relay.ts +++ /dev/null @@ -1,14 +0,0 @@ -import config from '@/config/index.js'; -import { Relay } from '@/models/entities/relay.js'; -import { ILocalUser } from '@/models/entities/user.js'; - -export function renderFollowRelay(relay: Relay, relayActor: ILocalUser) { - const follow = { - id: `${config.url}/activities/follow-relay/${relay.id}`, - type: 'Follow', - actor: `${config.url}/users/${relayActor.id}`, - object: 'https://www.w3.org/ns/activitystreams#Public', - }; - - return follow; -} diff --git a/packages/backend/src/services/remote/activitypub/renderer/follow-user.ts b/packages/backend/src/services/remote/activitypub/renderer/follow-user.ts deleted file mode 100644 index 9a8a16d74933..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/follow-user.ts +++ /dev/null @@ -1,12 +0,0 @@ -import config from '@/config/index.js'; -import { Users } from '@/models/index.js'; -import { User } from '@/models/entities/user.js'; - -/** - * Convert (local|remote)(Follower|Followee)ID to URL - * @param id Follower|Followee ID - */ -export default async function renderFollowUser(id: User['id']): Promise { - const user = await Users.findOneByOrFail({ id: id }); - return Users.isLocalUser(user) ? `${config.url}/users/${user.id}` : user.uri; -} diff --git a/packages/backend/src/services/remote/activitypub/renderer/follow.ts b/packages/backend/src/services/remote/activitypub/renderer/follow.ts deleted file mode 100644 index 00fac18ad5b2..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/follow.ts +++ /dev/null @@ -1,14 +0,0 @@ -import config from '@/config/index.js'; -import { User } from '@/models/entities/user.js'; -import { Users } from '@/models/index.js'; - -export default (follower: { id: User['id']; host: User['host']; uri: User['host'] }, followee: { id: User['id']; host: User['host']; uri: User['host'] }, requestId?: string) => { - const follow = { - id: requestId ?? `${config.url}/follows/${follower.id}/${followee.id}`, - type: 'Follow', - actor: Users.isLocalUser(follower) ? `${config.url}/users/${follower.id}` : follower.uri, - object: Users.isLocalUser(followee) ? `${config.url}/users/${followee.id}` : followee.uri, - } as any; - - return follow; -}; diff --git a/packages/backend/src/services/remote/activitypub/renderer/hashtag.ts b/packages/backend/src/services/remote/activitypub/renderer/hashtag.ts deleted file mode 100644 index a7b441e0060a..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/hashtag.ts +++ /dev/null @@ -1,7 +0,0 @@ -import config from '@/config/index.js'; - -export default (tag: string) => ({ - type: 'Hashtag', - href: `${config.url}/tags/${encodeURIComponent(tag)}`, - name: `#${tag}`, -}); diff --git a/packages/backend/src/services/remote/activitypub/renderer/image.ts b/packages/backend/src/services/remote/activitypub/renderer/image.ts deleted file mode 100644 index c7d5a31a27ab..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/image.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { DriveFile } from '@/models/entities/drive-file.js'; -import { DriveFiles } from '@/models/index.js'; - -export default (file: DriveFile) => ({ - type: 'Image', - url: DriveFiles.getPublicUrl(file), - sensitive: file.isSensitive, - name: file.comment, -}); diff --git a/packages/backend/src/services/remote/activitypub/renderer/index.ts b/packages/backend/src/services/remote/activitypub/renderer/index.ts deleted file mode 100644 index f100b77ce517..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/index.ts +++ /dev/null @@ -1,59 +0,0 @@ -import config from '@/config/index.js'; -import { v4 as uuid } from 'uuid'; -import { IActivity } from '../type.js'; -import { LdSignature } from '../misc/ld-signature.js'; -import { getUserKeypair } from '@/misc/keypair-store.js'; -import { User } from '@/models/entities/user.js'; - -export const renderActivity = (x: any): IActivity | null => { - if (x == null) return null; - - if (typeof x === 'object' && x.id == null) { - x.id = `${config.url}/${uuid()}`; - } - - return Object.assign({ - '@context': [ - 'https://www.w3.org/ns/activitystreams', - 'https://w3id.org/security/v1', - { - // as non-standards - manuallyApprovesFollowers: 'as:manuallyApprovesFollowers', - sensitive: 'as:sensitive', - Hashtag: 'as:Hashtag', - quoteUrl: 'as:quoteUrl', - // Mastodon - toot: 'http://joinmastodon.org/ns#', - Emoji: 'toot:Emoji', - featured: 'toot:featured', - discoverable: 'toot:discoverable', - // schema - schema: 'http://schema.org#', - PropertyValue: 'schema:PropertyValue', - value: 'schema:value', - // Misskey - misskey: 'https://misskey-hub.net/ns#', - '_misskey_content': 'misskey:_misskey_content', - '_misskey_quote': 'misskey:_misskey_quote', - '_misskey_reaction': 'misskey:_misskey_reaction', - '_misskey_votes': 'misskey:_misskey_votes', - '_misskey_talk': 'misskey:_misskey_talk', - 'isCat': 'misskey:isCat', - // vcard - vcard: 'http://www.w3.org/2006/vcard/ns#', - }, - ], - }, x); -}; - -export const attachLdSignature = async (activity: any, user: { id: User['id']; host: null; }): Promise => { - if (activity == null) return null; - - const keypair = await getUserKeypair(user.id); - - const ldSignature = new LdSignature(); - ldSignature.debug = false; - activity = await ldSignature.signRsaSignature2017(activity, keypair.privateKey, `${config.url}/users/${user.id}#main-key`); - - return activity; -}; diff --git a/packages/backend/src/services/remote/activitypub/renderer/key.ts b/packages/backend/src/services/remote/activitypub/renderer/key.ts deleted file mode 100644 index c4f3d464f89b..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/key.ts +++ /dev/null @@ -1,14 +0,0 @@ -import config from '@/config/index.js'; -import { ILocalUser } from '@/models/entities/user.js'; -import { UserKeypair } from '@/models/entities/user-keypair.js'; -import { createPublicKey } from 'node:crypto'; - -export default (user: ILocalUser, key: UserKeypair, postfix?: string) => ({ - id: `${config.url}/users/${user.id}${postfix || '/publickey'}`, - type: 'Key', - owner: `${config.url}/users/${user.id}`, - publicKeyPem: createPublicKey(key.publicKey).export({ - type: 'spki', - format: 'pem', - }), -}); diff --git a/packages/backend/src/services/remote/activitypub/renderer/like.ts b/packages/backend/src/services/remote/activitypub/renderer/like.ts deleted file mode 100644 index 00fb72e8a3bd..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/like.ts +++ /dev/null @@ -1,31 +0,0 @@ -import config from '@/config/index.js'; -import { NoteReaction } from '@/models/entities/note-reaction.js'; -import { Note } from '@/models/entities/note.js'; -import { Emojis } from '@/models/index.js'; -import { IsNull } from 'typeorm'; -import renderEmoji from './emoji.js'; - -export const renderLike = async (noteReaction: NoteReaction, note: Note) => { - const reaction = noteReaction.reaction; - - const object = { - type: 'Like', - id: `${config.url}/likes/${noteReaction.id}`, - actor: `${config.url}/users/${noteReaction.userId}`, - object: note.uri ? note.uri : `${config.url}/notes/${noteReaction.noteId}`, - content: reaction, - _misskey_reaction: reaction, - } as any; - - if (reaction.startsWith(':')) { - const name = reaction.replace(/:/g, ''); - const emoji = await Emojis.findOneBy({ - name, - host: IsNull(), - }); - - if (emoji) object.tag = [ renderEmoji(emoji) ]; - } - - return object; -}; diff --git a/packages/backend/src/services/remote/activitypub/renderer/mention.ts b/packages/backend/src/services/remote/activitypub/renderer/mention.ts deleted file mode 100644 index c7e62e884032..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/mention.ts +++ /dev/null @@ -1,9 +0,0 @@ -import config from '@/config/index.js'; -import { User, ILocalUser } from '@/models/entities/user.js'; -import { Users } from '@/models/index.js'; - -export default (mention: User) => ({ - type: 'Mention', - href: Users.isRemoteUser(mention) ? mention.uri : `${config.url}/users/${(mention as ILocalUser).id}`, - name: Users.isRemoteUser(mention) ? `@${mention.username}@${mention.host}` : `@${(mention as ILocalUser).username}`, -}); diff --git a/packages/backend/src/services/remote/activitypub/renderer/note.ts b/packages/backend/src/services/remote/activitypub/renderer/note.ts deleted file mode 100644 index b3bafaa3ab52..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/note.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { In, IsNull } from 'typeorm'; -import config from '@/config/index.js'; -import { Note, IMentionedRemoteUsers } from '@/models/entities/note.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; -import { DriveFiles, Notes, Users, Emojis, Polls } from '@/models/index.js'; -import { Emoji } from '@/models/entities/emoji.js'; -import { Poll } from '@/models/entities/poll.js'; -import toHtml from '../misc/get-note-html.js'; -import renderEmoji from './emoji.js'; -import renderMention from './mention.js'; -import renderHashtag from './hashtag.js'; -import renderDocument from './document.js'; - -export default async function renderNote(note: Note, dive = true, isTalk = false): Promise> { - const getPromisedFiles = async (ids: string[]) => { - if (!ids || ids.length === 0) return []; - const items = await DriveFiles.findBy({ id: In(ids) }); - return ids.map(id => items.find(item => item.id === id)).filter(item => item != null) as DriveFile[]; - }; - - let inReplyTo; - let inReplyToNote: Note | null; - - if (note.replyId) { - inReplyToNote = await Notes.findOneBy({ id: note.replyId }); - - if (inReplyToNote != null) { - const inReplyToUser = await Users.findOneBy({ id: inReplyToNote.userId }); - - if (inReplyToUser != null) { - if (inReplyToNote.uri) { - inReplyTo = inReplyToNote.uri; - } else { - if (dive) { - inReplyTo = await renderNote(inReplyToNote, false); - } else { - inReplyTo = `${config.url}/notes/${inReplyToNote.id}`; - } - } - } - } - } else { - inReplyTo = null; - } - - let quote; - - if (note.renoteId) { - const renote = await Notes.findOneBy({ id: note.renoteId }); - - if (renote) { - quote = renote.uri ? renote.uri : `${config.url}/notes/${renote.id}`; - } - } - - const attributedTo = `${config.url}/users/${note.userId}`; - - const mentions = (JSON.parse(note.mentionedRemoteUsers) as IMentionedRemoteUsers).map(x => x.uri); - - let to: string[] = []; - let cc: string[] = []; - - if (note.visibility === 'public') { - to = ['https://www.w3.org/ns/activitystreams#Public']; - cc = [`${attributedTo}/followers`].concat(mentions); - } else if (note.visibility === 'home') { - to = [`${attributedTo}/followers`]; - cc = ['https://www.w3.org/ns/activitystreams#Public'].concat(mentions); - } else if (note.visibility === 'followers') { - to = [`${attributedTo}/followers`]; - cc = mentions; - } else { - to = mentions; - } - - const mentionedUsers = note.mentions.length > 0 ? await Users.findBy({ - id: In(note.mentions), - }) : []; - - const hashtagTags = (note.tags || []).map(tag => renderHashtag(tag)); - const mentionTags = mentionedUsers.map(u => renderMention(u)); - - const files = await getPromisedFiles(note.fileIds); - - const text = note.text ?? ''; - let poll: Poll | null = null; - - if (note.hasPoll) { - poll = await Polls.findOneBy({ noteId: note.id }); - } - - let apText = text; - - if (quote) { - apText += `\n\nRE: ${quote}`; - } - - const summary = note.cw === '' ? String.fromCharCode(0x200B) : note.cw; - - const content = toHtml(Object.assign({}, note, { - text: apText, - })); - - const emojis = await getEmojis(note.emojis); - const apemojis = emojis.map(emoji => renderEmoji(emoji)); - - const tag = [ - ...hashtagTags, - ...mentionTags, - ...apemojis, - ]; - - const asPoll = poll ? { - type: 'Question', - content: toHtml(Object.assign({}, note, { - text: text, - })), - [poll.expiresAt && poll.expiresAt < new Date() ? 'closed' : 'endTime']: poll.expiresAt, - [poll.multiple ? 'anyOf' : 'oneOf']: poll.choices.map((text, i) => ({ - type: 'Note', - name: text, - replies: { - type: 'Collection', - totalItems: poll!.votes[i], - }, - })), - } : {}; - - const asTalk = isTalk ? { - _misskey_talk: true, - } : {}; - - return { - id: `${config.url}/notes/${note.id}`, - type: 'Note', - attributedTo, - summary, - content, - _misskey_content: text, - source: { - content: text, - mediaType: "text/x.misskeymarkdown", - }, - _misskey_quote: quote, - quoteUrl: quote, - published: note.createdAt.toISOString(), - to, - cc, - inReplyTo, - attachment: files.map(renderDocument), - sensitive: note.cw != null || files.some(file => file.isSensitive), - tag, - ...asPoll, - ...asTalk, - }; -} - -export async function getEmojis(names: string[]): Promise { - if (names == null || names.length === 0) return []; - - const emojis = await Promise.all( - names.map(name => Emojis.findOneBy({ - name, - host: IsNull(), - })), - ); - - return emojis.filter(emoji => emoji != null) as Emoji[]; -} diff --git a/packages/backend/src/services/remote/activitypub/renderer/ordered-collection-page.ts b/packages/backend/src/services/remote/activitypub/renderer/ordered-collection-page.ts deleted file mode 100644 index c5e25f577b63..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/ordered-collection-page.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Render OrderedCollectionPage - * @param id URL of self - * @param totalItems Number of total items - * @param orderedItems Items - * @param partOf URL of base - * @param prev URL of prev page (optional) - * @param next URL of next page (optional) - */ -export default function(id: string, totalItems: any, orderedItems: any, partOf: string, prev?: string, next?: string) { - const page = { - id, - partOf, - type: 'OrderedCollectionPage', - totalItems, - orderedItems, - } as any; - - if (prev) page.prev = prev; - if (next) page.next = next; - - return page; -} diff --git a/packages/backend/src/services/remote/activitypub/renderer/ordered-collection.ts b/packages/backend/src/services/remote/activitypub/renderer/ordered-collection.ts deleted file mode 100644 index ff9a77be3db2..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/ordered-collection.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Render OrderedCollection - * @param id URL of self - * @param totalItems Total number of items - * @param first URL of first page (optional) - * @param last URL of last page (optional) - * @param orderedItems attached objects (optional) - */ -export default function(id: string | null, totalItems: any, first?: string, last?: string, orderedItems?: Record[]): { - id: string | null; - type: 'OrderedCollection'; - totalItems: any; - first?: string; - last?: string; - orderedItems?: Record[]; -} { - const page: any = { - id, - type: 'OrderedCollection', - totalItems, - }; - - if (first) page.first = first; - if (last) page.last = last; - if (orderedItems) page.orderedItems = orderedItems; - - return page; -} diff --git a/packages/backend/src/services/remote/activitypub/renderer/person.ts b/packages/backend/src/services/remote/activitypub/renderer/person.ts deleted file mode 100644 index f301158eb985..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/person.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { URL } from 'node:url'; -import * as mfm from 'mfm-js'; -import config from '@/config/index.js'; -import type { ILocalUser } from '@/models/entities/user.js'; -import { DriveFiles, UserProfiles } from '@/models/index.js'; -import { getUserKeypair } from '@/misc/keypair-store.js'; -import { toHtml } from '../../../../mfm/to-html.js'; -import renderImage from './image.js'; -import renderKey from './key.js'; -import { getEmojis } from './note.js'; -import renderEmoji from './emoji.js'; -import renderHashtag from './hashtag.js'; -import type { IIdentifier } from '../models/identifier.js'; - -export async function renderPerson(user: ILocalUser) { - const id = `${config.url}/users/${user.id}`; - const isSystem = !!user.username.match(/\./); - - const [avatar, banner, profile] = await Promise.all([ - user.avatarId ? DriveFiles.findOneBy({ id: user.avatarId }) : Promise.resolve(undefined), - user.bannerId ? DriveFiles.findOneBy({ id: user.bannerId }) : Promise.resolve(undefined), - UserProfiles.findOneByOrFail({ userId: user.id }), - ]); - - const attachment: { - type: 'PropertyValue', - name: string, - value: string, - identifier?: IIdentifier - }[] = []; - - if (profile.fields) { - for (const field of profile.fields) { - attachment.push({ - type: 'PropertyValue', - name: field.name, - value: (field.value != null && field.value.match(/^https?:/)) - ? `${new URL(field.value).href}` - : field.value, - }); - } - } - - const emojis = await getEmojis(user.emojis); - const apemojis = emojis.map(emoji => renderEmoji(emoji)); - - const hashtagTags = (user.tags || []).map(tag => renderHashtag(tag)); - - const tag = [ - ...apemojis, - ...hashtagTags, - ]; - - const keypair = await getUserKeypair(user.id); - - const person = { - type: isSystem ? 'Application' : user.isBot ? 'Service' : 'Person', - id, - inbox: `${id}/inbox`, - outbox: `${id}/outbox`, - followers: `${id}/followers`, - following: `${id}/following`, - featured: `${id}/collections/featured`, - sharedInbox: `${config.url}/inbox`, - endpoints: { sharedInbox: `${config.url}/inbox` }, - url: `${config.url}/@${user.username}`, - preferredUsername: user.username, - name: user.name, - summary: profile.description ? toHtml(mfm.parse(profile.description)) : null, - icon: avatar ? renderImage(avatar) : null, - image: banner ? renderImage(banner) : null, - tag, - manuallyApprovesFollowers: user.isLocked, - discoverable: !!user.isExplorable, - publicKey: renderKey(user, keypair, '#main-key'), - isCat: user.isCat, - attachment: attachment.length ? attachment : undefined, - } as any; - - if (profile.birthday) { - person['vcard:bday'] = profile.birthday; - } - - if (profile.location) { - person['vcard:Address'] = profile.location; - } - - return person; -} diff --git a/packages/backend/src/services/remote/activitypub/renderer/question.ts b/packages/backend/src/services/remote/activitypub/renderer/question.ts deleted file mode 100644 index d4d1b590af8a..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/question.ts +++ /dev/null @@ -1,23 +0,0 @@ -import config from '@/config/index.js'; -import { User } from '@/models/entities/user.js'; -import { Note } from '@/models/entities/note.js'; -import { Poll } from '@/models/entities/poll.js'; - -export default async function renderQuestion(user: { id: User['id'] }, note: Note, poll: Poll) { - const question = { - type: 'Question', - id: `${config.url}/questions/${note.id}`, - actor: `${config.url}/users/${user.id}`, - content: note.text || '', - [poll.multiple ? 'anyOf' : 'oneOf']: poll.choices.map((text, i) => ({ - name: text, - _misskey_votes: poll.votes[i], - replies: { - type: 'Collection', - totalItems: poll.votes[i], - }, - })), - }; - - return question; -} diff --git a/packages/backend/src/services/remote/activitypub/renderer/read.ts b/packages/backend/src/services/remote/activitypub/renderer/read.ts deleted file mode 100644 index a30e649f64c0..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/read.ts +++ /dev/null @@ -1,9 +0,0 @@ -import config from '@/config/index.js'; -import { User } from '@/models/entities/user.js'; -import { MessagingMessage } from '@/models/entities/messaging-message.js'; - -export const renderReadActivity = (user: { id: User['id'] }, message: MessagingMessage) => ({ - type: 'Read', - actor: `${config.url}/users/${user.id}`, - object: message.uri, -}); diff --git a/packages/backend/src/services/remote/activitypub/renderer/reject.ts b/packages/backend/src/services/remote/activitypub/renderer/reject.ts deleted file mode 100644 index ab4cc1646aa7..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/reject.ts +++ /dev/null @@ -1,8 +0,0 @@ -import config from '@/config/index.js'; -import { User } from '@/models/entities/user.js'; - -export default (object: any, user: { id: User['id'] }) => ({ - type: 'Reject', - actor: `${config.url}/users/${user.id}`, - object, -}); diff --git a/packages/backend/src/services/remote/activitypub/renderer/remove.ts b/packages/backend/src/services/remote/activitypub/renderer/remove.ts deleted file mode 100644 index 1be3edc5d54f..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/remove.ts +++ /dev/null @@ -1,9 +0,0 @@ -import config from '@/config/index.js'; -import { User } from '@/models/entities/user.js'; - -export default (user: { id: User['id'] }, target: any, object: any) => ({ - type: 'Remove', - actor: `${config.url}/users/${user.id}`, - target, - object, -}); diff --git a/packages/backend/src/services/remote/activitypub/renderer/tombstone.ts b/packages/backend/src/services/remote/activitypub/renderer/tombstone.ts deleted file mode 100644 index 313ca74e9da9..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/tombstone.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default (id: string) => ({ - id, - type: 'Tombstone', -}); diff --git a/packages/backend/src/services/remote/activitypub/renderer/undo.ts b/packages/backend/src/services/remote/activitypub/renderer/undo.ts deleted file mode 100644 index 46631df9ea8c..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/undo.ts +++ /dev/null @@ -1,15 +0,0 @@ -import config from '@/config/index.js'; -import { ILocalUser, User } from '@/models/entities/user.js'; - -export default (object: any, user: { id: User['id'] }) => { - if (object == null) return null; - const id = typeof object.id === 'string' && object.id.startsWith(config.url) ? `${object.id}/undo` : undefined; - - return { - type: 'Undo', - ...(id ? { id } : {}), - actor: `${config.url}/users/${user.id}`, - object, - published: new Date().toISOString(), - }; -}; diff --git a/packages/backend/src/services/remote/activitypub/renderer/update.ts b/packages/backend/src/services/remote/activitypub/renderer/update.ts deleted file mode 100644 index cf880f03fc1a..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/update.ts +++ /dev/null @@ -1,15 +0,0 @@ -import config from '@/config/index.js'; -import { User } from '@/models/entities/user.js'; - -export default (object: any, user: { id: User['id'] }) => { - const activity = { - id: `${config.url}/users/${user.id}#updates/${new Date().getTime()}`, - actor: `${config.url}/users/${user.id}`, - type: 'Update', - to: [ 'https://www.w3.org/ns/activitystreams#Public' ], - object, - published: new Date().toISOString(), - } as any; - - return activity; -}; diff --git a/packages/backend/src/services/remote/activitypub/renderer/vote.ts b/packages/backend/src/services/remote/activitypub/renderer/vote.ts deleted file mode 100644 index b6eb8e095d1f..000000000000 --- a/packages/backend/src/services/remote/activitypub/renderer/vote.ts +++ /dev/null @@ -1,23 +0,0 @@ -import config from '@/config/index.js'; -import { Note } from '@/models/entities/note.js'; -import { IRemoteUser, User } from '@/models/entities/user.js'; -import { PollVote } from '@/models/entities/poll-vote.js'; -import { Poll } from '@/models/entities/poll.js'; - -export default async function renderVote(user: { id: User['id'] }, vote: PollVote, note: Note, poll: Poll, pollOwner: IRemoteUser): Promise { - return { - id: `${config.url}/users/${user.id}#votes/${vote.id}/activity`, - actor: `${config.url}/users/${user.id}`, - type: 'Create', - to: [pollOwner.uri], - published: new Date().toISOString(), - object: { - id: `${config.url}/users/${user.id}#votes/${vote.id}`, - type: 'Note', - attributedTo: `${config.url}/users/${user.id}`, - to: [pollOwner.uri], - inReplyTo: note.uri, - name: poll.choices[vote.choice], - }, - }; -} From bc46be34f7401d3791f6507e8f19e8c639cd310c Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 01:32:52 +0900 Subject: [PATCH 046/180] wip --- packages/backend/src/mfm/from-html.ts | 213 ---------- packages/backend/src/mfm/to-html.ts | 159 -------- packages/backend/src/services/MfmService.ts | 384 ++++++++++++++++++ .../remote/activitypub/ApRendererService.ts | 10 +- 4 files changed, 390 insertions(+), 376 deletions(-) delete mode 100644 packages/backend/src/mfm/from-html.ts delete mode 100644 packages/backend/src/mfm/to-html.ts create mode 100644 packages/backend/src/services/MfmService.ts diff --git a/packages/backend/src/mfm/from-html.ts b/packages/backend/src/mfm/from-html.ts deleted file mode 100644 index 7751bac5639e..000000000000 --- a/packages/backend/src/mfm/from-html.ts +++ /dev/null @@ -1,213 +0,0 @@ -import { URL } from 'node:url'; -import * as parse5 from 'parse5'; -import * as TreeAdapter from '../../node_modules/parse5/dist/tree-adapters/default.js'; - -const treeAdapter = TreeAdapter.defaultTreeAdapter; - -const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/; -const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/; - -export function fromHtml(html: string, hashtagNames?: string[]): string { - // some AP servers like Pixelfed use br tags as well as newlines - html = html.replace(/\r?\n/gi, '\n'); - - const dom = parse5.parseFragment(html); - - let text = ''; - - for (const n of dom.childNodes) { - analyze(n); - } - - return text.trim(); - - function getText(node: TreeAdapter.Node): string { - if (treeAdapter.isTextNode(node)) return node.value; - if (!treeAdapter.isElementNode(node)) return ''; - if (node.nodeName === 'br') return '\n'; - - if (node.childNodes) { - return node.childNodes.map(n => getText(n)).join(''); - } - - return ''; - } - - function appendChildren(childNodes: TreeAdapter.ChildNode[]): void { - if (childNodes) { - for (const n of childNodes) { - analyze(n); - } - } - } - - function analyze(node: TreeAdapter.Node) { - if (treeAdapter.isTextNode(node)) { - text += node.value; - return; - } - - // Skip comment or document type node - if (!treeAdapter.isElementNode(node)) return; - - switch (node.nodeName) { - case 'br': { - text += '\n'; - break; - } - - case 'a': - { - const txt = getText(node); - const rel = node.attrs.find(x => x.name === 'rel'); - const href = node.attrs.find(x => x.name === 'href'); - - // ハッシュタグ - if (hashtagNames && href && hashtagNames.map(x => x.toLowerCase()).includes(txt.toLowerCase())) { - text += txt; - // メンション - } else if (txt.startsWith('@') && !(rel && rel.value.match(/^me /))) { - const part = txt.split('@'); - - if (part.length === 2 && href) { - //#region ホスト名部分が省略されているので復元する - const acct = `${txt}@${(new URL(href.value)).hostname}`; - text += acct; - //#endregion - } else if (part.length === 3) { - text += txt; - } - // その他 - } else { - const generateLink = () => { - if (!href && !txt) { - return ''; - } - if (!href) { - return txt; - } - if (!txt || txt === href.value) { // #6383: Missing text node - if (href.value.match(urlRegexFull)) { - return href.value; - } else { - return `<${href.value}>`; - } - } - if (href.value.match(urlRegex) && !href.value.match(urlRegexFull)) { - return `[${txt}](<${href.value}>)`; // #6846 - } else { - return `[${txt}](${href.value})`; - } - }; - - text += generateLink(); - } - break; - } - - case 'h1': - { - text += '【'; - appendChildren(node.childNodes); - text += '】\n'; - break; - } - - case 'b': - case 'strong': - { - text += '**'; - appendChildren(node.childNodes); - text += '**'; - break; - } - - case 'small': - { - text += ''; - appendChildren(node.childNodes); - text += ''; - break; - } - - case 's': - case 'del': - { - text += '~~'; - appendChildren(node.childNodes); - text += '~~'; - break; - } - - case 'i': - case 'em': - { - text += ''; - appendChildren(node.childNodes); - text += ''; - break; - } - - // block code (
)
-			case 'pre': {
-				if (node.childNodes.length === 1 && node.childNodes[0].nodeName === 'code') {
-					text += '\n```\n';
-					text += getText(node.childNodes[0]);
-					text += '\n```\n';
-				} else {
-					appendChildren(node.childNodes);
-				}
-				break;
-			}
-
-			// inline code ()
-			case 'code': {
-				text += '`';
-				appendChildren(node.childNodes);
-				text += '`';
-				break;
-			}
-
-			case 'blockquote': {
-				const t = getText(node);
-				if (t) {
-					text += '\n> ';
-					text += t.split('\n').join('\n> ');
-				}
-				break;
-			}
-
-			case 'p':
-			case 'h2':
-			case 'h3':
-			case 'h4':
-			case 'h5':
-			case 'h6':
-			{
-				text += '\n\n';
-				appendChildren(node.childNodes);
-				break;
-			}
-
-			// other block elements
-			case 'div':
-			case 'header':
-			case 'footer':
-			case 'article':
-			case 'li':
-			case 'dt':
-			case 'dd':
-			{
-				text += '\n';
-				appendChildren(node.childNodes);
-				break;
-			}
-
-			default:	// includes inline elements
-			{
-				appendChildren(node.childNodes);
-				break;
-			}
-		}
-	}
-}
diff --git a/packages/backend/src/mfm/to-html.ts b/packages/backend/src/mfm/to-html.ts
deleted file mode 100644
index bcb5c86a3d31..000000000000
--- a/packages/backend/src/mfm/to-html.ts
+++ /dev/null
@@ -1,159 +0,0 @@
-import { JSDOM } from 'jsdom';
-import * as mfm from 'mfm-js';
-import config from '@/config/index.js';
-import { intersperse } from '@/prelude/array.js';
-import { IMentionedRemoteUsers } from '@/models/entities/note.js';
-
-export function toHtml(nodes: mfm.MfmNode[] | null, mentionedRemoteUsers: IMentionedRemoteUsers = []) {
-	if (nodes == null) {
-		return null;
-	}
-
-	const { window } = new JSDOM('');
-
-	const doc = window.document;
-
-	function appendChildren(children: mfm.MfmNode[], targetElement: any): void {
-		if (children) {
-			for (const child of children.map(x => (handlers as any)[x.type](x))) targetElement.appendChild(child);
-		}
-	}
-
-	const handlers: { [K in mfm.MfmNode['type']]: (node: mfm.NodeType) => any } = {
-		bold(node) {
-			const el = doc.createElement('b');
-			appendChildren(node.children, el);
-			return el;
-		},
-
-		small(node) {
-			const el = doc.createElement('small');
-			appendChildren(node.children, el);
-			return el;
-		},
-
-		strike(node) {
-			const el = doc.createElement('del');
-			appendChildren(node.children, el);
-			return el;
-		},
-
-		italic(node) {
-			const el = doc.createElement('i');
-			appendChildren(node.children, el);
-			return el;
-		},
-
-		fn(node) {
-			const el = doc.createElement('i');
-			appendChildren(node.children, el);
-			return el;
-		},
-
-		blockCode(node) {
-			const pre = doc.createElement('pre');
-			const inner = doc.createElement('code');
-			inner.textContent = node.props.code;
-			pre.appendChild(inner);
-			return pre;
-		},
-
-		center(node) {
-			const el = doc.createElement('div');
-			appendChildren(node.children, el);
-			return el;
-		},
-
-		emojiCode(node) {
-			return doc.createTextNode(`\u200B:${node.props.name}:\u200B`);
-		},
-
-		unicodeEmoji(node) {
-			return doc.createTextNode(node.props.emoji);
-		},
-
-		hashtag(node) {
-			const a = doc.createElement('a');
-			a.href = `${config.url}/tags/${node.props.hashtag}`;
-			a.textContent = `#${node.props.hashtag}`;
-			a.setAttribute('rel', 'tag');
-			return a;
-		},
-
-		inlineCode(node) {
-			const el = doc.createElement('code');
-			el.textContent = node.props.code;
-			return el;
-		},
-
-		mathInline(node) {
-			const el = doc.createElement('code');
-			el.textContent = node.props.formula;
-			return el;
-		},
-
-		mathBlock(node) {
-			const el = doc.createElement('code');
-			el.textContent = node.props.formula;
-			return el;
-		},
-
-		link(node) {
-			const a = doc.createElement('a');
-			a.href = node.props.url;
-			appendChildren(node.children, a);
-			return a;
-		},
-
-		mention(node) {
-			const a = doc.createElement('a');
-			const { username, host, acct } = node.props;
-			const remoteUserInfo = mentionedRemoteUsers.find(remoteUser => remoteUser.username === username && remoteUser.host === host);
-			a.href = remoteUserInfo ? (remoteUserInfo.url ? remoteUserInfo.url : remoteUserInfo.uri) : `${config.url}/${acct}`;
-			a.className = 'u-url mention';
-			a.textContent = acct;
-			return a;
-		},
-
-		quote(node) {
-			const el = doc.createElement('blockquote');
-			appendChildren(node.children, el);
-			return el;
-		},
-
-		text(node) {
-			const el = doc.createElement('span');
-			const nodes = node.props.text.split(/\r\n|\r|\n/).map(x => doc.createTextNode(x));
-
-			for (const x of intersperse('br', nodes)) {
-				el.appendChild(x === 'br' ? doc.createElement('br') : x);
-			}
-
-			return el;
-		},
-
-		url(node) {
-			const a = doc.createElement('a');
-			a.href = node.props.url;
-			a.textContent = node.props.url;
-			return a;
-		},
-
-		search(node) {
-			const a = doc.createElement('a');
-			a.href = `https://www.google.com/search?q=${node.props.query}`;
-			a.textContent = node.props.content;
-			return a;
-		},
-
-		plain(node) {
-			const el = doc.createElement('span');
-			appendChildren(node.children, el);
-			return el;
-		},
-	};
-
-	appendChildren(nodes, doc.body);
-
-	return `

${doc.body.innerHTML}

`; -} diff --git a/packages/backend/src/services/MfmService.ts b/packages/backend/src/services/MfmService.ts new file mode 100644 index 000000000000..49686705929d --- /dev/null +++ b/packages/backend/src/services/MfmService.ts @@ -0,0 +1,384 @@ +import { URL } from 'node:url'; +import { Inject, Injectable } from '@nestjs/common'; +import * as parse5 from 'parse5'; +import { JSDOM } from 'jsdom'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Users } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import { intersperse } from '@/prelude/array.js'; +import type { IMentionedRemoteUsers } from '@/models/entities/note.js'; +import * as TreeAdapter from '../../node_modules/parse5/dist/tree-adapters/default.js'; +import type * as mfm from 'mfm-js'; + +const treeAdapter = TreeAdapter.defaultTreeAdapter; + +const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/; +const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/; + +@Injectable() +export class MfmService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + ) { + } + + public fromHtml(html: string, hashtagNames?: string[]): string { + // some AP servers like Pixelfed use br tags as well as newlines + html = html.replace(/\r?\n/gi, '\n'); + + const dom = parse5.parseFragment(html); + + let text = ''; + + for (const n of dom.childNodes) { + analyze(n); + } + + return text.trim(); + + function getText(node: TreeAdapter.Node): string { + if (treeAdapter.isTextNode(node)) return node.value; + if (!treeAdapter.isElementNode(node)) return ''; + if (node.nodeName === 'br') return '\n'; + + if (node.childNodes) { + return node.childNodes.map(n => getText(n)).join(''); + } + + return ''; + } + + function appendChildren(childNodes: TreeAdapter.ChildNode[]): void { + if (childNodes) { + for (const n of childNodes) { + analyze(n); + } + } + } + + function analyze(node: TreeAdapter.Node) { + if (treeAdapter.isTextNode(node)) { + text += node.value; + return; + } + + // Skip comment or document type node + if (!treeAdapter.isElementNode(node)) return; + + switch (node.nodeName) { + case 'br': { + text += '\n'; + break; + } + + case 'a': + { + const txt = getText(node); + const rel = node.attrs.find(x => x.name === 'rel'); + const href = node.attrs.find(x => x.name === 'href'); + + // ハッシュタグ + if (hashtagNames && href && hashtagNames.map(x => x.toLowerCase()).includes(txt.toLowerCase())) { + text += txt; + // メンション + } else if (txt.startsWith('@') && !(rel && rel.value.match(/^me /))) { + const part = txt.split('@'); + + if (part.length === 2 && href) { + //#region ホスト名部分が省略されているので復元する + const acct = `${txt}@${(new URL(href.value)).hostname}`; + text += acct; + //#endregion + } else if (part.length === 3) { + text += txt; + } + // その他 + } else { + const generateLink = () => { + if (!href && !txt) { + return ''; + } + if (!href) { + return txt; + } + if (!txt || txt === href.value) { // #6383: Missing text node + if (href.value.match(urlRegexFull)) { + return href.value; + } else { + return `<${href.value}>`; + } + } + if (href.value.match(urlRegex) && !href.value.match(urlRegexFull)) { + return `[${txt}](<${href.value}>)`; // #6846 + } else { + return `[${txt}](${href.value})`; + } + }; + + text += generateLink(); + } + break; + } + + case 'h1': + { + text += '【'; + appendChildren(node.childNodes); + text += '】\n'; + break; + } + + case 'b': + case 'strong': + { + text += '**'; + appendChildren(node.childNodes); + text += '**'; + break; + } + + case 'small': + { + text += ''; + appendChildren(node.childNodes); + text += ''; + break; + } + + case 's': + case 'del': + { + text += '~~'; + appendChildren(node.childNodes); + text += '~~'; + break; + } + + case 'i': + case 'em': + { + text += ''; + appendChildren(node.childNodes); + text += ''; + break; + } + + // block code (
)
+				case 'pre': {
+					if (node.childNodes.length === 1 && node.childNodes[0].nodeName === 'code') {
+						text += '\n```\n';
+						text += getText(node.childNodes[0]);
+						text += '\n```\n';
+					} else {
+						appendChildren(node.childNodes);
+					}
+					break;
+				}
+	
+				// inline code ()
+				case 'code': {
+					text += '`';
+					appendChildren(node.childNodes);
+					text += '`';
+					break;
+				}
+	
+				case 'blockquote': {
+					const t = getText(node);
+					if (t) {
+						text += '\n> ';
+						text += t.split('\n').join('\n> ');
+					}
+					break;
+				}
+	
+				case 'p':
+				case 'h2':
+				case 'h3':
+				case 'h4':
+				case 'h5':
+				case 'h6':
+				{
+					text += '\n\n';
+					appendChildren(node.childNodes);
+					break;
+				}
+	
+				// other block elements
+				case 'div':
+				case 'header':
+				case 'footer':
+				case 'article':
+				case 'li':
+				case 'dt':
+				case 'dd':
+				{
+					text += '\n';
+					appendChildren(node.childNodes);
+					break;
+				}
+	
+				default:	// includes inline elements
+				{
+					appendChildren(node.childNodes);
+					break;
+				}
+			}
+		}
+	}
+
+	public toHtml(nodes: mfm.MfmNode[] | null, mentionedRemoteUsers: IMentionedRemoteUsers = []) {
+		if (nodes == null) {
+			return null;
+		}
+	
+		const { window } = new JSDOM('');
+	
+		const doc = window.document;
+	
+		function appendChildren(children: mfm.MfmNode[], targetElement: any): void {
+			if (children) {
+				for (const child of children.map(x => (handlers as any)[x.type](x))) targetElement.appendChild(child);
+			}
+		}
+	
+		const handlers: { [K in mfm.MfmNode['type']]: (node: mfm.NodeType) => any } = {
+			bold: (node) => {
+				const el = doc.createElement('b');
+				appendChildren(node.children, el);
+				return el;
+			},
+	
+			small: (node) => {
+				const el = doc.createElement('small');
+				appendChildren(node.children, el);
+				return el;
+			},
+	
+			strike: (node) => {
+				const el = doc.createElement('del');
+				appendChildren(node.children, el);
+				return el;
+			},
+	
+			italic: (node) => {
+				const el = doc.createElement('i');
+				appendChildren(node.children, el);
+				return el;
+			},
+	
+			fn: (node) => {
+				const el = doc.createElement('i');
+				appendChildren(node.children, el);
+				return el;
+			},
+	
+			blockCode: (node) => {
+				const pre = doc.createElement('pre');
+				const inner = doc.createElement('code');
+				inner.textContent = node.props.code;
+				pre.appendChild(inner);
+				return pre;
+			},
+	
+			center: (node) => {
+				const el = doc.createElement('div');
+				appendChildren(node.children, el);
+				return el;
+			},
+	
+			emojiCode: (node) => {
+				return doc.createTextNode(`\u200B:${node.props.name}:\u200B`);
+			},
+	
+			unicodeEmoji: (node) => {
+				return doc.createTextNode(node.props.emoji);
+			},
+	
+			hashtag: (node) => {
+				const a = doc.createElement('a');
+				a.href = `${this.config.url}/tags/${node.props.hashtag}`;
+				a.textContent = `#${node.props.hashtag}`;
+				a.setAttribute('rel', 'tag');
+				return a;
+			},
+	
+			inlineCode: (node) => {
+				const el = doc.createElement('code');
+				el.textContent = node.props.code;
+				return el;
+			},
+	
+			mathInline: (node) => {
+				const el = doc.createElement('code');
+				el.textContent = node.props.formula;
+				return el;
+			},
+	
+			mathBlock: (node) => {
+				const el = doc.createElement('code');
+				el.textContent = node.props.formula;
+				return el;
+			},
+	
+			link: (node) => {
+				const a = doc.createElement('a');
+				a.href = node.props.url;
+				appendChildren(node.children, a);
+				return a;
+			},
+	
+			mention: (node) => {
+				const a = doc.createElement('a');
+				const { username, host, acct } = node.props;
+				const remoteUserInfo = mentionedRemoteUsers.find(remoteUser => remoteUser.username === username && remoteUser.host === host);
+				a.href = remoteUserInfo ? (remoteUserInfo.url ? remoteUserInfo.url : remoteUserInfo.uri) : `${this.config.url}/${acct}`;
+				a.className = 'u-url mention';
+				a.textContent = acct;
+				return a;
+			},
+	
+			quote: (node) => {
+				const el = doc.createElement('blockquote');
+				appendChildren(node.children, el);
+				return el;
+			},
+	
+			text: (node) => {
+				const el = doc.createElement('span');
+				const nodes = node.props.text.split(/\r\n|\r|\n/).map(x => doc.createTextNode(x));
+	
+				for (const x of intersperse('br', nodes)) {
+					el.appendChild(x === 'br' ? doc.createElement('br') : x);
+				}
+	
+				return el;
+			},
+	
+			url: (node) => {
+				const a = doc.createElement('a');
+				a.href = node.props.url;
+				a.textContent = node.props.url;
+				return a;
+			},
+	
+			search: (node) => {
+				const a = doc.createElement('a');
+				a.href = `https://www.google.com/search?q=${node.props.query}`;
+				a.textContent = node.props.content;
+				return a;
+			},
+	
+			plain: (node) => {
+				const el = doc.createElement('span');
+				appendChildren(node.children, el);
+				return el;
+			},
+		};
+	
+		appendChildren(nodes, doc.body);
+	
+		return `

${doc.body.innerHTML}

`; + } +} diff --git a/packages/backend/src/services/remote/activitypub/ApRendererService.ts b/packages/backend/src/services/remote/activitypub/ApRendererService.ts index 304ccf4c4b01..8efd87240101 100644 --- a/packages/backend/src/services/remote/activitypub/ApRendererService.ts +++ b/packages/backend/src/services/remote/activitypub/ApRendererService.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, IsNull } from 'typeorm'; import { v4 as uuid } from 'uuid'; +import * as mfm from 'mfm-js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Polls , DriveFiles, Emojis, Notes, Users } from '@/models/index.js'; - import type { Config } from '@/config/types.js'; import type { ILocalUser, IRemoteUser, User } from '@/models/entities/user.js'; import type { IMentionedRemoteUsers, Note } from '@/models/entities/note.js'; @@ -16,6 +16,7 @@ import type { Poll } from '@/models/entities/poll.js'; import type { MessagingMessage } from '@/models/entities/messaging-message.js'; import type { PollVote } from '@/models/entities/poll-vote.js'; import type { UserKeypairStoreService } from '@/services/UserKeypairStoreService.js'; +import type { MfmService } from '@/services/MfmService.js'; import { LdSignature } from './misc/ld-signature.js'; import type { IActivity } from './type.js'; import type { IIdentifier } from './models/identifier.js'; @@ -42,6 +43,7 @@ export class ApRendererService { private pollsRepository: typeof Polls, private userKeypairStoreService: UserKeypairStoreService, + private mfmService: MfmService, ) { } @@ -348,7 +350,7 @@ export class ApRendererService { const summary = note.cw === '' ? String.fromCharCode(0x200B) : note.cw; - const content = toHtml(Object.assign({}, note, { + const content = this.mfmService.toHtml(Object.assign({}, note, { text: apText, })); @@ -363,7 +365,7 @@ export class ApRendererService { const asPoll = poll ? { type: 'Question', - content: toHtml(Object.assign({}, note, { + content: this.mfmService.toHtml(Object.assign({}, note, { text: text, })), [poll.expiresAt && poll.expiresAt < new Date() ? 'closed' : 'endTime']: poll.expiresAt, @@ -460,7 +462,7 @@ export class ApRendererService { url: `${this.config.url}/@${user.username}`, preferredUsername: user.username, name: user.name, - summary: profile.description ? toHtml(mfm.parse(profile.description)) : null, + summary: profile.description ? this.mfmService.toHtml(mfm.parse(profile.description)) : null, icon: avatar ? this.renderImage(avatar) : null, image: banner ? this.renderImage(banner) : null, tag, From 1fc76ec99bbd4e74284afb7704cd7d2d21796bc8 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 03:12:43 +0900 Subject: [PATCH 047/180] wip --- .../backend/src/services/NoteDeleteService.ts | 2 +- .../remote/activitypub/ApInboxService.ts | 687 ++++++++++++++++++ .../activitypub/kernel/accept/follow.ts | 29 - .../remote/activitypub/kernel/accept/index.ts | 24 - .../remote/activitypub/kernel/add/index.ts | 23 - .../activitypub/kernel/announce/index.ts | 19 - .../activitypub/kernel/announce/note.ts | 72 -- .../remote/activitypub/kernel/block/index.ts | 23 - .../remote/activitypub/kernel/create/index.ts | 43 -- .../remote/activitypub/kernel/create/note.ts | 44 -- .../remote/activitypub/kernel/delete/actor.ts | 27 - .../remote/activitypub/kernel/delete/index.ts | 49 -- .../remote/activitypub/kernel/delete/note.ts | 41 -- .../remote/activitypub/kernel/flag/index.ts | 30 - .../remote/activitypub/kernel/follow.ts | 20 - .../remote/activitypub/kernel/index.ts | 74 -- .../remote/activitypub/kernel/like.ts | 21 - .../remote/activitypub/kernel/read.ts | 28 - .../activitypub/kernel/reject/follow.ts | 30 - .../remote/activitypub/kernel/reject/index.ts | 24 - .../remote/activitypub/kernel/remove/index.ts | 23 - .../remote/activitypub/kernel/undo/accept.ts | 27 - .../activitypub/kernel/undo/announce.ts | 18 - .../remote/activitypub/kernel/undo/block.ts | 21 - .../remote/activitypub/kernel/undo/follow.ts | 41 -- .../remote/activitypub/kernel/undo/index.ts | 36 - .../remote/activitypub/kernel/undo/like.ts | 21 - .../remote/activitypub/kernel/update/index.ts | 34 - 28 files changed, 688 insertions(+), 843 deletions(-) create mode 100644 packages/backend/src/services/remote/activitypub/ApInboxService.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/accept/follow.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/accept/index.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/add/index.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/announce/index.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/announce/note.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/block/index.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/create/index.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/create/note.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/delete/actor.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/delete/index.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/delete/note.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/flag/index.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/follow.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/index.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/like.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/read.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/reject/follow.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/reject/index.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/remove/index.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/undo/accept.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/undo/announce.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/undo/block.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/undo/follow.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/undo/index.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/undo/like.ts delete mode 100644 packages/backend/src/services/remote/activitypub/kernel/update/index.ts diff --git a/packages/backend/src/services/NoteDeleteService.ts b/packages/backend/src/services/NoteDeleteService.ts index ff63e6545204..8e388c1deb59 100644 --- a/packages/backend/src/services/NoteDeleteService.ts +++ b/packages/backend/src/services/NoteDeleteService.ts @@ -21,7 +21,7 @@ import type InstanceChart from '@/services/chart/charts/instance.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; @Injectable() -export class NoteCreateService { +export class NoteDeleteService { constructor( @Inject(DI_SYMBOLS.config) private config: Config, diff --git a/packages/backend/src/services/remote/activitypub/ApInboxService.ts b/packages/backend/src/services/remote/activitypub/ApInboxService.ts new file mode 100644 index 000000000000..379d3843f47b --- /dev/null +++ b/packages/backend/src/services/remote/activitypub/ApInboxService.ts @@ -0,0 +1,687 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { Users } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type { CacheableRemoteUser } from '@/models/entities/user.js'; +import type { UserFollowingService } from '@/services/UserFollowingService.js'; +import type { ReactionService } from '@/services/ReactionService.js'; +import type { RelayService } from '@/services/RelayService.js'; +import type { NotePiningService } from '@/services/NotePiningService.js'; +import type { UserBlockingService } from '@/services/UserBlockingService.js'; +import { StatusError } from '@/services/HttpRequestService.js'; +import type { NoteDeleteService } from '@/services/NoteDeleteService.js'; +import type { NoteCreateService } from '@/services/NoteCreateService.js'; +import { createNote, fetchNote } from './models/note.js'; +import { updatePerson } from './models/person.js'; +import { updateQuestion } from './models/question.js'; +import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IRead, IReject, IRemove, IUndo, IUpdate } from './type.js'; + +@Injectable() +export class ApInboxService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + private userFollowingService: UserFollowingService, + private reactionService: ReactionService, + private relayService: RelayService, + private notePiningService: NotePiningService, + private userBlockingService: UserBlockingService, + private noteCreateService: NoteCreateService, + private noteDeleteService: NoteDeleteService, + ) { + } + + public async performActivity(actor: CacheableRemoteUser, activity: IObject) { + if (isCollectionOrOrderedCollection(activity)) { + const resolver = new Resolver(); + for (const item of toArray(isCollection(activity) ? activity.items : activity.orderedItems)) { + const act = await resolver.resolve(item); + try { + await performOneActivity(actor, act); + } catch (err) { + if (err instanceof Error || typeof err === 'string') { + apLogger.error(err); + } + } + } + } else { + await performOneActivity(actor, activity); + } + } + + public async performOneActivity(actor: CacheableRemoteUser, activity: IObject): Promise { + if (actor.isSuspended) return; + + if (isCreate(activity)) { + await create(actor, activity); + } else if (isDelete(activity)) { + await performDeleteActivity(actor, activity); + } else if (isUpdate(activity)) { + await performUpdateActivity(actor, activity); + } else if (isRead(activity)) { + await performReadActivity(actor, activity); + } else if (isFollow(activity)) { + await follow(actor, activity); + } else if (isAccept(activity)) { + await accept(actor, activity); + } else if (isReject(activity)) { + await reject(actor, activity); + } else if (isAdd(activity)) { + await add(actor, activity).catch(err => apLogger.error(err)); + } else if (isRemove(activity)) { + await remove(actor, activity).catch(err => apLogger.error(err)); + } else if (isAnnounce(activity)) { + await announce(actor, activity); + } else if (isLike(activity)) { + await like(actor, activity); + } else if (isUndo(activity)) { + await undo(actor, activity); + } else if (isBlock(activity)) { + await block(actor, activity); + } else if (isFlag(activity)) { + await flag(actor, activity); + } else { + apLogger.warn(`unrecognized activity type: ${(activity as any).type}`); + } + } + + async #follow(actor: CacheableRemoteUser, activity: IFollow): Promise { + const dbResolver = new DbResolver(); + const followee = await dbResolver.getUserFromApId(activity.object); + + if (followee == null) { + return 'skip: followee not found'; + } + + if (followee.host != null) { + return 'skip: フォローしようとしているユーザーはローカルユーザーではありません'; + } + + await this.userFollowingService.follow(actor, followee, activity.id); + return 'ok'; + } + + async #like(actor: CacheableRemoteUser, activity: ILike): Promise { + const targetUri = getApId(activity.object); + + const note = await fetchNote(targetUri); + if (!note) return `skip: target note not found ${targetUri}`; + + await extractEmojis(activity.tag || [], actor.host).catch(() => null); + + return await this.reactionService.create(actor, note, activity._misskey_reaction || activity.content || activity.name).catch(e => { + if (e.id === '51c42bb4-931a-456b-bff7-e5a8a70dd298') { + return 'skip: already reacted'; + } else { + throw e; + } + }).then(() => 'ok'); + } + + async #read(actor: CacheableRemoteUser, activity: IRead): Promise { + const id = await getApId(activity.object); + + if (!isSelfHost(extractDbHost(id))) { + return `skip: Read to foreign host (${id})`; + } + + const messageId = id.split('/').pop(); + + const message = await MessagingMessages.findOneBy({ id: messageId }); + if (message == null) { + return 'skip: message not found'; + } + + if (actor.id !== message.recipientId) { + return 'skip: actor is not a message recipient'; + } + + await readUserMessagingMessage(message.recipientId!, message.userId, [message.id]); + return `ok: mark as read (${message.userId} => ${message.recipientId} ${message.id})`; + } + + async #accept(actor: CacheableRemoteUser, activity: IAccept): Promise { + const uri = activity.id || activity; + + apLogger.info(`Accept: ${uri}`); + + const resolver = new Resolver(); + + const object = await resolver.resolve(activity.object).catch(e => { + apLogger.error(`Resolution failed: ${e}`); + throw e; + }); + + if (isFollow(object)) return await this.#acceptFollow(actor, object); + + return `skip: Unknown Accept type: ${getApType(object)}`; + } + + async #acceptFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { + // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある + + const dbResolver = new DbResolver(); + const follower = await dbResolver.getUserFromApId(activity.actor); + + if (follower == null) { + return 'skip: follower not found'; + } + + if (follower.host != null) { + return 'skip: follower is not a local user'; + } + + // relay + const match = activity.id?.match(/follow-relay\/(\w+)/); + if (match) { + return await this.relayService.relayAccepted(match[1]); + } + + await this.userFollowingService.acceptFollowRequest(actor, follower); + return 'ok'; + } + + async #add(actor: CacheableRemoteUser, activity: IAdd): Promise { + if ('actor' in activity && actor.uri !== activity.actor) { + throw new Error('invalid actor'); + } + + if (activity.target == null) { + throw new Error('target is null'); + } + + if (activity.target === actor.featured) { + const note = await resolveNote(activity.object); + if (note == null) throw new Error('note not found'); + await this.notePiningService.addPinned(actor, note.id); + return; + } + + throw new Error(`unknown target: ${activity.target}`); + } + + async #announce(actor: CacheableRemoteUser, activity: IAnnounce): Promise { + const uri = getApId(activity); + + logger.info(`Announce: ${uri}`); + + const targetUri = getApId(activity.object); + + this.#announceNote(actor, activity, targetUri); + } + + async #announceNote(actor: CacheableRemoteUser, activity: IAnnounce, targetUri: string): Promise { + const uri = getApId(activity); + + if (actor.isSuspended) { + return; + } + + // アナウンス先をブロックしてたら中断 + const meta = await fetchMeta(); + if (meta.blockedHosts.includes(extractDbHost(uri))) return; + + const unlock = await getApLock(uri); + + try { + // 既に同じURIを持つものが登録されていないかチェック + const exist = await fetchNote(uri); + if (exist) { + return; + } + + // Announce対象をresolve + let renote; + try { + renote = await resolveNote(targetUri); + } catch (e) { + // 対象が4xxならスキップ + if (e instanceof StatusError) { + if (e.isClientError) { + logger.warn(`Ignored announce target ${targetUri} - ${e.statusCode}`); + return; + } + + logger.warn(`Error in announce target ${targetUri} - ${e.statusCode || e}`); + } + throw e; + } + + if (!await Notes.isVisibleForMe(renote, actor.id)) return 'skip: invalid actor for this activity'; + + logger.info(`Creating the (Re)Note: ${uri}`); + + const activityAudience = await parseAudience(actor, activity.to, activity.cc); + + await post(actor, { + createdAt: activity.published ? new Date(activity.published) : null, + renote, + visibility: activityAudience.visibility, + visibleUsers: activityAudience.visibleUsers, + uri, + }); + } finally { + unlock(); + } + } + + async #block(actor: CacheableRemoteUser, activity: IBlock): Promise { + // ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず + + const dbResolver = new DbResolver(); + const blockee = await dbResolver.getUserFromApId(activity.object); + + if (blockee == null) { + return 'skip: blockee not found'; + } + + if (blockee.host != null) { + return 'skip: ブロックしようとしているユーザーはローカルユーザーではありません'; + } + + await this.userBlockingService.block(await Users.findOneByOrFail({ id: actor.id }), await Users.findOneByOrFail({ id: blockee.id })); + return 'ok'; + } + + async #create(actor: CacheableRemoteUser, activity: ICreate): Promise { + const uri = getApId(activity); + + logger.info(`Create: ${uri}`); + + // copy audiences between activity <=> object. + if (typeof activity.object === 'object') { + const to = unique(concat([toArray(activity.to), toArray(activity.object.to)])); + const cc = unique(concat([toArray(activity.cc), toArray(activity.object.cc)])); + + activity.to = to; + activity.cc = cc; + activity.object.to = to; + activity.object.cc = cc; + } + + // If there is no attributedTo, use Activity actor. + if (typeof activity.object === 'object' && !activity.object.attributedTo) { + activity.object.attributedTo = activity.actor; + } + + const resolver = new Resolver(); + + const object = await resolver.resolve(activity.object).catch(e => { + logger.error(`Resolution failed: ${e}`); + throw e; + }); + + if (isPost(object)) { + this.#createNote(resolver, actor, object, false, activity); + } else { + logger.warn(`Unknown type: ${getApType(object)}`); + } + } + + async #createNote(resolver: Resolver, actor: CacheableRemoteUser, note: IObject, silent = false, activity?: ICreate): Promise { + const uri = getApId(note); + + if (typeof note === 'object') { + if (actor.uri !== note.attributedTo) { + return 'skip: actor.uri !== note.attributedTo'; + } + + if (typeof note.id === 'string') { + if (extractDbHost(actor.uri) !== extractDbHost(note.id)) { + return 'skip: host in actor.uri !== note.id'; + } + } + } + + const unlock = await getApLock(uri); + + try { + const exist = await fetchNote(note); + if (exist) return 'skip: note exists'; + + await createNote(note, resolver, silent); + return 'ok'; + } catch (e) { + if (e instanceof StatusError && e.isClientError) { + return `skip ${e.statusCode}`; + } else { + throw e; + } + } finally { + unlock(); + } + } + + async #delete(actor: CacheableRemoteUser, activity: IDelete): Promise { + if ('actor' in activity && actor.uri !== activity.actor) { + throw new Error('invalid actor'); + } + + // 削除対象objectのtype + let formerType: string | undefined; + + if (typeof activity.object === 'string') { + // typeが不明だけど、どうせ消えてるのでremote resolveしない + formerType = undefined; + } else { + const object = activity.object as IObject; + if (isTombstone(object)) { + formerType = toSingle(object.formerType); + } else { + formerType = toSingle(object.type); + } + } + + const uri = getApId(activity.object); + + // type不明でもactorとobjectが同じならばそれはPersonに違いない + if (!formerType && actor.uri === uri) { + formerType = 'Person'; + } + + // それでもなかったらおそらくNote + if (!formerType) { + formerType = 'Note'; + } + + if (validPost.includes(formerType)) { + return await this.#deleteNote(actor, uri); + } else if (validActor.includes(formerType)) { + return await this.#deleteActor(actor, uri); + } else { + return `Unknown type ${formerType}`; + } + } + + async #deleteActor(actor: CacheableRemoteUser, uri: string): Promise { + logger.info(`Deleting the Actor: ${uri}`); + + if (actor.uri !== uri) { + return `skip: delete actor ${actor.uri} !== ${uri}`; + } + + const user = await Users.findOneByOrFail({ id: actor.id }); + if (user.isDeleted) { + logger.info('skip: already deleted'); + } + + const job = await createDeleteAccountJob(actor); + + await Users.update(actor.id, { + isDeleted: true, + }); + + return `ok: queued ${job.name} ${job.id}`; + } + + async #deleteNote(actor: CacheableRemoteUser, uri: string): Promise { + logger.info(`Deleting the Note: ${uri}`); + + const unlock = await getApLock(uri); + + try { + const dbResolver = new DbResolver(); + const note = await dbResolver.getNoteFromApId(uri); + + if (note == null) { + const message = await dbResolver.getMessageFromApId(uri); + if (message == null) return 'message not found'; + + if (message.userId !== actor.id) { + return '投稿を削除しようとしているユーザーは投稿の作成者ではありません'; + } + + await deleteMessage(message); + + return 'ok: message deleted'; + } + + if (note.userId !== actor.id) { + return '投稿を削除しようとしているユーザーは投稿の作成者ではありません'; + } + + await this.noteDeleteService.delete(actor, note); + return 'ok: note deleted'; + } finally { + unlock(); + } + } + + async #flag(actor: CacheableRemoteUser, activity: IFlag): Promise { + // objectは `(User|Note) | (User|Note)[]` だけど、全パターンDBスキーマと対応させられないので + // 対象ユーザーは一番最初のユーザー として あとはコメントとして格納する + const uris = getApIds(activity.object); + + const userIds = uris.filter(uri => uri.startsWith(config.url + '/users/')).map(uri => uri.split('/').pop()!); + const users = await Users.findBy({ + id: In(userIds), + }); + if (users.length < 1) return 'skip'; + + await AbuseUserReports.insert({ + id: genId(), + createdAt: new Date(), + targetUserId: users[0].id, + targetUserHost: users[0].host, + reporterId: actor.id, + reporterHost: actor.host, + comment: `${activity.content}\n${JSON.stringify(uris, null, 2)}`, + }); + + return 'ok'; + } + + async #reject(actor: CacheableRemoteUser, activity: IReject): Promise { + const uri = activity.id || activity; + + logger.info(`Reject: ${uri}`); + + const resolver = new Resolver(); + + const object = await resolver.resolve(activity.object).catch(e => { + logger.error(`Resolution failed: ${e}`); + throw e; + }); + + if (isFollow(object)) return await this.#rejectFollow(actor, object); + + return `skip: Unknown Reject type: ${getApType(object)}`; + } + + async #rejectFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { + // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある + + const dbResolver = new DbResolver(); + const follower = await dbResolver.getUserFromApId(activity.actor); + + if (follower == null) { + return 'skip: follower not found'; + } + + if (!Users.isLocalUser(follower)) { + return 'skip: follower is not a local user'; + } + + // relay + const match = activity.id?.match(/follow-relay\/(\w+)/); + if (match) { + return await this.relayService.relayRejected(match[1]); + } + + await this.userFollowingService.remoteReject(actor, follower); + return 'ok'; + } + + async #remove(actor: CacheableRemoteUser, activity: IRemove): Promise { + if ('actor' in activity && actor.uri !== activity.actor) { + throw new Error('invalid actor'); + } + + if (activity.target == null) { + throw new Error('target is null'); + } + + if (activity.target === actor.featured) { + const note = await resolveNote(activity.object); + if (note == null) throw new Error('note not found'); + await this.notePiningService.removePinned(actor, note.id); + return; + } + + throw new Error(`unknown target: ${activity.target}`); + } + + async #undo(actor: CacheableRemoteUser, activity: IUndo): Promise { + if ('actor' in activity && actor.uri !== activity.actor) { + throw new Error('invalid actor'); + } + + const uri = activity.id || activity; + + logger.info(`Undo: ${uri}`); + + const resolver = new Resolver(); + + const object = await resolver.resolve(activity.object).catch(e => { + logger.error(`Resolution failed: ${e}`); + throw e; + }); + + if (isFollow(object)) return await this.#undoFollow(actor, object); + if (isBlock(object)) return await this.#undoBlock(actor, object); + if (isLike(object)) return await this.#undoLike(actor, object); + if (isAnnounce(object)) return await this.#undoAnnounce(actor, object); + if (isAccept(object)) return await this.#undoAccept(actor, object); + + return `skip: unknown object type ${getApType(object)}`; + } + + async #undoAccept(actor: CacheableRemoteUser, activity: IAccept): Promise { + const dbResolver = new DbResolver(); + + const follower = await dbResolver.getUserFromApId(activity.object); + if (follower == null) { + return 'skip: follower not found'; + } + + const following = await Followings.findOneBy({ + followerId: follower.id, + followeeId: actor.id, + }); + + if (following) { + await this.userFollowingService.unfollow(follower, actor); + return 'ok: unfollowed'; + } + + return 'skip: フォローされていない'; + } + + async #undoAnnounce(actor: CacheableRemoteUser, activity: IAnnounce): Promise { + const uri = getApId(activity); + + const note = await Notes.findOneBy({ + uri, + userId: actor.id, + }); + + if (!note) return 'skip: no such Announce'; + + await this.noteDeleteService.delete(actor, note); + return 'ok: deleted'; + } + + async #undoBlock(actor: CacheableRemoteUser, activity: IBlock): Promise { + const dbResolver = new DbResolver(); + const blockee = await dbResolver.getUserFromApId(activity.object); + + if (blockee == null) { + return 'skip: blockee not found'; + } + + if (blockee.host != null) { + return 'skip: ブロック解除しようとしているユーザーはローカルユーザーではありません'; + } + + await this.userBlockingService.unblock(await Users.findOneByOrFail({ id: actor.id }), blockee); + return 'ok'; + } + + async #undoFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { + const dbResolver = new DbResolver(); + + const followee = await dbResolver.getUserFromApId(activity.object); + if (followee == null) { + return 'skip: followee not found'; + } + + if (followee.host != null) { + return 'skip: フォロー解除しようとしているユーザーはローカルユーザーではありません'; + } + + const req = await FollowRequests.findOneBy({ + followerId: actor.id, + followeeId: followee.id, + }); + + const following = await Followings.findOneBy({ + followerId: actor.id, + followeeId: followee.id, + }); + + if (req) { + await this.userFollowingService.cancelFollowRequest(followee, actor); + return 'ok: follow request canceled'; + } + + if (following) { + await this.userFollowingService.unfollow(actor, followee); + return 'ok: unfollowed'; + } + + return 'skip: リクエストもフォローもされていない'; + } + + async #undoLike(actor: CacheableRemoteUser, activity: ILike): Promise { + const targetUri = getApId(activity.object); + + const note = await fetchNote(targetUri); + if (!note) return `skip: target note not found ${targetUri}`; + + await this.reactionService.delete(actor, note).catch(e => { + if (e.id === '60527ec9-b4cb-4a88-a6bd-32d3ad26817d') return; + throw e; + }); + + return 'ok'; + } + + async #update(actor: CacheableRemoteUser, activity: IUpdate): Promise { + if ('actor' in activity && actor.uri !== activity.actor) { + return 'skip: invalid actor'; + } + + apLogger.debug('Update'); + + const resolver = new Resolver(); + + const object = await resolver.resolve(activity.object).catch(e => { + apLogger.error(`Resolution failed: ${e}`); + throw e; + }); + + if (isActor(object)) { + await updatePerson(actor.uri!, resolver, object); + return 'ok: Person updated'; + } else if (getApType(object) === 'Question') { + await updateQuestion(object).catch(e => console.log(e)); + return 'ok: Question updated'; + } else { + return `skip: Unknown type: ${getApType(object)}`; + } + } +} diff --git a/packages/backend/src/services/remote/activitypub/kernel/accept/follow.ts b/packages/backend/src/services/remote/activitypub/kernel/accept/follow.ts deleted file mode 100644 index 4350ef1333a3..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/accept/follow.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import accept from '@/services/following/requests/accept.js'; -import { IFollow } from '../../type.js'; -import DbResolver from '../../db-resolver.js'; -import { relayAccepted } from '@/services/relay.js'; - -export default async (actor: CacheableRemoteUser, activity: IFollow): Promise => { - // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある - - const dbResolver = new DbResolver(); - const follower = await dbResolver.getUserFromApId(activity.actor); - - if (follower == null) { - return `skip: follower not found`; - } - - if (follower.host != null) { - return `skip: follower is not a local user`; - } - - // relay - const match = activity.id?.match(/follow-relay\/(\w+)/); - if (match) { - return await relayAccepted(match[1]); - } - - await accept(actor, follower); - return `ok`; -}; diff --git a/packages/backend/src/services/remote/activitypub/kernel/accept/index.ts b/packages/backend/src/services/remote/activitypub/kernel/accept/index.ts deleted file mode 100644 index 78ef75ade392..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/accept/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import Resolver from '../../resolver.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import acceptFollow from './follow.js'; -import { IAccept, isFollow, getApType } from '../../type.js'; -import { apLogger } from '../../logger.js'; - -const logger = apLogger; - -export default async (actor: CacheableRemoteUser, activity: IAccept): Promise => { - const uri = activity.id || activity; - - logger.info(`Accept: ${uri}`); - - const resolver = new Resolver(); - - const object = await resolver.resolve(activity.object).catch(e => { - logger.error(`Resolution failed: ${e}`); - throw e; - }); - - if (isFollow(object)) return await acceptFollow(actor, object); - - return `skip: Unknown Accept type: ${getApType(object)}`; -}; diff --git a/packages/backend/src/services/remote/activitypub/kernel/add/index.ts b/packages/backend/src/services/remote/activitypub/kernel/add/index.ts deleted file mode 100644 index c813414f93bd..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/add/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import { IAdd } from '../../type.js'; -import { resolveNote } from '../../models/note.js'; -import { addPinned } from '@/services/i/pin.js'; - -export default async (actor: CacheableRemoteUser, activity: IAdd): Promise => { - if ('actor' in activity && actor.uri !== activity.actor) { - throw new Error('invalid actor'); - } - - if (activity.target == null) { - throw new Error('target is null'); - } - - if (activity.target === actor.featured) { - const note = await resolveNote(activity.object); - if (note == null) throw new Error('note not found'); - await addPinned(actor, note.id); - return; - } - - throw new Error(`unknown target: ${activity.target}`); -}; diff --git a/packages/backend/src/services/remote/activitypub/kernel/announce/index.ts b/packages/backend/src/services/remote/activitypub/kernel/announce/index.ts deleted file mode 100644 index ae7e507c99fc..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/announce/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -import Resolver from '../../resolver.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import announceNote from './note.js'; -import { IAnnounce, getApId } from '../../type.js'; -import { apLogger } from '../../logger.js'; - -const logger = apLogger; - -export default async (actor: CacheableRemoteUser, activity: IAnnounce): Promise => { - const uri = getApId(activity); - - logger.info(`Announce: ${uri}`); - - const resolver = new Resolver(); - - const targetUri = getApId(activity.object); - - announceNote(resolver, actor, activity, targetUri); -}; diff --git a/packages/backend/src/services/remote/activitypub/kernel/announce/note.ts b/packages/backend/src/services/remote/activitypub/kernel/announce/note.ts deleted file mode 100644 index 759cb4ae8396..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/announce/note.ts +++ /dev/null @@ -1,72 +0,0 @@ -import Resolver from '../../resolver.js'; -import post from '@/services/note/create.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import { IAnnounce, getApId } from '../../type.js'; -import { fetchNote, resolveNote } from '../../models/note.js'; -import { apLogger } from '../../logger.js'; -import { extractDbHost } from '@/misc/convert-host.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import { getApLock } from '@/misc/app-lock.js'; -import { parseAudience } from '../../audience.js'; -import { StatusError } from '@/misc/fetch.js'; -import { Notes } from '@/models/index.js'; - -const logger = apLogger; - -/** - * アナウンスアクティビティを捌きます - */ -export default async function(resolver: Resolver, actor: CacheableRemoteUser, activity: IAnnounce, targetUri: string): Promise { - const uri = getApId(activity); - - if (actor.isSuspended) { - return; - } - - // アナウンス先をブロックしてたら中断 - const meta = await fetchMeta(); - if (meta.blockedHosts.includes(extractDbHost(uri))) return; - - const unlock = await getApLock(uri); - - try { - // 既に同じURIを持つものが登録されていないかチェック - const exist = await fetchNote(uri); - if (exist) { - return; - } - - // Announce対象をresolve - let renote; - try { - renote = await resolveNote(targetUri); - } catch (e) { - // 対象が4xxならスキップ - if (e instanceof StatusError) { - if (e.isClientError) { - logger.warn(`Ignored announce target ${targetUri} - ${e.statusCode}`); - return; - } - - logger.warn(`Error in announce target ${targetUri} - ${e.statusCode || e}`); - } - throw e; - } - - if (!await Notes.isVisibleForMe(renote, actor.id)) return 'skip: invalid actor for this activity'; - - logger.info(`Creating the (Re)Note: ${uri}`); - - const activityAudience = await parseAudience(actor, activity.to, activity.cc); - - await post(actor, { - createdAt: activity.published ? new Date(activity.published) : null, - renote, - visibility: activityAudience.visibility, - visibleUsers: activityAudience.visibleUsers, - uri, - }); - } finally { - unlock(); - } -} diff --git a/packages/backend/src/services/remote/activitypub/kernel/block/index.ts b/packages/backend/src/services/remote/activitypub/kernel/block/index.ts deleted file mode 100644 index 5e230ad7b7be..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/block/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { IBlock } from '../../type.js'; -import block from '@/services/blocking/create.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import DbResolver from '../../db-resolver.js'; -import { Users } from '@/models/index.js'; - -export default async (actor: CacheableRemoteUser, activity: IBlock): Promise => { - // ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず - - const dbResolver = new DbResolver(); - const blockee = await dbResolver.getUserFromApId(activity.object); - - if (blockee == null) { - return `skip: blockee not found`; - } - - if (blockee.host != null) { - return `skip: ブロックしようとしているユーザーはローカルユーザーではありません`; - } - - await block(await Users.findOneByOrFail({ id: actor.id }), await Users.findOneByOrFail({ id: blockee.id })); - return `ok`; -}; diff --git a/packages/backend/src/services/remote/activitypub/kernel/create/index.ts b/packages/backend/src/services/remote/activitypub/kernel/create/index.ts deleted file mode 100644 index c253f9f66779..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/create/index.ts +++ /dev/null @@ -1,43 +0,0 @@ -import Resolver from '../../resolver.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import createNote from './note.js'; -import { ICreate, getApId, isPost, getApType } from '../../type.js'; -import { apLogger } from '../../logger.js'; -import { toArray, concat, unique } from '@/prelude/array.js'; - -const logger = apLogger; - -export default async (actor: CacheableRemoteUser, activity: ICreate): Promise => { - const uri = getApId(activity); - - logger.info(`Create: ${uri}`); - - // copy audiences between activity <=> object. - if (typeof activity.object === 'object') { - const to = unique(concat([toArray(activity.to), toArray(activity.object.to)])); - const cc = unique(concat([toArray(activity.cc), toArray(activity.object.cc)])); - - activity.to = to; - activity.cc = cc; - activity.object.to = to; - activity.object.cc = cc; - } - - // If there is no attributedTo, use Activity actor. - if (typeof activity.object === 'object' && !activity.object.attributedTo) { - activity.object.attributedTo = activity.actor; - } - - const resolver = new Resolver(); - - const object = await resolver.resolve(activity.object).catch(e => { - logger.error(`Resolution failed: ${e}`); - throw e; - }); - - if (isPost(object)) { - createNote(resolver, actor, object, false, activity); - } else { - logger.warn(`Unknown type: ${getApType(object)}`); - } -}; diff --git a/packages/backend/src/services/remote/activitypub/kernel/create/note.ts b/packages/backend/src/services/remote/activitypub/kernel/create/note.ts deleted file mode 100644 index f8dabe06e292..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/create/note.ts +++ /dev/null @@ -1,44 +0,0 @@ -import Resolver from '../../resolver.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import { createNote, fetchNote } from '../../models/note.js'; -import { getApId, IObject, ICreate } from '../../type.js'; -import { getApLock } from '@/misc/app-lock.js'; -import { extractDbHost } from '@/misc/convert-host.js'; -import { StatusError } from '@/misc/fetch.js'; - -/** - * 投稿作成アクティビティを捌きます - */ -export default async function(resolver: Resolver, actor: CacheableRemoteUser, note: IObject, silent = false, activity?: ICreate): Promise { - const uri = getApId(note); - - if (typeof note === 'object') { - if (actor.uri !== note.attributedTo) { - return `skip: actor.uri !== note.attributedTo`; - } - - if (typeof note.id === 'string') { - if (extractDbHost(actor.uri) !== extractDbHost(note.id)) { - return `skip: host in actor.uri !== note.id`; - } - } - } - - const unlock = await getApLock(uri); - - try { - const exist = await fetchNote(note); - if (exist) return 'skip: note exists'; - - await createNote(note, resolver, silent); - return 'ok'; - } catch (e) { - if (e instanceof StatusError && e.isClientError) { - return `skip ${e.statusCode}`; - } else { - throw e; - } - } finally { - unlock(); - } -} diff --git a/packages/backend/src/services/remote/activitypub/kernel/delete/actor.ts b/packages/backend/src/services/remote/activitypub/kernel/delete/actor.ts deleted file mode 100644 index 1f94df033dd2..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/delete/actor.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { apLogger } from '../../logger.js'; -import { createDeleteAccountJob } from '@/queue/index.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import { Users } from '@/models/index.js'; - -const logger = apLogger; - -export async function deleteActor(actor: CacheableRemoteUser, uri: string): Promise { - logger.info(`Deleting the Actor: ${uri}`); - - if (actor.uri !== uri) { - return `skip: delete actor ${actor.uri} !== ${uri}`; - } - - const user = await Users.findOneByOrFail({ id: actor.id }); - if (user.isDeleted) { - logger.info(`skip: already deleted`); - } - - const job = await createDeleteAccountJob(actor); - - await Users.update(actor.id, { - isDeleted: true, - }); - - return `ok: queued ${job.name} ${job.id}`; -} diff --git a/packages/backend/src/services/remote/activitypub/kernel/delete/index.ts b/packages/backend/src/services/remote/activitypub/kernel/delete/index.ts deleted file mode 100644 index c7064f553bd5..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/delete/index.ts +++ /dev/null @@ -1,49 +0,0 @@ -import deleteNote from './note.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import { IDelete, getApId, isTombstone, IObject, validPost, validActor } from '../../type.js'; -import { toSingle } from '@/prelude/array.js'; -import { deleteActor } from './actor.js'; - -/** - * 削除アクティビティを捌きます - */ -export default async (actor: CacheableRemoteUser, activity: IDelete): Promise => { - if ('actor' in activity && actor.uri !== activity.actor) { - throw new Error('invalid actor'); - } - - // 削除対象objectのtype - let formerType: string | undefined; - - if (typeof activity.object === 'string') { - // typeが不明だけど、どうせ消えてるのでremote resolveしない - formerType = undefined; - } else { - const object = activity.object as IObject; - if (isTombstone(object)) { - formerType = toSingle(object.formerType); - } else { - formerType = toSingle(object.type); - } - } - - const uri = getApId(activity.object); - - // type不明でもactorとobjectが同じならばそれはPersonに違いない - if (!formerType && actor.uri === uri) { - formerType = 'Person'; - } - - // それでもなかったらおそらくNote - if (!formerType) { - formerType = 'Note'; - } - - if (validPost.includes(formerType)) { - return await deleteNote(actor, uri); - } else if (validActor.includes(formerType)) { - return await deleteActor(actor, uri); - } else { - return `Unknown type ${formerType}`; - } -}; diff --git a/packages/backend/src/services/remote/activitypub/kernel/delete/note.ts b/packages/backend/src/services/remote/activitypub/kernel/delete/note.ts deleted file mode 100644 index 1f44c35562af..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/delete/note.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import deleteNode from '@/services/note/delete.js'; -import { apLogger } from '../../logger.js'; -import DbResolver from '../../db-resolver.js'; -import { getApLock } from '@/misc/app-lock.js'; -import { deleteMessage } from '@/services/messages/delete.js'; - -const logger = apLogger; - -export default async function(actor: CacheableRemoteUser, uri: string): Promise { - logger.info(`Deleting the Note: ${uri}`); - - const unlock = await getApLock(uri); - - try { - const dbResolver = new DbResolver(); - const note = await dbResolver.getNoteFromApId(uri); - - if (note == null) { - const message = await dbResolver.getMessageFromApId(uri); - if (message == null) return 'message not found'; - - if (message.userId !== actor.id) { - return '投稿を削除しようとしているユーザーは投稿の作成者ではありません'; - } - - await deleteMessage(message); - - return 'ok: message deleted'; - } - - if (note.userId !== actor.id) { - return '投稿を削除しようとしているユーザーは投稿の作成者ではありません'; - } - - await deleteNode(actor, note); - return 'ok: note deleted'; - } finally { - unlock(); - } -} diff --git a/packages/backend/src/services/remote/activitypub/kernel/flag/index.ts b/packages/backend/src/services/remote/activitypub/kernel/flag/index.ts deleted file mode 100644 index aa2f1f53620a..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/flag/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import config from '@/config/index.js'; -import { IFlag, getApIds } from '../../type.js'; -import { AbuseUserReports, Users } from '@/models/index.js'; -import { In } from 'typeorm'; -import { genId } from '@/misc/gen-id.js'; - -export default async (actor: CacheableRemoteUser, activity: IFlag): Promise => { - // objectは `(User|Note) | (User|Note)[]` だけど、全パターンDBスキーマと対応させられないので - // 対象ユーザーは一番最初のユーザー として あとはコメントとして格納する - const uris = getApIds(activity.object); - - const userIds = uris.filter(uri => uri.startsWith(config.url + '/users/')).map(uri => uri.split('/').pop()!); - const users = await Users.findBy({ - id: In(userIds), - }); - if (users.length < 1) return `skip`; - - await AbuseUserReports.insert({ - id: genId(), - createdAt: new Date(), - targetUserId: users[0].id, - targetUserHost: users[0].host, - reporterId: actor.id, - reporterHost: actor.host, - comment: `${activity.content}\n${JSON.stringify(uris, null, 2)}`, - }); - - return `ok`; -}; diff --git a/packages/backend/src/services/remote/activitypub/kernel/follow.ts b/packages/backend/src/services/remote/activitypub/kernel/follow.ts deleted file mode 100644 index a9e92fa2296f..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/follow.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import follow from '@/services/following/create.js'; -import { IFollow } from '../type.js'; -import DbResolver from '../db-resolver.js'; - -export default async (actor: CacheableRemoteUser, activity: IFollow): Promise => { - const dbResolver = new DbResolver(); - const followee = await dbResolver.getUserFromApId(activity.object); - - if (followee == null) { - return `skip: followee not found`; - } - - if (followee.host != null) { - return `skip: フォローしようとしているユーザーはローカルユーザーではありません`; - } - - await follow(actor, followee, activity.id); - return `ok`; -}; diff --git a/packages/backend/src/services/remote/activitypub/kernel/index.ts b/packages/backend/src/services/remote/activitypub/kernel/index.ts deleted file mode 100644 index 254a1216059d..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/index.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { IObject, isCreate, isDelete, isUpdate, isRead, isFollow, isAccept, isReject, isAdd, isRemove, isAnnounce, isLike, isUndo, isBlock, isCollectionOrOrderedCollection, isCollection, isFlag } from '../type.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import create from './create/index.js'; -import performDeleteActivity from './delete/index.js'; -import performUpdateActivity from './update/index.js'; -import { performReadActivity } from './read.js'; -import follow from './follow.js'; -import undo from './undo/index.js'; -import like from './like.js'; -import announce from './announce/index.js'; -import accept from './accept/index.js'; -import reject from './reject/index.js'; -import add from './add/index.js'; -import remove from './remove/index.js'; -import block from './block/index.js'; -import flag from './flag/index.js'; -import { apLogger } from '../logger.js'; -import Resolver from '../resolver.js'; -import { toArray } from '@/prelude/array.js'; -import { Users } from '@/models/index.js'; - -export async function performActivity(actor: CacheableRemoteUser, activity: IObject) { - if (isCollectionOrOrderedCollection(activity)) { - const resolver = new Resolver(); - for (const item of toArray(isCollection(activity) ? activity.items : activity.orderedItems)) { - const act = await resolver.resolve(item); - try { - await performOneActivity(actor, act); - } catch (err) { - if (err instanceof Error || typeof err === 'string') { - apLogger.error(err); - } - } - } - } else { - await performOneActivity(actor, activity); - } -} - -async function performOneActivity(actor: CacheableRemoteUser, activity: IObject): Promise { - if (actor.isSuspended) return; - - if (isCreate(activity)) { - await create(actor, activity); - } else if (isDelete(activity)) { - await performDeleteActivity(actor, activity); - } else if (isUpdate(activity)) { - await performUpdateActivity(actor, activity); - } else if (isRead(activity)) { - await performReadActivity(actor, activity); - } else if (isFollow(activity)) { - await follow(actor, activity); - } else if (isAccept(activity)) { - await accept(actor, activity); - } else if (isReject(activity)) { - await reject(actor, activity); - } else if (isAdd(activity)) { - await add(actor, activity).catch(err => apLogger.error(err)); - } else if (isRemove(activity)) { - await remove(actor, activity).catch(err => apLogger.error(err)); - } else if (isAnnounce(activity)) { - await announce(actor, activity); - } else if (isLike(activity)) { - await like(actor, activity); - } else if (isUndo(activity)) { - await undo(actor, activity); - } else if (isBlock(activity)) { - await block(actor, activity); - } else if (isFlag(activity)) { - await flag(actor, activity); - } else { - apLogger.warn(`unrecognized activity type: ${(activity as any).type}`); - } -} diff --git a/packages/backend/src/services/remote/activitypub/kernel/like.ts b/packages/backend/src/services/remote/activitypub/kernel/like.ts deleted file mode 100644 index 2b65ff7383e4..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/like.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import { ILike, getApId } from '../type.js'; -import create from '@/services/note/reaction/create.js'; -import { fetchNote, extractEmojis } from '../models/note.js'; - -export default async (actor: CacheableRemoteUser, activity: ILike) => { - const targetUri = getApId(activity.object); - - const note = await fetchNote(targetUri); - if (!note) return `skip: target note not found ${targetUri}`; - - await extractEmojis(activity.tag || [], actor.host).catch(() => null); - - return await create(actor, note, activity._misskey_reaction || activity.content || activity.name).catch(e => { - if (e.id === '51c42bb4-931a-456b-bff7-e5a8a70dd298') { - return 'skip: already reacted'; - } else { - throw e; - } - }).then(() => 'ok'); -}; diff --git a/packages/backend/src/services/remote/activitypub/kernel/read.ts b/packages/backend/src/services/remote/activitypub/kernel/read.ts deleted file mode 100644 index 1f21dd5fdd2e..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/read.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { CacheableRemoteUser } from '@/models/entities/user.js'; -import { isSelfHost, extractDbHost } from '@/misc/convert-host.js'; -import { MessagingMessages } from '@/models/index.js'; -import { getApId } from '../type.js'; -import { readUserMessagingMessage } from '../../../../server/api/common/read-messaging-message.js'; -import type { IRead } from '../type.js'; - -export const performReadActivity = async (actor: CacheableRemoteUser, activity: IRead): Promise => { - const id = await getApId(activity.object); - - if (!isSelfHost(extractDbHost(id))) { - return `skip: Read to foreign host (${id})`; - } - - const messageId = id.split('/').pop(); - - const message = await MessagingMessages.findOneBy({ id: messageId }); - if (message == null) { - return 'skip: message not found'; - } - - if (actor.id !== message.recipientId) { - return 'skip: actor is not a message recipient'; - } - - await readUserMessagingMessage(message.recipientId!, message.userId, [message.id]); - return `ok: mark as read (${message.userId} => ${message.recipientId} ${message.id})`; -}; diff --git a/packages/backend/src/services/remote/activitypub/kernel/reject/follow.ts b/packages/backend/src/services/remote/activitypub/kernel/reject/follow.ts deleted file mode 100644 index 824ac69d7089..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/reject/follow.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import { remoteReject } from '@/services/following/reject.js'; -import { IFollow } from '../../type.js'; -import DbResolver from '../../db-resolver.js'; -import { relayRejected } from '@/services/relay.js'; -import { Users } from '@/models/index.js'; - -export default async (actor: CacheableRemoteUser, activity: IFollow): Promise => { - // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある - - const dbResolver = new DbResolver(); - const follower = await dbResolver.getUserFromApId(activity.actor); - - if (follower == null) { - return `skip: follower not found`; - } - - if (!Users.isLocalUser(follower)) { - return `skip: follower is not a local user`; - } - - // relay - const match = activity.id?.match(/follow-relay\/(\w+)/); - if (match) { - return await relayRejected(match[1]); - } - - await remoteReject(actor, follower); - return `ok`; -}; diff --git a/packages/backend/src/services/remote/activitypub/kernel/reject/index.ts b/packages/backend/src/services/remote/activitypub/kernel/reject/index.ts deleted file mode 100644 index 00f08842f41b..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/reject/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import Resolver from '../../resolver.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import rejectFollow from './follow.js'; -import { IReject, isFollow, getApType } from '../../type.js'; -import { apLogger } from '../../logger.js'; - -const logger = apLogger; - -export default async (actor: CacheableRemoteUser, activity: IReject): Promise => { - const uri = activity.id || activity; - - logger.info(`Reject: ${uri}`); - - const resolver = new Resolver(); - - const object = await resolver.resolve(activity.object).catch(e => { - logger.error(`Resolution failed: ${e}`); - throw e; - }); - - if (isFollow(object)) return await rejectFollow(actor, object); - - return `skip: Unknown Reject type: ${getApType(object)}`; -}; diff --git a/packages/backend/src/services/remote/activitypub/kernel/remove/index.ts b/packages/backend/src/services/remote/activitypub/kernel/remove/index.ts deleted file mode 100644 index 11a994a83bc9..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/remove/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import { IRemove } from '../../type.js'; -import { resolveNote } from '../../models/note.js'; -import { removePinned } from '@/services/i/pin.js'; - -export default async (actor: CacheableRemoteUser, activity: IRemove): Promise => { - if ('actor' in activity && actor.uri !== activity.actor) { - throw new Error('invalid actor'); - } - - if (activity.target == null) { - throw new Error('target is null'); - } - - if (activity.target === actor.featured) { - const note = await resolveNote(activity.object); - if (note == null) throw new Error('note not found'); - await removePinned(actor, note.id); - return; - } - - throw new Error(`unknown target: ${activity.target}`); -}; diff --git a/packages/backend/src/services/remote/activitypub/kernel/undo/accept.ts b/packages/backend/src/services/remote/activitypub/kernel/undo/accept.ts deleted file mode 100644 index a6e3929b0f9b..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/undo/accept.ts +++ /dev/null @@ -1,27 +0,0 @@ -import unfollow from '@/services/following/delete.js'; -import cancelRequest from '@/services/following/requests/cancel.js'; -import { IAccept } from '../../type.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import { Followings } from '@/models/index.js'; -import DbResolver from '../../db-resolver.js'; - -export default async (actor: CacheableRemoteUser, activity: IAccept): Promise => { - const dbResolver = new DbResolver(); - - const follower = await dbResolver.getUserFromApId(activity.object); - if (follower == null) { - return `skip: follower not found`; - } - - const following = await Followings.findOneBy({ - followerId: follower.id, - followeeId: actor.id, - }); - - if (following) { - await unfollow(follower, actor); - return `ok: unfollowed`; - } - - return `skip: フォローされていない`; -}; diff --git a/packages/backend/src/services/remote/activitypub/kernel/undo/announce.ts b/packages/backend/src/services/remote/activitypub/kernel/undo/announce.ts deleted file mode 100644 index 417f39722f95..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/undo/announce.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Notes } from '@/models/index.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import { IAnnounce, getApId } from '../../type.js'; -import deleteNote from '@/services/note/delete.js'; - -export const undoAnnounce = async (actor: CacheableRemoteUser, activity: IAnnounce): Promise => { - const uri = getApId(activity); - - const note = await Notes.findOneBy({ - uri, - userId: actor.id, - }); - - if (!note) return 'skip: no such Announce'; - - await deleteNote(actor, note); - return 'ok: deleted'; -}; diff --git a/packages/backend/src/services/remote/activitypub/kernel/undo/block.ts b/packages/backend/src/services/remote/activitypub/kernel/undo/block.ts deleted file mode 100644 index 4ac669857861..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/undo/block.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { IBlock } from '../../type.js'; -import unblock from '@/services/blocking/delete.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import DbResolver from '../../db-resolver.js'; -import { Users } from '@/models/index.js'; - -export default async (actor: CacheableRemoteUser, activity: IBlock): Promise => { - const dbResolver = new DbResolver(); - const blockee = await dbResolver.getUserFromApId(activity.object); - - if (blockee == null) { - return `skip: blockee not found`; - } - - if (blockee.host != null) { - return `skip: ブロック解除しようとしているユーザーはローカルユーザーではありません`; - } - - await unblock(await Users.findOneByOrFail({ id: actor.id }), blockee); - return `ok`; -}; diff --git a/packages/backend/src/services/remote/activitypub/kernel/undo/follow.ts b/packages/backend/src/services/remote/activitypub/kernel/undo/follow.ts deleted file mode 100644 index 6a43c1444ad4..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/undo/follow.ts +++ /dev/null @@ -1,41 +0,0 @@ -import unfollow from '@/services/following/delete.js'; -import cancelRequest from '@/services/following/requests/cancel.js'; -import { IFollow } from '../../type.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import { FollowRequests, Followings } from '@/models/index.js'; -import DbResolver from '../../db-resolver.js'; - -export default async (actor: CacheableRemoteUser, activity: IFollow): Promise => { - const dbResolver = new DbResolver(); - - const followee = await dbResolver.getUserFromApId(activity.object); - if (followee == null) { - return `skip: followee not found`; - } - - if (followee.host != null) { - return `skip: フォロー解除しようとしているユーザーはローカルユーザーではありません`; - } - - const req = await FollowRequests.findOneBy({ - followerId: actor.id, - followeeId: followee.id, - }); - - const following = await Followings.findOneBy({ - followerId: actor.id, - followeeId: followee.id, - }); - - if (req) { - await cancelRequest(followee, actor); - return `ok: follow request canceled`; - } - - if (following) { - await unfollow(actor, followee); - return `ok: unfollowed`; - } - - return `skip: リクエストもフォローもされていない`; -}; diff --git a/packages/backend/src/services/remote/activitypub/kernel/undo/index.ts b/packages/backend/src/services/remote/activitypub/kernel/undo/index.ts deleted file mode 100644 index 27d433eb331c..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/undo/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import { IUndo, isFollow, isBlock, isLike, isAnnounce, getApType, isAccept } from '../../type.js'; -import unfollow from './follow.js'; -import unblock from './block.js'; -import undoLike from './like.js'; -import undoAccept from './accept.js'; -import { undoAnnounce } from './announce.js'; -import Resolver from '../../resolver.js'; -import { apLogger } from '../../logger.js'; - -const logger = apLogger; - -export default async (actor: CacheableRemoteUser, activity: IUndo): Promise => { - if ('actor' in activity && actor.uri !== activity.actor) { - throw new Error('invalid actor'); - } - - const uri = activity.id || activity; - - logger.info(`Undo: ${uri}`); - - const resolver = new Resolver(); - - const object = await resolver.resolve(activity.object).catch(e => { - logger.error(`Resolution failed: ${e}`); - throw e; - }); - - if (isFollow(object)) return await unfollow(actor, object); - if (isBlock(object)) return await unblock(actor, object); - if (isLike(object)) return await undoLike(actor, object); - if (isAnnounce(object)) return await undoAnnounce(actor, object); - if (isAccept(object)) return await undoAccept(actor, object); - - return `skip: unknown object type ${getApType(object)}`; -}; diff --git a/packages/backend/src/services/remote/activitypub/kernel/undo/like.ts b/packages/backend/src/services/remote/activitypub/kernel/undo/like.ts deleted file mode 100644 index 01aeba1fb750..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/undo/like.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import { ILike, getApId } from '../../type.js'; -import deleteReaction from '@/services/note/reaction/delete.js'; -import { fetchNote } from '../../models/note.js'; - -/** - * Process Undo.Like activity - */ -export default async (actor: CacheableRemoteUser, activity: ILike) => { - const targetUri = getApId(activity.object); - - const note = await fetchNote(targetUri); - if (!note) return `skip: target note not found ${targetUri}`; - - await deleteReaction(actor, note).catch(e => { - if (e.id === '60527ec9-b4cb-4a88-a6bd-32d3ad26817d') return; - throw e; - }); - - return `ok`; -}; diff --git a/packages/backend/src/services/remote/activitypub/kernel/update/index.ts b/packages/backend/src/services/remote/activitypub/kernel/update/index.ts deleted file mode 100644 index 9e8a81bb39fd..000000000000 --- a/packages/backend/src/services/remote/activitypub/kernel/update/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import { getApType, IUpdate, isActor } from '../../type.js'; -import { apLogger } from '../../logger.js'; -import { updateQuestion } from '../../models/question.js'; -import Resolver from '../../resolver.js'; -import { updatePerson } from '../../models/person.js'; - -/** - * Updateアクティビティを捌きます - */ -export default async (actor: CacheableRemoteUser, activity: IUpdate): Promise => { - if ('actor' in activity && actor.uri !== activity.actor) { - return `skip: invalid actor`; - } - - apLogger.debug('Update'); - - const resolver = new Resolver(); - - const object = await resolver.resolve(activity.object).catch(e => { - apLogger.error(`Resolution failed: ${e}`); - throw e; - }); - - if (isActor(object)) { - await updatePerson(actor.uri!, resolver, object); - return `ok: Person updated`; - } else if (getApType(object) === 'Question') { - await updateQuestion(object).catch(e => console.log(e)); - return `ok: Question updated`; - } else { - return `skip: Unknown type: ${getApType(object)}`; - } -}; From e2ce8dfcfdc7792d749f42249ca4e16fbc497314 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 03:18:19 +0900 Subject: [PATCH 048/180] Update ApInboxService.ts --- .../remote/activitypub/ApInboxService.ts | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/packages/backend/src/services/remote/activitypub/ApInboxService.ts b/packages/backend/src/services/remote/activitypub/ApInboxService.ts index 379d3843f47b..b32fe3a016ac 100644 --- a/packages/backend/src/services/remote/activitypub/ApInboxService.ts +++ b/packages/backend/src/services/remote/activitypub/ApInboxService.ts @@ -1,4 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; +import { In } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import { Users } from '@/models/index.js'; import type { Config } from '@/config/types.js'; @@ -11,9 +12,12 @@ import type { UserBlockingService } from '@/services/UserBlockingService.js'; import { StatusError } from '@/services/HttpRequestService.js'; import type { NoteDeleteService } from '@/services/NoteDeleteService.js'; import type { NoteCreateService } from '@/services/NoteCreateService.js'; +import { concat, toArray, toSingle, unique } from '@/prelude/array.js'; +import type { AppLockService } from '@/services/AppLockService.js'; import { createNote, fetchNote } from './models/note.js'; import { updatePerson } from './models/person.js'; import { updateQuestion } from './models/question.js'; +import { getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isPost, isRead, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js'; import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IRead, IReject, IRemove, IUndo, IUpdate } from './type.js'; @Injectable() @@ -32,6 +36,7 @@ export class ApInboxService { private userBlockingService: UserBlockingService, private noteCreateService: NoteCreateService, private noteDeleteService: NoteDeleteService, + private appLockService: AppLockService, ) { } @@ -41,7 +46,7 @@ export class ApInboxService { for (const item of toArray(isCollection(activity) ? activity.items : activity.orderedItems)) { const act = await resolver.resolve(item); try { - await performOneActivity(actor, act); + await this.performOneActivity(actor, act); } catch (err) { if (err instanceof Error || typeof err === 'string') { apLogger.error(err); @@ -49,7 +54,7 @@ export class ApInboxService { } } } else { - await performOneActivity(actor, activity); + await this.performOneActivity(actor, activity); } } @@ -57,33 +62,33 @@ export class ApInboxService { if (actor.isSuspended) return; if (isCreate(activity)) { - await create(actor, activity); + await this.#create(actor, activity); } else if (isDelete(activity)) { - await performDeleteActivity(actor, activity); + await this.#delete(actor, activity); } else if (isUpdate(activity)) { - await performUpdateActivity(actor, activity); + await this.#update(actor, activity); } else if (isRead(activity)) { - await performReadActivity(actor, activity); + await this.#read(actor, activity); } else if (isFollow(activity)) { - await follow(actor, activity); + await this.#follow(actor, activity); } else if (isAccept(activity)) { - await accept(actor, activity); + await this.#accept(actor, activity); } else if (isReject(activity)) { - await reject(actor, activity); + await this.#reject(actor, activity); } else if (isAdd(activity)) { - await add(actor, activity).catch(err => apLogger.error(err)); + await this.#add(actor, activity).catch(err => apLogger.error(err)); } else if (isRemove(activity)) { - await remove(actor, activity).catch(err => apLogger.error(err)); + await this.#remove(actor, activity).catch(err => apLogger.error(err)); } else if (isAnnounce(activity)) { - await announce(actor, activity); + await this.#announce(actor, activity); } else if (isLike(activity)) { - await like(actor, activity); + await this.#like(actor, activity); } else if (isUndo(activity)) { - await undo(actor, activity); + await this.#undo(actor, activity); } else if (isBlock(activity)) { - await block(actor, activity); + await this.#block(actor, activity); } else if (isFlag(activity)) { - await flag(actor, activity); + await this.#flag(actor, activity); } else { apLogger.warn(`unrecognized activity type: ${(activity as any).type}`); } @@ -225,7 +230,7 @@ export class ApInboxService { const meta = await fetchMeta(); if (meta.blockedHosts.includes(extractDbHost(uri))) return; - const unlock = await getApLock(uri); + const unlock = await this.appLockService.getApLock(uri); try { // 既に同じURIを持つものが登録されていないかチェック @@ -337,7 +342,7 @@ export class ApInboxService { } } - const unlock = await getApLock(uri); + const unlock = await this.appLockService.getApLock(uri); try { const exist = await fetchNote(note); @@ -421,7 +426,7 @@ export class ApInboxService { async #deleteNote(actor: CacheableRemoteUser, uri: string): Promise { logger.info(`Deleting the Note: ${uri}`); - const unlock = await getApLock(uri); + const unlock = await this.appLockService.getApLock(uri); try { const dbResolver = new DbResolver(); @@ -456,7 +461,7 @@ export class ApInboxService { // 対象ユーザーは一番最初のユーザー として あとはコメントとして格納する const uris = getApIds(activity.object); - const userIds = uris.filter(uri => uri.startsWith(config.url + '/users/')).map(uri => uri.split('/').pop()!); + const userIds = uris.filter(uri => uri.startsWith(this.config.url + '/users/')).map(uri => uri.split('/').pop()!); const users = await Users.findBy({ id: In(userIds), }); From 91c84336f29b02cd79e5673f0ee93a202ddb55d8 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 03:29:51 +0900 Subject: [PATCH 049/180] Update resolver.ts --- .../services/remote/activitypub/resolver.ts | 62 ++++++++++--------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/packages/backend/src/services/remote/activitypub/resolver.ts b/packages/backend/src/services/remote/activitypub/resolver.ts index a8c97b9d9800..e01171fe8963 100644 --- a/packages/backend/src/services/remote/activitypub/resolver.ts +++ b/packages/backend/src/services/remote/activitypub/resolver.ts @@ -1,28 +1,33 @@ -import config from '@/config/index.js'; -import { getJson } from '@/misc/fetch.js'; import type { ILocalUser } from '@/models/entities/user.js'; import type { InstanceActorService } from '@/services/InstanceActorService.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; import { extractDbHost, isSelfHost } from '@/misc/convert-host.js'; -import { FollowRequests, Notes, NoteReactions, Polls, Users } from '@/models/index.js'; -import renderNote from '@/services/remote/activitypub/renderer/note.js'; -import { renderLike } from '@/services/remote/activitypub/renderer/like.js'; -import { renderPerson } from '@/services/remote/activitypub/renderer/person.js'; -import renderQuestion from '@/services/remote/activitypub/renderer/question.js'; -import renderCreate from '@/services/remote/activitypub/renderer/create.js'; -import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; -import renderFollow from '@/services/remote/activitypub/renderer/follow.js'; +import type { Notes , Polls , NoteReactions , Users } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type { MetaService } from '@/services/MetaService.js'; +import type { HttpRequestService } from '@/services/HttpRequestService.js'; import { isCollectionOrOrderedCollection } from './type.js'; import { parseUri } from './db-resolver.js'; -import { signedGet } from './request.js'; +import type { ApRendererService } from './ApRendererService.js'; import type { IObject, ICollection, IOrderedCollection } from './type.js'; +import type { ApRequestService } from './ApRequestService.js'; -export default class Resolver { +export class ApResolver { private history: Set; private user?: ILocalUser; constructor( + private config: Config, + + private usersRepository: typeof Users, + private notesRepository: typeof Notes, + private pollsRepository: typeof Polls, + private noteReactionsRepository: typeof NoteReactions, + private instanceActorService: InstanceActorService, + private metaService: MetaService, + private apRequestService: ApRequestService, + private httpRequestService: HttpRequestService, + private apRendererService: ApRendererService, ) { this.history = new Set(); } @@ -70,18 +75,18 @@ export default class Resolver { return await this.resolveLocal(value); } - const meta = await fetchMeta(); + const meta = await this.metaService.fetch(); if (meta.blockedHosts.includes(host)) { throw new Error('Instance is blocked'); } - if (config.signToActivityPubGet && !this.user) { + if (this.config.signToActivityPubGet && !this.user) { this.user = await this.instanceActorService.getInstanceActor(); } const object = (this.user - ? await signedGet(value, this.user) - : await getJson(value, 'application/activity+json, application/ld+json')) as IObject; + ? await this.apRequestService.signedGet(value, this.user) + : await this.httpRequestService.getJson(value, 'application/activity+json, application/ld+json')) as IObject; if (object == null || ( Array.isArray(object['@context']) ? @@ -100,35 +105,36 @@ export default class Resolver { switch (parsed.type) { case 'notes': - return Notes.findOneByOrFail({ id: parsed.id }) + return this.notesRepository.findOneByOrFail({ id: parsed.id }) .then(note => { if (parsed.rest === 'activity') { // this refers to the create activity and not the note itself - return renderActivity(renderCreate(renderNote(note))); + return this.apRendererService.renderActivity(this.apRendererService.renderCreate(this.apRendererService.renderNote(note))); } else { - return renderNote(note); + return this.apRendererService.renderNote(note); } }); case 'users': - return Users.findOneByOrFail({ id: parsed.id }) - .then(user => renderPerson(user as ILocalUser)); + return this.usersRepository.findOneByOrFail({ id: parsed.id }) + .then(user => this.apRendererService.renderPerson(user as ILocalUser)); case 'questions': // Polls are indexed by the note they are attached to. return Promise.all([ - Notes.findOneByOrFail({ id: parsed.id }), - Polls.findOneByOrFail({ noteId: parsed.id }), + this.notesRepository.findOneByOrFail({ id: parsed.id }), + this.pollsRepository.findOneByOrFail({ noteId: parsed.id }), ]) - .then(([note, poll]) => renderQuestion({ id: note.userId }, note, poll)); + .then(([note, poll]) => this.apRendererService.renderQuestion({ id: note.userId }, note, poll)); case 'likes': - return NoteReactions.findOneByOrFail({ id: parsed.id }).then(reaction => renderActivity(renderLike(reaction, { uri: null }))); + return this.noteReactionsRepository.findOneByOrFail({ id: parsed.id }).then(reaction => + this.apRendererService.renderActivity(this.apRendererService.renderLike(reaction, { uri: null }))); case 'follows': // rest should be if (parsed.rest == null || !/^\w+$/.test(parsed.rest)) throw new Error('resolveLocal: invalid follow URI'); return Promise.all( - [parsed.id, parsed.rest].map(id => Users.findOneByOrFail({ id })), + [parsed.id, parsed.rest].map(id => this.usersRepository.findOneByOrFail({ id })), ) - .then(([follower, followee]) => renderActivity(renderFollow(follower, followee, url))); + .then(([follower, followee]) => this.apRendererService.renderActivity(this.apRendererService.renderFollow(follower, followee, url))); default: throw new Error(`resolveLocal: type ${type} unhandled`); } From e097ab203b1e0383ab0ad2677964b3d1dbfa0a7f Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 03:30:13 +0900 Subject: [PATCH 050/180] wip --- .../services/remote/activitypub/{resolver.ts => ApResolver.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/backend/src/services/remote/activitypub/{resolver.ts => ApResolver.ts} (100%) diff --git a/packages/backend/src/services/remote/activitypub/resolver.ts b/packages/backend/src/services/remote/activitypub/ApResolver.ts similarity index 100% rename from packages/backend/src/services/remote/activitypub/resolver.ts rename to packages/backend/src/services/remote/activitypub/ApResolver.ts From eb99014540605e1564e00e33341ca9674d0febf2 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 04:52:59 +0900 Subject: [PATCH 051/180] wip --- .../remote/activitypub/ApInboxService.ts | 15 +++--- .../{ApResolver.ts => ApResolverService.ts} | 48 +++++++++++++++++-- 2 files changed, 54 insertions(+), 9 deletions(-) rename packages/backend/src/services/remote/activitypub/{ApResolver.ts => ApResolverService.ts} (82%) diff --git a/packages/backend/src/services/remote/activitypub/ApInboxService.ts b/packages/backend/src/services/remote/activitypub/ApInboxService.ts index b32fe3a016ac..a69a0db11a7c 100644 --- a/packages/backend/src/services/remote/activitypub/ApInboxService.ts +++ b/packages/backend/src/services/remote/activitypub/ApInboxService.ts @@ -14,10 +14,12 @@ import type { NoteDeleteService } from '@/services/NoteDeleteService.js'; import type { NoteCreateService } from '@/services/NoteCreateService.js'; import { concat, toArray, toSingle, unique } from '@/prelude/array.js'; import type { AppLockService } from '@/services/AppLockService.js'; +import { extractDbHost } from '@/misc/convert-host.js'; import { createNote, fetchNote } from './models/note.js'; import { updatePerson } from './models/person.js'; import { updateQuestion } from './models/question.js'; import { getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isPost, isRead, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js'; +import type { ApResolverService, Resolver } from './ApResolverService.js'; import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IRead, IReject, IRemove, IUndo, IUpdate } from './type.js'; @Injectable() @@ -37,12 +39,13 @@ export class ApInboxService { private noteCreateService: NoteCreateService, private noteDeleteService: NoteDeleteService, private appLockService: AppLockService, + private apResolverService: ApResolverService, ) { } public async performActivity(actor: CacheableRemoteUser, activity: IObject) { if (isCollectionOrOrderedCollection(activity)) { - const resolver = new Resolver(); + const resolver = this.apResolverService.createResolver(); for (const item of toArray(isCollection(activity) ? activity.items : activity.orderedItems)) { const act = await resolver.resolve(item); try { @@ -154,7 +157,7 @@ export class ApInboxService { apLogger.info(`Accept: ${uri}`); - const resolver = new Resolver(); + const resolver = this.apResolverService.createResolver(); const object = await resolver.resolve(activity.object).catch(e => { apLogger.error(`Resolution failed: ${e}`); @@ -313,7 +316,7 @@ export class ApInboxService { activity.object.attributedTo = activity.actor; } - const resolver = new Resolver(); + const resolver = this.apResolverService.createResolver(); const object = await resolver.resolve(activity.object).catch(e => { logger.error(`Resolution failed: ${e}`); @@ -485,7 +488,7 @@ export class ApInboxService { logger.info(`Reject: ${uri}`); - const resolver = new Resolver(); + const resolver = this.apResolverService.createResolver(); const object = await resolver.resolve(activity.object).catch(e => { logger.error(`Resolution failed: ${e}`); @@ -549,7 +552,7 @@ export class ApInboxService { logger.info(`Undo: ${uri}`); - const resolver = new Resolver(); + const resolver = this.apResolverService.createResolver(); const object = await resolver.resolve(activity.object).catch(e => { logger.error(`Resolution failed: ${e}`); @@ -672,7 +675,7 @@ export class ApInboxService { apLogger.debug('Update'); - const resolver = new Resolver(); + const resolver = this.apResolverService.createResolver(); const object = await resolver.resolve(activity.object).catch(e => { apLogger.error(`Resolution failed: ${e}`); diff --git a/packages/backend/src/services/remote/activitypub/ApResolver.ts b/packages/backend/src/services/remote/activitypub/ApResolverService.ts similarity index 82% rename from packages/backend/src/services/remote/activitypub/ApResolver.ts rename to packages/backend/src/services/remote/activitypub/ApResolverService.ts index e01171fe8963..d04afacc79f3 100644 --- a/packages/backend/src/services/remote/activitypub/ApResolver.ts +++ b/packages/backend/src/services/remote/activitypub/ApResolverService.ts @@ -1,3 +1,4 @@ +import { Inject, Injectable } from '@nestjs/common'; import type { ILocalUser } from '@/models/entities/user.js'; import type { InstanceActorService } from '@/services/InstanceActorService.js'; import { extractDbHost, isSelfHost } from '@/misc/convert-host.js'; @@ -5,24 +6,65 @@ import type { Notes , Polls , NoteReactions , Users } from '@/models/index.js'; import type { Config } from '@/config/types.js'; import type { MetaService } from '@/services/MetaService.js'; import type { HttpRequestService } from '@/services/HttpRequestService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import { isCollectionOrOrderedCollection } from './type.js'; import { parseUri } from './db-resolver.js'; import type { ApRendererService } from './ApRendererService.js'; import type { IObject, ICollection, IOrderedCollection } from './type.js'; import type { ApRequestService } from './ApRequestService.js'; -export class ApResolver { +@Injectable() +export class ApResolverService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + + @Inject('pollsRepository') + private pollsRepository: typeof Polls, + + @Inject('noteReactionsRepository') + private noteReactionsRepository: typeof NoteReactions, + + private instanceActorService: InstanceActorService, + private metaService: MetaService, + private apRequestService: ApRequestService, + private httpRequestService: HttpRequestService, + private apRendererService: ApRendererService, + ) { + } + + public createResolver(): Resolver { + return new Resolver( + this.config, + this.usersRepository, + this.notesRepository, + this.pollsRepository, + this.noteReactionsRepository, + this.instanceActorService, + this.metaService, + this.apRequestService, + this.httpRequestService, + this.apRendererService, + ); + } +} + +export class Resolver { private history: Set; private user?: ILocalUser; constructor( private config: Config, - private usersRepository: typeof Users, private notesRepository: typeof Notes, private pollsRepository: typeof Polls, private noteReactionsRepository: typeof NoteReactions, - private instanceActorService: InstanceActorService, private metaService: MetaService, private apRequestService: ApRequestService, From a537c9a4a1c071ed0aa001b10cb2948c03765c0a Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 05:16:34 +0900 Subject: [PATCH 052/180] wip --- .../backend/src/services/LoggerService.ts | 13 + .../activitypub/models/ApNoteService.ts | 365 ++++++++++++++++++ .../remote/activitypub/models/note.ts | 359 ----------------- 3 files changed, 378 insertions(+), 359 deletions(-) create mode 100644 packages/backend/src/services/LoggerService.ts create mode 100644 packages/backend/src/services/remote/activitypub/models/ApNoteService.ts delete mode 100644 packages/backend/src/services/remote/activitypub/models/note.ts diff --git a/packages/backend/src/services/LoggerService.ts b/packages/backend/src/services/LoggerService.ts new file mode 100644 index 000000000000..e3673e76a020 --- /dev/null +++ b/packages/backend/src/services/LoggerService.ts @@ -0,0 +1,13 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Users } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; + +@Injectable() +export class LoggerService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + ) { + } +} diff --git a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts new file mode 100644 index 000000000000..bbee92d53a17 --- /dev/null +++ b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts @@ -0,0 +1,365 @@ +import { Inject, Injectable } from '@nestjs/common'; +import promiseLimit from 'promise-limit'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Users } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type { CacheableRemoteUser } from '@/models/entities/user.js'; +import type { MfmService } from '@/services/MfmService.js'; +import { extractDbHost, toPuny } from '@/misc/convert-host'; +import type { Note } from '@/models/entities/note.js'; +import { StatusError } from '@/services/HttpRequestService.js'; +import { toArray, toSingle, unique } from '@/prelude/array.js'; +import type { Emoji } from '@/models/entities/emoji.js'; +import type { MetaService } from '@/services/MetaService.js'; +import type { AppLockService } from '@/services/AppLockService.js'; +import type { DriveFile } from '@/models/entities/drive-file.js'; +import type { NoteCreateService } from '@/services/NoteCreateService.js'; +import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js'; +import type { ApResolverService, Resolver } from '../ApResolverService.js'; +import type { IObject, IPost } from '../type.js'; + +@Injectable() +export class ApNoteService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + private mfmService: MfmService, + private apResolverService: ApResolverService, + private metaService: MetaService, + private appLockService: AppLockService, + private noteCreateService: NoteCreateService, + ) { + } + + public validateNote(object: any, uri: string) { + const expectHost = extractDbHost(uri); + + if (object == null) { + return new Error('invalid Note: object is null'); + } + + if (!validPost.includes(getApType(object))) { + return new Error(`invalid Note: invalid object type ${getApType(object)}`); + } + + if (object.id && extractDbHost(object.id) !== expectHost) { + return new Error(`invalid Note: id has different host. expected: ${expectHost}, actual: ${extractDbHost(object.id)}`); + } + + if (object.attributedTo && extractDbHost(getOneApId(object.attributedTo)) !== expectHost) { + return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${extractDbHost(object.attributedTo)}`); + } + + return null; + } + + /** + * Noteをフェッチします。 + * + * Misskeyに対象のNoteが登録されていればそれを返します。 + */ + public async fetchNote(object: string | IObject): Promise { + const dbResolver = new DbResolver(); + return await dbResolver.getNoteFromApId(object); + } + + /** + * Noteを作成します。 + */ + public async createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise { + if (resolver == null) resolver = this.apResolverService.createResolver(); + + const object: any = await resolver.resolve(value); + + const entryUri = getApId(value); + const err = this.validateNote(object, entryUri); + if (err) { + logger.error(`${err.message}`, { + resolver: { + history: resolver.getHistory(), + }, + value: value, + object: object, + }); + throw new Error('invalid note'); + } + + const note: IPost = object; + + logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`); + + logger.info(`Creating the Note: ${note.id}`); + + // 投稿者をフェッチ + const actor = await resolvePerson(getOneApId(note.attributedTo), resolver) as CacheableRemoteUser; + + // 投稿者が凍結されていたらスキップ + if (actor.isSuspended) { + throw new Error('actor has been suspended'); + } + + const noteAudience = await parseAudience(actor, note.to, note.cc); + let visibility = noteAudience.visibility; + const visibleUsers = noteAudience.visibleUsers; + + // Audience (to, cc) が指定されてなかった場合 + if (visibility === 'specified' && visibleUsers.length === 0) { + if (typeof value === 'string') { // 入力がstringならばresolverでGETが発生している + // こちらから匿名GET出来たものならばpublic + visibility = 'public'; + } + } + + let isTalk = note._misskey_talk && visibility === 'specified'; + + const apMentions = await extractApMentions(note.tag); + const apHashtags = await extractApHashtags(note.tag); + + // 添付ファイル + // TODO: attachmentは必ずしもImageではない + // TODO: attachmentは必ずしも配列ではない + // Noteがsensitiveなら添付もsensitiveにする + const limit = promiseLimit(2); + + note.attachment = Array.isArray(note.attachment) ? note.attachment : note.attachment ? [note.attachment] : []; + const files = note.attachment + .map(attach => attach.sensitive = note.sensitive) + ? (await Promise.all(note.attachment.map(x => limit(() => resolveImage(actor, x)) as Promise))) + .filter(image => image != null) + : []; + + // リプライ + const reply: Note | null = note.inReplyTo + ? await this.resolveNote(note.inReplyTo, resolver).then(x => { + if (x == null) { + logger.warn('Specified inReplyTo, but nout found'); + throw new Error('inReplyTo not found'); + } else { + return x; + } + }).catch(async err => { + // トークだったらinReplyToのエラーは無視 + const uri = getApId(note.inReplyTo); + if (uri.startsWith(this.config.url + '/')) { + const id = uri.split('/').pop(); + const talk = await MessagingMessages.findOneBy({ id }); + if (talk) { + isTalk = true; + return null; + } + } + + logger.warn(`Error in inReplyTo ${note.inReplyTo} - ${err.statusCode ?? err}`); + throw err; + }) + : null; + + // 引用 + let quote: Note | undefined | null; + + if (note._misskey_quote || note.quoteUrl) { + const tryResolveNote = async (uri: string): Promise<{ + status: 'ok'; + res: Note | null; + } | { + status: 'permerror' | 'temperror'; + }> => { + if (typeof uri !== 'string' || !uri.match(/^https?:/)) return { status: 'permerror' }; + try { + const res = await resolveNote(uri); + if (res) { + return { + status: 'ok', + res, + }; + } else { + return { + status: 'permerror', + }; + } + } catch (e) { + return { + status: (e instanceof StatusError && e.isClientError) ? 'permerror' : 'temperror', + }; + } + }; + + const uris = unique([note._misskey_quote, note.quoteUrl].filter((x): x is string => typeof x === 'string')); + const results = await Promise.all(uris.map(uri => tryResolveNote(uri))); + + quote = results.filter((x): x is { status: 'ok', res: Note | null } => x.status === 'ok').map(x => x.res).find(x => x); + if (!quote) { + if (results.some(x => x.status === 'temperror')) { + throw 'quote resolve failed'; + } + } + } + + const cw = note.summary === '' ? null : note.summary; + + // テキストのパース + let text: string | null = null; + if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source.content === 'string') { + text = note.source.content; + } else if (typeof note._misskey_content !== 'undefined') { + text = note._misskey_content; + } else if (typeof note.content === 'string') { + text = this.mfmService.fromHtml(note.content, note.tag); + } + + // vote + if (reply && reply.hasPoll) { + const poll = await Polls.findOneByOrFail({ noteId: reply.id }); + + const tryCreateVote = async (name: string, index: number): Promise => { + if (poll.expiresAt && Date.now() > new Date(poll.expiresAt).getTime()) { + logger.warn(`vote to expired poll from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`); + } else if (index >= 0) { + logger.info(`vote from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`); + await vote(actor, reply, index); + + // リモートフォロワーにUpdate配信 + deliverQuestionUpdate(reply.id); + } + return null; + }; + + if (note.name) { + return await tryCreateVote(note.name, poll.choices.findIndex(x => x === note.name)); + } + } + + const emojis = await this.extractEmojis(note.tag || [], actor.host).catch(e => { + logger.info(`extractEmojis: ${e}`); + return [] as Emoji[]; + }); + + const apEmojis = emojis.map(emoji => emoji.name); + + const poll = await extractPollFromQuestion(note, resolver).catch(() => undefined); + + if (isTalk) { + for (const recipient of visibleUsers) { + await createMessage(actor, recipient, undefined, text || undefined, (files && files.length > 0) ? files[0] : null, object.id); + return null; + } + } + + return await this.noteCreateService.create(actor, { + createdAt: note.published ? new Date(note.published) : null, + files, + reply, + renote: quote, + name: note.name, + cw, + text, + localOnly: false, + visibility, + visibleUsers, + apMentions, + apHashtags, + apEmojis, + poll, + uri: note.id, + url: getOneApHrefNullable(note.url), + }, silent); + } + + /** + * Noteを解決します。 + * + * Misskeyに対象のNoteが登録されていればそれを返し、そうでなければ + * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 + */ + public async resolveNote(value: string | IObject, resolver?: Resolver): Promise { + const uri = typeof value === 'string' ? value : value.id; + if (uri == null) throw new Error('missing uri'); + + // ブロックしてたら中断 + const meta = await this.metaService.fetch(); + if (meta.blockedHosts.includes(extractDbHost(uri))) throw { statusCode: 451 }; + + const unlock = await this.appLockService.getApLock(uri); + + try { + //#region このサーバーに既に登録されていたらそれを返す + const exist = await this.fetchNote(uri); + + if (exist) { + return exist; + } + //#endregion + + if (uri.startsWith(this.config.url)) { + throw new StatusError('cannot resolve local note', 400, 'cannot resolve local note'); + } + + // リモートサーバーからフェッチしてきて登録 + // ここでuriの代わりに添付されてきたNote Objectが指定されていると、サーバーフェッチを経ずにノートが生成されるが + // 添付されてきたNote Objectは偽装されている可能性があるため、常にuriを指定してサーバーフェッチを行う。 + return await this.createNote(uri, resolver, true); + } finally { + unlock(); + } + } + + public async extractEmojis(tags: IObject | IObject[], host: string): Promise { + host = toPuny(host); + + if (!tags) return []; + + const eomjiTags = toArray(tags).filter(isEmoji); + + return await Promise.all(eomjiTags.map(async tag => { + const name = tag.name!.replace(/^:/, '').replace(/:$/, ''); + tag.icon = toSingle(tag.icon); + + const exists = await Emojis.findOneBy({ + host, + name, + }); + + if (exists) { + if ((tag.updated != null && exists.updatedAt == null) + || (tag.id != null && exists.uri == null) + || (tag.updated != null && exists.updatedAt != null && new Date(tag.updated) > exists.updatedAt) + || (tag.icon!.url !== exists.originalUrl) + ) { + await Emojis.update({ + host, + name, + }, { + uri: tag.id, + originalUrl: tag.icon!.url, + publicUrl: tag.icon!.url, + updatedAt: new Date(), + }); + + return await Emojis.findOneBy({ + host, + name, + }) as Emoji; + } + + return exists; + } + + logger.info(`register emoji host=${host}, name=${name}`); + + return await Emojis.insert({ + id: genId(), + host, + name, + uri: tag.id, + originalUrl: tag.icon!.url, + publicUrl: tag.icon!.url, + updatedAt: new Date(), + aliases: [], + } as Partial).then(x => Emojis.findOneByOrFail(x.identifiers[0])); + })); + } +} diff --git a/packages/backend/src/services/remote/activitypub/models/note.ts b/packages/backend/src/services/remote/activitypub/models/note.ts deleted file mode 100644 index 5d63f2605af7..000000000000 --- a/packages/backend/src/services/remote/activitypub/models/note.ts +++ /dev/null @@ -1,359 +0,0 @@ -import promiseLimit from 'promise-limit'; - -import config from '@/config/index.js'; -import Resolver from '../resolver.js'; -import post from '@/services/note/create.js'; -import { resolvePerson } from './person.js'; -import { resolveImage } from './image.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; -import { htmlToMfm } from '../misc/html-to-mfm.js'; -import { extractApHashtags } from './tag.js'; -import { unique, toArray, toSingle } from '@/prelude/array.js'; -import { extractPollFromQuestion } from './question.js'; -import vote from '@/services/note/polls/vote.js'; -import { apLogger } from '../logger.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; -import { deliverQuestionUpdate } from '@/services/note/polls/update.js'; -import { extractDbHost, toPuny } from '@/misc/convert-host.js'; -import { Emojis, Polls, MessagingMessages } from '@/models/index.js'; -import { Note } from '@/models/entities/note.js'; -import { IObject, getOneApId, getApId, getOneApHrefNullable, validPost, IPost, isEmoji, getApType } from '../type.js'; -import { Emoji } from '@/models/entities/emoji.js'; -import { genId } from '@/misc/gen-id.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import { getApLock } from '@/misc/app-lock.js'; -import { createMessage } from '@/services/messages/create.js'; -import { parseAudience } from '../audience.js'; -import { extractApMentions } from './mention.js'; -import DbResolver from '../db-resolver.js'; -import { StatusError } from '@/misc/fetch.js'; - -const logger = apLogger; - -export function validateNote(object: any, uri: string) { - const expectHost = extractDbHost(uri); - - if (object == null) { - return new Error('invalid Note: object is null'); - } - - if (!validPost.includes(getApType(object))) { - return new Error(`invalid Note: invalid object type ${getApType(object)}`); - } - - if (object.id && extractDbHost(object.id) !== expectHost) { - return new Error(`invalid Note: id has different host. expected: ${expectHost}, actual: ${extractDbHost(object.id)}`); - } - - if (object.attributedTo && extractDbHost(getOneApId(object.attributedTo)) !== expectHost) { - return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${extractDbHost(object.attributedTo)}`); - } - - return null; -} - -/** - * Noteをフェッチします。 - * - * Misskeyに対象のNoteが登録されていればそれを返します。 - */ -export async function fetchNote(object: string | IObject): Promise { - const dbResolver = new DbResolver(); - return await dbResolver.getNoteFromApId(object); -} - -/** - * Noteを作成します。 - */ -export async function createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise { - if (resolver == null) resolver = new Resolver(); - - const object: any = await resolver.resolve(value); - - const entryUri = getApId(value); - const err = validateNote(object, entryUri); - if (err) { - logger.error(`${err.message}`, { - resolver: { - history: resolver.getHistory(), - }, - value: value, - object: object, - }); - throw new Error('invalid note'); - } - - const note: IPost = object; - - logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`); - - logger.info(`Creating the Note: ${note.id}`); - - // 投稿者をフェッチ - const actor = await resolvePerson(getOneApId(note.attributedTo), resolver) as CacheableRemoteUser; - - // 投稿者が凍結されていたらスキップ - if (actor.isSuspended) { - throw new Error('actor has been suspended'); - } - - const noteAudience = await parseAudience(actor, note.to, note.cc); - let visibility = noteAudience.visibility; - const visibleUsers = noteAudience.visibleUsers; - - // Audience (to, cc) が指定されてなかった場合 - if (visibility === 'specified' && visibleUsers.length === 0) { - if (typeof value === 'string') { // 入力がstringならばresolverでGETが発生している - // こちらから匿名GET出来たものならばpublic - visibility = 'public'; - } - } - - let isTalk = note._misskey_talk && visibility === 'specified'; - - const apMentions = await extractApMentions(note.tag); - const apHashtags = await extractApHashtags(note.tag); - - // 添付ファイル - // TODO: attachmentは必ずしもImageではない - // TODO: attachmentは必ずしも配列ではない - // Noteがsensitiveなら添付もsensitiveにする - const limit = promiseLimit(2); - - note.attachment = Array.isArray(note.attachment) ? note.attachment : note.attachment ? [note.attachment] : []; - const files = note.attachment - .map(attach => attach.sensitive = note.sensitive) - ? (await Promise.all(note.attachment.map(x => limit(() => resolveImage(actor, x)) as Promise))) - .filter(image => image != null) - : []; - - // リプライ - const reply: Note | null = note.inReplyTo - ? await resolveNote(note.inReplyTo, resolver).then(x => { - if (x == null) { - logger.warn(`Specified inReplyTo, but nout found`); - throw new Error('inReplyTo not found'); - } else { - return x; - } - }).catch(async e => { - // トークだったらinReplyToのエラーは無視 - const uri = getApId(note.inReplyTo); - if (uri.startsWith(config.url + '/')) { - const id = uri.split('/').pop(); - const talk = await MessagingMessages.findOneBy({ id }); - if (talk) { - isTalk = true; - return null; - } - } - - logger.warn(`Error in inReplyTo ${note.inReplyTo} - ${e.statusCode || e}`); - throw e; - }) - : null; - - // 引用 - let quote: Note | undefined | null; - - if (note._misskey_quote || note.quoteUrl) { - const tryResolveNote = async (uri: string): Promise<{ - status: 'ok'; - res: Note | null; - } | { - status: 'permerror' | 'temperror'; - }> => { - if (typeof uri !== 'string' || !uri.match(/^https?:/)) return { status: 'permerror' }; - try { - const res = await resolveNote(uri); - if (res) { - return { - status: 'ok', - res, - }; - } else { - return { - status: 'permerror', - }; - } - } catch (e) { - return { - status: (e instanceof StatusError && e.isClientError) ? 'permerror' : 'temperror', - }; - } - }; - - const uris = unique([note._misskey_quote, note.quoteUrl].filter((x): x is string => typeof x === 'string')); - const results = await Promise.all(uris.map(uri => tryResolveNote(uri))); - - quote = results.filter((x): x is { status: 'ok', res: Note | null } => x.status === 'ok').map(x => x.res).find(x => x); - if (!quote) { - if (results.some(x => x.status === 'temperror')) { - throw 'quote resolve failed'; - } - } - } - - const cw = note.summary === '' ? null : note.summary; - - // テキストのパース - let text: string | null = null; - if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source?.content === 'string') { - text = note.source.content; - } else if (typeof note._misskey_content !== 'undefined') { - text = note._misskey_content; - } else if (typeof note.content === 'string') { - text = htmlToMfm(note.content, note.tag); - } - - // vote - if (reply && reply.hasPoll) { - const poll = await Polls.findOneByOrFail({ noteId: reply.id }); - - const tryCreateVote = async (name: string, index: number): Promise => { - if (poll.expiresAt && Date.now() > new Date(poll.expiresAt).getTime()) { - logger.warn(`vote to expired poll from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`); - } else if (index >= 0) { - logger.info(`vote from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`); - await vote(actor, reply, index); - - // リモートフォロワーにUpdate配信 - deliverQuestionUpdate(reply.id); - } - return null; - }; - - if (note.name) { - return await tryCreateVote(note.name, poll.choices.findIndex(x => x === note.name)); - } - } - - const emojis = await extractEmojis(note.tag || [], actor.host).catch(e => { - logger.info(`extractEmojis: ${e}`); - return [] as Emoji[]; - }); - - const apEmojis = emojis.map(emoji => emoji.name); - - const poll = await extractPollFromQuestion(note, resolver).catch(() => undefined); - - if (isTalk) { - for (const recipient of visibleUsers) { - await createMessage(actor, recipient, undefined, text || undefined, (files && files.length > 0) ? files[0] : null, object.id); - return null; - } - } - - return await post(actor, { - createdAt: note.published ? new Date(note.published) : null, - files, - reply, - renote: quote, - name: note.name, - cw, - text, - localOnly: false, - visibility, - visibleUsers, - apMentions, - apHashtags, - apEmojis, - poll, - uri: note.id, - url: getOneApHrefNullable(note.url), - }, silent); -} - -/** - * Noteを解決します。 - * - * Misskeyに対象のNoteが登録されていればそれを返し、そうでなければ - * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 - */ -export async function resolveNote(value: string | IObject, resolver?: Resolver): Promise { - const uri = typeof value === 'string' ? value : value.id; - if (uri == null) throw new Error('missing uri'); - - // ブロックしてたら中断 - const meta = await fetchMeta(); - if (meta.blockedHosts.includes(extractDbHost(uri))) throw { statusCode: 451 }; - - const unlock = await getApLock(uri); - - try { - //#region このサーバーに既に登録されていたらそれを返す - const exist = await fetchNote(uri); - - if (exist) { - return exist; - } - //#endregion - - if (uri.startsWith(config.url)) { - throw new StatusError('cannot resolve local note', 400, 'cannot resolve local note'); - } - - // リモートサーバーからフェッチしてきて登録 - // ここでuriの代わりに添付されてきたNote Objectが指定されていると、サーバーフェッチを経ずにノートが生成されるが - // 添付されてきたNote Objectは偽装されている可能性があるため、常にuriを指定してサーバーフェッチを行う。 - return await createNote(uri, resolver, true); - } finally { - unlock(); - } -} - -export async function extractEmojis(tags: IObject | IObject[], host: string): Promise { - host = toPuny(host); - - if (!tags) return []; - - const eomjiTags = toArray(tags).filter(isEmoji); - - return await Promise.all(eomjiTags.map(async tag => { - const name = tag.name!.replace(/^:/, '').replace(/:$/, ''); - tag.icon = toSingle(tag.icon); - - const exists = await Emojis.findOneBy({ - host, - name, - }); - - if (exists) { - if ((tag.updated != null && exists.updatedAt == null) - || (tag.id != null && exists.uri == null) - || (tag.updated != null && exists.updatedAt != null && new Date(tag.updated) > exists.updatedAt) - || (tag.icon!.url !== exists.originalUrl) - ) { - await Emojis.update({ - host, - name, - }, { - uri: tag.id, - originalUrl: tag.icon!.url, - publicUrl: tag.icon!.url, - updatedAt: new Date(), - }); - - return await Emojis.findOneBy({ - host, - name, - }) as Emoji; - } - - return exists; - } - - logger.info(`register emoji host=${host}, name=${name}`); - - return await Emojis.insert({ - id: genId(), - host, - name, - uri: tag.id, - originalUrl: tag.icon!.url, - publicUrl: tag.icon!.url, - updatedAt: new Date(), - aliases: [], - } as Partial).then(x => Emojis.findOneByOrFail(x.identifiers[0])); - })); -} From 57f5a808e258ac00ef87fd427bda914f3db31f23 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 05:24:52 +0900 Subject: [PATCH 053/180] wip --- .../remote/activitypub/ApDbResolverService.ts | 175 ++++++++++++++++++ .../remote/activitypub/ApInboxService.ts | 30 ++- .../remote/activitypub/db-resolver.ts | 155 ---------------- 3 files changed, 186 insertions(+), 174 deletions(-) create mode 100644 packages/backend/src/services/remote/activitypub/ApDbResolverService.ts delete mode 100644 packages/backend/src/services/remote/activitypub/db-resolver.ts diff --git a/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts b/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts new file mode 100644 index 000000000000..dfa7525f6711 --- /dev/null +++ b/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts @@ -0,0 +1,175 @@ +import { Inject, Injectable } from '@nestjs/common'; +import escapeRegexp from 'escape-regexp'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { MessagingMessages, Notes, UserPublickeys } from '@/models/index.js'; +import { Users } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type { CacheableRemoteUser, CacheableUser } from '@/models/entities/user.js'; +import { Cache } from '@/misc/cache.js'; +import type { UserPublickey } from '@/models/entities/user-publickey.js'; +import type { UserCacheService } from '@/services/UserCacheService.js'; +import type { Note } from '@/models/entities/note.js'; +import type { MessagingMessage } from '@/models/entities/messaging-message.js'; +import { getApId } from './type.js'; +import type { IObject } from './type.js'; + +export type UriParseResult = { + /** wether the URI was generated by us */ + local: true; + /** id in DB */ + id: string; + /** hint of type, e.g. "notes", "users" */ + type: string; + /** any remaining text after type and id, not including the slash after id. undefined if empty */ + rest?: string; +} | { + /** wether the URI was generated by us */ + local: false; + /** uri in DB */ + uri: string; +}; + +@Injectable() +export class ApDbResolverService { + #publicKeyCache: Cache; + #publicKeyByUserIdCache: Cache; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('messagingMessagesRepository') + private messagingMessagesRepository: typeof MessagingMessages, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + + @Inject('userPublickeysRepository') + private userPublickeysRepository: typeof UserPublickeys, + + private userCacheService: UserCacheService, + ) { + this.#publicKeyCache = new Cache(Infinity); + this.#publicKeyByUserIdCache = new Cache(Infinity); + } + + public parseUri(value: string | IObject): UriParseResult { + const uri = getApId(value); + + // the host part of a URL is case insensitive, so use the 'i' flag. + const localRegex = new RegExp('^' + escapeRegexp(this.config.url) + '/(\\w+)/(\\w+)(?:\/(.+))?', 'i'); + const matchLocal = uri.match(localRegex); + + if (matchLocal) { + return { + local: true, + type: matchLocal[1], + id: matchLocal[2], + rest: matchLocal[3], + }; + } else { + return { + local: false, + uri, + }; + } + } + + /** + * AP Note => Misskey Note in DB + */ + public async getNoteFromApId(value: string | IObject): Promise { + const parsed = this.parseUri(value); + + if (parsed.local) { + if (parsed.type !== 'notes') return null; + + return await this.notesRepository.findOneBy({ + id: parsed.id, + }); + } else { + return await this.notesRepository.findOneBy({ + uri: parsed.uri, + }); + } + } + + public async getMessageFromApId(value: string | IObject): Promise { + const parsed = this.parseUri(value); + + if (parsed.local) { + if (parsed.type !== 'notes') return null; + + return await this.messagingMessagesRepository.findOneBy({ + id: parsed.id, + }); + } else { + return await this.messagingMessagesRepository.findOneBy({ + uri: parsed.uri, + }); + } + } + + /** + * AP Person => Misskey User in DB + */ + public async getUserFromApId(value: string | IObject): Promise { + const parsed = this.parseUri(value); + + if (parsed.local) { + if (parsed.type !== 'users') return null; + + return await this.userCacheService.userByIdCache.fetchMaybe(parsed.id, () => Users.findOneBy({ + id: parsed.id, + }).then(x => x ?? undefined)) ?? null; + } else { + return await this.userCacheService.uriPersonCache.fetch(parsed.uri, () => Users.findOneBy({ + uri: parsed.uri, + })); + } + } + + /** + * AP KeyId => Misskey User and Key + */ + public async getAuthUserFromKeyId(keyId: string): Promise<{ + user: CacheableRemoteUser; + key: UserPublickey; + } | null> { + const key = await this.#publicKeyCache.fetch(keyId, async () => { + const key = await this.userPublickeysRepository.findOneBy({ + keyId, + }); + + if (key == null) return null; + + return key; + }, key => key != null); + + if (key == null) return null; + + return { + user: await this.userCacheService.userByIdCache.fetch(key.userId, () => Users.findOneByOrFail({ id: key.userId })) as CacheableRemoteUser, + key, + }; + } + + /** + * AP Actor id => Misskey User and Key + */ + public async getAuthUserFromApId(uri: string): Promise<{ + user: CacheableRemoteUser; + key: UserPublickey | null; + } | null> { + const user = await resolvePerson(uri) as CacheableRemoteUser; + + if (user == null) return null; + + const key = await this.#publicKeyByUserIdCache.fetch(user.id, () => this.userPublickeysRepository.findOneBy({ userId: user.id }), v => v != null); + + return { + user, + key, + }; + } +} diff --git a/packages/backend/src/services/remote/activitypub/ApInboxService.ts b/packages/backend/src/services/remote/activitypub/ApInboxService.ts index a69a0db11a7c..f6bf6d8568ae 100644 --- a/packages/backend/src/services/remote/activitypub/ApInboxService.ts +++ b/packages/backend/src/services/remote/activitypub/ApInboxService.ts @@ -19,6 +19,7 @@ import { createNote, fetchNote } from './models/note.js'; import { updatePerson } from './models/person.js'; import { updateQuestion } from './models/question.js'; import { getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isPost, isRead, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js'; +import type { ApDbResolverService } from './ApDbResolverService.js'; import type { ApResolverService, Resolver } from './ApResolverService.js'; import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IRead, IReject, IRemove, IUndo, IUpdate } from './type.js'; @@ -40,6 +41,7 @@ export class ApInboxService { private noteDeleteService: NoteDeleteService, private appLockService: AppLockService, private apResolverService: ApResolverService, + private apDbResolverService: ApDbResolverService, ) { } @@ -98,8 +100,7 @@ export class ApInboxService { } async #follow(actor: CacheableRemoteUser, activity: IFollow): Promise { - const dbResolver = new DbResolver(); - const followee = await dbResolver.getUserFromApId(activity.object); + const followee = await this.apDbResolverService.getUserFromApId(activity.object); if (followee == null) { return 'skip: followee not found'; @@ -172,8 +173,7 @@ export class ApInboxService { async #acceptFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある - const dbResolver = new DbResolver(); - const follower = await dbResolver.getUserFromApId(activity.actor); + const follower = await this.apDbResolverService.getUserFromApId(activity.actor); if (follower == null) { return 'skip: follower not found'; @@ -280,8 +280,7 @@ export class ApInboxService { async #block(actor: CacheableRemoteUser, activity: IBlock): Promise { // ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず - const dbResolver = new DbResolver(); - const blockee = await dbResolver.getUserFromApId(activity.object); + const blockee = await this.apDbResolverService.getUserFromApId(activity.object); if (blockee == null) { return 'skip: blockee not found'; @@ -432,11 +431,10 @@ export class ApInboxService { const unlock = await this.appLockService.getApLock(uri); try { - const dbResolver = new DbResolver(); - const note = await dbResolver.getNoteFromApId(uri); + const note = await this.apDbResolverService.getNoteFromApId(uri); if (note == null) { - const message = await dbResolver.getMessageFromApId(uri); + const message = await this.apDbResolverService.getMessageFromApId(uri); if (message == null) return 'message not found'; if (message.userId !== actor.id) { @@ -503,8 +501,7 @@ export class ApInboxService { async #rejectFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある - const dbResolver = new DbResolver(); - const follower = await dbResolver.getUserFromApId(activity.actor); + const follower = await this.apDbResolverService.getUserFromApId(activity.actor); if (follower == null) { return 'skip: follower not found'; @@ -569,9 +566,7 @@ export class ApInboxService { } async #undoAccept(actor: CacheableRemoteUser, activity: IAccept): Promise { - const dbResolver = new DbResolver(); - - const follower = await dbResolver.getUserFromApId(activity.object); + const follower = await this.apDbResolverService.getUserFromApId(activity.object); if (follower == null) { return 'skip: follower not found'; } @@ -604,8 +599,7 @@ export class ApInboxService { } async #undoBlock(actor: CacheableRemoteUser, activity: IBlock): Promise { - const dbResolver = new DbResolver(); - const blockee = await dbResolver.getUserFromApId(activity.object); + const blockee = await this.apDbResolverService.getUserFromApId(activity.object); if (blockee == null) { return 'skip: blockee not found'; @@ -620,9 +614,7 @@ export class ApInboxService { } async #undoFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { - const dbResolver = new DbResolver(); - - const followee = await dbResolver.getUserFromApId(activity.object); + const followee = await this.apDbResolverService.getUserFromApId(activity.object); if (followee == null) { return 'skip: followee not found'; } diff --git a/packages/backend/src/services/remote/activitypub/db-resolver.ts b/packages/backend/src/services/remote/activitypub/db-resolver.ts deleted file mode 100644 index 1a02f675ca40..000000000000 --- a/packages/backend/src/services/remote/activitypub/db-resolver.ts +++ /dev/null @@ -1,155 +0,0 @@ -import escapeRegexp from 'escape-regexp'; -import config from '@/config/index.js'; -import { Note } from '@/models/entities/note.js'; -import { User, IRemoteUser, CacheableRemoteUser, CacheableUser } from '@/models/entities/user.js'; -import { UserPublickey } from '@/models/entities/user-publickey.js'; -import { MessagingMessage } from '@/models/entities/messaging-message.js'; -import { Notes, Users, UserPublickeys, MessagingMessages } from '@/models/index.js'; -import { Cache } from '@/misc/cache.js'; -import { uriPersonCache, userByIdCache } from '@/services/user-cache.js'; -import { IObject, getApId } from './type.js'; -import { resolvePerson } from './models/person.js'; - -const publicKeyCache = new Cache(Infinity); -const publicKeyByUserIdCache = new Cache(Infinity); - -export type UriParseResult = { - /** wether the URI was generated by us */ - local: true; - /** id in DB */ - id: string; - /** hint of type, e.g. "notes", "users" */ - type: string; - /** any remaining text after type and id, not including the slash after id. undefined if empty */ - rest?: string; -} | { - /** wether the URI was generated by us */ - local: false; - /** uri in DB */ - uri: string; -}; - -export function parseUri(value: string | IObject): UriParseResult { - const uri = getApId(value); - - // the host part of a URL is case insensitive, so use the 'i' flag. - const localRegex = new RegExp('^' + escapeRegexp(config.url) + '/(\\w+)/(\\w+)(?:\/(.+))?', 'i'); - const matchLocal = uri.match(localRegex); - - if (matchLocal) { - return { - local: true, - type: matchLocal[1], - id: matchLocal[2], - rest: matchLocal[3], - }; - } else { - return { - local: false, - uri, - }; - } -} - -export default class DbResolver { - constructor() { - } - - /** - * AP Note => Misskey Note in DB - */ - public async getNoteFromApId(value: string | IObject): Promise { - const parsed = parseUri(value); - - if (parsed.local) { - if (parsed.type !== 'notes') return null; - - return await Notes.findOneBy({ - id: parsed.id, - }); - } else { - return await Notes.findOneBy({ - uri: parsed.uri, - }); - } - } - - public async getMessageFromApId(value: string | IObject): Promise { - const parsed = parseUri(value); - - if (parsed.local) { - if (parsed.type !== 'notes') return null; - - return await MessagingMessages.findOneBy({ - id: parsed.id, - }); - } else { - return await MessagingMessages.findOneBy({ - uri: parsed.uri, - }); - } - } - - /** - * AP Person => Misskey User in DB - */ - public async getUserFromApId(value: string | IObject): Promise { - const parsed = parseUri(value); - - if (parsed.local) { - if (parsed.type !== 'users') return null; - - return await userByIdCache.fetchMaybe(parsed.id, () => Users.findOneBy({ - id: parsed.id, - }).then(x => x ?? undefined)) ?? null; - } else { - return await uriPersonCache.fetch(parsed.uri, () => Users.findOneBy({ - uri: parsed.uri, - })); - } - } - - /** - * AP KeyId => Misskey User and Key - */ - public async getAuthUserFromKeyId(keyId: string): Promise<{ - user: CacheableRemoteUser; - key: UserPublickey; - } | null> { - const key = await publicKeyCache.fetch(keyId, async () => { - const key = await UserPublickeys.findOneBy({ - keyId, - }); - - if (key == null) return null; - - return key; - }, key => key != null); - - if (key == null) return null; - - return { - user: await userByIdCache.fetch(key.userId, () => Users.findOneByOrFail({ id: key.userId })) as CacheableRemoteUser, - key, - }; - } - - /** - * AP Actor id => Misskey User and Key - */ - public async getAuthUserFromApId(uri: string): Promise<{ - user: CacheableRemoteUser; - key: UserPublickey | null; - } | null> { - const user = await resolvePerson(uri) as CacheableRemoteUser; - - if (user == null) return null; - - const key = await publicKeyByUserIdCache.fetch(user.id, () => UserPublickeys.findOneBy({ userId: user.id }), v => v != null); - - return { - user, - key, - }; - } -} From 50f932931991deefa2cfa666bd36f97aa8cdf28b Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 14:54:38 +0900 Subject: [PATCH 054/180] wip --- packages/backend/src/{services => }/logger.ts | 22 +------ .../backend/src/services/LoggerService.ts | 13 ----- .../services/remote/RemoteLoggerService.ts | 12 ++++ .../src/services/remote/ResolveUserService.ts | 34 ++++++----- .../remote/activitypub/ApInboxService.ts | 58 +++++++++++-------- .../remote/activitypub/ApLoggerService.ts | 14 +++++ .../remote/activitypub/ApResolverService.ts | 7 ++- .../src/services/remote/activitypub/logger.ts | 3 - .../activitypub/models/ApNoteService.ts | 5 +- .../backend/src/services/remote/logger.ts | 3 - 10 files changed, 89 insertions(+), 82 deletions(-) rename packages/backend/src/{services => }/logger.ts (88%) delete mode 100644 packages/backend/src/services/LoggerService.ts create mode 100644 packages/backend/src/services/remote/RemoteLoggerService.ts create mode 100644 packages/backend/src/services/remote/activitypub/ApLoggerService.ts delete mode 100644 packages/backend/src/services/remote/activitypub/logger.ts delete mode 100644 packages/backend/src/services/remote/logger.ts diff --git a/packages/backend/src/services/logger.ts b/packages/backend/src/logger.ts similarity index 88% rename from packages/backend/src/services/logger.ts rename to packages/backend/src/logger.ts index 89d6d5720959..35b7dbf3c940 100644 --- a/packages/backend/src/services/logger.ts +++ b/packages/backend/src/logger.ts @@ -2,10 +2,7 @@ import cluster from 'node:cluster'; import chalk from 'chalk'; import { default as convertColor } from 'color-convert'; import { format as dateFormat } from 'date-fns'; -import { envOption } from '../env.js'; -import config from '@/config/index.js'; - -import * as SyslogPro from 'syslog-pro'; +import { envOption } from './env.js'; type Domain = { name: string; @@ -20,26 +17,13 @@ export default class Logger { private store: boolean; private syslogClient: any | null = null; - constructor(domain: string, color?: string, store = true) { + constructor(domain: string, color?: string, store = true, syslogClient = null) { this.domain = { name: domain, color: color, }; this.store = store; - - if (config.syslog) { - this.syslogClient = new SyslogPro.RFC5424({ - applacationName: 'Misskey', - timestamp: true, - encludeStructuredData: true, - color: true, - extendedColor: true, - server: { - target: config.syslog.host, - port: config.syslog.port, - }, - }); - } + this.syslogClient = syslogClient; } public createSubLogger(domain: string, color?: string, store = true): Logger { diff --git a/packages/backend/src/services/LoggerService.ts b/packages/backend/src/services/LoggerService.ts deleted file mode 100644 index e3673e76a020..000000000000 --- a/packages/backend/src/services/LoggerService.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Users } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; - -@Injectable() -export class LoggerService { - constructor( - @Inject(DI_SYMBOLS.config) - private config: Config, - ) { - } -} diff --git a/packages/backend/src/services/remote/RemoteLoggerService.ts b/packages/backend/src/services/remote/RemoteLoggerService.ts new file mode 100644 index 000000000000..7ce8fe6cfc11 --- /dev/null +++ b/packages/backend/src/services/remote/RemoteLoggerService.ts @@ -0,0 +1,12 @@ +import { Inject, Injectable } from '@nestjs/common'; +import Logger from '@/logger.js'; + +@Injectable() +export class RemoteLoggerService { + public logger: Logger; + + constructor( + ) { + this.logger = new Logger('remote', 'cyan'); + } +} diff --git a/packages/backend/src/services/remote/ResolveUserService.ts b/packages/backend/src/services/remote/ResolveUserService.ts index 2ddc15486d41..4c33ec4124a3 100644 --- a/packages/backend/src/services/remote/ResolveUserService.ts +++ b/packages/backend/src/services/remote/ResolveUserService.ts @@ -7,28 +7,32 @@ import type { Users } from '@/models/index.js'; import type { IRemoteUser, User } from '@/models/entities/user.js'; import type { Config } from '@/config/types.js'; import { toPuny } from '@/misc/convert-host.js'; -import { remoteLogger } from './logger.js'; +import type Logger from '@/logger.js'; import { createPerson, updatePerson } from './activitypub/models/person.js'; import webFinger from './webfinger.js'; - -const logger = remoteLogger.createSubLogger('resolve-user'); +import type { RemoteLoggerService } from './RemoteLoggerService.js'; @Injectable() export class ResolveUserService { + #logger: Logger; + constructor( @Inject(DI_SYMBOLS.config) private config: Config, @Inject('usersRepository') private usersRepository: typeof Users, + + private remoteLoggerService: RemoteLoggerService, ) { + this.#logger = this.remoteLoggerService.logger.createSubLogger('resolve-user'); } public async resolveUser(username: string, host: string | null): Promise { const usernameLower = username.toLowerCase(); if (host == null) { - logger.info(`return local user: ${usernameLower}`); + this.#logger.info(`return local user: ${usernameLower}`); return await this.usersRepository.findOneBy({ usernameLower, host: IsNull() }).then(u => { if (u == null) { throw new Error('user not found'); @@ -41,7 +45,7 @@ export class ResolveUserService { host = toPuny(host); if (this.config.host === host) { - logger.info(`return local user: ${usernameLower}`); + this.#logger.info(`return local user: ${usernameLower}`); return await this.usersRepository.findOneBy({ usernameLower, host: IsNull() }).then(u => { if (u == null) { throw new Error('user not found'); @@ -58,7 +62,7 @@ export class ResolveUserService { if (user == null) { const self = await this.#resolveSelf(acctLower); - logger.succ(`return new remote user: ${chalk.magenta(acctLower)}`); + this.#logger.succ(`return new remote user: ${chalk.magenta(acctLower)}`); return await createPerson(self.href); } @@ -69,13 +73,13 @@ export class ResolveUserService { lastFetchedAt: new Date(), }); - logger.info(`try resync: ${acctLower}`); + this.#logger.info(`try resync: ${acctLower}`); const self = await this.#resolveSelf(acctLower); if (user.uri !== self.href) { // if uri mismatch, Fix (user@host <=> AP's Person id(IRemoteUser.uri)) mapping. - logger.info(`uri missmatch: ${acctLower}`); - logger.info(`recovery missmatch uri for (username=${username}, host=${host}) from ${user.uri} to ${self.href}`); + this.#logger.info(`uri missmatch: ${acctLower}`); + this.#logger.info(`recovery missmatch uri for (username=${username}, host=${host}) from ${user.uri} to ${self.href}`); // validate uri const uri = new URL(self.href); @@ -90,12 +94,12 @@ export class ResolveUserService { uri: self.href, }); } else { - logger.info(`uri is fine: ${acctLower}`); + this.#logger.info(`uri is fine: ${acctLower}`); } await updatePerson(self.href); - logger.info(`return resynced remote user: ${acctLower}`); + this.#logger.info(`return resynced remote user: ${acctLower}`); return await this.usersRepository.findOneBy({ uri: self.href }).then(u => { if (u == null) { throw new Error('user not found'); @@ -105,19 +109,19 @@ export class ResolveUserService { }); } - logger.info(`return existing remote user: ${acctLower}`); + this.#logger.info(`return existing remote user: ${acctLower}`); return user; } async #resolveSelf(acctLower: string) { - logger.info(`WebFinger for ${chalk.yellow(acctLower)}`); + this.#logger.info(`WebFinger for ${chalk.yellow(acctLower)}`); const finger = await webFinger(acctLower).catch(e => { - logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: ${ e.statusCode || e.message }`); + this.#logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: ${ e.statusCode || e.message }`); throw new Error(`Failed to WebFinger for ${acctLower}: ${ e.statusCode || e.message }`); }); const self = finger.links.find(link => link.rel != null && link.rel.toLowerCase() === 'self'); if (!self) { - logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: self link not found`); + this.#logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: self link not found`); throw new Error('self link not found'); } return self; diff --git a/packages/backend/src/services/remote/activitypub/ApInboxService.ts b/packages/backend/src/services/remote/activitypub/ApInboxService.ts index f6bf6d8568ae..8f5adeeb9ae7 100644 --- a/packages/backend/src/services/remote/activitypub/ApInboxService.ts +++ b/packages/backend/src/services/remote/activitypub/ApInboxService.ts @@ -14,17 +14,22 @@ import type { NoteDeleteService } from '@/services/NoteDeleteService.js'; import type { NoteCreateService } from '@/services/NoteCreateService.js'; import { concat, toArray, toSingle, unique } from '@/prelude/array.js'; import type { AppLockService } from '@/services/AppLockService.js'; -import { extractDbHost } from '@/misc/convert-host.js'; +import { extractDbHost, isSelfHost } from '@/misc/convert-host.js'; +import type Logger from '@/this.#logger.js'; +import type { MetaService } from '@/services/MetaService.js'; import { createNote, fetchNote } from './models/note.js'; import { updatePerson } from './models/person.js'; import { updateQuestion } from './models/question.js'; import { getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isPost, isRead, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js'; +import type { ApLoggerService } from './ApLoggerService.js'; import type { ApDbResolverService } from './ApDbResolverService.js'; import type { ApResolverService, Resolver } from './ApResolverService.js'; import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IRead, IReject, IRemove, IUndo, IUpdate } from './type.js'; @Injectable() export class ApInboxService { + #logger: Logger; + constructor( @Inject(DI_SYMBOLS.config) private config: Config, @@ -32,6 +37,7 @@ export class ApInboxService { @Inject('usersRepository') private usersRepository: typeof Users, + private metaService: MetaService, private userFollowingService: UserFollowingService, private reactionService: ReactionService, private relayService: RelayService, @@ -42,7 +48,9 @@ export class ApInboxService { private appLockService: AppLockService, private apResolverService: ApResolverService, private apDbResolverService: ApDbResolverService, + private apLoggerService: ApLoggerService, ) { + this.#logger = this.apLoggerService.logger; } public async performActivity(actor: CacheableRemoteUser, activity: IObject) { @@ -54,7 +62,7 @@ export class ApInboxService { await this.performOneActivity(actor, act); } catch (err) { if (err instanceof Error || typeof err === 'string') { - apLogger.error(err); + this.#logger.error(err); } } } @@ -81,9 +89,9 @@ export class ApInboxService { } else if (isReject(activity)) { await this.#reject(actor, activity); } else if (isAdd(activity)) { - await this.#add(actor, activity).catch(err => apLogger.error(err)); + await this.#add(actor, activity).catch(err => this.#logger.error(err)); } else if (isRemove(activity)) { - await this.#remove(actor, activity).catch(err => apLogger.error(err)); + await this.#remove(actor, activity).catch(err => this.#logger.error(err)); } else if (isAnnounce(activity)) { await this.#announce(actor, activity); } else if (isLike(activity)) { @@ -95,7 +103,7 @@ export class ApInboxService { } else if (isFlag(activity)) { await this.#flag(actor, activity); } else { - apLogger.warn(`unrecognized activity type: ${(activity as any).type}`); + this.#logger.warn(`unrecognized activity type: ${(activity as any).type}`); } } @@ -156,12 +164,12 @@ export class ApInboxService { async #accept(actor: CacheableRemoteUser, activity: IAccept): Promise { const uri = activity.id || activity; - apLogger.info(`Accept: ${uri}`); + this.#logger.info(`Accept: ${uri}`); const resolver = this.apResolverService.createResolver(); const object = await resolver.resolve(activity.object).catch(e => { - apLogger.error(`Resolution failed: ${e}`); + this.#logger.error(`Resolution failed: ${e}`); throw e; }); @@ -215,7 +223,7 @@ export class ApInboxService { async #announce(actor: CacheableRemoteUser, activity: IAnnounce): Promise { const uri = getApId(activity); - logger.info(`Announce: ${uri}`); + this.#logger.info(`Announce: ${uri}`); const targetUri = getApId(activity.object); @@ -230,7 +238,7 @@ export class ApInboxService { } // アナウンス先をブロックしてたら中断 - const meta = await fetchMeta(); + const meta = await this.metaService.fetch(); if (meta.blockedHosts.includes(extractDbHost(uri))) return; const unlock = await this.appLockService.getApLock(uri); @@ -250,22 +258,22 @@ export class ApInboxService { // 対象が4xxならスキップ if (e instanceof StatusError) { if (e.isClientError) { - logger.warn(`Ignored announce target ${targetUri} - ${e.statusCode}`); + this.#logger.warn(`Ignored announce target ${targetUri} - ${e.statusCode}`); return; } - logger.warn(`Error in announce target ${targetUri} - ${e.statusCode || e}`); + this.#logger.warn(`Error in announce target ${targetUri} - ${e.statusCode || e}`); } throw e; } if (!await Notes.isVisibleForMe(renote, actor.id)) return 'skip: invalid actor for this activity'; - logger.info(`Creating the (Re)Note: ${uri}`); + this.#logger.info(`Creating the (Re)Note: ${uri}`); const activityAudience = await parseAudience(actor, activity.to, activity.cc); - await post(actor, { + await this.noteCreateService.create(actor, { createdAt: activity.published ? new Date(activity.published) : null, renote, visibility: activityAudience.visibility, @@ -297,7 +305,7 @@ export class ApInboxService { async #create(actor: CacheableRemoteUser, activity: ICreate): Promise { const uri = getApId(activity); - logger.info(`Create: ${uri}`); + this.#logger.info(`Create: ${uri}`); // copy audiences between activity <=> object. if (typeof activity.object === 'object') { @@ -318,14 +326,14 @@ export class ApInboxService { const resolver = this.apResolverService.createResolver(); const object = await resolver.resolve(activity.object).catch(e => { - logger.error(`Resolution failed: ${e}`); + this.#logger.error(`Resolution failed: ${e}`); throw e; }); if (isPost(object)) { this.#createNote(resolver, actor, object, false, activity); } else { - logger.warn(`Unknown type: ${getApType(object)}`); + this.#logger.warn(`Unknown type: ${getApType(object)}`); } } @@ -405,7 +413,7 @@ export class ApInboxService { } async #deleteActor(actor: CacheableRemoteUser, uri: string): Promise { - logger.info(`Deleting the Actor: ${uri}`); + this.#logger.info(`Deleting the Actor: ${uri}`); if (actor.uri !== uri) { return `skip: delete actor ${actor.uri} !== ${uri}`; @@ -413,7 +421,7 @@ export class ApInboxService { const user = await Users.findOneByOrFail({ id: actor.id }); if (user.isDeleted) { - logger.info('skip: already deleted'); + this.#logger.info('skip: already deleted'); } const job = await createDeleteAccountJob(actor); @@ -426,7 +434,7 @@ export class ApInboxService { } async #deleteNote(actor: CacheableRemoteUser, uri: string): Promise { - logger.info(`Deleting the Note: ${uri}`); + this.#logger.info(`Deleting the Note: ${uri}`); const unlock = await this.appLockService.getApLock(uri); @@ -484,12 +492,12 @@ export class ApInboxService { async #reject(actor: CacheableRemoteUser, activity: IReject): Promise { const uri = activity.id || activity; - logger.info(`Reject: ${uri}`); + this.#logger.info(`Reject: ${uri}`); const resolver = this.apResolverService.createResolver(); const object = await resolver.resolve(activity.object).catch(e => { - logger.error(`Resolution failed: ${e}`); + this.#logger.error(`Resolution failed: ${e}`); throw e; }); @@ -547,12 +555,12 @@ export class ApInboxService { const uri = activity.id || activity; - logger.info(`Undo: ${uri}`); + this.#logger.info(`Undo: ${uri}`); const resolver = this.apResolverService.createResolver(); const object = await resolver.resolve(activity.object).catch(e => { - logger.error(`Resolution failed: ${e}`); + this.#logger.error(`Resolution failed: ${e}`); throw e; }); @@ -665,12 +673,12 @@ export class ApInboxService { return 'skip: invalid actor'; } - apLogger.debug('Update'); + this.#logger.debug('Update'); const resolver = this.apResolverService.createResolver(); const object = await resolver.resolve(activity.object).catch(e => { - apLogger.error(`Resolution failed: ${e}`); + this.#logger.error(`Resolution failed: ${e}`); throw e; }); diff --git a/packages/backend/src/services/remote/activitypub/ApLoggerService.ts b/packages/backend/src/services/remote/activitypub/ApLoggerService.ts new file mode 100644 index 000000000000..eaa1ffb1609b --- /dev/null +++ b/packages/backend/src/services/remote/activitypub/ApLoggerService.ts @@ -0,0 +1,14 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type Logger from '@/logger.js'; +import type { RemoteLoggerService } from '@/services/remote/RemoteLoggerService.js'; + +@Injectable() +export class ApLoggerService { + public logger: Logger; + + constructor( + private remoteLoggerService: RemoteLoggerService, + ) { + this.logger = this.remoteLoggerService.logger.createSubLogger('ap', 'magenta'); + } +} diff --git a/packages/backend/src/services/remote/activitypub/ApResolverService.ts b/packages/backend/src/services/remote/activitypub/ApResolverService.ts index d04afacc79f3..6e69debc0081 100644 --- a/packages/backend/src/services/remote/activitypub/ApResolverService.ts +++ b/packages/backend/src/services/remote/activitypub/ApResolverService.ts @@ -8,7 +8,7 @@ import type { MetaService } from '@/services/MetaService.js'; import type { HttpRequestService } from '@/services/HttpRequestService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import { isCollectionOrOrderedCollection } from './type.js'; -import { parseUri } from './db-resolver.js'; +import type { ApDbResolverService } from './ApDbResolverService.js'; import type { ApRendererService } from './ApRendererService.js'; import type { IObject, ICollection, IOrderedCollection } from './type.js'; import type { ApRequestService } from './ApRequestService.js'; @@ -36,6 +36,7 @@ export class ApResolverService { private apRequestService: ApRequestService, private httpRequestService: HttpRequestService, private apRendererService: ApRendererService, + private apDbResolverService: ApDbResolverService, ) { } @@ -51,6 +52,7 @@ export class ApResolverService { this.apRequestService, this.httpRequestService, this.apRendererService, + this.apDbResolverService, ); } } @@ -70,6 +72,7 @@ export class Resolver { private apRequestService: ApRequestService, private httpRequestService: HttpRequestService, private apRendererService: ApRendererService, + private apDbResolverService: ApDbResolverService, ) { this.history = new Set(); } @@ -142,7 +145,7 @@ export class Resolver { } private resolveLocal(url: string): Promise { - const parsed = parseUri(url); + const parsed = this.apDbResolverService.parseUri(url); if (!parsed.local) throw new Error('resolveLocal: not local'); switch (parsed.type) { diff --git a/packages/backend/src/services/remote/activitypub/logger.ts b/packages/backend/src/services/remote/activitypub/logger.ts deleted file mode 100644 index cab51b3bf588..000000000000 --- a/packages/backend/src/services/remote/activitypub/logger.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { remoteLogger } from '../logger.js'; - -export const apLogger = remoteLogger.createSubLogger('ap', 'magenta'); diff --git a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts index bbee92d53a17..f78494f490d2 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts @@ -15,6 +15,7 @@ import type { AppLockService } from '@/services/AppLockService.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import type { NoteCreateService } from '@/services/NoteCreateService.js'; import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js'; +import type { ApDbResolverService } from '../ApDbResolverService.js'; import type { ApResolverService, Resolver } from '../ApResolverService.js'; import type { IObject, IPost } from '../type.js'; @@ -32,6 +33,7 @@ export class ApNoteService { private metaService: MetaService, private appLockService: AppLockService, private noteCreateService: NoteCreateService, + private apDbResolverService: ApDbResolverService, ) { } @@ -63,8 +65,7 @@ export class ApNoteService { * Misskeyに対象のNoteが登録されていればそれを返します。 */ public async fetchNote(object: string | IObject): Promise { - const dbResolver = new DbResolver(); - return await dbResolver.getNoteFromApId(object); + return await this.apDbResolverService.getNoteFromApId(object); } /** diff --git a/packages/backend/src/services/remote/logger.ts b/packages/backend/src/services/remote/logger.ts deleted file mode 100644 index 4921f53bd8c2..000000000000 --- a/packages/backend/src/services/remote/logger.ts +++ /dev/null @@ -1,3 +0,0 @@ -import Logger from '@/services/logger.js'; - -export const remoteLogger = new Logger('remote', 'cyan'); From a1e2af8b9c187ab726780dfd1260b70958e222e5 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 16:05:28 +0900 Subject: [PATCH 055/180] wip --- .../src/services/AccountUpdateService.ts | 36 ++++++ packages/backend/src/services/i/update.ts | 19 ---- ...-manager.ts => ApDeliverManagerService.ts} | 105 ++++++++++++------ 3 files changed, 106 insertions(+), 54 deletions(-) create mode 100644 packages/backend/src/services/AccountUpdateService.ts delete mode 100644 packages/backend/src/services/i/update.ts rename packages/backend/src/services/remote/activitypub/{deliver-manager.ts => ApDeliverManagerService.ts} (56%) diff --git a/packages/backend/src/services/AccountUpdateService.ts b/packages/backend/src/services/AccountUpdateService.ts new file mode 100644 index 000000000000..2f5c58ea0859 --- /dev/null +++ b/packages/backend/src/services/AccountUpdateService.ts @@ -0,0 +1,36 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { Users } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type { User } from '@/models/entities/user.js'; +import type { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; +import type { RelayService } from '@/services/RelayService.js'; +import type { ApDeliverManagerService } from '@/services/remote/activitypub/ApDeliverManagerService.js'; + +@Injectable() +export class AccountUpdateService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + private apRendererService: ApRendererService, + private apDeliverManagerService: ApDeliverManagerService, + private relayService: RelayService, + ) { + } + + public async publishToFollowers(userId: User['id']) { + const user = await this.usersRepository.findOneBy({ id: userId }); + if (user == null) throw new Error('user not found'); + + // フォロワーがリモートユーザーかつ投稿者がローカルユーザーならUpdateを配信 + if (Users.isLocalUser(user)) { + const content = this.apRendererService.renderActivity(this.apRendererService.renderUpdate(await this.apRendererService.renderPerson(user), user)); + this.apDeliverManagerService.deliverToFollowers(user, content); + this.relayService.deliverToRelays(user, content); + } + } +} diff --git a/packages/backend/src/services/i/update.ts b/packages/backend/src/services/i/update.ts deleted file mode 100644 index 257db678113a..000000000000 --- a/packages/backend/src/services/i/update.ts +++ /dev/null @@ -1,19 +0,0 @@ -import renderUpdate from '@/services/remote/activitypub/renderer/update.js'; -import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; -import { Users } from '@/models/index.js'; -import type { User } from '@/models/entities/user.js'; -import { renderPerson } from '@/services/remote/activitypub/renderer/person.js'; -import { deliverToFollowers } from '@/services/remote/activitypub/deliver-manager.js'; -import { deliverToRelays } from '../relay.js'; - -export async function publishToFollowers(userId: User['id']) { - const user = await Users.findOneBy({ id: userId }); - if (user == null) throw new Error('user not found'); - - // フォロワーがリモートユーザーかつ投稿者がローカルユーザーならUpdateを配信 - if (Users.isLocalUser(user)) { - const content = renderActivity(renderUpdate(await renderPerson(user), user)); - deliverToFollowers(user, content); - deliverToRelays(user, content); - } -} diff --git a/packages/backend/src/services/remote/activitypub/deliver-manager.ts b/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts similarity index 56% rename from packages/backend/src/services/remote/activitypub/deliver-manager.ts rename to packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts index 4c1999e4cb3e..5d75d83a2306 100644 --- a/packages/backend/src/services/remote/activitypub/deliver-manager.ts +++ b/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts @@ -1,9 +1,11 @@ -import { Users, Followings } from '@/models/index.js'; -import { ILocalUser, IRemoteUser, User } from '@/models/entities/user.js'; -import { deliver } from '@/queue/index.js'; +import { Inject, Injectable } from '@nestjs/common'; import { IsNull, Not } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Followings , Users } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type { ILocalUser, IRemoteUser, User } from '@/models/entities/user.js'; +import type { QueueService } from '@/queue/queue.service'; -//#region types interface IRecipe { type: string; } @@ -22,9 +24,59 @@ const isFollowers = (recipe: any): recipe is IFollowersRecipe => const isDirect = (recipe: any): recipe is IDirectRecipe => recipe.type === 'Direct'; -//#endregion -export default class DeliverManager { +@Injectable() +export class ApDeliverManagerService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + private queueService: QueueService, + ) { + } + + /** + * Deliver activity to followers + * @param activity Activity + * @param from Followee + */ + public async deliverToFollowers(actor: { id: ILocalUser['id']; host: null; }, activity: any) { + const manager = new DeliverManager( + this.usersRepository, + this.followingsRepository, + this.queueService, + actor, + activity, + ); + manager.addFollowersRecipe(); + await manager.execute(); + } + + /** + * Deliver activity to user + * @param activity Activity + * @param to Target user + */ + public async deliverToUser(actor: { id: ILocalUser['id']; host: null; }, activity: any, to: IRemoteUser) { + const manager = new DeliverManager( + this.usersRepository, + this.followingsRepository, + this.queueService, + actor, + activity, + ); + manager.addDirectRecipe(to); + await manager.execute(); + } +} + +class DeliverManager { private actor: { id: User['id']; host: null; }; private activity: any; private recipes: IRecipe[] = []; @@ -34,7 +86,14 @@ export default class DeliverManager { * @param actor Actor * @param activity Activity to deliver */ - constructor(actor: { id: User['id']; host: null; }, activity: any) { + constructor( + private usersRepository: typeof Users, + private followingsRepository: typeof Followings, + private queueService: QueueService, + + actor: { id: User['id']; host: null; }, + activity: any, + ) { this.actor = actor; this.activity = activity; } @@ -75,7 +134,7 @@ export default class DeliverManager { * Execute delivers */ public async execute() { - if (!Users.isLocalUser(this.actor)) return; + if (!this.usersRepository.isLocalUser(this.actor)) return; const inboxes = new Set(); @@ -89,7 +148,7 @@ export default class DeliverManager { // followers deliver // TODO: SELECT DISTINCT ON ("followerSharedInbox") "followerSharedInbox" みたいな問い合わせにすればよりパフォーマンス向上できそう // ただ、sharedInboxがnullなリモートユーザーも稀におり、その対応ができなさそう? - const followers = await Followings.find({ + const followers = await this.followingsRepository.find({ where: { followeeId: this.actor.id, followerHost: Not(IsNull()), @@ -117,35 +176,11 @@ export default class DeliverManager { // check that they actually have an inbox && recipe.to.inbox != null, ) - .forEach(recipe => inboxes.add(recipe.to.inbox!)); + .forEach(recipe => inboxes.add(recipe.to.inbox!)); // deliver for (const inbox of inboxes) { - deliver(this.actor, this.activity, inbox); + this.queueService.deliver(this.actor, this.activity, inbox); } } } - -//#region Utilities -/** - * Deliver activity to followers - * @param activity Activity - * @param from Followee - */ -export async function deliverToFollowers(actor: { id: ILocalUser['id']; host: null; }, activity: any) { - const manager = new DeliverManager(actor, activity); - manager.addFollowersRecipe(); - await manager.execute(); -} - -/** - * Deliver activity to user - * @param activity Activity - * @param to Target user - */ -export async function deliverToUser(actor: { id: ILocalUser['id']; host: null; }, activity: any, to: IRemoteUser) { - const manager = new DeliverManager(actor, activity); - manager.addDirectRecipe(to); - await manager.execute(); -} -//#endregion From 130442bb4b5a6fcf382529f08182d93a1447fc25 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 16:38:06 +0900 Subject: [PATCH 056/180] wip --- .../src/services/ImageProcessingService.ts | 99 +++ .../src/services/VideoProcessingService.ts | 43 ++ .../src/services/drive/DriveLoggerService.ts | 12 + .../src/services/drive/DriveService.ts | 727 ++++++++++++++++++ .../services/drive/InternalStorageService.ts | 45 ++ .../backend/src/services/drive/S3Service.ts | 38 + .../backend/src/services/drive/add-file.ts | 540 ------------- .../backend/src/services/drive/delete-file.ts | 101 --- .../drive/generate-video-thumbnail.ts | 29 - .../src/services/drive/image-processor.ts | 87 --- .../src/services/drive/internal-storage.ts | 34 - packages/backend/src/services/drive/logger.ts | 3 - packages/backend/src/services/drive/s3.ts | 24 - .../src/services/drive/upload-from-url.ts | 68 -- 14 files changed, 964 insertions(+), 886 deletions(-) create mode 100644 packages/backend/src/services/ImageProcessingService.ts create mode 100644 packages/backend/src/services/VideoProcessingService.ts create mode 100644 packages/backend/src/services/drive/DriveLoggerService.ts create mode 100644 packages/backend/src/services/drive/DriveService.ts create mode 100644 packages/backend/src/services/drive/InternalStorageService.ts create mode 100644 packages/backend/src/services/drive/S3Service.ts delete mode 100644 packages/backend/src/services/drive/add-file.ts delete mode 100644 packages/backend/src/services/drive/delete-file.ts delete mode 100644 packages/backend/src/services/drive/generate-video-thumbnail.ts delete mode 100644 packages/backend/src/services/drive/image-processor.ts delete mode 100644 packages/backend/src/services/drive/internal-storage.ts delete mode 100644 packages/backend/src/services/drive/logger.ts delete mode 100644 packages/backend/src/services/drive/s3.ts delete mode 100644 packages/backend/src/services/drive/upload-from-url.ts diff --git a/packages/backend/src/services/ImageProcessingService.ts b/packages/backend/src/services/ImageProcessingService.ts new file mode 100644 index 000000000000..6e8cdd865b25 --- /dev/null +++ b/packages/backend/src/services/ImageProcessingService.ts @@ -0,0 +1,99 @@ +import { Inject, Injectable } from '@nestjs/common'; +import sharp from 'sharp'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; + +export type IImage = { + data: Buffer; + ext: string | null; + type: string; +}; + +@Injectable() +export class ImageProcessingService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + ) { + } + + /** + * Convert to JPEG + * with resize, remove metadata, resolve orientation, stop animation + */ + public async convertToJpeg(path: string, width: number, height: number): Promise { + return this.convertSharpToJpeg(await sharp(path), width, height); + } + + public async convertSharpToJpeg(sharp: sharp.Sharp, width: number, height: number): Promise { + const data = await sharp + .resize(width, height, { + fit: 'inside', + withoutEnlargement: true, + }) + .rotate() + .jpeg({ + quality: 85, + progressive: true, + }) + .toBuffer(); + + return { + data, + ext: 'jpg', + type: 'image/jpeg', + }; + } + + /** + * Convert to WebP + * with resize, remove metadata, resolve orientation, stop animation + */ + public async convertToWebp(path: string, width: number, height: number, quality = 85): Promise { + return this.convertSharpToWebp(await sharp(path), width, height, quality); + } + + public async convertSharpToWebp(sharp: sharp.Sharp, width: number, height: number, quality = 85): Promise { + const data = await sharp + .resize(width, height, { + fit: 'inside', + withoutEnlargement: true, + }) + .rotate() + .webp({ + quality, + }) + .toBuffer(); + + return { + data, + ext: 'webp', + type: 'image/webp', + }; + } + + /** + * Convert to PNG + * with resize, remove metadata, resolve orientation, stop animation + */ + public async convertToPng(path: string, width: number, height: number): Promise { + return this.convertSharpToPng(await sharp(path), width, height); + } + + public async convertSharpToPng(sharp: sharp.Sharp, width: number, height: number): Promise { + const data = await sharp + .resize(width, height, { + fit: 'inside', + withoutEnlargement: true, + }) + .rotate() + .png() + .toBuffer(); + + return { + data, + ext: 'png', + type: 'image/png', + }; + } +} diff --git a/packages/backend/src/services/VideoProcessingService.ts b/packages/backend/src/services/VideoProcessingService.ts new file mode 100644 index 000000000000..b2c312651b0f --- /dev/null +++ b/packages/backend/src/services/VideoProcessingService.ts @@ -0,0 +1,43 @@ +import { Inject, Injectable } from '@nestjs/common'; +import FFmpeg from 'fluent-ffmpeg'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; +import type { IImage, ImageProcessingService } from '@/services/ImageProcessingService.js'; +import { createTempDir } from '@/misc/create-temp'; + +@Injectable() +export class VideoProcessingService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + private imageProcessingService: ImageProcessingService, + ) { + } + + public async generateVideoThumbnail(source: string): Promise { + const [dir, cleanup] = await createTempDir(); + + try { + await new Promise((res, rej) => { + FFmpeg({ + source, + }) + .on('end', res) + .on('error', rej) + .screenshot({ + folder: dir, + filename: 'out.png', // must have .png extension + count: 1, + timestamps: ['5%'], + }); + }); + + // JPEGに変換 (Webpでもいいが、MastodonはWebpをサポートせず表示できなくなる) + return await this.imageProcessingService.convertToJpeg(`${dir}/out.png`, 498, 280); + } finally { + cleanup(); + } + } +} + diff --git a/packages/backend/src/services/drive/DriveLoggerService.ts b/packages/backend/src/services/drive/DriveLoggerService.ts new file mode 100644 index 000000000000..eea998e15b26 --- /dev/null +++ b/packages/backend/src/services/drive/DriveLoggerService.ts @@ -0,0 +1,12 @@ +import { Inject, Injectable } from '@nestjs/common'; +import Logger from '@/logger.js'; + +@Injectable() +export class DriveLoggerService { + public logger: Logger; + + constructor( + ) { + this.logger = new Logger('drive', 'blue'); + } +} diff --git a/packages/backend/src/services/drive/DriveService.ts b/packages/backend/src/services/drive/DriveService.ts new file mode 100644 index 000000000000..f0303dd4f860 --- /dev/null +++ b/packages/backend/src/services/drive/DriveService.ts @@ -0,0 +1,727 @@ +import * as fs from 'node:fs'; +import { Inject, Injectable } from '@nestjs/common'; +import { v4 as uuid } from 'uuid'; +import sharp from 'sharp'; +import { IsNull } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DriveFiles , DriveFolders, Users } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/Logger.js'; +import type { IRemoteUser, User } from '@/models/entities/user.js'; +import type { MetaService } from '@/services/MetaService.js'; +import { DriveFile } from '@/models/entities/drive-file.js'; +import { genId } from '@/misc/gen-id.js'; +import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; +import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; +import { contentDisposition } from '@/misc/content-disposition.js'; +import { getFileInfo } from '@/misc/get-file-info.js'; +import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import type { VideoProcessingService } from '@/services/VideoProcessingService.js'; +import type { IImage, ImageProcessingService } from '@/services/ImageProcessingService.js'; +import type { QueueService } from '@/queue/queue.service.js'; +import type { DriveFolder } from '@/models/entities/drive-folder.js'; +import { downloadUrl } from '@/misc/download-url.js'; +import { createTemp } from '@/misc/create-temp.js'; +import type DriveChart from '@/services/chart/charts/drive.js'; +import type PerUserDriveChart from '@/services/chart/charts/per-user-drive.js'; +import type InstanceChart from '@/services/chart/charts/instance.js'; +import type { S3Service } from './S3Service.js'; +import type { InternalStorageService } from './InternalStorageService.js'; +import type S3 from 'aws-sdk/clients/s3.js'; +import type { DriveLoggerService } from './DriveLoggerService.js'; + +type AddFileArgs = { + /** User who wish to add file */ + user: { id: User['id']; host: User['host']; driveCapacityOverrideMb: User['driveCapacityOverrideMb'] } | null; + /** File path */ + path: string; + /** Name */ + name?: string | null; + /** Comment */ + comment?: string | null; + /** Folder ID */ + folderId?: any; + /** If set to true, forcibly upload the file even if there is a file with the same hash. */ + force?: boolean; + /** Do not save file to local */ + isLink?: boolean; + /** URL of source (URLからアップロードされた場合(ローカル/リモート)の元URL) */ + url?: string | null; + /** URL of source (リモートインスタンスのURLからアップロードされた場合の元URL) */ + uri?: string | null; + /** Mark file as sensitive */ + sensitive?: boolean | null; + + requestIp?: string | null; + requestHeaders?: Record | null; +}; + +type UploadFromUrlArgs = { + url: string; + user: { id: User['id']; host: User['host'] } | null; + folderId?: DriveFolder['id'] | null; + uri?: string | null; + sensitive?: boolean; + force?: boolean; + isLink?: boolean; + comment?: string | null; + requestIp?: string | null; + requestHeaders?: Record | null; +}; + +@Injectable() +export class DriveService { + #registerLogger: Logger; + #downloaderLogger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private metaService: MetaService, + private internalStorageService: InternalStorageService, + private s3Service: S3Service, + private imageProcessingService: ImageProcessingService, + private videoProcessingService: VideoProcessingService, + private globalEventService: GlobalEventService, + private queueService: QueueService, + private driveChart: DriveChart, + private perUserDriveChart: PerUserDriveChart, + private instanceChart: InstanceChart, + private driveLoggerService: DriveLoggerService, + ) { + this.#registerLogger = this.driveLoggerService.logger.createSubLogger('register', 'yellow'); + this.#downloaderLogger = this.driveLoggerService.logger.createSubLogger('downloader'); + } + + /*** + * Save file + * @param path Path for original + * @param name Name for original + * @param type Content-Type for original + * @param hash Hash for original + * @param size Size for original + */ + async #save(file: DriveFile, path: string, name: string, type: string, hash: string, size: number): Promise { + // thunbnail, webpublic を必要なら生成 + const alts = await this.generateAlts(path, type, !file.uri); + + const meta = await this.metaService.fetch(); + + if (meta.useObjectStorage) { + //#region ObjectStorage params + let [ext] = (name.match(/\.([a-zA-Z0-9_-]+)$/) || ['']); + + if (ext === '') { + if (type === 'image/jpeg') ext = '.jpg'; + if (type === 'image/png') ext = '.png'; + if (type === 'image/webp') ext = '.webp'; + if (type === 'image/apng') ext = '.apng'; + if (type === 'image/vnd.mozilla.apng') ext = '.apng'; + } + + // 拡張子からContent-Typeを設定してそうな挙動を示すオブジェクトストレージ (upcloud?) も存在するので、 + // 許可されているファイル形式でしか拡張子をつけない + if (!FILE_TYPE_BROWSERSAFE.includes(type)) { + ext = ''; + } + + const baseUrl = meta.objectStorageBaseUrl + || `${ meta.objectStorageUseSSL ? 'https' : 'http' }://${ meta.objectStorageEndpoint }${ meta.objectStoragePort ? `:${meta.objectStoragePort}` : '' }/${ meta.objectStorageBucket }`; + + // for original + const key = `${meta.objectStoragePrefix}/${uuid()}${ext}`; + const url = `${ baseUrl }/${ key }`; + + // for alts + let webpublicKey: string | null = null; + let webpublicUrl: string | null = null; + let thumbnailKey: string | null = null; + let thumbnailUrl: string | null = null; + //#endregion + + //#region Uploads + this.#registerLogger.info(`uploading original: ${key}`); + const uploads = [ + this.#upload(key, fs.createReadStream(path), type, name), + ]; + + if (alts.webpublic) { + webpublicKey = `${meta.objectStoragePrefix}/webpublic-${uuid()}.${alts.webpublic.ext}`; + webpublicUrl = `${ baseUrl }/${ webpublicKey }`; + + this.#registerLogger.info(`uploading webpublic: ${webpublicKey}`); + uploads.push(this.#upload(webpublicKey, alts.webpublic.data, alts.webpublic.type, name)); + } + + if (alts.thumbnail) { + thumbnailKey = `${meta.objectStoragePrefix}/thumbnail-${uuid()}.${alts.thumbnail.ext}`; + thumbnailUrl = `${ baseUrl }/${ thumbnailKey }`; + + this.#registerLogger.info(`uploading thumbnail: ${thumbnailKey}`); + uploads.push(this.#upload(thumbnailKey, alts.thumbnail.data, alts.thumbnail.type)); + } + + await Promise.all(uploads); + //#endregion + + file.url = url; + file.thumbnailUrl = thumbnailUrl; + file.webpublicUrl = webpublicUrl; + file.accessKey = key; + file.thumbnailAccessKey = thumbnailKey; + file.webpublicAccessKey = webpublicKey; + file.webpublicType = alts.webpublic?.type ?? null; + file.name = name; + file.type = type; + file.md5 = hash; + file.size = size; + file.storedInternal = false; + + return await this.driveFilesRepository.insert(file).then(x => this.driveFilesRepository.findOneByOrFail(x.identifiers[0])); + } else { // use internal storage + const accessKey = uuid(); + const thumbnailAccessKey = 'thumbnail-' + uuid(); + const webpublicAccessKey = 'webpublic-' + uuid(); + + const url = this.internalStorageService.saveFromPath(accessKey, path); + + let thumbnailUrl: string | null = null; + let webpublicUrl: string | null = null; + + if (alts.thumbnail) { + thumbnailUrl = this.internalStorageService.saveFromBuffer(thumbnailAccessKey, alts.thumbnail.data); + this.#registerLogger.info(`thumbnail stored: ${thumbnailAccessKey}`); + } + + if (alts.webpublic) { + webpublicUrl = this.internalStorageService.saveFromBuffer(webpublicAccessKey, alts.webpublic.data); + this.#registerLogger.info(`web stored: ${webpublicAccessKey}`); + } + + file.storedInternal = true; + file.url = url; + file.thumbnailUrl = thumbnailUrl; + file.webpublicUrl = webpublicUrl; + file.accessKey = accessKey; + file.thumbnailAccessKey = thumbnailAccessKey; + file.webpublicAccessKey = webpublicAccessKey; + file.webpublicType = alts.webpublic?.type ?? null; + file.name = name; + file.type = type; + file.md5 = hash; + file.size = size; + + return await this.driveFilesRepository.insert(file).then(x => this.driveFilesRepository.findOneByOrFail(x.identifiers[0])); + } + } + + /** + * Generate webpublic, thumbnail, etc + * @param path Path for original + * @param type Content-Type for original + * @param generateWeb Generate webpublic or not + */ + public async generateAlts(path: string, type: string, generateWeb: boolean) { + if (type.startsWith('video/')) { + try { + const thumbnail = await this.videoProcessingService.generateVideoThumbnail(path); + return { + webpublic: null, + thumbnail, + }; + } catch (err) { + this.#registerLogger.warn(`GenerateVideoThumbnail failed: ${err}`); + return { + webpublic: null, + thumbnail: null, + }; + } + } + + if (!['image/jpeg', 'image/png', 'image/webp', 'image/svg+xml'].includes(type)) { + this.#registerLogger.debug('web image and thumbnail not created (not an required file)'); + return { + webpublic: null, + thumbnail: null, + }; + } + + let img: sharp.Sharp | null = null; + let satisfyWebpublic: boolean; + + try { + img = sharp(path); + const metadata = await img.metadata(); + const isAnimated = metadata.pages && metadata.pages > 1; + + // skip animated + if (isAnimated) { + return { + webpublic: null, + thumbnail: null, + }; + } + + satisfyWebpublic = !!( + type !== 'image/svg+xml' && type !== 'image/webp' && + !(metadata.exif || metadata.iptc || metadata.xmp || metadata.tifftagPhotoshop) && + metadata.width && metadata.width <= 2048 && + metadata.height && metadata.height <= 2048 + ); + } catch (err) { + this.#registerLogger.warn(`sharp failed: ${err}`); + return { + webpublic: null, + thumbnail: null, + }; + } + + // #region webpublic + let webpublic: IImage | null = null; + + if (generateWeb && !satisfyWebpublic) { + this.#registerLogger.info('creating web image'); + + try { + if (['image/jpeg', 'image/webp'].includes(type)) { + webpublic = await this.imageProcessingService.convertSharpToJpeg(img, 2048, 2048); + } else if (['image/png'].includes(type)) { + webpublic = await this.imageProcessingService.convertSharpToPng(img, 2048, 2048); + } else if (['image/svg+xml'].includes(type)) { + webpublic = await this.imageProcessingService.convertSharpToPng(img, 2048, 2048); + } else { + this.#registerLogger.debug('web image not created (not an required image)'); + } + } catch (err) { + this.#registerLogger.warn('web image not created (an error occured)', err as Error); + } + } else { + if (satisfyWebpublic) this.#registerLogger.info('web image not created (original satisfies webpublic)'); + else this.#registerLogger.info('web image not created (from remote)'); + } + // #endregion webpublic + + // #region thumbnail + let thumbnail: IImage | null = null; + + try { + if (['image/jpeg', 'image/webp', 'image/png', 'image/svg+xml'].includes(type)) { + thumbnail = await this.imageProcessingService.convertSharpToWebp(img, 498, 280); + } else { + this.#registerLogger.debug('thumbnail not created (not an required file)'); + } + } catch (err) { + this.#registerLogger.warn('thumbnail not created (an error occured)', err as Error); + } + // #endregion thumbnail + + return { + webpublic, + thumbnail, + }; + } + + /** + * Upload to ObjectStorage + */ + async #upload(key: string, stream: fs.ReadStream | Buffer, type: string, filename?: string) { + if (type === 'image/apng') type = 'image/png'; + if (!FILE_TYPE_BROWSERSAFE.includes(type)) type = 'application/octet-stream'; + + const meta = await this.metaService.fetch(); + + const params = { + Bucket: meta.objectStorageBucket, + Key: key, + Body: stream, + ContentType: type, + CacheControl: 'max-age=31536000, immutable', + } as S3.PutObjectRequest; + + if (filename) params.ContentDisposition = contentDisposition('inline', filename); + if (meta.objectStorageSetPublicRead) params.ACL = 'public-read'; + + const s3 = this.s3Service.getS3(meta); + + const upload = s3.upload(params, { + partSize: s3.endpoint.hostname === 'storage.googleapis.com' ? 500 * 1024 * 1024 : 8 * 1024 * 1024, + }); + + const result = await upload.promise(); + if (result) this.#registerLogger.debug(`Uploaded: ${result.Bucket}/${result.Key} => ${result.Location}`); + } + + async #deleteOldFile(user: IRemoteUser) { + const q = this.driveFilesRepository.createQueryBuilder('file') + .where('file.userId = :userId', { userId: user.id }) + .andWhere('file.isLink = FALSE'); + + if (user.avatarId) { + q.andWhere('file.id != :avatarId', { avatarId: user.avatarId }); + } + + if (user.bannerId) { + q.andWhere('file.id != :bannerId', { bannerId: user.bannerId }); + } + + q.orderBy('file.id', 'ASC'); + + const oldFile = await q.getOne(); + + if (oldFile) { + this.deleteFile(oldFile, true); + } + } + + /** + * Add file to drive + * + */ + public async addFile({ + user, + path, + name = null, + comment = null, + folderId = null, + force = false, + isLink = false, + url = null, + uri = null, + sensitive = null, + requestIp = null, + requestHeaders = null, + }: AddFileArgs): Promise { + let skipNsfwCheck = false; + const instance = await this.metaService.fetch(); + if (user == null) skipNsfwCheck = true; + if (instance.sensitiveMediaDetection === 'none') skipNsfwCheck = true; + if (user && instance.sensitiveMediaDetection === 'local' && Users.isRemoteUser(user)) skipNsfwCheck = true; + if (user && instance.sensitiveMediaDetection === 'remote' && Users.isLocalUser(user)) skipNsfwCheck = true; + + const info = await getFileInfo(path, { + skipSensitiveDetection: skipNsfwCheck, + sensitiveThreshold: // 感度が高いほどしきい値は低くすることになる + instance.sensitiveMediaDetectionSensitivity === 'veryHigh' ? 0.1 : + instance.sensitiveMediaDetectionSensitivity === 'high' ? 0.3 : + instance.sensitiveMediaDetectionSensitivity === 'low' ? 0.7 : + instance.sensitiveMediaDetectionSensitivity === 'veryLow' ? 0.9 : + 0.5, + sensitiveThresholdForPorn: 0.75, + enableSensitiveMediaDetectionForVideos: instance.enableSensitiveMediaDetectionForVideos, + }); + this.#registerLogger.info(`${JSON.stringify(info)}`); + + // 現状 false positive が多すぎて実用に耐えない + //if (info.porn && instance.disallowUploadWhenPredictedAsPorn) { + // throw new IdentifiableError('282f77bf-5816-4f72-9264-aa14d8261a21', 'Detected as porn.'); + //} + + // detect name + const detectedName = name || (info.type.ext ? `untitled.${info.type.ext}` : 'untitled'); + + if (user && !force) { + // Check if there is a file with the same hash + const much = await this.driveFilesRepository.findOneBy({ + md5: info.md5, + userId: user.id, + }); + + if (much) { + this.#registerLogger.info(`file with same hash is found: ${much.id}`); + return much; + } + } + + //#region Check drive usage + if (user && !isLink) { + const usage = await this.driveFilesRepository.calcDriveUsageOf(user); + const u = await Users.findOneBy({ id: user.id }); + + const instance = await this.metaService.fetch(); + let driveCapacity = 1024 * 1024 * (Users.isLocalUser(user) ? instance.localDriveCapacityMb : instance.remoteDriveCapacityMb); + + if (Users.isLocalUser(user) && u?.driveCapacityOverrideMb != null) { + driveCapacity = 1024 * 1024 * u.driveCapacityOverrideMb; + this.#registerLogger.debug('drive capacity override applied'); + this.#registerLogger.debug(`overrideCap: ${driveCapacity}bytes, usage: ${usage}bytes, u+s: ${usage + info.size}bytes`); + } + + this.#registerLogger.debug(`drive usage is ${usage} (max: ${driveCapacity})`); + + // If usage limit exceeded + if (usage + info.size > driveCapacity) { + if (Users.isLocalUser(user)) { + throw new IdentifiableError('c6244ed2-a39a-4e1c-bf93-f0fbd7764fa6', 'No free space.'); + } else { + // (アバターまたはバナーを含まず)最も古いファイルを削除する + this.#deleteOldFile(await Users.findOneByOrFail({ id: user.id }) as IRemoteUser); + } + } + } + //#endregion + + const fetchFolder = async () => { + if (!folderId) { + return null; + } + + const driveFolder = await DriveFolders.findOneBy({ + id: folderId, + userId: user ? user.id : IsNull(), + }); + + if (driveFolder == null) throw new Error('folder-not-found'); + + return driveFolder; + }; + + const properties: { + width?: number; + height?: number; + orientation?: number; + } = {}; + + if (info.width) { + properties['width'] = info.width; + properties['height'] = info.height; + } + if (info.orientation != null) { + properties['orientation'] = info.orientation; + } + + const profile = user ? await UserProfiles.findOneBy({ userId: user.id }) : null; + + const folder = await fetchFolder(); + + let file = new DriveFile(); + file.id = genId(); + file.createdAt = new Date(); + file.userId = user ? user.id : null; + file.userHost = user ? user.host : null; + file.folderId = folder !== null ? folder.id : null; + file.comment = comment; + file.properties = properties; + file.blurhash = info.blurhash || null; + file.isLink = isLink; + file.requestIp = requestIp; + file.requestHeaders = requestHeaders; + file.maybeSensitive = info.sensitive; + file.maybePorn = info.porn; + file.isSensitive = user + ? Users.isLocalUser(user) && profile!.alwaysMarkNsfw ? true : + (sensitive !== null && sensitive !== undefined) + ? sensitive + : false + : false; + + if (info.sensitive && profile!.autoSensitive) file.isSensitive = true; + if (info.sensitive && instance.setSensitiveFlagAutomatically) file.isSensitive = true; + + if (url !== null) { + file.src = url; + + if (isLink) { + file.url = url; + // ローカルプロキシ用 + file.accessKey = uuid(); + file.thumbnailAccessKey = 'thumbnail-' + uuid(); + file.webpublicAccessKey = 'webpublic-' + uuid(); + } + } + + if (uri !== null) { + file.uri = uri; + } + + if (isLink) { + try { + file.size = 0; + file.md5 = info.md5; + file.name = detectedName; + file.type = info.type.mime; + file.storedInternal = false; + + file = await this.driveFilesRepository.insert(file).then(x => this.driveFilesRepository.findOneByOrFail(x.identifiers[0])); + } catch (err) { + // duplicate key error (when already registered) + if (isDuplicateKeyValueError(err)) { + this.#registerLogger.info(`already registered ${file.uri}`); + + file = await this.driveFilesRepository.findOneBy({ + uri: file.uri!, + userId: user ? user.id : IsNull(), + }) as DriveFile; + } else { + this.#registerLogger.error(err as Error); + throw err; + } + } + } else { + file = await (this.#save(file, path, detectedName, info.type.mime, info.md5, info.size)); + } + + this.#registerLogger.succ(`drive file has been created ${file.id}`); + + if (user) { + this.driveFilesRepository.pack(file, { self: true }).then(packedFile => { + // Publish driveFileCreated event + this.globalEventService.publishMainStream(user.id, 'driveFileCreated', packedFile); + this.globalEventService.publishDriveStream(user.id, 'fileCreated', packedFile); + }); + } + + // 統計を更新 + this.driveChart.update(file, true); + this.perUserDriveChart.update(file, true); + if (file.userHost !== null) { + this.instanceChart.updateDrive(file, true); + } + + return file; + } + + public async deleteFile(file: DriveFile, isExpired = false) { + if (file.storedInternal) { + this.internalStorageService.del(file.accessKey!); + + if (file.thumbnailUrl) { + this.internalStorageService.del(file.thumbnailAccessKey!); + } + + if (file.webpublicUrl) { + this.internalStorageService.del(file.webpublicAccessKey!); + } + } else if (!file.isLink) { + this.queueService.createDeleteObjectStorageFileJob(file.accessKey!); + + if (file.thumbnailUrl) { + this.queueService.createDeleteObjectStorageFileJob(file.thumbnailAccessKey!); + } + + if (file.webpublicUrl) { + this.queueService.createDeleteObjectStorageFileJob(file.webpublicAccessKey!); + } + } + + this.#deletePostProcess(file, isExpired); + } + + public async deleteFileSync(file: DriveFile, isExpired = false) { + if (file.storedInternal) { + this.internalStorageService.del(file.accessKey!); + + if (file.thumbnailUrl) { + this.internalStorageService.del(file.thumbnailAccessKey!); + } + + if (file.webpublicUrl) { + this.internalStorageService.del(file.webpublicAccessKey!); + } + } else if (!file.isLink) { + const promises = []; + + promises.push(this.deleteObjectStorageFile(file.accessKey!)); + + if (file.thumbnailUrl) { + promises.push(this.deleteObjectStorageFile(file.thumbnailAccessKey!)); + } + + if (file.webpublicUrl) { + promises.push(this.deleteObjectStorageFile(file.webpublicAccessKey!)); + } + + await Promise.all(promises); + } + + this.#deletePostProcess(file, isExpired); + } + + async #deletePostProcess(file: DriveFile, isExpired = false) { + // リモートファイル期限切れ削除後は直リンクにする + if (isExpired && file.userHost !== null && file.uri != null) { + this.driveFilesRepository.update(file.id, { + isLink: true, + url: file.uri, + thumbnailUrl: null, + webpublicUrl: null, + storedInternal: false, + // ローカルプロキシ用 + accessKey: uuid(), + thumbnailAccessKey: 'thumbnail-' + uuid(), + webpublicAccessKey: 'webpublic-' + uuid(), + }); + } else { + this.driveFilesRepository.delete(file.id); + } + + // 統計を更新 + this.driveChart.update(file, false); + this.perUserDriveChart.update(file, false); + if (file.userHost !== null) { + this.instanceChart.updateDrive(file, false); + } + } + + public async deleteObjectStorageFile(key: string) { + const meta = await this.metaService.fetch(); + + const s3 = this.s3Service.getS3(meta); + + await s3.deleteObject({ + Bucket: meta.objectStorageBucket!, + Key: key, + }).promise(); + } + + public async uploadFromUrl({ + url, + user, + folderId = null, + uri = null, + sensitive = false, + force = false, + isLink = false, + comment = null, + requestIp = null, + requestHeaders = null, + }: UploadFromUrlArgs): Promise { + let name = new URL(url).pathname.split('/').pop() || null; + if (name == null || !DriveFiles.validateFileName(name)) { + name = null; + } + + // If the comment is same as the name, skip comment + // (image.name is passed in when receiving attachment) + if (comment !== null && name === comment) { + comment = null; + } + + // Create temp file + const [path, cleanup] = await createTemp(); + + try { + // write content at URL to temp file + await downloadUrl(url, path); + + const driveFile = await this.addFile({ user, path, name, comment, folderId, force, isLink, url, uri, sensitive, requestIp, requestHeaders }); + this.#downloaderLogger.succ(`Got: ${driveFile.id}`); + return driveFile!; + } catch (err) { + this.#downloaderLogger.error(`Failed to create drive file: ${err}`, { + url: url, + e: err, + }); + throw err; + } finally { + cleanup(); + } + } +} diff --git a/packages/backend/src/services/drive/InternalStorageService.ts b/packages/backend/src/services/drive/InternalStorageService.ts new file mode 100644 index 000000000000..7b916ed60b2e --- /dev/null +++ b/packages/backend/src/services/drive/InternalStorageService.ts @@ -0,0 +1,45 @@ +import * as fs from 'node:fs'; +import * as Path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { dirname } from 'node:path'; +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; + +const _filename = fileURLToPath(import.meta.url); +const _dirname = dirname(_filename); + +const path = Path.resolve(_dirname, '../../../../../files'); + +@Injectable() +export class InternalStorageService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + ) { + } + + public resolvePath(key: string) { + return Path.resolve(path, key); + } + + public read(key: string) { + return fs.createReadStream(this.resolvePath(key)); + } + + public saveFromPath(key: string, srcPath: string) { + fs.mkdirSync(path, { recursive: true }); + fs.copyFileSync(srcPath, this.resolvePath(key)); + return `${this.config.url}/files/${key}`; + } + + public saveFromBuffer(key: string, data: Buffer) { + fs.mkdirSync(path, { recursive: true }); + fs.writeFileSync(this.resolvePath(key), data); + return `${this.config.url}/files/${key}`; + } + + public del(key: string) { + fs.unlink(this.resolvePath(key), () => {}); + } +} diff --git a/packages/backend/src/services/drive/S3Service.ts b/packages/backend/src/services/drive/S3Service.ts new file mode 100644 index 000000000000..c7e49a1b7a69 --- /dev/null +++ b/packages/backend/src/services/drive/S3Service.ts @@ -0,0 +1,38 @@ +import { URL } from 'node:url'; +import { Inject, Injectable } from '@nestjs/common'; +import S3 from 'aws-sdk/clients/s3.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; +import type { Meta } from '@/models/entities/meta'; +import type { HttpRequestService } from '../HttpRequestService'; + +@Injectable() +export class S3Service { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + private httpRequestService: HttpRequestService, + ) { + } + + public getS3(meta: Meta) { + const u = meta.objectStorageEndpoint != null + ? `${meta.objectStorageUseSSL ? 'https://' : 'http://'}${meta.objectStorageEndpoint}` + : `${meta.objectStorageUseSSL ? 'https://' : 'http://'}example.net`; + + return new S3({ + endpoint: meta.objectStorageEndpoint || undefined, + accessKeyId: meta.objectStorageAccessKey!, + secretAccessKey: meta.objectStorageSecretKey!, + region: meta.objectStorageRegion || undefined, + sslEnabled: meta.objectStorageUseSSL, + s3ForcePathStyle: !meta.objectStorageEndpoint // AWS with endPoint omitted + ? false + : meta.objectStorageS3ForcePathStyle, + httpOptions: { + agent: this.httpRequestService.getAgentByUrl(new URL(u), !meta.objectStorageUseProxy), + }, + }); + } +} diff --git a/packages/backend/src/services/drive/add-file.ts b/packages/backend/src/services/drive/add-file.ts deleted file mode 100644 index 709db88f2f05..000000000000 --- a/packages/backend/src/services/drive/add-file.ts +++ /dev/null @@ -1,540 +0,0 @@ -import * as fs from 'node:fs'; - -import { v4 as uuid } from 'uuid'; - -import S3 from 'aws-sdk/clients/s3.js'; -import sharp from 'sharp'; -import { IsNull } from 'typeorm'; -import { publishMainStream, publishDriveStream } from '@/services/stream.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import { contentDisposition } from '@/misc/content-disposition.js'; -import { getFileInfo } from '@/misc/get-file-info.js'; -import { DriveFiles, DriveFolders, Users, Instances, UserProfiles } from '@/models/index.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; -import { IRemoteUser, User } from '@/models/entities/user.js'; -import { driveChart, perUserDriveChart, instanceChart } from '@/services/chart/index.js'; -import { genId } from '@/misc/gen-id.js'; -import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; -import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; -import { IdentifiableError } from '@/misc/identifiable-error.js'; -import { getS3 } from './s3.js'; -import { InternalStorage } from './internal-storage.js'; -import { IImage, convertSharpToJpeg, convertSharpToWebp, convertSharpToPng } from './image-processor.js'; -import { driveLogger } from './logger.js'; -import { GenerateVideoThumbnail } from './generate-video-thumbnail.js'; -import { deleteFile } from './delete-file.js'; - -const logger = driveLogger.createSubLogger('register', 'yellow'); - -/*** - * Save file - * @param path Path for original - * @param name Name for original - * @param type Content-Type for original - * @param hash Hash for original - * @param size Size for original - */ -async function save(file: DriveFile, path: string, name: string, type: string, hash: string, size: number): Promise { - // thunbnail, webpublic を必要なら生成 - const alts = await generateAlts(path, type, !file.uri); - - const meta = await fetchMeta(); - - if (meta.useObjectStorage) { - //#region ObjectStorage params - let [ext] = (name.match(/\.([a-zA-Z0-9_-]+)$/) || ['']); - - if (ext === '') { - if (type === 'image/jpeg') ext = '.jpg'; - if (type === 'image/png') ext = '.png'; - if (type === 'image/webp') ext = '.webp'; - if (type === 'image/apng') ext = '.apng'; - if (type === 'image/vnd.mozilla.apng') ext = '.apng'; - } - - // 拡張子からContent-Typeを設定してそうな挙動を示すオブジェクトストレージ (upcloud?) も存在するので、 - // 許可されているファイル形式でしか拡張子をつけない - if (!FILE_TYPE_BROWSERSAFE.includes(type)) { - ext = ''; - } - - const baseUrl = meta.objectStorageBaseUrl - || `${ meta.objectStorageUseSSL ? 'https' : 'http' }://${ meta.objectStorageEndpoint }${ meta.objectStoragePort ? `:${meta.objectStoragePort}` : '' }/${ meta.objectStorageBucket }`; - - // for original - const key = `${meta.objectStoragePrefix}/${uuid()}${ext}`; - const url = `${ baseUrl }/${ key }`; - - // for alts - let webpublicKey: string | null = null; - let webpublicUrl: string | null = null; - let thumbnailKey: string | null = null; - let thumbnailUrl: string | null = null; - //#endregion - - //#region Uploads - logger.info(`uploading original: ${key}`); - const uploads = [ - upload(key, fs.createReadStream(path), type, name), - ]; - - if (alts.webpublic) { - webpublicKey = `${meta.objectStoragePrefix}/webpublic-${uuid()}.${alts.webpublic.ext}`; - webpublicUrl = `${ baseUrl }/${ webpublicKey }`; - - logger.info(`uploading webpublic: ${webpublicKey}`); - uploads.push(upload(webpublicKey, alts.webpublic.data, alts.webpublic.type, name)); - } - - if (alts.thumbnail) { - thumbnailKey = `${meta.objectStoragePrefix}/thumbnail-${uuid()}.${alts.thumbnail.ext}`; - thumbnailUrl = `${ baseUrl }/${ thumbnailKey }`; - - logger.info(`uploading thumbnail: ${thumbnailKey}`); - uploads.push(upload(thumbnailKey, alts.thumbnail.data, alts.thumbnail.type)); - } - - await Promise.all(uploads); - //#endregion - - file.url = url; - file.thumbnailUrl = thumbnailUrl; - file.webpublicUrl = webpublicUrl; - file.accessKey = key; - file.thumbnailAccessKey = thumbnailKey; - file.webpublicAccessKey = webpublicKey; - file.webpublicType = alts.webpublic?.type ?? null; - file.name = name; - file.type = type; - file.md5 = hash; - file.size = size; - file.storedInternal = false; - - return await DriveFiles.insert(file).then(x => DriveFiles.findOneByOrFail(x.identifiers[0])); - } else { // use internal storage - const accessKey = uuid(); - const thumbnailAccessKey = 'thumbnail-' + uuid(); - const webpublicAccessKey = 'webpublic-' + uuid(); - - const url = InternalStorage.saveFromPath(accessKey, path); - - let thumbnailUrl: string | null = null; - let webpublicUrl: string | null = null; - - if (alts.thumbnail) { - thumbnailUrl = InternalStorage.saveFromBuffer(thumbnailAccessKey, alts.thumbnail.data); - logger.info(`thumbnail stored: ${thumbnailAccessKey}`); - } - - if (alts.webpublic) { - webpublicUrl = InternalStorage.saveFromBuffer(webpublicAccessKey, alts.webpublic.data); - logger.info(`web stored: ${webpublicAccessKey}`); - } - - file.storedInternal = true; - file.url = url; - file.thumbnailUrl = thumbnailUrl; - file.webpublicUrl = webpublicUrl; - file.accessKey = accessKey; - file.thumbnailAccessKey = thumbnailAccessKey; - file.webpublicAccessKey = webpublicAccessKey; - file.webpublicType = alts.webpublic?.type ?? null; - file.name = name; - file.type = type; - file.md5 = hash; - file.size = size; - - return await DriveFiles.insert(file).then(x => DriveFiles.findOneByOrFail(x.identifiers[0])); - } -} - -/** - * Generate webpublic, thumbnail, etc - * @param path Path for original - * @param type Content-Type for original - * @param generateWeb Generate webpublic or not - */ -export async function generateAlts(path: string, type: string, generateWeb: boolean) { - if (type.startsWith('video/')) { - try { - const thumbnail = await GenerateVideoThumbnail(path); - return { - webpublic: null, - thumbnail, - }; - } catch (err) { - logger.warn(`GenerateVideoThumbnail failed: ${err}`); - return { - webpublic: null, - thumbnail: null, - }; - } - } - - if (!['image/jpeg', 'image/png', 'image/webp', 'image/svg+xml'].includes(type)) { - logger.debug('web image and thumbnail not created (not an required file)'); - return { - webpublic: null, - thumbnail: null, - }; - } - - let img: sharp.Sharp | null = null; - let satisfyWebpublic: boolean; - - try { - img = sharp(path); - const metadata = await img.metadata(); - const isAnimated = metadata.pages && metadata.pages > 1; - - // skip animated - if (isAnimated) { - return { - webpublic: null, - thumbnail: null, - }; - } - - satisfyWebpublic = !!( - type !== 'image/svg+xml' && type !== 'image/webp' && - !(metadata.exif || metadata.iptc || metadata.xmp || metadata.tifftagPhotoshop) && - metadata.width && metadata.width <= 2048 && - metadata.height && metadata.height <= 2048 - ); - } catch (err) { - logger.warn(`sharp failed: ${err}`); - return { - webpublic: null, - thumbnail: null, - }; - } - - // #region webpublic - let webpublic: IImage | null = null; - - if (generateWeb && !satisfyWebpublic) { - logger.info('creating web image'); - - try { - if (['image/jpeg', 'image/webp'].includes(type)) { - webpublic = await convertSharpToJpeg(img, 2048, 2048); - } else if (['image/png'].includes(type)) { - webpublic = await convertSharpToPng(img, 2048, 2048); - } else if (['image/svg+xml'].includes(type)) { - webpublic = await convertSharpToPng(img, 2048, 2048); - } else { - logger.debug('web image not created (not an required image)'); - } - } catch (err) { - logger.warn('web image not created (an error occured)', err as Error); - } - } else { - if (satisfyWebpublic) logger.info('web image not created (original satisfies webpublic)'); - else logger.info('web image not created (from remote)'); - } - // #endregion webpublic - - // #region thumbnail - let thumbnail: IImage | null = null; - - try { - if (['image/jpeg', 'image/webp', 'image/png', 'image/svg+xml'].includes(type)) { - thumbnail = await convertSharpToWebp(img, 498, 280); - } else { - logger.debug('thumbnail not created (not an required file)'); - } - } catch (err) { - logger.warn('thumbnail not created (an error occured)', err as Error); - } - // #endregion thumbnail - - return { - webpublic, - thumbnail, - }; -} - -/** - * Upload to ObjectStorage - */ -async function upload(key: string, stream: fs.ReadStream | Buffer, type: string, filename?: string) { - if (type === 'image/apng') type = 'image/png'; - if (!FILE_TYPE_BROWSERSAFE.includes(type)) type = 'application/octet-stream'; - - const meta = await fetchMeta(); - - const params = { - Bucket: meta.objectStorageBucket, - Key: key, - Body: stream, - ContentType: type, - CacheControl: 'max-age=31536000, immutable', - } as S3.PutObjectRequest; - - if (filename) params.ContentDisposition = contentDisposition('inline', filename); - if (meta.objectStorageSetPublicRead) params.ACL = 'public-read'; - - const s3 = getS3(meta); - - const upload = s3.upload(params, { - partSize: s3.endpoint.hostname === 'storage.googleapis.com' ? 500 * 1024 * 1024 : 8 * 1024 * 1024, - }); - - const result = await upload.promise(); - if (result) logger.debug(`Uploaded: ${result.Bucket}/${result.Key} => ${result.Location}`); -} - -async function deleteOldFile(user: IRemoteUser) { - const q = DriveFiles.createQueryBuilder('file') - .where('file.userId = :userId', { userId: user.id }) - .andWhere('file.isLink = FALSE'); - - if (user.avatarId) { - q.andWhere('file.id != :avatarId', { avatarId: user.avatarId }); - } - - if (user.bannerId) { - q.andWhere('file.id != :bannerId', { bannerId: user.bannerId }); - } - - q.orderBy('file.id', 'ASC'); - - const oldFile = await q.getOne(); - - if (oldFile) { - deleteFile(oldFile, true); - } -} - -type AddFileArgs = { - /** User who wish to add file */ - user: { id: User['id']; host: User['host']; driveCapacityOverrideMb: User['driveCapacityOverrideMb'] } | null; - /** File path */ - path: string; - /** Name */ - name?: string | null; - /** Comment */ - comment?: string | null; - /** Folder ID */ - folderId?: any; - /** If set to true, forcibly upload the file even if there is a file with the same hash. */ - force?: boolean; - /** Do not save file to local */ - isLink?: boolean; - /** URL of source (URLからアップロードされた場合(ローカル/リモート)の元URL) */ - url?: string | null; - /** URL of source (リモートインスタンスのURLからアップロードされた場合の元URL) */ - uri?: string | null; - /** Mark file as sensitive */ - sensitive?: boolean | null; - - requestIp?: string | null; - requestHeaders?: Record | null; -}; - -/** - * Add file to drive - * - */ -export async function addFile({ - user, - path, - name = null, - comment = null, - folderId = null, - force = false, - isLink = false, - url = null, - uri = null, - sensitive = null, - requestIp = null, - requestHeaders = null, -}: AddFileArgs): Promise { - let skipNsfwCheck = false; - const instance = await fetchMeta(); - if (user == null) skipNsfwCheck = true; - if (instance.sensitiveMediaDetection === 'none') skipNsfwCheck = true; - if (user && instance.sensitiveMediaDetection === 'local' && Users.isRemoteUser(user)) skipNsfwCheck = true; - if (user && instance.sensitiveMediaDetection === 'remote' && Users.isLocalUser(user)) skipNsfwCheck = true; - - const info = await getFileInfo(path, { - skipSensitiveDetection: skipNsfwCheck, - sensitiveThreshold: // 感度が高いほどしきい値は低くすることになる - instance.sensitiveMediaDetectionSensitivity === 'veryHigh' ? 0.1 : - instance.sensitiveMediaDetectionSensitivity === 'high' ? 0.3 : - instance.sensitiveMediaDetectionSensitivity === 'low' ? 0.7 : - instance.sensitiveMediaDetectionSensitivity === 'veryLow' ? 0.9 : - 0.5, - sensitiveThresholdForPorn: 0.75, - enableSensitiveMediaDetectionForVideos: instance.enableSensitiveMediaDetectionForVideos, - }); - logger.info(`${JSON.stringify(info)}`); - - // 現状 false positive が多すぎて実用に耐えない - //if (info.porn && instance.disallowUploadWhenPredictedAsPorn) { - // throw new IdentifiableError('282f77bf-5816-4f72-9264-aa14d8261a21', 'Detected as porn.'); - //} - - // detect name - const detectedName = name || (info.type.ext ? `untitled.${info.type.ext}` : 'untitled'); - - if (user && !force) { - // Check if there is a file with the same hash - const much = await DriveFiles.findOneBy({ - md5: info.md5, - userId: user.id, - }); - - if (much) { - logger.info(`file with same hash is found: ${much.id}`); - return much; - } - } - - //#region Check drive usage - if (user && !isLink) { - const usage = await DriveFiles.calcDriveUsageOf(user); - const u = await Users.findOneBy({ id: user.id }); - - const instance = await fetchMeta(); - let driveCapacity = 1024 * 1024 * (Users.isLocalUser(user) ? instance.localDriveCapacityMb : instance.remoteDriveCapacityMb); - - if (Users.isLocalUser(user) && u?.driveCapacityOverrideMb != null) { - driveCapacity = 1024 * 1024 * u.driveCapacityOverrideMb; - logger.debug('drive capacity override applied'); - logger.debug(`overrideCap: ${driveCapacity}bytes, usage: ${usage}bytes, u+s: ${usage + info.size}bytes`); - } - - logger.debug(`drive usage is ${usage} (max: ${driveCapacity})`); - - // If usage limit exceeded - if (usage + info.size > driveCapacity) { - if (Users.isLocalUser(user)) { - throw new IdentifiableError('c6244ed2-a39a-4e1c-bf93-f0fbd7764fa6', 'No free space.'); - } else { - // (アバターまたはバナーを含まず)最も古いファイルを削除する - deleteOldFile(await Users.findOneByOrFail({ id: user.id }) as IRemoteUser); - } - } - } - //#endregion - - const fetchFolder = async () => { - if (!folderId) { - return null; - } - - const driveFolder = await DriveFolders.findOneBy({ - id: folderId, - userId: user ? user.id : IsNull(), - }); - - if (driveFolder == null) throw new Error('folder-not-found'); - - return driveFolder; - }; - - const properties: { - width?: number; - height?: number; - orientation?: number; - } = {}; - - if (info.width) { - properties['width'] = info.width; - properties['height'] = info.height; - } - if (info.orientation != null) { - properties['orientation'] = info.orientation; - } - - const profile = user ? await UserProfiles.findOneBy({ userId: user.id }) : null; - - const folder = await fetchFolder(); - - let file = new DriveFile(); - file.id = genId(); - file.createdAt = new Date(); - file.userId = user ? user.id : null; - file.userHost = user ? user.host : null; - file.folderId = folder !== null ? folder.id : null; - file.comment = comment; - file.properties = properties; - file.blurhash = info.blurhash || null; - file.isLink = isLink; - file.requestIp = requestIp; - file.requestHeaders = requestHeaders; - file.maybeSensitive = info.sensitive; - file.maybePorn = info.porn; - file.isSensitive = user - ? Users.isLocalUser(user) && profile!.alwaysMarkNsfw ? true : - (sensitive !== null && sensitive !== undefined) - ? sensitive - : false - : false; - - if (info.sensitive && profile!.autoSensitive) file.isSensitive = true; - if (info.sensitive && instance.setSensitiveFlagAutomatically) file.isSensitive = true; - - if (url !== null) { - file.src = url; - - if (isLink) { - file.url = url; - // ローカルプロキシ用 - file.accessKey = uuid(); - file.thumbnailAccessKey = 'thumbnail-' + uuid(); - file.webpublicAccessKey = 'webpublic-' + uuid(); - } - } - - if (uri !== null) { - file.uri = uri; - } - - if (isLink) { - try { - file.size = 0; - file.md5 = info.md5; - file.name = detectedName; - file.type = info.type.mime; - file.storedInternal = false; - - file = await DriveFiles.insert(file).then(x => DriveFiles.findOneByOrFail(x.identifiers[0])); - } catch (err) { - // duplicate key error (when already registered) - if (isDuplicateKeyValueError(err)) { - logger.info(`already registered ${file.uri}`); - - file = await DriveFiles.findOneBy({ - uri: file.uri!, - userId: user ? user.id : IsNull(), - }) as DriveFile; - } else { - logger.error(err as Error); - throw err; - } - } - } else { - file = await (save(file, path, detectedName, info.type.mime, info.md5, info.size)); - } - - logger.succ(`drive file has been created ${file.id}`); - - if (user) { - DriveFiles.pack(file, { self: true }).then(packedFile => { - // Publish driveFileCreated event - publishMainStream(user.id, 'driveFileCreated', packedFile); - publishDriveStream(user.id, 'fileCreated', packedFile); - }); - } - - // 統計を更新 - driveChart.update(file, true); - perUserDriveChart.update(file, true); - if (file.userHost !== null) { - instanceChart.updateDrive(file, true); - } - - return file; -} diff --git a/packages/backend/src/services/drive/delete-file.ts b/packages/backend/src/services/drive/delete-file.ts deleted file mode 100644 index 4816a3a31b68..000000000000 --- a/packages/backend/src/services/drive/delete-file.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { DriveFile } from '@/models/entities/drive-file.js'; -import { InternalStorage } from './internal-storage.js'; -import { DriveFiles, Instances } from '@/models/index.js'; -import { driveChart, perUserDriveChart, instanceChart } from '@/services/chart/index.js'; -import { createDeleteObjectStorageFileJob } from '@/queue/index.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import { getS3 } from './s3.js'; -import { v4 as uuid } from 'uuid'; - -export async function deleteFile(file: DriveFile, isExpired = false) { - if (file.storedInternal) { - InternalStorage.del(file.accessKey!); - - if (file.thumbnailUrl) { - InternalStorage.del(file.thumbnailAccessKey!); - } - - if (file.webpublicUrl) { - InternalStorage.del(file.webpublicAccessKey!); - } - } else if (!file.isLink) { - createDeleteObjectStorageFileJob(file.accessKey!); - - if (file.thumbnailUrl) { - createDeleteObjectStorageFileJob(file.thumbnailAccessKey!); - } - - if (file.webpublicUrl) { - createDeleteObjectStorageFileJob(file.webpublicAccessKey!); - } - } - - postProcess(file, isExpired); -} - -export async function deleteFileSync(file: DriveFile, isExpired = false) { - if (file.storedInternal) { - InternalStorage.del(file.accessKey!); - - if (file.thumbnailUrl) { - InternalStorage.del(file.thumbnailAccessKey!); - } - - if (file.webpublicUrl) { - InternalStorage.del(file.webpublicAccessKey!); - } - } else if (!file.isLink) { - const promises = []; - - promises.push(deleteObjectStorageFile(file.accessKey!)); - - if (file.thumbnailUrl) { - promises.push(deleteObjectStorageFile(file.thumbnailAccessKey!)); - } - - if (file.webpublicUrl) { - promises.push(deleteObjectStorageFile(file.webpublicAccessKey!)); - } - - await Promise.all(promises); - } - - postProcess(file, isExpired); -} - -async function postProcess(file: DriveFile, isExpired = false) { - // リモートファイル期限切れ削除後は直リンクにする - if (isExpired && file.userHost !== null && file.uri != null) { - DriveFiles.update(file.id, { - isLink: true, - url: file.uri, - thumbnailUrl: null, - webpublicUrl: null, - storedInternal: false, - // ローカルプロキシ用 - accessKey: uuid(), - thumbnailAccessKey: 'thumbnail-' + uuid(), - webpublicAccessKey: 'webpublic-' + uuid(), - }); - } else { - DriveFiles.delete(file.id); - } - - // 統計を更新 - driveChart.update(file, false); - perUserDriveChart.update(file, false); - if (file.userHost !== null) { - instanceChart.updateDrive(file, false); - } -} - -export async function deleteObjectStorageFile(key: string) { - const meta = await fetchMeta(); - - const s3 = getS3(meta); - - await s3.deleteObject({ - Bucket: meta.objectStorageBucket!, - Key: key, - }).promise(); -} diff --git a/packages/backend/src/services/drive/generate-video-thumbnail.ts b/packages/backend/src/services/drive/generate-video-thumbnail.ts deleted file mode 100644 index 6e6666481d5c..000000000000 --- a/packages/backend/src/services/drive/generate-video-thumbnail.ts +++ /dev/null @@ -1,29 +0,0 @@ -import * as fs from 'node:fs'; -import { createTempDir } from '@/misc/create-temp.js'; -import { IImage, convertToJpeg } from './image-processor.js'; -import FFmpeg from 'fluent-ffmpeg'; - -export async function GenerateVideoThumbnail(source: string): Promise { - const [dir, cleanup] = await createTempDir(); - - try { - await new Promise((res, rej) => { - FFmpeg({ - source, - }) - .on('end', res) - .on('error', rej) - .screenshot({ - folder: dir, - filename: 'out.png', // must have .png extension - count: 1, - timestamps: ['5%'], - }); - }); - - // JPEGに変換 (Webpでもいいが、MastodonはWebpをサポートせず表示できなくなる) - return await convertToJpeg(`${dir}/out.png`, 498, 280); - } finally { - cleanup(); - } -} diff --git a/packages/backend/src/services/drive/image-processor.ts b/packages/backend/src/services/drive/image-processor.ts deleted file mode 100644 index 2c564ea59568..000000000000 --- a/packages/backend/src/services/drive/image-processor.ts +++ /dev/null @@ -1,87 +0,0 @@ -import sharp from 'sharp'; - -export type IImage = { - data: Buffer; - ext: string | null; - type: string; -}; - -/** - * Convert to JPEG - * with resize, remove metadata, resolve orientation, stop animation - */ -export async function convertToJpeg(path: string, width: number, height: number): Promise { - return convertSharpToJpeg(await sharp(path), width, height); -} - -export async function convertSharpToJpeg(sharp: sharp.Sharp, width: number, height: number): Promise { - const data = await sharp - .resize(width, height, { - fit: 'inside', - withoutEnlargement: true, - }) - .rotate() - .jpeg({ - quality: 85, - progressive: true, - }) - .toBuffer(); - - return { - data, - ext: 'jpg', - type: 'image/jpeg', - }; -} - -/** - * Convert to WebP - * with resize, remove metadata, resolve orientation, stop animation - */ -export async function convertToWebp(path: string, width: number, height: number, quality: number = 85): Promise { - return convertSharpToWebp(await sharp(path), width, height, quality); -} - -export async function convertSharpToWebp(sharp: sharp.Sharp, width: number, height: number, quality: number = 85): Promise { - const data = await sharp - .resize(width, height, { - fit: 'inside', - withoutEnlargement: true, - }) - .rotate() - .webp({ - quality, - }) - .toBuffer(); - - return { - data, - ext: 'webp', - type: 'image/webp', - }; -} - -/** - * Convert to PNG - * with resize, remove metadata, resolve orientation, stop animation - */ -export async function convertToPng(path: string, width: number, height: number): Promise { - return convertSharpToPng(await sharp(path), width, height); -} - -export async function convertSharpToPng(sharp: sharp.Sharp, width: number, height: number): Promise { - const data = await sharp - .resize(width, height, { - fit: 'inside', - withoutEnlargement: true, - }) - .rotate() - .png() - .toBuffer(); - - return { - data, - ext: 'png', - type: 'image/png', - }; -} diff --git a/packages/backend/src/services/drive/internal-storage.ts b/packages/backend/src/services/drive/internal-storage.ts deleted file mode 100644 index 8f76c81ca39e..000000000000 --- a/packages/backend/src/services/drive/internal-storage.ts +++ /dev/null @@ -1,34 +0,0 @@ -import * as fs from 'node:fs'; -import * as Path from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { dirname } from 'node:path'; -import config from '@/config/index.js'; - -const _filename = fileURLToPath(import.meta.url); -const _dirname = dirname(_filename); - -export class InternalStorage { - private static readonly path = Path.resolve(_dirname, '../../../../../files'); - - public static resolvePath = (key: string) => Path.resolve(InternalStorage.path, key); - - public static read(key: string) { - return fs.createReadStream(InternalStorage.resolvePath(key)); - } - - public static saveFromPath(key: string, srcPath: string) { - fs.mkdirSync(InternalStorage.path, { recursive: true }); - fs.copyFileSync(srcPath, InternalStorage.resolvePath(key)); - return `${config.url}/files/${key}`; - } - - public static saveFromBuffer(key: string, data: Buffer) { - fs.mkdirSync(InternalStorage.path, { recursive: true }); - fs.writeFileSync(InternalStorage.resolvePath(key), data); - return `${config.url}/files/${key}`; - } - - public static del(key: string) { - fs.unlink(InternalStorage.resolvePath(key), () => {}); - } -} diff --git a/packages/backend/src/services/drive/logger.ts b/packages/backend/src/services/drive/logger.ts deleted file mode 100644 index 917a8317e276..000000000000 --- a/packages/backend/src/services/drive/logger.ts +++ /dev/null @@ -1,3 +0,0 @@ -import Logger from '../logger.js'; - -export const driveLogger = new Logger('drive', 'blue'); diff --git a/packages/backend/src/services/drive/s3.ts b/packages/backend/src/services/drive/s3.ts deleted file mode 100644 index 80e34be9563e..000000000000 --- a/packages/backend/src/services/drive/s3.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { URL } from 'node:url'; -import S3 from 'aws-sdk/clients/s3.js'; -import { Meta } from '@/models/entities/meta.js'; -import { getAgentByUrl } from '@/misc/fetch.js'; - -export function getS3(meta: Meta) { - const u = meta.objectStorageEndpoint != null - ? `${meta.objectStorageUseSSL ? 'https://' : 'http://'}${meta.objectStorageEndpoint}` - : `${meta.objectStorageUseSSL ? 'https://' : 'http://'}example.net`; - - return new S3({ - endpoint: meta.objectStorageEndpoint || undefined, - accessKeyId: meta.objectStorageAccessKey!, - secretAccessKey: meta.objectStorageSecretKey!, - region: meta.objectStorageRegion || undefined, - sslEnabled: meta.objectStorageUseSSL, - s3ForcePathStyle: !meta.objectStorageEndpoint // AWS with endPoint omitted - ? false - : meta.objectStorageS3ForcePathStyle, - httpOptions: { - agent: getAgentByUrl(new URL(u), !meta.objectStorageUseProxy), - }, - }); -} diff --git a/packages/backend/src/services/drive/upload-from-url.ts b/packages/backend/src/services/drive/upload-from-url.ts deleted file mode 100644 index 3c5e1aa5c123..000000000000 --- a/packages/backend/src/services/drive/upload-from-url.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { URL } from 'node:url'; -import { User } from '@/models/entities/user.js'; -import { createTemp } from '@/misc/create-temp.js'; -import { downloadUrl } from '@/misc/download-url.js'; -import { DriveFolder } from '@/models/entities/drive-folder.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; -import { DriveFiles } from '@/models/index.js'; -import { driveLogger } from './logger.js'; -import { addFile } from './add-file.js'; - -const logger = driveLogger.createSubLogger('downloader'); - -type Args = { - url: string; - user: { id: User['id']; host: User['host'] } | null; - folderId?: DriveFolder['id'] | null; - uri?: string | null; - sensitive?: boolean; - force?: boolean; - isLink?: boolean; - comment?: string | null; - requestIp?: string | null; - requestHeaders?: Record | null; -}; - -export async function uploadFromUrl({ - url, - user, - folderId = null, - uri = null, - sensitive = false, - force = false, - isLink = false, - comment = null, - requestIp = null, - requestHeaders = null, -}: Args): Promise { - let name = new URL(url).pathname.split('/').pop() || null; - if (name == null || !DriveFiles.validateFileName(name)) { - name = null; - } - - // If the comment is same as the name, skip comment - // (image.name is passed in when receiving attachment) - if (comment !== null && name === comment) { - comment = null; - } - - // Create temp file - const [path, cleanup] = await createTemp(); - - try { - // write content at URL to temp file - await downloadUrl(url, path); - - const driveFile = await addFile({ user, path, name, comment, folderId, force, isLink, url, uri, sensitive, requestIp, requestHeaders }); - logger.succ(`Got: ${driveFile.id}`); - return driveFile!; - } catch (e) { - logger.error(`Failed to create drive file: ${e}`, { - url: url, - e: e, - }); - throw e; - } finally { - cleanup(); - } -} From d9547568125316a559b0173bab7a8e421ba98855 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 16:42:59 +0900 Subject: [PATCH 057/180] wip --- .../src/services/remote/ResolveUserService.ts | 5 +- .../src/services/remote/WebfingerService.ts | 48 +++++++++++++++++++ .../backend/src/services/remote/webfinger.ts | 34 ------------- 3 files changed, 51 insertions(+), 36 deletions(-) create mode 100644 packages/backend/src/services/remote/WebfingerService.ts delete mode 100644 packages/backend/src/services/remote/webfinger.ts diff --git a/packages/backend/src/services/remote/ResolveUserService.ts b/packages/backend/src/services/remote/ResolveUserService.ts index 4c33ec4124a3..f22ca69e3112 100644 --- a/packages/backend/src/services/remote/ResolveUserService.ts +++ b/packages/backend/src/services/remote/ResolveUserService.ts @@ -9,7 +9,7 @@ import type { Config } from '@/config/types.js'; import { toPuny } from '@/misc/convert-host.js'; import type Logger from '@/logger.js'; import { createPerson, updatePerson } from './activitypub/models/person.js'; -import webFinger from './webfinger.js'; +import type { WebfingerService } from './WebfingerService.js'; import type { RemoteLoggerService } from './RemoteLoggerService.js'; @Injectable() @@ -23,6 +23,7 @@ export class ResolveUserService { @Inject('usersRepository') private usersRepository: typeof Users, + private webfingerService: WebfingerService, private remoteLoggerService: RemoteLoggerService, ) { this.#logger = this.remoteLoggerService.logger.createSubLogger('resolve-user'); @@ -115,7 +116,7 @@ export class ResolveUserService { async #resolveSelf(acctLower: string) { this.#logger.info(`WebFinger for ${chalk.yellow(acctLower)}`); - const finger = await webFinger(acctLower).catch(e => { + const finger = await this.webfingerService.webfinger(acctLower).catch(e => { this.#logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: ${ e.statusCode || e.message }`); throw new Error(`Failed to WebFinger for ${acctLower}: ${ e.statusCode || e.message }`); }); diff --git a/packages/backend/src/services/remote/WebfingerService.ts b/packages/backend/src/services/remote/WebfingerService.ts new file mode 100644 index 000000000000..536322f25fae --- /dev/null +++ b/packages/backend/src/services/remote/WebfingerService.ts @@ -0,0 +1,48 @@ +import { URL } from 'node:url'; +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; +import { query as urlQuery } from '@/prelude/url.js'; +import type { HttpRequestService } from '@/services/HttpRequestService.js'; + +type ILink = { + href: string; + rel?: string; +}; + +type IWebFinger = { + links: ILink[]; + subject: string; +}; + +@Injectable() +export class WebfingerService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + private httpRequestService: HttpRequestService, + ) { + } + + public async webfinger(query: string): Promise { + const url = this.#genUrl(query); + + return await this.httpRequestService.getJson(url, 'application/jrd+json, application/json') as IWebFinger; + } + + #genUrl(query: string): string { + if (query.match(/^https?:\/\//)) { + const u = new URL(query); + return `${u.protocol}//${u.hostname}/.well-known/webfinger?` + urlQuery({ resource: query }); + } + + const m = query.match(/^([^@]+)@(.*)/); + if (m) { + const hostname = m[2]; + return `https://${hostname}/.well-known/webfinger?` + urlQuery({ resource: `acct:${query}` }); + } + + throw new Error(`Invalid query (${query})`); + } +} diff --git a/packages/backend/src/services/remote/webfinger.ts b/packages/backend/src/services/remote/webfinger.ts deleted file mode 100644 index 337df34c2d7c..000000000000 --- a/packages/backend/src/services/remote/webfinger.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { URL } from 'node:url'; -import { getJson } from '@/misc/fetch.js'; -import { query as urlQuery } from '@/prelude/url.js'; - -type ILink = { - href: string; - rel?: string; -}; - -type IWebFinger = { - links: ILink[]; - subject: string; -}; - -export default async function(query: string): Promise { - const url = genUrl(query); - - return await getJson(url, 'application/jrd+json, application/json') as IWebFinger; -} - -function genUrl(query: string) { - if (query.match(/^https?:\/\//)) { - const u = new URL(query); - return `${u.protocol}//${u.hostname}/.well-known/webfinger?` + urlQuery({ resource: query }); - } - - const m = query.match(/^([^@]+)@(.*)/); - if (m) { - const hostname = m[2]; - return `https://${hostname}/.well-known/webfinger?` + urlQuery({ resource: `acct:${query}` }); - } - - throw new Error(`Invalid query (${query})`); -} From fbad838a0bdf3e2ef8147c7b0ce8294dc16dee58 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 16:46:35 +0900 Subject: [PATCH 058/180] wip --- packages/backend/src/services/AiService.ts | 60 +++++++++++++++++++ .../backend/src/services/detect-sensitive.ts | 48 --------------- 2 files changed, 60 insertions(+), 48 deletions(-) create mode 100644 packages/backend/src/services/AiService.ts delete mode 100644 packages/backend/src/services/detect-sensitive.ts diff --git a/packages/backend/src/services/AiService.ts b/packages/backend/src/services/AiService.ts new file mode 100644 index 000000000000..286f1c2e28a5 --- /dev/null +++ b/packages/backend/src/services/AiService.ts @@ -0,0 +1,60 @@ +import * as fs from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { dirname } from 'node:path'; +import { Inject, Injectable } from '@nestjs/common'; +import * as nsfw from 'nsfwjs'; +import si from 'systeminformation'; +import type { Config } from '@/config/types.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; + +const _filename = fileURLToPath(import.meta.url); +const _dirname = dirname(_filename); + +const REQUIRED_CPU_FLAGS = ['avx2', 'fma']; +let isSupportedCpu: undefined | boolean = undefined; + +@Injectable() +export class AiService { + #model: nsfw.NSFWJS; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + ) { + } + + public async detectSensitive(path: string): Promise { + try { + if (isSupportedCpu === undefined) { + const cpuFlags = await this.#getCpuFlags(); + isSupportedCpu = REQUIRED_CPU_FLAGS.every(required => cpuFlags.includes(required)); + } + + if (!isSupportedCpu) { + console.error('This CPU cannot use TensorFlow.'); + return null; + } + + const tf = await import('@tensorflow/tfjs-node'); + + if (this.#model == null) this.#model = await nsfw.load(`file://${_dirname}/../../nsfw-model/`, { size: 299 }); + + const buffer = await fs.promises.readFile(path); + const image = await tf.node.decodeImage(buffer, 3) as any; + try { + const predictions = await this.#model.classify(image); + return predictions; + } finally { + image.dispose(); + } + } catch (err) { + console.error(err); + return null; + } + } + + async #getCpuFlags(): Promise { + const str = await si.cpuFlags(); + return str.split(/\s+/); + } +} diff --git a/packages/backend/src/services/detect-sensitive.ts b/packages/backend/src/services/detect-sensitive.ts deleted file mode 100644 index 2ade39d52413..000000000000 --- a/packages/backend/src/services/detect-sensitive.ts +++ /dev/null @@ -1,48 +0,0 @@ -import * as fs from 'node:fs'; -import { fileURLToPath } from 'node:url'; -import { dirname } from 'node:path'; -import * as nsfw from 'nsfwjs'; -import si from 'systeminformation'; - -const _filename = fileURLToPath(import.meta.url); -const _dirname = dirname(_filename); - -const REQUIRED_CPU_FLAGS = ['avx2', 'fma']; -let isSupportedCpu: undefined | boolean = undefined; - -let model: nsfw.NSFWJS; - -export async function detectSensitive(path: string): Promise { - try { - if (isSupportedCpu === undefined) { - const cpuFlags = await getCpuFlags(); - isSupportedCpu = REQUIRED_CPU_FLAGS.every(required => cpuFlags.includes(required)); - } - - if (!isSupportedCpu) { - console.error('This CPU cannot use TensorFlow.'); - return null; - } - - const tf = await import('@tensorflow/tfjs-node'); - - if (model == null) model = await nsfw.load(`file://${_dirname}/../../nsfw-model/`, { size: 299 }); - - const buffer = await fs.promises.readFile(path); - const image = await tf.node.decodeImage(buffer, 3) as any; - try { - const predictions = await model.classify(image); - return predictions; - } finally { - image.dispose(); - } - } catch (err) { - console.error(err); - return null; - } -} - -async function getCpuFlags(): Promise { - const str = await si.cpuFlags(); - return str.split(/\s+/); -} From ccf67eb0aa2029c05d58e5b18aaaa106dc7dac00 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 18:27:55 +0900 Subject: [PATCH 059/180] wip --- .../backend/src/queue/QueueLoggerService.ts | 12 ++ packages/backend/src/queue/logger.ts | 3 - .../DeleteAccountProcessorService.ts | 123 +++++++++++++++ .../DeleteDriveFilesProcessorService.ts | 78 ++++++++++ .../ExportBlockingProcessorService.ts | 115 ++++++++++++++ .../ExportCustomEmojisProcessorService.ts | 134 ++++++++++++++++ .../ExportFollowingProcessorService.ts | 120 +++++++++++++++ .../ExportMutingProcessorService.ts | 119 +++++++++++++++ .../processors/ExportNotesProcessorService.ts | 143 ++++++++++++++++++ .../ExportUserListsProcessorService.ts | 95 ++++++++++++ .../ImportBlockingProcessorService.ts | 101 +++++++++++++ .../ImportCustomEmojisProcessorService.ts | 109 +++++++++++++ .../src/queue/processors/db/delete-account.ts | 94 ------------ .../queue/processors/db/delete-drive-files.ts | 56 ------- .../queue/processors/db/export-blocking.ts | 93 ------------ .../processors/db/export-custom-emojis.ts | 114 -------------- .../queue/processors/db/export-following.ts | 94 ------------ .../src/queue/processors/db/export-mute.ts | 94 ------------ .../src/queue/processors/db/export-notes.ts | 118 --------------- .../queue/processors/db/export-user-lists.ts | 70 --------- .../queue/processors/db/import-blocking.ts | 75 --------- .../processors/db/import-custom-emojis.ts | 81 ---------- .../src/services/CustomEmojiService.ts | 78 ++++++++++ 23 files changed, 1227 insertions(+), 892 deletions(-) create mode 100644 packages/backend/src/queue/QueueLoggerService.ts delete mode 100644 packages/backend/src/queue/logger.ts create mode 100644 packages/backend/src/queue/processors/DeleteAccountProcessorService.ts create mode 100644 packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts create mode 100644 packages/backend/src/queue/processors/ExportBlockingProcessorService.ts create mode 100644 packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts create mode 100644 packages/backend/src/queue/processors/ExportFollowingProcessorService.ts create mode 100644 packages/backend/src/queue/processors/ExportMutingProcessorService.ts create mode 100644 packages/backend/src/queue/processors/ExportNotesProcessorService.ts create mode 100644 packages/backend/src/queue/processors/ExportUserListsProcessorService.ts create mode 100644 packages/backend/src/queue/processors/ImportBlockingProcessorService.ts create mode 100644 packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts delete mode 100644 packages/backend/src/queue/processors/db/delete-account.ts delete mode 100644 packages/backend/src/queue/processors/db/delete-drive-files.ts delete mode 100644 packages/backend/src/queue/processors/db/export-blocking.ts delete mode 100644 packages/backend/src/queue/processors/db/export-custom-emojis.ts delete mode 100644 packages/backend/src/queue/processors/db/export-following.ts delete mode 100644 packages/backend/src/queue/processors/db/export-mute.ts delete mode 100644 packages/backend/src/queue/processors/db/export-notes.ts delete mode 100644 packages/backend/src/queue/processors/db/export-user-lists.ts delete mode 100644 packages/backend/src/queue/processors/db/import-blocking.ts delete mode 100644 packages/backend/src/queue/processors/db/import-custom-emojis.ts create mode 100644 packages/backend/src/services/CustomEmojiService.ts diff --git a/packages/backend/src/queue/QueueLoggerService.ts b/packages/backend/src/queue/QueueLoggerService.ts new file mode 100644 index 000000000000..4cdd4edfbb11 --- /dev/null +++ b/packages/backend/src/queue/QueueLoggerService.ts @@ -0,0 +1,12 @@ +import { Inject, Injectable } from '@nestjs/common'; +import Logger from '@/logger.js'; + +@Injectable() +export class QueueLoggerService { + public logger: Logger; + + constructor( + ) { + this.logger = new Logger('queue', 'orange'); + } +} diff --git a/packages/backend/src/queue/logger.ts b/packages/backend/src/queue/logger.ts deleted file mode 100644 index 2843a3c26394..000000000000 --- a/packages/backend/src/queue/logger.ts +++ /dev/null @@ -1,3 +0,0 @@ -import Logger from '@/services/logger.js'; - -export const queueLogger = new Logger('queue', 'orange'); diff --git a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts new file mode 100644 index 000000000000..655f9022fa87 --- /dev/null +++ b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts @@ -0,0 +1,123 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { MoreThan } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { DriveFiles, UserProfiles } from '@/models/index.js'; +import { Notes, Users } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import type { DriveService } from '@/services/drive/DriveService.js'; +import type { DriveFile } from '@/models/entities/drive-file.js'; +import type { Note } from '@/models/entities/note.js'; +import type Bull from 'bull'; +import type { DbUserDeleteJobData } from '../types.js'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; + +@Injectable() +export class DeleteAccountProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private driveService: DriveService, + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('delete-account'); + } + + public async process(job: Bull.Job): Promise { + this.#logger.info(`Deleting account of ${job.data.user.id} ...`); + + const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); + if (user == null) { + return; + } + + { // Delete notes + let cursor: Note['id'] | null = null; + + while (true) { + const notes = await this.notesRepository.find({ + where: { + userId: user.id, + ...(cursor ? { id: MoreThan(cursor) } : {}), + }, + take: 100, + order: { + id: 1, + }, + }) as Note[]; + + if (notes.length === 0) { + break; + } + + cursor = notes[notes.length - 1].id; + + await Notes.delete(notes.map(note => note.id)); + } + + this.#logger.succ('All of notes deleted'); + } + + { // Delete files + let cursor: DriveFile['id'] | null = null; + + while (true) { + const files = await this.driveFilesRepository.find({ + where: { + userId: user.id, + ...(cursor ? { id: MoreThan(cursor) } : {}), + }, + take: 10, + order: { + id: 1, + }, + }) as DriveFile[]; + + if (files.length === 0) { + break; + } + + cursor = files[files.length - 1].id; + + for (const file of files) { + await this.driveService.deleteFileSync(file); + } + } + + this.#logger.succ('All of files deleted'); + } + + { // Send email notification + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); + if (profile.email && profile.emailVerified) { + sendEmail(profile.email, 'Account deleted', + 'Your account has been deleted.', + 'Your account has been deleted.'); + } + } + + // soft指定されている場合は物理削除しない + if (job.data.soft) { + // nop + } else { + await Users.delete(job.data.user.id); + } + + return 'Account deleted'; + } +} diff --git a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts new file mode 100644 index 000000000000..cbe8edc9362c --- /dev/null +++ b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts @@ -0,0 +1,78 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { MoreThan } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Users, DriveFiles } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import type { DriveService } from '@/services/drive/DriveService.js'; +import type Bull from 'bull'; +import type { DbUserJobData } from '../types.js'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; + +@Injectable() +export class DeleteDriveFilesProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private driveService: DriveService, + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('delete-drive-files'); + } + + public async process(job: Bull.Job, done: () => void): Promise { + this.#logger.info(`Deleting drive files of ${job.data.user.id} ...`); + + const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); + if (user == null) { + done(); + return; + } + + let deletedCount = 0; + let cursor: any = null; + + while (true) { + const files = await this.driveFilesRepository.find({ + where: { + userId: user.id, + ...(cursor ? { id: MoreThan(cursor) } : {}), + }, + take: 100, + order: { + id: 1, + }, + }); + + if (files.length === 0) { + job.progress(100); + break; + } + + cursor = files[files.length - 1].id; + + for (const file of files) { + await this.driveService.deleteFileSync(file); + deletedCount++; + } + + const total = await this.driveFilesRepository.countBy({ + userId: user.id, + }); + + job.progress(deletedCount / total); + } + + this.#logger.succ(`All drive files (${deletedCount}) of ${user.id} has been deleted.`); + done(); + } +} diff --git a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts new file mode 100644 index 000000000000..79b531c2961b --- /dev/null +++ b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts @@ -0,0 +1,115 @@ +import * as fs from 'node:fs'; +import { Inject, Injectable } from '@nestjs/common'; +import { MoreThan } from 'typeorm'; +import { format as dateFormat } from 'date-fns'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { DriveFiles, UserProfiles , Notes , Users , Blockings } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import type { DriveService } from '@/services/drive/DriveService.js'; +import { getFullApAccount } from '@/misc/convert-host.js'; +import { createTemp } from '@/misc/create-temp.js'; +import type Bull from 'bull'; +import type { DbUserJobData } from '../types.js'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; + +@Injectable() +export class ExportBlockingProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('blockingsRepository') + private blockingsRepository: typeof Blockings, + + private driveService: DriveService, + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('export-blocking'); + } + + public async process(job: Bull.Job, done: () => void): Promise { + this.#logger.info(`Exporting blocking of ${job.data.user.id} ...`); + + const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); + if (user == null) { + done(); + return; + } + + // Create temp file + const [path, cleanup] = await createTemp(); + + this.#logger.info(`Temp file is ${path}`); + + try { + const stream = fs.createWriteStream(path, { flags: 'a' }); + + let exportedCount = 0; + let cursor: any = null; + + while (true) { + const blockings = await this.blockingsRepository.find({ + where: { + blockerId: user.id, + ...(cursor ? { id: MoreThan(cursor) } : {}), + }, + take: 100, + order: { + id: 1, + }, + }); + + if (blockings.length === 0) { + job.progress(100); + break; + } + + cursor = blockings[blockings.length - 1].id; + + for (const block of blockings) { + const u = await this.usersRepository.findOneBy({ id: block.blockeeId }); + if (u == null) { + exportedCount++; continue; + } + + const content = getFullApAccount(u.username, u.host); + await new Promise((res, rej) => { + stream.write(content + '\n', err => { + if (err) { + this.#logger.error(err); + rej(err); + } else { + res(); + } + }); + }); + exportedCount++; + } + + const total = await this.blockingsRepository.countBy({ + blockerId: user.id, + }); + + job.progress(exportedCount / total); + } + + stream.end(); + this.#logger.succ(`Exported to: ${path}`); + + const fileName = 'blocking-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv'; + const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true }); + + this.#logger.succ(`Exported to: ${driveFile.id}`); + } finally { + cleanup(); + } + + done(); + } +} diff --git a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts new file mode 100644 index 000000000000..bba4c018186f --- /dev/null +++ b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts @@ -0,0 +1,134 @@ +import * as fs from 'node:fs'; +import { Inject, Injectable } from '@nestjs/common'; +import { IsNull, MoreThan } from 'typeorm'; +import { format as dateFormat } from 'date-fns'; +import { ulid } from 'ulid'; +import mime from 'mime-types'; +import archiver from 'archiver'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Emojis, Users } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import type { DriveService } from '@/services/drive/DriveService.js'; +import { createTemp, createTempDir } from '@/misc/create-temp.js'; +import { downloadUrl } from '@/misc/download-url.js'; +import type Bull from 'bull'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; + +@Injectable() +export class ExportCustomEmojisProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('emojisRepository') + private emojisRepository: typeof Emojis, + + private driveService: DriveService, + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('export-custom-emojis'); + } + + public async process(job: Bull.Job, done: () => void): Promise { + this.#logger.info('Exporting custom emojis ...'); + + const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); + if (user == null) { + done(); + return; + } + + const [path, cleanup] = await createTempDir(); + + this.#logger.info(`Temp dir is ${path}`); + + const metaPath = path + '/meta.json'; + + fs.writeFileSync(metaPath, '', 'utf-8'); + + const metaStream = fs.createWriteStream(metaPath, { flags: 'a' }); + + const writeMeta = (text: string): Promise => { + return new Promise((res, rej) => { + metaStream.write(text, err => { + if (err) { + this.#logger.error(err); + rej(err); + } else { + res(); + } + }); + }); + }; + + await writeMeta(`{"metaVersion":2,"host":"${this.config.host}","exportedAt":"${new Date().toString()}","emojis":[`); + + const customEmojis = await this.emojisRepository.find({ + where: { + host: IsNull(), + }, + order: { + id: 'ASC', + }, + }); + + for (const emoji of customEmojis) { + const ext = mime.extension(emoji.type); + const fileName = emoji.name + (ext ? '.' + ext : ''); + const emojiPath = path + '/' + fileName; + fs.writeFileSync(emojiPath, '', 'binary'); + let downloaded = false; + + try { + await downloadUrl(emoji.originalUrl, emojiPath); + downloaded = true; + } catch (e) { // TODO: 何度か再試行 + this.#logger.error(e instanceof Error ? e : new Error(e as string)); + } + + if (!downloaded) { + fs.unlinkSync(emojiPath); + } + + const content = JSON.stringify({ + fileName: fileName, + downloaded: downloaded, + emoji: emoji, + }); + const isFirst = customEmojis.indexOf(emoji) === 0; + + await writeMeta(isFirst ? content : ',\n' + content); + } + + await writeMeta(']}'); + + metaStream.end(); + + // Create archive + const [archivePath, archiveCleanup] = await createTemp(); + const archiveStream = fs.createWriteStream(archivePath); + const archive = archiver('zip', { + zlib: { level: 0 }, + }); + archiveStream.on('close', async () => { + this.#logger.succ(`Exported to: ${archivePath}`); + + const fileName = 'custom-emojis-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.zip'; + const driveFile = await this.driveService.addFile({ user, path: archivePath, name: fileName, force: true }); + + this.#logger.succ(`Exported to: ${driveFile.id}`); + cleanup(); + archiveCleanup(); + done(); + }); + archive.pipe(archiveStream); + archive.directory(path, false); + archive.finalize(); + } +} diff --git a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts new file mode 100644 index 000000000000..2d8a616aef39 --- /dev/null +++ b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts @@ -0,0 +1,120 @@ +import * as fs from 'node:fs'; +import { Inject, Injectable } from '@nestjs/common'; +import { In, MoreThan, Not } from 'typeorm'; +import { format as dateFormat } from 'date-fns'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { Users } from '@/models/index.js'; +import type { Followings, Mutings } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import type { DriveService } from '@/services/drive/DriveService.js'; +import { getFullApAccount } from '@/misc/convert-host.js'; +import { createTemp } from '@/misc/create-temp.js'; +import type { Following } from '@/models/entities/following.js'; +import type Bull from 'bull'; +import type { DbUserJobData } from '../types.js'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; + +@Injectable() +export class ExportFollowingProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + @Inject('mutingsRepository') + private mutingsRepository: typeof Mutings, + + private driveService: DriveService, + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('export-following'); + } + + public async process(job: Bull.Job, done: () => void): Promise { + this.#logger.info(`Exporting following of ${job.data.user.id} ...`); + + const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); + if (user == null) { + done(); + return; + } + + // Create temp file + const [path, cleanup] = await createTemp(); + + this.#logger.info(`Temp file is ${path}`); + + try { + const stream = fs.createWriteStream(path, { flags: 'a' }); + + let cursor: Following['id'] | null = null; + + const mutings = job.data.excludeMuting ? await this.mutingsRepository.findBy({ + muterId: user.id, + }) : []; + + while (true) { + const followings = await this.followingsRepository.find({ + where: { + followerId: user.id, + ...(mutings.length > 0 ? { followeeId: Not(In(mutings.map(x => x.muteeId))) } : {}), + ...(cursor ? { id: MoreThan(cursor) } : {}), + }, + take: 100, + order: { + id: 1, + }, + }) as Following[]; + + if (followings.length === 0) { + break; + } + + cursor = followings[followings.length - 1].id; + + for (const following of followings) { + const u = await Users.findOneBy({ id: following.followeeId }); + if (u == null) { + continue; + } + + if (job.data.excludeInactive && u.updatedAt && (Date.now() - u.updatedAt.getTime() > 1000 * 60 * 60 * 24 * 90)) { + continue; + } + + const content = getFullApAccount(u.username, u.host); + await new Promise((res, rej) => { + stream.write(content + '\n', err => { + if (err) { + this.#logger.error(err); + rej(err); + } else { + res(); + } + }); + }); + } + } + + stream.end(); + this.#logger.succ(`Exported to: ${path}`); + + const fileName = 'following-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv'; + const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true }); + + this.#logger.succ(`Exported to: ${driveFile.id}`); + } finally { + cleanup(); + } + + done(); + } +} diff --git a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts new file mode 100644 index 000000000000..709cf3167bb2 --- /dev/null +++ b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts @@ -0,0 +1,119 @@ +import * as fs from 'node:fs'; +import { Inject, Injectable } from '@nestjs/common'; +import { IsNull, MoreThan } from 'typeorm'; +import { format as dateFormat } from 'date-fns'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Mutings, Users , Blockings } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import type { DriveService } from '@/services/drive/DriveService.js'; +import { getFullApAccount } from '@/misc/convert-host.js'; +import { createTemp } from '@/misc/create-temp.js'; +import type Bull from 'bull'; +import type { DbUserJobData } from '../types.js'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; + +@Injectable() +export class ExportMutingProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('blockingsRepository') + private blockingsRepository: typeof Blockings, + + @Inject('mutingsRepository') + private mutingsRepository: typeof Mutings, + + private driveService: DriveService, + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('export-muting'); + } + + public async process(job: Bull.Job, done: () => void): Promise { + this.#logger.info(`Exporting muting of ${job.data.user.id} ...`); + + const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); + if (user == null) { + done(); + return; + } + + // Create temp file + const [path, cleanup] = await createTemp(); + + this.#logger.info(`Temp file is ${path}`); + + try { + const stream = fs.createWriteStream(path, { flags: 'a' }); + + let exportedCount = 0; + let cursor: any = null; + + while (true) { + const mutes = await this.mutingsRepository.find({ + where: { + muterId: user.id, + expiresAt: IsNull(), + ...(cursor ? { id: MoreThan(cursor) } : {}), + }, + take: 100, + order: { + id: 1, + }, + }); + + if (mutes.length === 0) { + job.progress(100); + break; + } + + cursor = mutes[mutes.length - 1].id; + + for (const mute of mutes) { + const u = await this.usersRepository.findOneBy({ id: mute.muteeId }); + if (u == null) { + exportedCount++; continue; + } + + const content = getFullApAccount(u.username, u.host); + await new Promise((res, rej) => { + stream.write(content + '\n', err => { + if (err) { + this.#logger.error(err); + rej(err); + } else { + res(); + } + }); + }); + exportedCount++; + } + + const total = await this.mutingsRepository.countBy({ + muterId: user.id, + }); + + job.progress(exportedCount / total); + } + + stream.end(); + this.#logger.succ(`Exported to: ${path}`); + + const fileName = 'mute-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv'; + const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true }); + + this.#logger.succ(`Exported to: ${driveFile.id}`); + } finally { + cleanup(); + } + + done(); + } +} diff --git a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts new file mode 100644 index 000000000000..9ef4d8c07b97 --- /dev/null +++ b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts @@ -0,0 +1,143 @@ +import * as fs from 'node:fs'; +import { Inject, Injectable } from '@nestjs/common'; +import { IsNull, MoreThan } from 'typeorm'; +import { format as dateFormat } from 'date-fns'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Notes, Polls , Users } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import type { DriveService } from '@/services/drive/DriveService.js'; +import { createTemp } from '@/misc/create-temp.js'; +import type { Poll } from '@/models/entities/poll.js'; +import type { Note } from '@/models/entities/note.js'; +import type Bull from 'bull'; +import type { DbUserJobData } from '../types.js'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; + +@Injectable() +export class ExportNotesProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('pollsRepository') + private pollsRepository: typeof Polls, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + + private driveService: DriveService, + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('export-notes'); + } + + public async process(job: Bull.Job, done: () => void): Promise { + this.#logger.info(`Exporting notes of ${job.data.user.id} ...`); + + const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); + if (user == null) { + done(); + return; + } + + // Create temp file + const [path, cleanup] = await createTemp(); + + this.#logger.info(`Temp file is ${path}`); + + try { + const stream = fs.createWriteStream(path, { flags: 'a' }); + + const write = (text: string): Promise => { + return new Promise((res, rej) => { + stream.write(text, err => { + if (err) { + this.#logger.error(err); + rej(err); + } else { + res(); + } + }); + }); + }; + + await write('['); + + let exportedNotesCount = 0; + let cursor: Note['id'] | null = null; + + while (true) { + const notes = await this.notesRepository.find({ + where: { + userId: user.id, + ...(cursor ? { id: MoreThan(cursor) } : {}), + }, + take: 100, + order: { + id: 1, + }, + }) as Note[]; + + if (notes.length === 0) { + job.progress(100); + break; + } + + cursor = notes[notes.length - 1].id; + + for (const note of notes) { + let poll: Poll | undefined; + if (note.hasPoll) { + poll = await this.pollsRepository.findOneByOrFail({ noteId: note.id }); + } + const content = JSON.stringify(serialize(note, poll)); + const isFirst = exportedNotesCount === 0; + await write(isFirst ? content : ',\n' + content); + exportedNotesCount++; + } + + const total = await this.notesRepository.countBy({ + userId: user.id, + }); + + job.progress(exportedNotesCount / total); + } + + await write(']'); + + stream.end(); + this.#logger.succ(`Exported to: ${path}`); + + const fileName = 'notes-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.json'; + const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true }); + + this.#logger.succ(`Exported to: ${driveFile.id}`); + } finally { + cleanup(); + } + + done(); + } +} + +function serialize(note: Note, poll: Poll | null = null): Record { + return { + id: note.id, + text: note.text, + createdAt: note.createdAt, + fileIds: note.fileIds, + replyId: note.replyId, + renoteId: note.renoteId, + poll: poll, + cw: note.cw, + visibility: note.visibility, + visibleUserIds: note.visibleUserIds, + localOnly: note.localOnly, + }; +} diff --git a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts new file mode 100644 index 000000000000..65d95a60127c --- /dev/null +++ b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts @@ -0,0 +1,95 @@ +import * as fs from 'node:fs'; +import { Inject, Injectable } from '@nestjs/common'; +import { In, IsNull, MoreThan } from 'typeorm'; +import { format as dateFormat } from 'date-fns'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { UserListJoinings, UserLists, Users } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import type { DriveService } from '@/services/drive/DriveService.js'; +import { createTemp } from '@/misc/create-temp.js'; +import { getFullApAccount } from '@/misc/convert-host.js'; +import type Bull from 'bull'; +import type { DbUserJobData } from '../types.js'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; + +@Injectable() +export class ExportUserListsProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('userListsRepository') + private userListsRepository: typeof UserLists, + + @Inject('userListJoiningsRepository') + private userListJoiningsRepository: typeof UserListJoinings, + + private driveService: DriveService, + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('export-user-lists'); + } + + public async process(job: Bull.Job, done: () => void): Promise { + this.#logger.info(`Exporting user lists of ${job.data.user.id} ...`); + + const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); + if (user == null) { + done(); + return; + } + + const lists = await this.userListsRepository.findBy({ + userId: user.id, + }); + + // Create temp file + const [path, cleanup] = await createTemp(); + + this.#logger.info(`Temp file is ${path}`); + + try { + const stream = fs.createWriteStream(path, { flags: 'a' }); + + for (const list of lists) { + const joinings = await this.userListJoiningsRepository.findBy({ userListId: list.id }); + const users = await this.usersRepository.findBy({ + id: In(joinings.map(j => j.userId)), + }); + + for (const u of users) { + const acct = getFullApAccount(u.username, u.host); + const content = `${list.name},${acct}`; + await new Promise((res, rej) => { + stream.write(content + '\n', err => { + if (err) { + this.#logger.error(err); + rej(err); + } else { + res(); + } + }); + }); + } + } + + stream.end(); + this.#logger.succ(`Exported to: ${path}`); + + const fileName = 'user-lists-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv'; + const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true }); + + this.#logger.succ(`Exported to: ${driveFile.id}`); + } finally { + cleanup(); + } + + done(); + } +} diff --git a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts new file mode 100644 index 000000000000..f6e384b3f11e --- /dev/null +++ b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts @@ -0,0 +1,101 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { IsNull, MoreThan } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { Users } from '@/models/index.js'; +import type { Blockings , DriveFiles } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import { isSelfHost, toPuny } from '@/misc/convert-host.js'; +import * as Acct from '@/misc/acct.js'; +import { downloadTextFile } from '@/misc/download-text-file.js'; +import type { ResolveUserService } from '@/services/remote/ResolveUserService.js'; +import type { UserBlockingService } from '@/services/UserBlockingService.js'; +import type Bull from 'bull'; +import type { DbUserImportJobData } from '../types.js'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; + +@Injectable() +export class ImportBlockingProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('blockingsRepository') + private blockingsRepository: typeof Blockings, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private userBlockingService: UserBlockingService, + private resolveUserService: ResolveUserService, + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('import-blocking'); + } + + public async process(job: Bull.Job, done: () => void): Promise { + this.#logger.info(`Importing blocking of ${job.data.user.id} ...`); + + const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); + if (user == null) { + done(); + return; + } + + const file = await this.driveFilesRepository.findOneBy({ + id: job.data.fileId, + }); + if (file == null) { + done(); + return; + } + + const csv = await downloadTextFile(file.url); + + let linenum = 0; + + for (const line of csv.trim().split('\n')) { + linenum++; + + try { + const acct = line.split(',')[0].trim(); + const { username, host } = Acct.parse(acct); + + let target = isSelfHost(host!) ? await this.usersRepository.findOneBy({ + host: IsNull(), + usernameLower: username.toLowerCase(), + }) : await Users.findOneBy({ + host: toPuny(host!), + usernameLower: username.toLowerCase(), + }); + + if (host == null && target == null) continue; + + if (target == null) { + target = await this.resolveUserService.resolveUser(username, host); + } + + if (target == null) { + throw `cannot resolve user: @${username}@${host}`; + } + + // skip myself + if (target.id === job.data.user.id) continue; + + this.#logger.info(`Block[${linenum}] ${target.id} ...`); + + await this.userBlockingService.block(user, target); + } catch (e) { + this.#logger.warn(`Error in line:${linenum} ${e}`); + } + } + + this.#logger.succ('Imported'); + done(); + } +} diff --git a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts new file mode 100644 index 000000000000..8d6a24cd22a1 --- /dev/null +++ b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts @@ -0,0 +1,109 @@ +import * as fs from 'node:fs'; +import { Inject, Injectable } from '@nestjs/common'; +import { IsNull, MoreThan } from 'typeorm'; +import unzipper from 'unzipper'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Emojis , DriveFiles , Users } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import type { CustomEmojiService } from '@/services/CustomEmojiService.js'; +import { createTempDir } from '@/misc/create-temp.js'; +import type { DriveService } from '@/services/drive/DriveService.js'; +import type { DataSource } from 'typeorm'; +import type Bull from 'bull'; +import type { DbUserImportJobData } from '../types.js'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; + +// TODO: 名前衝突時の動作を選べるようにする +@Injectable() +export class ImportCustomEmojisProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject(DI_SYMBOLS.db) + private db: DataSource, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + @Inject('emojisRepository') + private emojisRepository: typeof Emojis, + + private customEmojiService: CustomEmojiService, + private driveService: DriveService, + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('import-custom-emojis'); + } + + public async process(job: Bull.Job, done: () => void): Promise { + this.#logger.info('Importing custom emojis ...'); + + const file = await this.driveFilesRepository.findOneBy({ + id: job.data.fileId, + }); + if (file == null) { + done(); + return; + } + + const [path, cleanup] = await createTempDir(); + + this.#logger.info(`Temp dir is ${path}`); + + const destPath = path + '/emojis.zip'; + + try { + fs.writeFileSync(destPath, '', 'binary'); + await downloadUrl(file.url, destPath); + } catch (e) { // TODO: 何度か再試行 + if (e instanceof Error || typeof e === 'string') { + this.#logger.error(e); + } + throw e; + } + + const outputPath = path + '/emojis'; + const unzipStream = fs.createReadStream(destPath); + const extractor = unzipper.Extract({ path: outputPath }); + extractor.on('close', async () => { + const metaRaw = fs.readFileSync(outputPath + '/meta.json', 'utf-8'); + const meta = JSON.parse(metaRaw); + + for (const record of meta.emojis) { + if (!record.downloaded) continue; + const emojiInfo = record.emoji; + const emojiPath = outputPath + '/' + record.fileName; + await this.emojisRepository.delete({ + name: emojiInfo.name, + }); + const driveFile = await this.driveService.addFile({ + user: null, + path: emojiPath, + name: record.fileName, + force: true, + }); + await this.customEmojiService.add({ + name: emojiInfo.name, + category: emojiInfo.category, + host: null, + aliases: emojiInfo.aliases, + driveFile, + }); + } + + cleanup(); + + this.#logger.succ('Imported'); + done(); + }); + unzipStream.pipe(extractor); + this.#logger.succ(`Unzipping to ${outputPath}`); + } +} diff --git a/packages/backend/src/queue/processors/db/delete-account.ts b/packages/backend/src/queue/processors/db/delete-account.ts deleted file mode 100644 index c1657b4be68a..000000000000 --- a/packages/backend/src/queue/processors/db/delete-account.ts +++ /dev/null @@ -1,94 +0,0 @@ -import Bull from 'bull'; -import { queueLogger } from '../../logger.js'; -import { DriveFiles, Notes, UserProfiles, Users } from '@/models/index.js'; -import { DbUserDeleteJobData } from '@/queue/types.js'; -import { Note } from '@/models/entities/note.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; -import { MoreThan } from 'typeorm'; -import { deleteFileSync } from '@/services/drive/delete-file.js'; -import { sendEmail } from '@/services/send-email.js'; - -const logger = queueLogger.createSubLogger('delete-account'); - -export async function deleteAccount(job: Bull.Job): Promise { - logger.info(`Deleting account of ${job.data.user.id} ...`); - - const user = await Users.findOneBy({ id: job.data.user.id }); - if (user == null) { - return; - } - - { // Delete notes - let cursor: Note['id'] | null = null; - - while (true) { - const notes = await Notes.find({ - where: { - userId: user.id, - ...(cursor ? { id: MoreThan(cursor) } : {}), - }, - take: 100, - order: { - id: 1, - }, - }) as Note[]; - - if (notes.length === 0) { - break; - } - - cursor = notes[notes.length - 1].id; - - await Notes.delete(notes.map(note => note.id)); - } - - logger.succ(`All of notes deleted`); - } - - { // Delete files - let cursor: DriveFile['id'] | null = null; - - while (true) { - const files = await DriveFiles.find({ - where: { - userId: user.id, - ...(cursor ? { id: MoreThan(cursor) } : {}), - }, - take: 10, - order: { - id: 1, - }, - }) as DriveFile[]; - - if (files.length === 0) { - break; - } - - cursor = files[files.length - 1].id; - - for (const file of files) { - await deleteFileSync(file); - } - } - - logger.succ(`All of files deleted`); - } - - { // Send email notification - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - if (profile.email && profile.emailVerified) { - sendEmail(profile.email, 'Account deleted', - `Your account has been deleted.`, - `Your account has been deleted.`); - } - } - - // soft指定されている場合は物理削除しない - if (job.data.soft) { - // nop - } else { - await Users.delete(job.data.user.id); - } - - return 'Account deleted'; -} diff --git a/packages/backend/src/queue/processors/db/delete-drive-files.ts b/packages/backend/src/queue/processors/db/delete-drive-files.ts deleted file mode 100644 index b3832d9f04b5..000000000000 --- a/packages/backend/src/queue/processors/db/delete-drive-files.ts +++ /dev/null @@ -1,56 +0,0 @@ -import Bull from 'bull'; - -import { queueLogger } from '../../logger.js'; -import { deleteFileSync } from '@/services/drive/delete-file.js'; -import { Users, DriveFiles } from '@/models/index.js'; -import { MoreThan } from 'typeorm'; -import { DbUserJobData } from '@/queue/types.js'; - -const logger = queueLogger.createSubLogger('delete-drive-files'); - -export async function deleteDriveFiles(job: Bull.Job, done: any): Promise { - logger.info(`Deleting drive files of ${job.data.user.id} ...`); - - const user = await Users.findOneBy({ id: job.data.user.id }); - if (user == null) { - done(); - return; - } - - let deletedCount = 0; - let cursor: any = null; - - while (true) { - const files = await DriveFiles.find({ - where: { - userId: user.id, - ...(cursor ? { id: MoreThan(cursor) } : {}), - }, - take: 100, - order: { - id: 1, - }, - }); - - if (files.length === 0) { - job.progress(100); - break; - } - - cursor = files[files.length - 1].id; - - for (const file of files) { - await deleteFileSync(file); - deletedCount++; - } - - const total = await DriveFiles.countBy({ - userId: user.id, - }); - - job.progress(deletedCount / total); - } - - logger.succ(`All drive files (${deletedCount}) of ${user.id} has been deleted.`); - done(); -} diff --git a/packages/backend/src/queue/processors/db/export-blocking.ts b/packages/backend/src/queue/processors/db/export-blocking.ts deleted file mode 100644 index f5e0424a79c4..000000000000 --- a/packages/backend/src/queue/processors/db/export-blocking.ts +++ /dev/null @@ -1,93 +0,0 @@ -import Bull from 'bull'; -import * as fs from 'node:fs'; - -import { queueLogger } from '../../logger.js'; -import { addFile } from '@/services/drive/add-file.js'; -import { format as dateFormat } from 'date-fns'; -import { getFullApAccount } from '@/misc/convert-host.js'; -import { createTemp } from '@/misc/create-temp.js'; -import { Users, Blockings } from '@/models/index.js'; -import { MoreThan } from 'typeorm'; -import { DbUserJobData } from '@/queue/types.js'; - -const logger = queueLogger.createSubLogger('export-blocking'); - -export async function exportBlocking(job: Bull.Job, done: any): Promise { - logger.info(`Exporting blocking of ${job.data.user.id} ...`); - - const user = await Users.findOneBy({ id: job.data.user.id }); - if (user == null) { - done(); - return; - } - - // Create temp file - const [path, cleanup] = await createTemp(); - - logger.info(`Temp file is ${path}`); - - try { - const stream = fs.createWriteStream(path, { flags: 'a' }); - - let exportedCount = 0; - let cursor: any = null; - - while (true) { - const blockings = await Blockings.find({ - where: { - blockerId: user.id, - ...(cursor ? { id: MoreThan(cursor) } : {}), - }, - take: 100, - order: { - id: 1, - }, - }); - - if (blockings.length === 0) { - job.progress(100); - break; - } - - cursor = blockings[blockings.length - 1].id; - - for (const block of blockings) { - const u = await Users.findOneBy({ id: block.blockeeId }); - if (u == null) { - exportedCount++; continue; - } - - const content = getFullApAccount(u.username, u.host); - await new Promise((res, rej) => { - stream.write(content + '\n', err => { - if (err) { - logger.error(err); - rej(err); - } else { - res(); - } - }); - }); - exportedCount++; - } - - const total = await Blockings.countBy({ - blockerId: user.id, - }); - - job.progress(exportedCount / total); - } - - stream.end(); - logger.succ(`Exported to: ${path}`); - - const fileName = 'blocking-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv'; - const driveFile = await addFile({ user, path, name: fileName, force: true }); - - logger.succ(`Exported to: ${driveFile.id}`); - } finally { - cleanup(); - } - - done(); -} diff --git a/packages/backend/src/queue/processors/db/export-custom-emojis.ts b/packages/backend/src/queue/processors/db/export-custom-emojis.ts deleted file mode 100644 index 3da887cda21c..000000000000 --- a/packages/backend/src/queue/processors/db/export-custom-emojis.ts +++ /dev/null @@ -1,114 +0,0 @@ -import Bull from 'bull'; -import * as fs from 'node:fs'; - -import { ulid } from 'ulid'; -import mime from 'mime-types'; -import archiver from 'archiver'; -import { queueLogger } from '../../logger.js'; -import { addFile } from '@/services/drive/add-file.js'; -import { format as dateFormat } from 'date-fns'; -import { Users, Emojis } from '@/models/index.js'; -import { } from '@/queue/types.js'; -import { createTemp, createTempDir } from '@/misc/create-temp.js'; -import { downloadUrl } from '@/misc/download-url.js'; -import config from '@/config/index.js'; -import { IsNull } from 'typeorm'; - -const logger = queueLogger.createSubLogger('export-custom-emojis'); - -export async function exportCustomEmojis(job: Bull.Job, done: () => void): Promise { - logger.info(`Exporting custom emojis ...`); - - const user = await Users.findOneBy({ id: job.data.user.id }); - if (user == null) { - done(); - return; - } - - const [path, cleanup] = await createTempDir(); - - logger.info(`Temp dir is ${path}`); - - const metaPath = path + '/meta.json'; - - fs.writeFileSync(metaPath, '', 'utf-8'); - - const metaStream = fs.createWriteStream(metaPath, { flags: 'a' }); - - const writeMeta = (text: string): Promise => { - return new Promise((res, rej) => { - metaStream.write(text, err => { - if (err) { - logger.error(err); - rej(err); - } else { - res(); - } - }); - }); - }; - - await writeMeta(`{"metaVersion":2,"host":"${config.host}","exportedAt":"${new Date().toString()}","emojis":[`); - - const customEmojis = await Emojis.find({ - where: { - host: IsNull(), - }, - order: { - id: 'ASC', - }, - }); - - for (const emoji of customEmojis) { - const ext = mime.extension(emoji.type); - const fileName = emoji.name + (ext ? '.' + ext : ''); - const emojiPath = path + '/' + fileName; - fs.writeFileSync(emojiPath, '', 'binary'); - let downloaded = false; - - try { - await downloadUrl(emoji.originalUrl, emojiPath); - downloaded = true; - } catch (e) { // TODO: 何度か再試行 - logger.error(e instanceof Error ? e : new Error(e as string)); - } - - if (!downloaded) { - fs.unlinkSync(emojiPath); - } - - const content = JSON.stringify({ - fileName: fileName, - downloaded: downloaded, - emoji: emoji, - }); - const isFirst = customEmojis.indexOf(emoji) === 0; - - await writeMeta(isFirst ? content : ',\n' + content); - } - - await writeMeta(']}'); - - metaStream.end(); - - // Create archive - const [archivePath, archiveCleanup] = await createTemp(); - const archiveStream = fs.createWriteStream(archivePath); - const archive = archiver('zip', { - zlib: { level: 0 }, - }); - archiveStream.on('close', async () => { - logger.succ(`Exported to: ${archivePath}`); - - const fileName = 'custom-emojis-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.zip'; - const driveFile = await addFile({ user, path: archivePath, name: fileName, force: true }); - - logger.succ(`Exported to: ${driveFile.id}`); - cleanup(); - archiveCleanup(); - done(); - }); - archive.pipe(archiveStream); - archive.directory(path, false); - archive.finalize(); -} diff --git a/packages/backend/src/queue/processors/db/export-following.ts b/packages/backend/src/queue/processors/db/export-following.ts deleted file mode 100644 index 4ac165567b03..000000000000 --- a/packages/backend/src/queue/processors/db/export-following.ts +++ /dev/null @@ -1,94 +0,0 @@ -import Bull from 'bull'; -import * as fs from 'node:fs'; - -import { queueLogger } from '../../logger.js'; -import { addFile } from '@/services/drive/add-file.js'; -import { format as dateFormat } from 'date-fns'; -import { getFullApAccount } from '@/misc/convert-host.js'; -import { createTemp } from '@/misc/create-temp.js'; -import { Users, Followings, Mutings } from '@/models/index.js'; -import { In, MoreThan, Not } from 'typeorm'; -import { DbUserJobData } from '@/queue/types.js'; -import { Following } from '@/models/entities/following.js'; - -const logger = queueLogger.createSubLogger('export-following'); - -export async function exportFollowing(job: Bull.Job, done: () => void): Promise { - logger.info(`Exporting following of ${job.data.user.id} ...`); - - const user = await Users.findOneBy({ id: job.data.user.id }); - if (user == null) { - done(); - return; - } - - // Create temp file - const [path, cleanup] = await createTemp(); - - logger.info(`Temp file is ${path}`); - - try { - const stream = fs.createWriteStream(path, { flags: 'a' }); - - let cursor: Following['id'] | null = null; - - const mutings = job.data.excludeMuting ? await Mutings.findBy({ - muterId: user.id, - }) : []; - - while (true) { - const followings = await Followings.find({ - where: { - followerId: user.id, - ...(mutings.length > 0 ? { followeeId: Not(In(mutings.map(x => x.muteeId))) } : {}), - ...(cursor ? { id: MoreThan(cursor) } : {}), - }, - take: 100, - order: { - id: 1, - }, - }) as Following[]; - - if (followings.length === 0) { - break; - } - - cursor = followings[followings.length - 1].id; - - for (const following of followings) { - const u = await Users.findOneBy({ id: following.followeeId }); - if (u == null) { - continue; - } - - if (job.data.excludeInactive && u.updatedAt && (Date.now() - u.updatedAt.getTime() > 1000 * 60 * 60 * 24 * 90)) { - continue; - } - - const content = getFullApAccount(u.username, u.host); - await new Promise((res, rej) => { - stream.write(content + '\n', err => { - if (err) { - logger.error(err); - rej(err); - } else { - res(); - } - }); - }); - } - } - - stream.end(); - logger.succ(`Exported to: ${path}`); - - const fileName = 'following-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv'; - const driveFile = await addFile({ user, path, name: fileName, force: true }); - - logger.succ(`Exported to: ${driveFile.id}`); - } finally { - cleanup(); - } - - done(); -} diff --git a/packages/backend/src/queue/processors/db/export-mute.ts b/packages/backend/src/queue/processors/db/export-mute.ts deleted file mode 100644 index 6a36cfa07201..000000000000 --- a/packages/backend/src/queue/processors/db/export-mute.ts +++ /dev/null @@ -1,94 +0,0 @@ -import Bull from 'bull'; -import * as fs from 'node:fs'; - -import { queueLogger } from '../../logger.js'; -import { addFile } from '@/services/drive/add-file.js'; -import { format as dateFormat } from 'date-fns'; -import { getFullApAccount } from '@/misc/convert-host.js'; -import { createTemp } from '@/misc/create-temp.js'; -import { Users, Mutings } from '@/models/index.js'; -import { IsNull, MoreThan } from 'typeorm'; -import { DbUserJobData } from '@/queue/types.js'; - -const logger = queueLogger.createSubLogger('export-mute'); - -export async function exportMute(job: Bull.Job, done: any): Promise { - logger.info(`Exporting mute of ${job.data.user.id} ...`); - - const user = await Users.findOneBy({ id: job.data.user.id }); - if (user == null) { - done(); - return; - } - - // Create temp file - const [path, cleanup] = await createTemp(); - - logger.info(`Temp file is ${path}`); - - try { - const stream = fs.createWriteStream(path, { flags: 'a' }); - - let exportedCount = 0; - let cursor: any = null; - - while (true) { - const mutes = await Mutings.find({ - where: { - muterId: user.id, - expiresAt: IsNull(), - ...(cursor ? { id: MoreThan(cursor) } : {}), - }, - take: 100, - order: { - id: 1, - }, - }); - - if (mutes.length === 0) { - job.progress(100); - break; - } - - cursor = mutes[mutes.length - 1].id; - - for (const mute of mutes) { - const u = await Users.findOneBy({ id: mute.muteeId }); - if (u == null) { - exportedCount++; continue; - } - - const content = getFullApAccount(u.username, u.host); - await new Promise((res, rej) => { - stream.write(content + '\n', err => { - if (err) { - logger.error(err); - rej(err); - } else { - res(); - } - }); - }); - exportedCount++; - } - - const total = await Mutings.countBy({ - muterId: user.id, - }); - - job.progress(exportedCount / total); - } - - stream.end(); - logger.succ(`Exported to: ${path}`); - - const fileName = 'mute-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv'; - const driveFile = await addFile({ user, path, name: fileName, force: true }); - - logger.succ(`Exported to: ${driveFile.id}`); - } finally { - cleanup(); - } - - done(); -} diff --git a/packages/backend/src/queue/processors/db/export-notes.ts b/packages/backend/src/queue/processors/db/export-notes.ts deleted file mode 100644 index 051fcdf385fa..000000000000 --- a/packages/backend/src/queue/processors/db/export-notes.ts +++ /dev/null @@ -1,118 +0,0 @@ -import Bull from 'bull'; -import * as fs from 'node:fs'; - -import { queueLogger } from '../../logger.js'; -import { addFile } from '@/services/drive/add-file.js'; -import { format as dateFormat } from 'date-fns'; -import { Users, Notes, Polls } from '@/models/index.js'; -import { MoreThan } from 'typeorm'; -import { Note } from '@/models/entities/note.js'; -import { Poll } from '@/models/entities/poll.js'; -import { DbUserJobData } from '@/queue/types.js'; -import { createTemp } from '@/misc/create-temp.js'; - -const logger = queueLogger.createSubLogger('export-notes'); - -export async function exportNotes(job: Bull.Job, done: any): Promise { - logger.info(`Exporting notes of ${job.data.user.id} ...`); - - const user = await Users.findOneBy({ id: job.data.user.id }); - if (user == null) { - done(); - return; - } - - // Create temp file - const [path, cleanup] = await createTemp(); - - logger.info(`Temp file is ${path}`); - - try { - const stream = fs.createWriteStream(path, { flags: 'a' }); - - const write = (text: string): Promise => { - return new Promise((res, rej) => { - stream.write(text, err => { - if (err) { - logger.error(err); - rej(err); - } else { - res(); - } - }); - }); - }; - - await write('['); - - let exportedNotesCount = 0; - let cursor: Note['id'] | null = null; - - while (true) { - const notes = await Notes.find({ - where: { - userId: user.id, - ...(cursor ? { id: MoreThan(cursor) } : {}), - }, - take: 100, - order: { - id: 1, - }, - }) as Note[]; - - if (notes.length === 0) { - job.progress(100); - break; - } - - cursor = notes[notes.length - 1].id; - - for (const note of notes) { - let poll: Poll | undefined; - if (note.hasPoll) { - poll = await Polls.findOneByOrFail({ noteId: note.id }); - } - const content = JSON.stringify(serialize(note, poll)); - const isFirst = exportedNotesCount === 0; - await write(isFirst ? content : ',\n' + content); - exportedNotesCount++; - } - - const total = await Notes.countBy({ - userId: user.id, - }); - - job.progress(exportedNotesCount / total); - } - - await write(']'); - - stream.end(); - logger.succ(`Exported to: ${path}`); - - const fileName = 'notes-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.json'; - const driveFile = await addFile({ user, path, name: fileName, force: true }); - - logger.succ(`Exported to: ${driveFile.id}`); - } finally { - cleanup(); - } - - done(); -} - -function serialize(note: Note, poll: Poll | null = null): Record { - return { - id: note.id, - text: note.text, - createdAt: note.createdAt, - fileIds: note.fileIds, - replyId: note.replyId, - renoteId: note.renoteId, - poll: poll, - cw: note.cw, - visibility: note.visibility, - visibleUserIds: note.visibleUserIds, - localOnly: note.localOnly, - }; -} diff --git a/packages/backend/src/queue/processors/db/export-user-lists.ts b/packages/backend/src/queue/processors/db/export-user-lists.ts deleted file mode 100644 index 71dd72df27c5..000000000000 --- a/packages/backend/src/queue/processors/db/export-user-lists.ts +++ /dev/null @@ -1,70 +0,0 @@ -import Bull from 'bull'; -import * as fs from 'node:fs'; - -import { queueLogger } from '../../logger.js'; -import { addFile } from '@/services/drive/add-file.js'; -import { format as dateFormat } from 'date-fns'; -import { getFullApAccount } from '@/misc/convert-host.js'; -import { createTemp } from '@/misc/create-temp.js'; -import { Users, UserLists, UserListJoinings } from '@/models/index.js'; -import { In } from 'typeorm'; -import { DbUserJobData } from '@/queue/types.js'; - -const logger = queueLogger.createSubLogger('export-user-lists'); - -export async function exportUserLists(job: Bull.Job, done: any): Promise { - logger.info(`Exporting user lists of ${job.data.user.id} ...`); - - const user = await Users.findOneBy({ id: job.data.user.id }); - if (user == null) { - done(); - return; - } - - const lists = await UserLists.findBy({ - userId: user.id, - }); - - // Create temp file - const [path, cleanup] = await createTemp(); - - logger.info(`Temp file is ${path}`); - - try { - const stream = fs.createWriteStream(path, { flags: 'a' }); - - for (const list of lists) { - const joinings = await UserListJoinings.findBy({ userListId: list.id }); - const users = await Users.findBy({ - id: In(joinings.map(j => j.userId)), - }); - - for (const u of users) { - const acct = getFullApAccount(u.username, u.host); - const content = `${list.name},${acct}`; - await new Promise((res, rej) => { - stream.write(content + '\n', err => { - if (err) { - logger.error(err); - rej(err); - } else { - res(); - } - }); - }); - } - } - - stream.end(); - logger.succ(`Exported to: ${path}`); - - const fileName = 'user-lists-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv'; - const driveFile = await addFile({ user, path, name: fileName, force: true }); - - logger.succ(`Exported to: ${driveFile.id}`); - } finally { - cleanup(); - } - - done(); -} diff --git a/packages/backend/src/queue/processors/db/import-blocking.ts b/packages/backend/src/queue/processors/db/import-blocking.ts deleted file mode 100644 index a216720bb861..000000000000 --- a/packages/backend/src/queue/processors/db/import-blocking.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { IsNull } from 'typeorm'; -import * as Acct from '@/misc/acct.js'; - -import { resolveUser } from '@/services/remote/resolve-user.js'; -import { downloadTextFile } from '@/misc/download-text-file.js'; -import { isSelfHost, toPuny } from '@/misc/convert-host.js'; -import { Users, DriveFiles, Blockings } from '@/models/index.js'; -import type { DbUserImportJobData } from '@/queue/types.js'; -import block from '@/services/blocking/create.js'; -import { queueLogger } from '../../logger.js'; -import type Bull from 'bull'; - -const logger = queueLogger.createSubLogger('import-blocking'); - -export async function importBlocking(job: Bull.Job, done: any): Promise { - logger.info(`Importing blocking of ${job.data.user.id} ...`); - - const user = await Users.findOneBy({ id: job.data.user.id }); - if (user == null) { - done(); - return; - } - - const file = await DriveFiles.findOneBy({ - id: job.data.fileId, - }); - if (file == null) { - done(); - return; - } - - const csv = await downloadTextFile(file.url); - - let linenum = 0; - - for (const line of csv.trim().split('\n')) { - linenum++; - - try { - const acct = line.split(',')[0].trim(); - const { username, host } = Acct.parse(acct); - - let target = isSelfHost(host!) ? await Users.findOneBy({ - host: IsNull(), - usernameLower: username.toLowerCase(), - }) : await Users.findOneBy({ - host: toPuny(host!), - usernameLower: username.toLowerCase(), - }); - - if (host == null && target == null) continue; - - if (target == null) { - target = await resolveUser(username, host); - } - - if (target == null) { - throw `cannot resolve user: @${username}@${host}`; - } - - // skip myself - if (target.id === job.data.user.id) continue; - - logger.info(`Block[${linenum}] ${target.id} ...`); - - await block(user, target); - } catch (e) { - logger.warn(`Error in line:${linenum} ${e}`); - } - } - - logger.succ('Imported'); - done(); -} - diff --git a/packages/backend/src/queue/processors/db/import-custom-emojis.ts b/packages/backend/src/queue/processors/db/import-custom-emojis.ts deleted file mode 100644 index 64dfe85374a2..000000000000 --- a/packages/backend/src/queue/processors/db/import-custom-emojis.ts +++ /dev/null @@ -1,81 +0,0 @@ -import Bull from 'bull'; -import * as fs from 'node:fs'; -import unzipper from 'unzipper'; - -import { queueLogger } from '../../logger.js'; -import { createTempDir } from '@/misc/create-temp.js'; -import { downloadUrl } from '@/misc/download-url.js'; -import { DriveFiles, Emojis } from '@/models/index.js'; -import { DbUserImportJobData } from '@/queue/types.js'; -import { addFile } from '@/services/drive/add-file.js'; -import { genId } from '@/misc/gen-id.js'; -import { db } from '@/db/postgre.js'; - -const logger = queueLogger.createSubLogger('import-custom-emojis'); - -// TODO: 名前衝突時の動作を選べるようにする -export async function importCustomEmojis(job: Bull.Job, done: any): Promise { - logger.info(`Importing custom emojis ...`); - - const file = await DriveFiles.findOneBy({ - id: job.data.fileId, - }); - if (file == null) { - done(); - return; - } - - const [path, cleanup] = await createTempDir(); - - logger.info(`Temp dir is ${path}`); - - const destPath = path + '/emojis.zip'; - - try { - fs.writeFileSync(destPath, '', 'binary'); - await downloadUrl(file.url, destPath); - } catch (e) { // TODO: 何度か再試行 - if (e instanceof Error || typeof e === 'string') { - logger.error(e); - } - throw e; - } - - const outputPath = path + '/emojis'; - const unzipStream = fs.createReadStream(destPath); - const extractor = unzipper.Extract({ path: outputPath }); - extractor.on('close', async () => { - const metaRaw = fs.readFileSync(outputPath + '/meta.json', 'utf-8'); - const meta = JSON.parse(metaRaw); - - for (const record of meta.emojis) { - if (!record.downloaded) continue; - const emojiInfo = record.emoji; - const emojiPath = outputPath + '/' + record.fileName; - await Emojis.delete({ - name: emojiInfo.name, - }); - const driveFile = await addFile({ user: null, path: emojiPath, name: record.fileName, force: true }); - const emoji = await Emojis.insert({ - id: genId(), - updatedAt: new Date(), - name: emojiInfo.name, - category: emojiInfo.category, - host: null, - aliases: emojiInfo.aliases, - originalUrl: driveFile.url, - publicUrl: driveFile.webpublicUrl ?? driveFile.url, - type: driveFile.webpublicType ?? driveFile.type, - }).then(x => Emojis.findOneByOrFail(x.identifiers[0])); - } - - await db.queryResultCache!.remove(['meta_emojis']); - - cleanup(); - - logger.succ('Imported'); - done(); - }); - unzipStream.pipe(extractor); - logger.succ(`Unzipping to ${outputPath}`); -} diff --git a/packages/backend/src/services/CustomEmojiService.ts b/packages/backend/src/services/CustomEmojiService.ts new file mode 100644 index 000000000000..ce541a5b66ee --- /dev/null +++ b/packages/backend/src/services/CustomEmojiService.ts @@ -0,0 +1,78 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Emojis } from '@/models/index.js'; +import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; +import { genId } from '@/misc/gen-id.js'; +import type { DriveFile } from '@/models/entities/drive-file.js'; +import type { Emoji } from '@/models/entities/emoji.js'; +import type { DataSource } from 'typeorm'; + +@Injectable() +export class CustomEmojiService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject(DI_SYMBOLS.db) + private db: DataSource, + + @Inject('emojisRepository') + private emojisRepository: typeof Emojis, + + private globalEventServie: GlobalEventService, + ) { + } + + public async add(data: { + driveFile: DriveFile; + name: string; + category: string | null; + aliases: string[]; + }): Promise { + const emoji = await this.emojisRepository.insert({ + id: genId(), + updatedAt: new Date(), + name: data.name, + category: data.category, + host: null, + aliases: data.aliases, + originalUrl: data.driveFile.url, + publicUrl: data.driveFile.webpublicUrl ?? data.driveFile.url, + type: data.driveFile.webpublicType ?? data.driveFile.type, + }).then(x => Emojis.findOneByOrFail(x.identifiers[0])); + + await this.db.queryResultCache!.remove(['meta_emojis']); + + return emoji; + } + + public async addBulk(datas: { + driveFile: DriveFile; + name: string; + category: string | null; + aliases: string[]; + }[]): Promise { + const result: Emoji[] = []; + + for (const data of datas) { + const emoji = await this.emojisRepository.insert({ + id: genId(), + updatedAt: new Date(), + name: data.name, + category: data.category, + host: null, + aliases: data.aliases, + originalUrl: data.driveFile.url, + publicUrl: data.driveFile.webpublicUrl ?? data.driveFile.url, + type: data.driveFile.webpublicType ?? data.driveFile.type, + }).then(x => Emojis.findOneByOrFail(x.identifiers[0])); + + result.push(emoji); + } + + await this.db.queryResultCache!.remove(['meta_emojis']); + + return result; + } +} From 00baf2e64aae8814f0d35100cc42c31ec5864417 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 19:20:52 +0900 Subject: [PATCH 060/180] wip --- .../backend/src/misc/download-text-file.ts | 25 ---- packages/backend/src/misc/download-url.ts | 89 ------------- .../ImportCustomEmojisProcessorService.ts | 4 +- .../src/services/CustomEmojiService.ts | 32 +---- .../backend/src/services/DownloadService.ts | 123 ++++++++++++++++++ 5 files changed, 128 insertions(+), 145 deletions(-) delete mode 100644 packages/backend/src/misc/download-text-file.ts delete mode 100644 packages/backend/src/misc/download-url.ts create mode 100644 packages/backend/src/services/DownloadService.ts diff --git a/packages/backend/src/misc/download-text-file.ts b/packages/backend/src/misc/download-text-file.ts deleted file mode 100644 index c62c70ee33b1..000000000000 --- a/packages/backend/src/misc/download-text-file.ts +++ /dev/null @@ -1,25 +0,0 @@ -import * as fs from 'node:fs'; -import * as util from 'node:util'; -import Logger from '@/services/logger.js'; -import { createTemp } from './create-temp.js'; -import { downloadUrl } from './download-url.js'; - -const logger = new Logger('download-text-file'); - -export async function downloadTextFile(url: string): Promise { - // Create temp file - const [path, cleanup] = await createTemp(); - - logger.info(`Temp file is ${path}`); - - try { - // write content at URL to temp file - await downloadUrl(url, path); - - const text = await util.promisify(fs.readFile)(path, 'utf8'); - - return text; - } finally { - cleanup(); - } -} diff --git a/packages/backend/src/misc/download-url.ts b/packages/backend/src/misc/download-url.ts deleted file mode 100644 index 7c57b140efde..000000000000 --- a/packages/backend/src/misc/download-url.ts +++ /dev/null @@ -1,89 +0,0 @@ -import * as fs from 'node:fs'; -import * as stream from 'node:stream'; -import * as util from 'node:util'; -import got, * as Got from 'got'; -import { httpAgent, httpsAgent, StatusError } from './fetch.js'; -import config from '@/config/index.js'; -import chalk from 'chalk'; -import Logger from '@/services/logger.js'; -import IPCIDR from 'ip-cidr'; -import PrivateIp from 'private-ip'; - -const pipeline = util.promisify(stream.pipeline); - -export async function downloadUrl(url: string, path: string): Promise { - const logger = new Logger('download'); - - logger.info(`Downloading ${chalk.cyan(url)} ...`); - - const timeout = 30 * 1000; - const operationTimeout = 60 * 1000; - const maxSize = config.maxFileSize || 262144000; - - const req = got.stream(url, { - headers: { - 'User-Agent': config.userAgent, - }, - timeout: { - lookup: timeout, - connect: timeout, - secureConnect: timeout, - socket: timeout, // read timeout - response: timeout, - send: timeout, - request: operationTimeout, // whole operation timeout - }, - agent: { - http: httpAgent, - https: httpsAgent, - }, - http2: false, // default - retry: { - limit: 0, - }, - }).on('response', (res: Got.Response) => { - if ((process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'test') && !config.proxy && res.ip) { - if (isPrivateIp(res.ip)) { - logger.warn(`Blocked address: ${res.ip}`); - req.destroy(); - } - } - - const contentLength = res.headers['content-length']; - if (contentLength != null) { - const size = Number(contentLength); - if (size > maxSize) { - logger.warn(`maxSize exceeded (${size} > ${maxSize}) on response`); - req.destroy(); - } - } - }).on('downloadProgress', (progress: Got.Progress) => { - if (progress.transferred > maxSize) { - logger.warn(`maxSize exceeded (${progress.transferred} > ${maxSize}) on downloadProgress`); - req.destroy(); - } - }); - - try { - await pipeline(req, fs.createWriteStream(path)); - } catch (e) { - if (e instanceof Got.HTTPError) { - throw new StatusError(`${e.response.statusCode} ${e.response.statusMessage}`, e.response.statusCode, e.response.statusMessage); - } else { - throw e; - } - } - - logger.succ(`Download finished: ${chalk.cyan(url)}`); -} - -function isPrivateIp(ip: string): boolean { - for (const net of config.allowedPrivateNetworks || []) { - const cidr = new IPCIDR(net); - if (cidr.contains(ip)) { - return false; - } - } - - return PrivateIp(ip); -} diff --git a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts index 8d6a24cd22a1..6250208e1136 100644 --- a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts @@ -9,6 +9,7 @@ import type Logger from '@/logger.js'; import type { CustomEmojiService } from '@/services/CustomEmojiService.js'; import { createTempDir } from '@/misc/create-temp.js'; import type { DriveService } from '@/services/drive/DriveService.js'; +import type { DownloadService } from '@/services/DownloadService.js'; import type { DataSource } from 'typeorm'; import type Bull from 'bull'; import type { DbUserImportJobData } from '../types.js'; @@ -37,6 +38,7 @@ export class ImportCustomEmojisProcessorService { private customEmojiService: CustomEmojiService, private driveService: DriveService, + private downloadService: DownloadService, private queueLoggerService: QueueLoggerService, ) { this.queueLoggerService.logger.createSubLogger('import-custom-emojis'); @@ -61,7 +63,7 @@ export class ImportCustomEmojisProcessorService { try { fs.writeFileSync(destPath, '', 'binary'); - await downloadUrl(file.url, destPath); + await this.downloadService.downloadUrl(file.url, destPath); } catch (e) { // TODO: 何度か再試行 if (e instanceof Error || typeof e === 'string') { this.#logger.error(e); diff --git a/packages/backend/src/services/CustomEmojiService.ts b/packages/backend/src/services/CustomEmojiService.ts index ce541a5b66ee..497dcd6b04fc 100644 --- a/packages/backend/src/services/CustomEmojiService.ts +++ b/packages/backend/src/services/CustomEmojiService.ts @@ -29,13 +29,14 @@ export class CustomEmojiService { name: string; category: string | null; aliases: string[]; + host: string | null; }): Promise { const emoji = await this.emojisRepository.insert({ id: genId(), updatedAt: new Date(), name: data.name, category: data.category, - host: null, + host: data.host, aliases: data.aliases, originalUrl: data.driveFile.url, publicUrl: data.driveFile.webpublicUrl ?? data.driveFile.url, @@ -46,33 +47,4 @@ export class CustomEmojiService { return emoji; } - - public async addBulk(datas: { - driveFile: DriveFile; - name: string; - category: string | null; - aliases: string[]; - }[]): Promise { - const result: Emoji[] = []; - - for (const data of datas) { - const emoji = await this.emojisRepository.insert({ - id: genId(), - updatedAt: new Date(), - name: data.name, - category: data.category, - host: null, - aliases: data.aliases, - originalUrl: data.driveFile.url, - publicUrl: data.driveFile.webpublicUrl ?? data.driveFile.url, - type: data.driveFile.webpublicType ?? data.driveFile.type, - }).then(x => Emojis.findOneByOrFail(x.identifiers[0])); - - result.push(emoji); - } - - await this.db.queryResultCache!.remove(['meta_emojis']); - - return result; - } } diff --git a/packages/backend/src/services/DownloadService.ts b/packages/backend/src/services/DownloadService.ts new file mode 100644 index 000000000000..9d72e3dcbd7e --- /dev/null +++ b/packages/backend/src/services/DownloadService.ts @@ -0,0 +1,123 @@ +import * as fs from 'node:fs'; +import * as stream from 'node:stream'; +import * as util from 'node:util'; +import { Inject, Injectable } from '@nestjs/common'; +import IPCIDR from 'ip-cidr'; +import PrivateIp from 'private-ip'; +import got, * as Got from 'got'; +import chalk from 'chalk'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; +import Logger from '@/logger.js'; +import { StatusError } from '@/services/HttpRequestService.js'; +import type { HttpRequestService } from '@/services/HttpRequestService.js'; +import { createTemp } from '@/misc/create-temp'; + +const pipeline = util.promisify(stream.pipeline); + +@Injectable() +export class DownloadService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + private httpRequestService: HttpRequestService, + ) { + this.#logger = new Logger('download'); + } + + public async downloadUrl(url: string, path: string): Promise { + this.#logger.info(`Downloading ${chalk.cyan(url)} ...`); + + const timeout = 30 * 1000; + const operationTimeout = 60 * 1000; + const maxSize = this.config.maxFileSize || 262144000; + + const req = got.stream(url, { + headers: { + 'User-Agent': this.config.userAgent, + }, + timeout: { + lookup: timeout, + connect: timeout, + secureConnect: timeout, + socket: timeout, // read timeout + response: timeout, + send: timeout, + request: operationTimeout, // whole operation timeout + }, + agent: { + http: this.httpRequestService.httpAgent, + https: this.httpRequestService.httpsAgent, + }, + http2: false, // default + retry: { + limit: 0, + }, + }).on('response', (res: Got.Response) => { + if ((process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'test') && !this.config.proxy && res.ip) { + if (this.#isPrivateIp(res.ip)) { + this.#logger.warn(`Blocked address: ${res.ip}`); + req.destroy(); + } + } + + const contentLength = res.headers['content-length']; + if (contentLength != null) { + const size = Number(contentLength); + if (size > maxSize) { + this.#logger.warn(`maxSize exceeded (${size} > ${maxSize}) on response`); + req.destroy(); + } + } + }).on('downloadProgress', (progress: Got.Progress) => { + if (progress.transferred > maxSize) { + this.#logger.warn(`maxSize exceeded (${progress.transferred} > ${maxSize}) on downloadProgress`); + req.destroy(); + } + }); + + try { + await pipeline(req, fs.createWriteStream(path)); + } catch (e) { + if (e instanceof Got.HTTPError) { + throw new StatusError(`${e.response.statusCode} ${e.response.statusMessage}`, e.response.statusCode, e.response.statusMessage); + } else { + throw e; + } + } + + this.#logger.succ(`Download finished: ${chalk.cyan(url)}`); + } + + public async downloadTextFile(url: string): Promise { + // Create temp file + const [path, cleanup] = await createTemp(); + + this.#logger.info(`text file: Temp file is ${path}`); + + try { + // write content at URL to temp file + await this.downloadUrl(url, path); + + const text = await util.promisify(fs.readFile)(path, 'utf8'); + + return text; + } finally { + cleanup(); + } + } + + #isPrivateIp(ip: string): boolean { + for (const net of this.config.allowedPrivateNetworks || []) { + const cidr = new IPCIDR(net); + if (cidr.contains(ip)) { + return false; + } + } + + return PrivateIp(ip); + } +} From 1af4a07abbc789ef37fbd78933397ef568d100d7 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 19:24:36 +0900 Subject: [PATCH 061/180] wip --- .../DeleteAccountProcessorService.ts | 2 +- .../DeleteDriveFilesProcessorService.ts | 2 +- .../ExportBlockingProcessorService.ts | 2 +- .../ExportCustomEmojisProcessorService.ts | 2 +- .../ExportFollowingProcessorService.ts | 2 +- .../ExportMutingProcessorService.ts | 2 +- .../processors/ExportNotesProcessorService.ts | 2 +- .../ExportUserListsProcessorService.ts | 2 +- .../ImportCustomEmojisProcessorService.ts | 2 +- .../src/services/{drive => }/DriveService.ts | 23 ++++++++++--------- .../{drive => }/InternalStorageService.ts | 0 .../src/services/{drive => }/S3Service.ts | 0 .../src/services/drive/DriveLoggerService.ts | 12 ---------- 13 files changed, 21 insertions(+), 32 deletions(-) rename packages/backend/src/services/{drive => }/DriveService.ts (96%) rename packages/backend/src/services/{drive => }/InternalStorageService.ts (100%) rename packages/backend/src/services/{drive => }/S3Service.ts (100%) delete mode 100644 packages/backend/src/services/drive/DriveLoggerService.ts diff --git a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts index 655f9022fa87..62a086a3d2b4 100644 --- a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts @@ -5,7 +5,7 @@ import type { DriveFiles, UserProfiles } from '@/models/index.js'; import { Notes, Users } from '@/models/index.js'; import type { Config } from '@/config/types.js'; import type Logger from '@/logger.js'; -import type { DriveService } from '@/services/drive/DriveService.js'; +import type { DriveService } from '@/services/DriveService.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import type { Note } from '@/models/entities/note.js'; import type Bull from 'bull'; diff --git a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts index cbe8edc9362c..87f422036542 100644 --- a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts @@ -4,7 +4,7 @@ import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Users, DriveFiles } from '@/models/index.js'; import type { Config } from '@/config/types.js'; import type Logger from '@/logger.js'; -import type { DriveService } from '@/services/drive/DriveService.js'; +import type { DriveService } from '@/services/DriveService.js'; import type Bull from 'bull'; import type { DbUserJobData } from '../types.js'; import type { QueueLoggerService } from '../QueueLoggerService.js'; diff --git a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts index 79b531c2961b..2534fae00877 100644 --- a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts @@ -6,7 +6,7 @@ import { DI_SYMBOLS } from '@/di-symbols.js'; import type { DriveFiles, UserProfiles , Notes , Users , Blockings } from '@/models/index.js'; import type { Config } from '@/config/types.js'; import type Logger from '@/logger.js'; -import type { DriveService } from '@/services/drive/DriveService.js'; +import type { DriveService } from '@/services/DriveService.js'; import { getFullApAccount } from '@/misc/convert-host.js'; import { createTemp } from '@/misc/create-temp.js'; import type Bull from 'bull'; diff --git a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts index bba4c018186f..0b82f78ceca4 100644 --- a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts @@ -9,7 +9,7 @@ import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Emojis, Users } from '@/models/index.js'; import type { Config } from '@/config/types.js'; import type Logger from '@/logger.js'; -import type { DriveService } from '@/services/drive/DriveService.js'; +import type { DriveService } from '@/services/DriveService.js'; import { createTemp, createTempDir } from '@/misc/create-temp.js'; import { downloadUrl } from '@/misc/download-url.js'; import type Bull from 'bull'; diff --git a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts index 2d8a616aef39..6e1a63de35bc 100644 --- a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts @@ -7,7 +7,7 @@ import { Users } from '@/models/index.js'; import type { Followings, Mutings } from '@/models/index.js'; import type { Config } from '@/config/types.js'; import type Logger from '@/logger.js'; -import type { DriveService } from '@/services/drive/DriveService.js'; +import type { DriveService } from '@/services/DriveService.js'; import { getFullApAccount } from '@/misc/convert-host.js'; import { createTemp } from '@/misc/create-temp.js'; import type { Following } from '@/models/entities/following.js'; diff --git a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts index 709cf3167bb2..5645c9c9a785 100644 --- a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts @@ -6,7 +6,7 @@ import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Mutings, Users , Blockings } from '@/models/index.js'; import type { Config } from '@/config/types.js'; import type Logger from '@/logger.js'; -import type { DriveService } from '@/services/drive/DriveService.js'; +import type { DriveService } from '@/services/DriveService.js'; import { getFullApAccount } from '@/misc/convert-host.js'; import { createTemp } from '@/misc/create-temp.js'; import type Bull from 'bull'; diff --git a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts index 9ef4d8c07b97..c481f222c15e 100644 --- a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts @@ -6,7 +6,7 @@ import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Notes, Polls , Users } from '@/models/index.js'; import type { Config } from '@/config/types.js'; import type Logger from '@/logger.js'; -import type { DriveService } from '@/services/drive/DriveService.js'; +import type { DriveService } from '@/services/DriveService.js'; import { createTemp } from '@/misc/create-temp.js'; import type { Poll } from '@/models/entities/poll.js'; import type { Note } from '@/models/entities/note.js'; diff --git a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts index 65d95a60127c..f461ee40d8e4 100644 --- a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts @@ -6,7 +6,7 @@ import { DI_SYMBOLS } from '@/di-symbols.js'; import type { UserListJoinings, UserLists, Users } from '@/models/index.js'; import type { Config } from '@/config/types.js'; import type Logger from '@/logger.js'; -import type { DriveService } from '@/services/drive/DriveService.js'; +import type { DriveService } from '@/services/DriveService.js'; import { createTemp } from '@/misc/create-temp.js'; import { getFullApAccount } from '@/misc/convert-host.js'; import type Bull from 'bull'; diff --git a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts index 6250208e1136..dcb19bc8fc9d 100644 --- a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts @@ -8,7 +8,7 @@ import type { Config } from '@/config/types.js'; import type Logger from '@/logger.js'; import type { CustomEmojiService } from '@/services/CustomEmojiService.js'; import { createTempDir } from '@/misc/create-temp.js'; -import type { DriveService } from '@/services/drive/DriveService.js'; +import type { DriveService } from '@/services/DriveService.js'; import type { DownloadService } from '@/services/DownloadService.js'; import type { DataSource } from 'typeorm'; import type Bull from 'bull'; diff --git a/packages/backend/src/services/drive/DriveService.ts b/packages/backend/src/services/DriveService.ts similarity index 96% rename from packages/backend/src/services/drive/DriveService.ts rename to packages/backend/src/services/DriveService.ts index f0303dd4f860..9e39304bdb3c 100644 --- a/packages/backend/src/services/drive/DriveService.ts +++ b/packages/backend/src/services/DriveService.ts @@ -4,9 +4,10 @@ import { v4 as uuid } from 'uuid'; import sharp from 'sharp'; import { IsNull } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import { DriveFiles , DriveFolders, Users } from '@/models/index.js'; +import type { DriveFiles } from '@/models/index.js'; +import { DriveFolders, Users } from '@/models/index.js'; import type { Config } from '@/config/types.js'; -import type Logger from '@/Logger.js'; +import Logger from '@/Logger.js'; import type { IRemoteUser, User } from '@/models/entities/user.js'; import type { MetaService } from '@/services/MetaService.js'; import { DriveFile } from '@/models/entities/drive-file.js'; @@ -21,15 +22,14 @@ import type { VideoProcessingService } from '@/services/VideoProcessingService.j import type { IImage, ImageProcessingService } from '@/services/ImageProcessingService.js'; import type { QueueService } from '@/queue/queue.service.js'; import type { DriveFolder } from '@/models/entities/drive-folder.js'; -import { downloadUrl } from '@/misc/download-url.js'; import { createTemp } from '@/misc/create-temp.js'; import type DriveChart from '@/services/chart/charts/drive.js'; import type PerUserDriveChart from '@/services/chart/charts/per-user-drive.js'; import type InstanceChart from '@/services/chart/charts/instance.js'; -import type { S3Service } from './S3Service.js'; -import type { InternalStorageService } from './InternalStorageService.js'; +import type { DownloadService } from '@/services/DownloadService.js'; +import type { S3Service } from '@/services/drive/S3Service.js'; +import type { InternalStorageService } from '@/services/drive/InternalStorageService.js'; import type S3 from 'aws-sdk/clients/s3.js'; -import type { DriveLoggerService } from './DriveLoggerService.js'; type AddFileArgs = { /** User who wish to add file */ @@ -86,6 +86,7 @@ export class DriveService { private driveFilesRepository: typeof DriveFiles, private metaService: MetaService, + private downloadService: DownloadService, private internalStorageService: InternalStorageService, private s3Service: S3Service, private imageProcessingService: ImageProcessingService, @@ -95,10 +96,10 @@ export class DriveService { private driveChart: DriveChart, private perUserDriveChart: PerUserDriveChart, private instanceChart: InstanceChart, - private driveLoggerService: DriveLoggerService, ) { - this.#registerLogger = this.driveLoggerService.logger.createSubLogger('register', 'yellow'); - this.#downloaderLogger = this.driveLoggerService.logger.createSubLogger('downloader'); + const logger = new Logger('drive', 'blue'); + this.#registerLogger = logger.createSubLogger('register', 'yellow'); + this.#downloaderLogger = logger.createSubLogger('downloader'); } /*** @@ -694,7 +695,7 @@ export class DriveService { requestHeaders = null, }: UploadFromUrlArgs): Promise { let name = new URL(url).pathname.split('/').pop() || null; - if (name == null || !DriveFiles.validateFileName(name)) { + if (name == null || !this.driveFilesRepository.validateFileName(name)) { name = null; } @@ -709,7 +710,7 @@ export class DriveService { try { // write content at URL to temp file - await downloadUrl(url, path); + await this.downloadService.downloadUrl(url, path); const driveFile = await this.addFile({ user, path, name, comment, folderId, force, isLink, url, uri, sensitive, requestIp, requestHeaders }); this.#downloaderLogger.succ(`Got: ${driveFile.id}`); diff --git a/packages/backend/src/services/drive/InternalStorageService.ts b/packages/backend/src/services/InternalStorageService.ts similarity index 100% rename from packages/backend/src/services/drive/InternalStorageService.ts rename to packages/backend/src/services/InternalStorageService.ts diff --git a/packages/backend/src/services/drive/S3Service.ts b/packages/backend/src/services/S3Service.ts similarity index 100% rename from packages/backend/src/services/drive/S3Service.ts rename to packages/backend/src/services/S3Service.ts diff --git a/packages/backend/src/services/drive/DriveLoggerService.ts b/packages/backend/src/services/drive/DriveLoggerService.ts deleted file mode 100644 index eea998e15b26..000000000000 --- a/packages/backend/src/services/drive/DriveLoggerService.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import Logger from '@/logger.js'; - -@Injectable() -export class DriveLoggerService { - public logger: Logger; - - constructor( - ) { - this.logger = new Logger('drive', 'blue'); - } -} From 059537c90f66db59f179e35792bad84a1a1199cf Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 19:52:25 +0900 Subject: [PATCH 062/180] wip --- packages/backend/src/misc/gen-id.ts | 21 ---- .../db/index.ts => DbQueueService.ts} | 4 +- .../ImportBlockingProcessorService.ts | 5 +- .../ImportFollowingProcessorService.ts | 99 ++++++++++++++++ .../ImportMutingProcessorService.ts | 101 ++++++++++++++++ .../ImportUserListsProcessorService.ts | 112 ++++++++++++++++++ .../queue/processors/db/import-following.ts | 74 ------------ .../src/queue/processors/db/import-muting.ts | 84 ------------- .../queue/processors/db/import-user-lists.ts | 80 ------------- .../backend/src/server/api/common/signin.ts | 4 +- .../backend/src/server/api/common/signup.ts | 4 +- .../server/api/endpoints/admin/ad/create.ts | 6 +- .../endpoints/admin/announcements/create.ts | 5 +- .../server/api/endpoints/admin/emoji/add.ts | 6 +- .../server/api/endpoints/admin/emoji/copy.ts | 6 +- .../src/server/api/endpoints/admin/invite.ts | 10 +- .../server/api/endpoints/antennas/create.ts | 5 +- .../src/server/api/endpoints/app/create.ts | 5 +- .../src/server/api/endpoints/auth/accept.ts | 5 +- .../api/endpoints/auth/session/generate.ts | 5 +- .../server/api/endpoints/channels/create.ts | 5 +- .../server/api/endpoints/channels/follow.ts | 5 +- .../server/api/endpoints/clips/add-note.ts | 5 +- .../src/server/api/endpoints/clips/create.ts | 5 +- .../api/endpoints/drive/folders/create.ts | 5 +- .../api/endpoints/gallery/posts/create.ts | 2 +- .../api/endpoints/gallery/posts/like.ts | 5 +- .../api/endpoints/i/2fa/register-key.ts | 5 +- .../api/endpoints/i/read-announcement.ts | 6 +- .../server/api/endpoints/i/registry/set.ts | 5 +- .../server/api/endpoints/i/webhooks/create.ts | 5 +- .../server/api/endpoints/miauth/gen-token.ts | 5 +- .../src/server/api/endpoints/mute/create.ts | 5 +- .../api/endpoints/notes/favorites/create.ts | 6 +- .../server/api/endpoints/notes/polls/vote.ts | 6 +- .../endpoints/notes/thread-muting/create.ts | 5 +- .../src/server/api/endpoints/pages/create.ts | 5 +- .../src/server/api/endpoints/pages/like.ts | 5 +- .../src/server/api/endpoints/promo/read.ts | 5 +- .../api/endpoints/request-reset-password.ts | 6 +- .../src/server/api/endpoints/sw/register.ts | 5 +- .../api/endpoints/users/groups/create.ts | 7 +- .../users/groups/invitations/accept.ts | 5 +- .../api/endpoints/users/groups/invite.ts | 6 +- .../api/endpoints/users/lists/create.ts | 5 +- .../api/endpoints/users/report-abuse.ts | 6 +- .../backend/src/server/api/private/signin.ts | 6 +- .../backend/src/server/api/private/signup.ts | 4 +- .../backend/src/services/AntennaService.ts | 5 +- .../src/services/CreateNotificationService.ts | 5 +- .../src/services/CreateSystemUserService.ts | 6 +- .../src/services/CustomEmojiService.ts | 5 +- packages/backend/src/services/DriveService.ts | 5 +- packages/backend/src/services/EmailService.ts | 11 +- .../src/services/FederatedInstanceService.ts | 6 +- .../backend/src/services/HashtagService.ts | 7 +- packages/backend/src/services/IdService.ts | 33 ++++++ .../src/services/ModerationLogService.ts | 6 +- .../backend/src/services/NoteCreateService.ts | 5 +- .../backend/src/services/NotePiningService.ts | 5 +- .../backend/src/services/NoteReadService.ts | 5 +- packages/backend/src/services/PollService.ts | 5 +- .../backend/src/services/ReactionService.ts | 5 +- packages/backend/src/services/RelayService.ts | 5 +- .../src/services/UserBlockingService.ts | 5 +- .../src/services/UserFollowingService.ts | 7 +- .../backend/src/services/UserListService.ts | 5 +- .../backend/src/services/UserMutingService.ts | 32 +++++ .../backend/src/services/messages/create.ts | 4 +- .../remote/activitypub/ApInboxService.ts | 18 +-- .../activitypub/models/ApNoteService.ts | 2 +- .../remote/activitypub/models/person.ts | 4 +- 72 files changed, 580 insertions(+), 402 deletions(-) delete mode 100644 packages/backend/src/misc/gen-id.ts rename packages/backend/src/queue/{processors/db/index.ts => DbQueueService.ts} (93%) create mode 100644 packages/backend/src/queue/processors/ImportFollowingProcessorService.ts create mode 100644 packages/backend/src/queue/processors/ImportMutingProcessorService.ts create mode 100644 packages/backend/src/queue/processors/ImportUserListsProcessorService.ts delete mode 100644 packages/backend/src/queue/processors/db/import-following.ts delete mode 100644 packages/backend/src/queue/processors/db/import-muting.ts delete mode 100644 packages/backend/src/queue/processors/db/import-user-lists.ts create mode 100644 packages/backend/src/services/IdService.ts create mode 100644 packages/backend/src/services/UserMutingService.ts diff --git a/packages/backend/src/misc/gen-id.ts b/packages/backend/src/misc/gen-id.ts deleted file mode 100644 index fcf476857fe2..000000000000 --- a/packages/backend/src/misc/gen-id.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ulid } from 'ulid'; -import { genAid } from './id/aid.js'; -import { genMeid } from './id/meid.js'; -import { genMeidg } from './id/meidg.js'; -import { genObjectId } from './id/object-id.js'; -import config from '@/config/index.js'; - -const metohd = config.id.toLowerCase(); - -export function genId(date?: Date): string { - if (!date || (date > new Date())) date = new Date(); - - switch (metohd) { - case 'aid': return genAid(date); - case 'meid': return genMeid(date); - case 'meidg': return genMeidg(date); - case 'ulid': return ulid(date.getTime()); - case 'objectid': return genObjectId(date); - default: throw new Error('unrecognized id generation method'); - } -} diff --git a/packages/backend/src/queue/processors/db/index.ts b/packages/backend/src/queue/DbQueueService.ts similarity index 93% rename from packages/backend/src/queue/processors/db/index.ts rename to packages/backend/src/queue/DbQueueService.ts index e91d569779fe..2293341b5b38 100644 --- a/packages/backend/src/queue/processors/db/index.ts +++ b/packages/backend/src/queue/DbQueueService.ts @@ -1,5 +1,4 @@ -import Bull from 'bull'; -import { DbJobData } from '@/queue/types.js'; +import type { DbJobData } from '@/queue/types.js'; import { deleteDriveFiles } from './delete-drive-files.js'; import { exportCustomEmojis } from './export-custom-emojis.js'; import { exportNotes } from './export-notes.js'; @@ -13,6 +12,7 @@ import { deleteAccount } from './delete-account.js'; import { importMuting } from './import-muting.js'; import { importBlocking } from './import-blocking.js'; import { importCustomEmojis } from './import-custom-emojis.js'; +import type Bull from 'bull'; const jobs = { deleteDriveFiles, diff --git a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts index f6e384b3f11e..8eb1af8b2125 100644 --- a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts @@ -7,9 +7,9 @@ import type { Config } from '@/config/types.js'; import type Logger from '@/logger.js'; import { isSelfHost, toPuny } from '@/misc/convert-host.js'; import * as Acct from '@/misc/acct.js'; -import { downloadTextFile } from '@/misc/download-text-file.js'; import type { ResolveUserService } from '@/services/remote/ResolveUserService.js'; import type { UserBlockingService } from '@/services/UserBlockingService.js'; +import type { DownloadService } from '@/services/DownloadService.js'; import type Bull from 'bull'; import type { DbUserImportJobData } from '../types.js'; import type { QueueLoggerService } from '../QueueLoggerService.js'; @@ -33,6 +33,7 @@ export class ImportBlockingProcessorService { private userBlockingService: UserBlockingService, private resolveUserService: ResolveUserService, + private downloadService: DownloadService, private queueLoggerService: QueueLoggerService, ) { this.queueLoggerService.logger.createSubLogger('import-blocking'); @@ -55,7 +56,7 @@ export class ImportBlockingProcessorService { return; } - const csv = await downloadTextFile(file.url); + const csv = await this.downloadService.downloadTextFile(file.url); let linenum = 0; diff --git a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts new file mode 100644 index 000000000000..7b4a36f4b80c --- /dev/null +++ b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts @@ -0,0 +1,99 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { IsNull, MoreThan } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { Users } from '@/models/index.js'; +import type { DriveFiles } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import { isSelfHost, toPuny } from '@/misc/convert-host.js'; +import * as Acct from '@/misc/acct.js'; +import type { ResolveUserService } from '@/services/remote/ResolveUserService.js'; +import type { DownloadService } from '@/services/DownloadService.js'; +import type { UserFollowingService } from '@/services/UserFollowingService.js'; +import type Bull from 'bull'; +import type { DbUserImportJobData } from '../types.js'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; + +@Injectable() +export class ImportFollowingProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private userFollowingService: UserFollowingService, + private resolveUserService: ResolveUserService, + private downloadService: DownloadService, + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('import-following'); + } + + public async process(job: Bull.Job, done: () => void): Promise { + this.#logger.info(`Importing following of ${job.data.user.id} ...`); + + const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); + if (user == null) { + done(); + return; + } + + const file = await this.driveFilesRepository.findOneBy({ + id: job.data.fileId, + }); + if (file == null) { + done(); + return; + } + + const csv = await this.downloadService.downloadTextFile(file.url); + + let linenum = 0; + + for (const line of csv.trim().split('\n')) { + linenum++; + + try { + const acct = line.split(',')[0].trim(); + const { username, host } = Acct.parse(acct); + + let target = isSelfHost(host!) ? await Users.findOneBy({ + host: IsNull(), + usernameLower: username.toLowerCase(), + }) : await Users.findOneBy({ + host: toPuny(host!), + usernameLower: username.toLowerCase(), + }); + + if (host == null && target == null) continue; + + if (target == null) { + target = await this.resolveUserService.resolveUser(username, host); + } + + if (target == null) { + throw `cannot resolve user: @${username}@${host}`; + } + + // skip myself + if (target.id === job.data.user.id) continue; + + this.#logger.info(`Follow[${linenum}] ${target.id} ...`); + + this.userFollowingService.follow(user, target); + } catch (e) { + this.#logger.warn(`Error in line:${linenum} ${e}`); + } + } + + this.#logger.succ('Imported'); + done(); + } +} diff --git a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts new file mode 100644 index 000000000000..4359a435c9a3 --- /dev/null +++ b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts @@ -0,0 +1,101 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { IsNull, MoreThan } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { DriveFiles } from '@/models/index.js'; +import { Users } from '@/models/index.js'; + +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import { isSelfHost, toPuny } from '@/misc/convert-host.js'; +import * as Acct from '@/misc/acct.js'; +import type { ResolveUserService } from '@/services/remote/ResolveUserService.js'; +import type { DownloadService } from '@/services/DownloadService.js'; +import type { UserFollowingService } from '@/services/UserFollowingService.js'; +import type { UserMutingService } from '@/services/UserMutingService.js'; +import type Bull from 'bull'; +import type { DbUserImportJobData } from '../types.js'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; + +@Injectable() +export class ImportMutingProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private userMutingService: UserMutingService, + private resolveUserService: ResolveUserService, + private downloadService: DownloadService, + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('import-muting'); + } + + public async process(job: Bull.Job, done: () => void): Promise { + this.#logger.info(`Importing muting of ${job.data.user.id} ...`); + + const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); + if (user == null) { + done(); + return; + } + + const file = await this.driveFilesRepository.findOneBy({ + id: job.data.fileId, + }); + if (file == null) { + done(); + return; + } + + const csv = await this.downloadService.downloadTextFile(file.url); + + let linenum = 0; + + for (const line of csv.trim().split('\n')) { + linenum++; + + try { + const acct = line.split(',')[0].trim(); + const { username, host } = Acct.parse(acct); + + let target = isSelfHost(host!) ? await Users.findOneBy({ + host: IsNull(), + usernameLower: username.toLowerCase(), + }) : await Users.findOneBy({ + host: toPuny(host!), + usernameLower: username.toLowerCase(), + }); + + if (host == null && target == null) continue; + + if (target == null) { + target = await this.resolveUserService.resolveUser(username, host); + } + + if (target == null) { + throw `cannot resolve user: @${username}@${host}`; + } + + // skip myself + if (target.id === job.data.user.id) continue; + + this.#logger.info(`Mute[${linenum}] ${target.id} ...`); + + await this.userMutingService.mute(user, target); + } catch (e) { + this.#logger.warn(`Error in line:${linenum} ${e}`); + } + } + + this.#logger.succ('Imported'); + done(); + } +} diff --git a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts new file mode 100644 index 000000000000..a65ccc769d69 --- /dev/null +++ b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts @@ -0,0 +1,112 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { IsNull, MoreThan } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { DriveFiles , UserListJoinings , UserLists } from '@/models/index.js'; +import { Users } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import { isSelfHost, toPuny } from '@/misc/convert-host.js'; +import * as Acct from '@/misc/acct.js'; +import type { ResolveUserService } from '@/services/remote/ResolveUserService.js'; +import type { DownloadService } from '@/services/DownloadService.js'; +import type { UserListService } from '@/services/UserListService.js'; +import type { IdService } from '@/services/IdService.js'; +import type Bull from 'bull'; +import type { DbUserImportJobData } from '../types.js'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; + +@Injectable() +export class ImportUserListsProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + @Inject('userListsRepository') + private userListsRepository: typeof UserLists, + + @Inject('userListJoiningsRepository') + private userListJoiningsRepository: typeof UserListJoinings, + + private idService: IdService, + private userListService: UserListService, + private resolveUserService: ResolveUserService, + private downloadService: DownloadService, + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('import-user-lists'); + } + + public async process(job: Bull.Job, done: () => void): Promise { + this.#logger.info(`Importing user lists of ${job.data.user.id} ...`); + + const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); + if (user == null) { + done(); + return; + } + + const file = await this.driveFilesRepository.findOneBy({ + id: job.data.fileId, + }); + if (file == null) { + done(); + return; + } + + const csv = await this.downloadService.downloadTextFile(file.url); + + let linenum = 0; + + for (const line of csv.trim().split('\n')) { + linenum++; + + try { + const listName = line.split(',')[0].trim(); + const { username, host } = Acct.parse(line.split(',')[1].trim()); + + let list = await this.userListsRepository.findOneBy({ + userId: user.id, + name: listName, + }); + + if (list == null) { + list = await this.userListsRepository.insert({ + id: this.idService.genId(), + createdAt: new Date(), + userId: user.id, + name: listName, + }).then(x => this.userListsRepository.findOneByOrFail(x.identifiers[0])); + } + + let target = isSelfHost(host!) ? await Users.findOneBy({ + host: IsNull(), + usernameLower: username.toLowerCase(), + }) : await Users.findOneBy({ + host: toPuny(host!), + usernameLower: username.toLowerCase(), + }); + + if (target == null) { + target = await this.resolveUserService.resolveUser(username, host); + } + + if (await this.userListJoiningsRepository.findOneBy({ userListId: list!.id, userId: target.id }) != null) continue; + + this.userListService.push(target, list!); + } catch (e) { + this.#logger.warn(`Error in line:${linenum} ${e}`); + } + } + + this.#logger.succ('Imported'); + done(); + } +} diff --git a/packages/backend/src/queue/processors/db/import-following.ts b/packages/backend/src/queue/processors/db/import-following.ts deleted file mode 100644 index 09a3c49085be..000000000000 --- a/packages/backend/src/queue/processors/db/import-following.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { IsNull } from 'typeorm'; -import follow from '@/services/following/create.js'; - -import * as Acct from '@/misc/acct.js'; -import { resolveUser } from '@/services/remote/resolve-user.js'; -import { downloadTextFile } from '@/misc/download-text-file.js'; -import { isSelfHost, toPuny } from '@/misc/convert-host.js'; -import { Users, DriveFiles } from '@/models/index.js'; -import type { DbUserImportJobData } from '@/queue/types.js'; -import { queueLogger } from '../../logger.js'; -import type Bull from 'bull'; - -const logger = queueLogger.createSubLogger('import-following'); - -export async function importFollowing(job: Bull.Job, done: any): Promise { - logger.info(`Importing following of ${job.data.user.id} ...`); - - const user = await Users.findOneBy({ id: job.data.user.id }); - if (user == null) { - done(); - return; - } - - const file = await DriveFiles.findOneBy({ - id: job.data.fileId, - }); - if (file == null) { - done(); - return; - } - - const csv = await downloadTextFile(file.url); - - let linenum = 0; - - for (const line of csv.trim().split('\n')) { - linenum++; - - try { - const acct = line.split(',')[0].trim(); - const { username, host } = Acct.parse(acct); - - let target = isSelfHost(host!) ? await Users.findOneBy({ - host: IsNull(), - usernameLower: username.toLowerCase(), - }) : await Users.findOneBy({ - host: toPuny(host!), - usernameLower: username.toLowerCase(), - }); - - if (host == null && target == null) continue; - - if (target == null) { - target = await resolveUser(username, host); - } - - if (target == null) { - throw `cannot resolve user: @${username}@${host}`; - } - - // skip myself - if (target.id === job.data.user.id) continue; - - logger.info(`Follow[${linenum}] ${target.id} ...`); - - follow(user, target); - } catch (e) { - logger.warn(`Error in line:${linenum} ${e}`); - } - } - - logger.succ('Imported'); - done(); -} diff --git a/packages/backend/src/queue/processors/db/import-muting.ts b/packages/backend/src/queue/processors/db/import-muting.ts deleted file mode 100644 index ca966aa49245..000000000000 --- a/packages/backend/src/queue/processors/db/import-muting.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { IsNull } from 'typeorm'; -import * as Acct from '@/misc/acct.js'; - -import { resolveUser } from '@/services/remote/resolve-user.js'; -import { downloadTextFile } from '@/misc/download-text-file.js'; -import { isSelfHost, toPuny } from '@/misc/convert-host.js'; -import { Users, DriveFiles, Mutings } from '@/models/index.js'; -import type { DbUserImportJobData } from '@/queue/types.js'; -import type { User } from '@/models/entities/user.js'; -import { genId } from '@/misc/gen-id.js'; -import { queueLogger } from '../../logger.js'; -import type Bull from 'bull'; - -const logger = queueLogger.createSubLogger('import-muting'); - -export async function importMuting(job: Bull.Job, done: any): Promise { - logger.info(`Importing muting of ${job.data.user.id} ...`); - - const user = await Users.findOneBy({ id: job.data.user.id }); - if (user == null) { - done(); - return; - } - - const file = await DriveFiles.findOneBy({ - id: job.data.fileId, - }); - if (file == null) { - done(); - return; - } - - const csv = await downloadTextFile(file.url); - - let linenum = 0; - - for (const line of csv.trim().split('\n')) { - linenum++; - - try { - const acct = line.split(',')[0].trim(); - const { username, host } = Acct.parse(acct); - - let target = isSelfHost(host!) ? await Users.findOneBy({ - host: IsNull(), - usernameLower: username.toLowerCase(), - }) : await Users.findOneBy({ - host: toPuny(host!), - usernameLower: username.toLowerCase(), - }); - - if (host == null && target == null) continue; - - if (target == null) { - target = await resolveUser(username, host); - } - - if (target == null) { - throw `cannot resolve user: @${username}@${host}`; - } - - // skip myself - if (target.id === job.data.user.id) continue; - - logger.info(`Mute[${linenum}] ${target.id} ...`); - - await mute(user, target); - } catch (e) { - logger.warn(`Error in line:${linenum} ${e}`); - } - } - - logger.succ('Imported'); - done(); -} - -async function mute(user: User, target: User) { - await Mutings.insert({ - id: genId(), - createdAt: new Date(), - muterId: user.id, - muteeId: target.id, - }); -} diff --git a/packages/backend/src/queue/processors/db/import-user-lists.ts b/packages/backend/src/queue/processors/db/import-user-lists.ts deleted file mode 100644 index 3ea5cca57bcb..000000000000 --- a/packages/backend/src/queue/processors/db/import-user-lists.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { IsNull } from 'typeorm'; -import * as Acct from '@/misc/acct.js'; - -import { resolveUser } from '@/services/remote/resolve-user.js'; -import { pushUserToUserList } from '@/services/user-list/push.js'; -import { downloadTextFile } from '@/misc/download-text-file.js'; -import { isSelfHost, toPuny } from '@/misc/convert-host.js'; -import { DriveFiles, Users, UserLists, UserListJoinings } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; -import type { DbUserImportJobData } from '@/queue/types.js'; -import { queueLogger } from '../../logger.js'; -import type Bull from 'bull'; - -const logger = queueLogger.createSubLogger('import-user-lists'); - -export async function importUserLists(job: Bull.Job, done: any): Promise { - logger.info(`Importing user lists of ${job.data.user.id} ...`); - - const user = await Users.findOneBy({ id: job.data.user.id }); - if (user == null) { - done(); - return; - } - - const file = await DriveFiles.findOneBy({ - id: job.data.fileId, - }); - if (file == null) { - done(); - return; - } - - const csv = await downloadTextFile(file.url); - - let linenum = 0; - - for (const line of csv.trim().split('\n')) { - linenum++; - - try { - const listName = line.split(',')[0].trim(); - const { username, host } = Acct.parse(line.split(',')[1].trim()); - - let list = await UserLists.findOneBy({ - userId: user.id, - name: listName, - }); - - if (list == null) { - list = await UserLists.insert({ - id: genId(), - createdAt: new Date(), - userId: user.id, - name: listName, - }).then(x => UserLists.findOneByOrFail(x.identifiers[0])); - } - - let target = isSelfHost(host!) ? await Users.findOneBy({ - host: IsNull(), - usernameLower: username.toLowerCase(), - }) : await Users.findOneBy({ - host: toPuny(host!), - usernameLower: username.toLowerCase(), - }); - - if (target == null) { - target = await resolveUser(username, host); - } - - if (await UserListJoinings.findOneBy({ userListId: list!.id, userId: target.id }) != null) continue; - - pushUserToUserList(target, list!); - } catch (e) { - logger.warn(`Error in line:${linenum} ${e}`); - } - } - - logger.succ('Imported'); - done(); -} diff --git a/packages/backend/src/server/api/common/signin.ts b/packages/backend/src/server/api/common/signin.ts index 038fd8d96e88..592630009627 100644 --- a/packages/backend/src/server/api/common/signin.ts +++ b/packages/backend/src/server/api/common/signin.ts @@ -3,7 +3,7 @@ import Koa from 'koa'; import config from '@/config/index.js'; import { ILocalUser } from '@/models/entities/user.js'; import { Signins } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { publishMainStream } from '@/services/stream.js'; export default function(ctx: Koa.Context, user: ILocalUser, redirect = false) { @@ -30,7 +30,7 @@ export default function(ctx: Koa.Context, user: ILocalUser, redirect = false) { (async () => { // Append signin history const record = await Signins.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), userId: user.id, ip: ctx.ip, diff --git a/packages/backend/src/server/api/common/signup.ts b/packages/backend/src/server/api/common/signup.ts index abc142472a08..89fa9ad85382 100644 --- a/packages/backend/src/server/api/common/signup.ts +++ b/packages/backend/src/server/api/common/signup.ts @@ -5,7 +5,7 @@ import { User } from '@/models/entities/user.js'; import { Users, UsedUsernames } from '@/models/index.js'; import { UserProfile } from '@/models/entities/user-profile.js'; import { IsNull } from 'typeorm'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { toPunyNullable } from '@/misc/convert-host.js'; import { UserKeypair } from '@/models/entities/user-keypair.js'; import { usersChart } from '@/services/chart/index.js'; @@ -79,7 +79,7 @@ export async function signup(opts: { if (exist) throw new Error(' the username is already used'); account = await transactionalEntityManager.save(new User({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), username: username, usernameLower: username.toLowerCase(), diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts index e749e93eed6d..a1b415e057c7 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Ads } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; export const meta = { tags: ['admin'], @@ -30,10 +30,12 @@ export default class extends Endpoint { constructor( @Inject('adsRepository') private adsRepository: typeof Ads, + + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { await this.adsRepository.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), expiresAt: new Date(ps.expiresAt), url: ps.url, diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts index c080ff2dd5d0..21d3bc4b5666 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Announcements } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; export const meta = { tags: ['admin'], @@ -59,10 +59,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const announcement = await Announcements.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), updatedAt: null, title: ps.title, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index f9823687fb82..a5f6cdba38d7 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import rndstr from 'rndstr'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis, DriveFiles } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { insertModerationLog } from '@/services/insert-moderation-log.js'; import { publishBroadcastStream } from '@/services/stream.js'; import { db } from '@/db/postgre.js'; @@ -40,6 +40,8 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const file = await DriveFiles.findOneBy({ id: ps.fileId }); @@ -49,7 +51,7 @@ export default class extends Endpoint { const name = file.name.split('.')[0].match(/^[a-z0-9_]+$/) ? file.name.split('.')[0] : `_${rndstr('a-z0-9', 8)}_`; const emoji = await Emojis.insert({ - id: genId(), + id: this.idService.genId(), updatedAt: new Date(), name: name, category: null, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index 4c7fa295e859..5ec82c26b8a9 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import { uploadFromUrl } from '@/services/drive/upload-from-url.js'; import { publishBroadcastStream } from '@/services/stream.js'; @@ -52,6 +52,8 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const emoji = await Emojis.findOneBy({ id: ps.emojiId }); @@ -70,7 +72,7 @@ export default class extends Endpoint { } const copied = await Emojis.insert({ - id: genId(), + id: this.idService.genId(), updatedAt: new Date(), name: emoji.name, host: null, diff --git a/packages/backend/src/server/api/endpoints/admin/invite.ts b/packages/backend/src/server/api/endpoints/admin/invite.ts index 49397b2e0b1b..665ecd10a869 100644 --- a/packages/backend/src/server/api/endpoints/admin/invite.ts +++ b/packages/backend/src/server/api/endpoints/admin/invite.ts @@ -2,7 +2,7 @@ import rndstr from 'rndstr'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { RegistrationTickets } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; export const meta = { tags: ['admin'], @@ -35,11 +35,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + private idService: IdService, ) { super(meta, paramDef, async () => { const code = rndstr({ @@ -48,7 +44,7 @@ export default class extends Endpoint { }); await RegistrationTickets.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), code, }); diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index 633d3f6eb763..d7b2b753fcd6 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { Antennas, UserLists, UserGroupJoinings } from '@/models/index.js'; import { publishInternalEvent } from '@/services/stream.js'; import { ApiError } from '../../error.js'; @@ -65,6 +65,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { let userList; @@ -91,7 +92,7 @@ export default class extends Endpoint { } const antenna = await Antennas.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), userId: me.id, name: ps.name, diff --git a/packages/backend/src/server/api/endpoints/app/create.ts b/packages/backend/src/server/api/endpoints/app/create.ts index 6d2892ffb981..984a306ca7d3 100644 --- a/packages/backend/src/server/api/endpoints/app/create.ts +++ b/packages/backend/src/server/api/endpoints/app/create.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Apps } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { unique } from '@/prelude/array.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; @@ -34,6 +34,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { // Generate secret @@ -44,7 +45,7 @@ export default class extends Endpoint { // Create account const app = await Apps.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), userId: me ? me.id : null, name: ps.name, diff --git a/packages/backend/src/server/api/endpoints/auth/accept.ts b/packages/backend/src/server/api/endpoints/auth/accept.ts index bf9146bdb2b8..543a05797b10 100644 --- a/packages/backend/src/server/api/endpoints/auth/accept.ts +++ b/packages/backend/src/server/api/endpoints/auth/accept.ts @@ -2,7 +2,7 @@ import * as crypto from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { AuthSessions, AccessTokens, Apps } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; import { ApiError } from '../../error.js'; @@ -34,6 +34,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { // Fetch token @@ -66,7 +67,7 @@ export default class extends Endpoint { // Insert access token doc await AccessTokens.insert({ - id: genId(), + id: this.idService.genId(), createdAt: now, lastUsedAt: now, appId: session.appId, diff --git a/packages/backend/src/server/api/endpoints/auth/session/generate.ts b/packages/backend/src/server/api/endpoints/auth/session/generate.ts index aa42b29b5dd4..efdbc2ac1fa6 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/generate.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/generate.ts @@ -3,7 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import config from '@/config/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Apps, AuthSessions } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -48,6 +48,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { // Lookup app @@ -64,7 +65,7 @@ export default class extends Endpoint { // Create session token document const doc = await AuthSessions.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), appId: app.id, token: token, diff --git a/packages/backend/src/server/api/endpoints/channels/create.ts b/packages/backend/src/server/api/endpoints/channels/create.ts index 78aaba56ea5a..7c5b67088ede 100644 --- a/packages/backend/src/server/api/endpoints/channels/create.ts +++ b/packages/backend/src/server/api/endpoints/channels/create.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Channels, DriveFiles } from '@/models/index.js'; import type { Channel } from '@/models/entities/channel.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -41,6 +41,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { let banner = null; @@ -56,7 +57,7 @@ export default class extends Endpoint { } const channel = await Channels.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), userId: me.id, name: ps.name, diff --git a/packages/backend/src/server/api/endpoints/channels/follow.ts b/packages/backend/src/server/api/endpoints/channels/follow.ts index 3eb77975746e..4c6f04dcfc82 100644 --- a/packages/backend/src/server/api/endpoints/channels/follow.ts +++ b/packages/backend/src/server/api/endpoints/channels/follow.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Channels, ChannelFollowings } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { publishUserEvent } from '@/services/stream.js'; import { ApiError } from '../../error.js'; @@ -33,6 +33,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const channel = await Channels.findOneBy({ @@ -44,7 +45,7 @@ export default class extends Endpoint { } await ChannelFollowings.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), followerId: me.id, followeeId: channel.id, diff --git a/packages/backend/src/server/api/endpoints/clips/add-note.ts b/packages/backend/src/server/api/endpoints/clips/add-note.ts index f9d6c2a70cc1..1239d22c78f1 100644 --- a/packages/backend/src/server/api/endpoints/clips/add-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/add-note.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ClipNotes, Clips } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { ApiError } from '../../error.js'; import { getNote } from '../../common/getters.js'; @@ -46,6 +46,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const clip = await Clips.findOneBy({ @@ -72,7 +73,7 @@ export default class extends Endpoint { } await ClipNotes.insert({ - id: genId(), + id: this.idService.genId(), noteId: note.id, clipId: clip.id, }); diff --git a/packages/backend/src/server/api/endpoints/clips/create.ts b/packages/backend/src/server/api/endpoints/clips/create.ts index f011bb1fc7bf..703f0d80cea9 100644 --- a/packages/backend/src/server/api/endpoints/clips/create.ts +++ b/packages/backend/src/server/api/endpoints/clips/create.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { Clips } from '@/models/index.js'; export const meta = { @@ -31,10 +31,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const clip = await Clips.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), userId: me.id, name: ps.name, diff --git a/packages/backend/src/server/api/endpoints/drive/folders/create.ts b/packages/backend/src/server/api/endpoints/drive/folders/create.ts index b2e5c0ec6ac9..894b0a759786 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/create.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { publishDriveStream } from '@/services/stream.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFolders } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -40,6 +40,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { // If the parent folder is specified @@ -58,7 +59,7 @@ export default class extends Endpoint { // Create folder const folder = await DriveFolders.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), name: ps.name, parentId: parent !== null ? parent.id : null, diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts index c1b222c3b643..d3a32e135de2 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts @@ -61,7 +61,7 @@ export default class extends Endpoint { } const post = await GalleryPosts.insert(new GalleryPost({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), updatedAt: new Date(), title: ps.title, diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts index c59acf0db014..93ff709e79bb 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GalleryPosts, GalleryLikes } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -44,6 +44,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const post = await GalleryPosts.findOneBy({ id: ps.postId }); @@ -67,7 +68,7 @@ export default class extends Endpoint { // Create like await GalleryLikes.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), postId: post.id, userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts index 934f5ba33052..c513d7126063 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts @@ -4,7 +4,7 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserProfiles, AttestationChallenges } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { hash } from '../../../2fa.js'; const randomBytes = promisify(crypto.randomBytes); @@ -27,6 +27,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); @@ -49,7 +50,7 @@ export default class extends Endpoint { .replace(/\+/g, '-') .replace(/\//g, '_'); - const challengeId = genId(); + const challengeId = this.idService.genId(); await AttestationChallenges.insert({ userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/i/read-announcement.ts b/packages/backend/src/server/api/endpoints/i/read-announcement.ts index fb3a10679f15..6452d78012f2 100644 --- a/packages/backend/src/server/api/endpoints/i/read-announcement.ts +++ b/packages/backend/src/server/api/endpoints/i/read-announcement.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import type { Users } from '@/models/index.js'; import { AnnouncementReads, Announcements } from '@/models/index.js'; import { publishMainStream } from '@/services/stream.js'; @@ -36,6 +36,8 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { // Check if announcement exists @@ -57,7 +59,7 @@ export default class extends Endpoint { // Create read await AnnouncementReads.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), announcementId: ps.announcementId, userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/i/registry/set.ts b/packages/backend/src/server/api/endpoints/i/registry/set.ts index 88af9a29346e..8052389dff53 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/set.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/set.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { publishMainStream } from '@/services/stream.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { RegistryItems } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; export const meta = { requireCredential: true, @@ -26,6 +26,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const query = RegistryItems.createQueryBuilder('item') @@ -43,7 +44,7 @@ export default class extends Endpoint { }); } else { await RegistryItems.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), updatedAt: new Date(), userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts index 9fe700c2184f..9375244f23aa 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { Webhooks } from '@/models/index.js'; import { publishInternalEvent } from '@/services/stream.js'; import { webhookEventTypes } from '@/models/entities/webhook.js'; @@ -30,10 +30,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const webhook = await Webhooks.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), userId: me.id, name: ps.name, diff --git a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts index a78df77d310e..cac954dec425 100644 --- a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts +++ b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { AccessTokens } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; export const meta = { @@ -41,6 +41,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { // Generate access token @@ -50,7 +51,7 @@ export default class extends Endpoint { // Insert access token doc await AccessTokens.insert({ - id: genId(), + id: this.idService.genId(), createdAt: now, lastUsedAt: now, session: ps.session, diff --git a/packages/backend/src/server/api/endpoints/mute/create.ts b/packages/backend/src/server/api/endpoints/mute/create.ts index 3befee46b61a..da613907f96d 100644 --- a/packages/backend/src/server/api/endpoints/mute/create.ts +++ b/packages/backend/src/server/api/endpoints/mute/create.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { Mutings, NoteWatchings } from '@/models/index.js'; import type { Muting } from '@/models/entities/muting.js'; import { publishUserEvent } from '@/services/stream.js'; @@ -52,6 +52,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const muter = me; @@ -83,7 +84,7 @@ export default class extends Endpoint { // Create mute await Mutings.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), expiresAt: ps.expiresAt ? new Date(ps.expiresAt) : null, muterId: muter.id, diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts index fa2cf7d12b3a..3c16374706eb 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { NoteFavorites } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; import { getNote } from '../../../common/getters.js'; @@ -39,7 +39,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { // Get favoritee @@ -60,7 +60,7 @@ export default class extends Endpoint { // Create favorite await NoteFavorites.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), noteId: note.id, userId: me.id, 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 a433b66741fb..3e5d6429c5f5 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -9,7 +9,7 @@ import { deliverQuestionUpdate } from '@/services/note/polls/update.js'; import type { Users } from '@/models/index.js'; import { PollVotes, NoteWatchings, Polls, Blockings } from '@/models/index.js'; import type { IRemoteUser } from '@/models/entities/user.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; @@ -75,6 +75,8 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const createdAt = new Date(); @@ -128,7 +130,7 @@ export default class extends Endpoint { // Create vote const vote = await PollVotes.insert({ - id: genId(), + id: this.idService.genId(), createdAt, noteId: note.id, userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts index 0bae91a19dd6..990808b328e5 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Notes, NoteThreadMutings } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import readNote from '@/services/note/read.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../../common/getters.js'; @@ -34,6 +34,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { @@ -52,7 +53,7 @@ export default class extends Endpoint { await readNote(me.id, mutedNotes); await NoteThreadMutings.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), threadId: note.threadId || note.id, userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/pages/create.ts b/packages/backend/src/server/api/endpoints/pages/create.ts index 7521ad2838de..24e7b562f540 100644 --- a/packages/backend/src/server/api/endpoints/pages/create.ts +++ b/packages/backend/src/server/api/endpoints/pages/create.ts @@ -1,7 +1,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Pages, DriveFiles } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { Page } from '@/models/entities/page.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; @@ -63,6 +63,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { let eyeCatchingImage = null; @@ -87,7 +88,7 @@ export default class extends Endpoint { }); const page = await Pages.insert(new Page({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), updatedAt: new Date(), title: ps.title, diff --git a/packages/backend/src/server/api/endpoints/pages/like.ts b/packages/backend/src/server/api/endpoints/pages/like.ts index 60be55795490..b6f62839e7cf 100644 --- a/packages/backend/src/server/api/endpoints/pages/like.ts +++ b/packages/backend/src/server/api/endpoints/pages/like.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Pages, PageLikes } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; @@ -44,6 +44,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const page = await Pages.findOneBy({ id: ps.pageId }); @@ -67,7 +68,7 @@ export default class extends Endpoint { // Create like await PageLikes.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), pageId: page.id, userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/promo/read.ts b/packages/backend/src/server/api/endpoints/promo/read.ts index e98245564fc7..2c8015abb0d5 100644 --- a/packages/backend/src/server/api/endpoints/promo/read.ts +++ b/packages/backend/src/server/api/endpoints/promo/read.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { PromoReads } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { getNote } from '../../common/getters.js'; @@ -31,6 +31,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { @@ -48,7 +49,7 @@ export default class extends Endpoint { } await PromoReads.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), noteId: note.id, userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/request-reset-password.ts b/packages/backend/src/server/api/endpoints/request-reset-password.ts index 4dae4fc69d61..4d62e910fc76 100644 --- a/packages/backend/src/server/api/endpoints/request-reset-password.ts +++ b/packages/backend/src/server/api/endpoints/request-reset-password.ts @@ -7,8 +7,8 @@ import config from '@/config/index.js'; import type { Users } from '@/models/index.js'; import { UserProfiles, PasswordResetRequests } from '@/models/index.js'; import { sendEmail } from '@/services/send-email.js'; -import { genId } from '@/misc/gen-id.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { IdService } from '@/services/IdService.js'; import { ApiError } from '../error.js'; export const meta = { @@ -43,6 +43,8 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy({ @@ -70,7 +72,7 @@ export default class extends Endpoint { const token = rndstr('a-z0-9', 64); await PasswordResetRequests.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), userId: profile.userId, token, diff --git a/packages/backend/src/server/api/endpoints/sw/register.ts b/packages/backend/src/server/api/endpoints/sw/register.ts index a13593351ac4..ae326ca7a6de 100644 --- a/packages/backend/src/server/api/endpoints/sw/register.ts +++ b/packages/backend/src/server/api/endpoints/sw/register.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { fetchMeta } from '@/misc/fetch-meta.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { SwSubscriptions } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -42,6 +42,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { // if already subscribed @@ -62,7 +63,7 @@ export default class extends Endpoint { } await SwSubscriptions.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), userId: me.id, endpoint: ps.endpoint, diff --git a/packages/backend/src/server/api/endpoints/users/groups/create.ts b/packages/backend/src/server/api/endpoints/users/groups/create.ts index 127ea2f3f331..51d0338a95bd 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/create.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/create.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { UserGroups, UserGroupJoinings } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import type { UserGroup } from '@/models/entities/user-group.js'; import type { UserGroupJoining } from '@/models/entities/user-group-joining.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -33,10 +33,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const userGroup = await UserGroups.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), userId: me.id, name: ps.name, @@ -44,7 +45,7 @@ export default class extends Endpoint { // Push the owner await UserGroupJoinings.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), userId: me.id, userGroupId: userGroup.id, diff --git a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts b/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts index 8e2843444a6b..7ba0e1b697c3 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { UserGroupJoinings, UserGroupInvitations } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import type { UserGroupJoining } from '@/models/entities/user-group-joining.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../../error.js'; @@ -35,6 +35,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { // Fetch the invitation @@ -52,7 +53,7 @@ export default class extends Endpoint { // Push the user await UserGroupJoinings.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), userId: me.id, userGroupId: invitation.userGroupId, diff --git a/packages/backend/src/server/api/endpoints/users/groups/invite.ts b/packages/backend/src/server/api/endpoints/users/groups/invite.ts index bf8f10050e64..bb78e570a405 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invite.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invite.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { UserGroups, UserGroupJoinings, UserGroupInvitations } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import type { UserGroupInvitation } from '@/models/entities/user-group-invitation.js'; import { createNotification } from '@/services/create-notification.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -61,6 +61,8 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { // Fetch the group @@ -98,7 +100,7 @@ export default class extends Endpoint { } const invitation = await UserGroupInvitations.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), userId: user.id, userGroupId: userGroup.id, diff --git a/packages/backend/src/server/api/endpoints/users/lists/create.ts b/packages/backend/src/server/api/endpoints/users/lists/create.ts index 41013065af16..029e7f2c697f 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { UserLists } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import type { UserList } from '@/models/entities/user-list.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -32,10 +32,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const userList = await UserLists.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), userId: me.id, name: ps.name, diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts index 2e656f8cfd94..13a811110d06 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -3,7 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { publishAdminStream } from '@/services/stream.js'; import type { Users } from '@/models/index.js'; import { AbuseUserReports } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { sendEmail } from '@/services/send-email.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -56,6 +56,8 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { // Lookup user @@ -73,7 +75,7 @@ export default class extends Endpoint { } const report = await AbuseUserReports.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), targetUserId: user.id, targetUserHost: user.host, diff --git a/packages/backend/src/server/api/private/signin.ts b/packages/backend/src/server/api/private/signin.ts index 79b31764fd28..807f51f4c37d 100644 --- a/packages/backend/src/server/api/private/signin.ts +++ b/packages/backend/src/server/api/private/signin.ts @@ -5,7 +5,7 @@ import signin from '../common/signin.js'; import config from '@/config/index.js'; import { Users, Signins, UserProfiles, UserSecurityKeys, AttestationChallenges } from '@/models/index.js'; import { ILocalUser } from '@/models/entities/user.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { verifyLogin, hash } from '../2fa.js'; import { randomBytes } from 'node:crypto'; import { IsNull } from 'typeorm'; @@ -84,7 +84,7 @@ export default async (ctx: Koa.Context) => { async function fail(status?: number, failure?: { id: string }) { // Append signin history await Signins.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), userId: user.id, ip: ctx.ip, @@ -226,7 +226,7 @@ export default async (ctx: Koa.Context) => { .replace(/\+/g, '-') .replace(/\//g, '_'); - const challengeId = genId(); + const challengeId = this.idService.genId(); await AttestationChallenges.insert({ userId: user.id, diff --git a/packages/backend/src/server/api/private/signup.ts b/packages/backend/src/server/api/private/signup.ts index 26f172637cf9..21069db32593 100644 --- a/packages/backend/src/server/api/private/signup.ts +++ b/packages/backend/src/server/api/private/signup.ts @@ -7,7 +7,7 @@ import { Users, RegistrationTickets, UserPendings } from '@/models/index.js'; import { signup } from '../common/signup.js'; import config from '@/config/index.js'; import { sendEmail } from '@/services/send-email.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { validateEmailForAccount } from '@/services/validate-email-for-account.js'; export default async (ctx: Koa.Context) => { @@ -76,7 +76,7 @@ export default async (ctx: Koa.Context) => { const hash = await bcrypt.hash(password, salt); await UserPendings.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), code, email: emailAddress, diff --git a/packages/backend/src/services/AntennaService.ts b/packages/backend/src/services/AntennaService.ts index 4b88432c1f6d..b0479fdda7c8 100644 --- a/packages/backend/src/services/AntennaService.ts +++ b/packages/backend/src/services/AntennaService.ts @@ -3,7 +3,7 @@ import type { AntennaNotes, Mutings, Notes, Users } from '@/models/index.js'; import type { Antenna } from '@/models/entities/antenna.js'; import type { Note } from '@/models/entities/note.js'; import type { User } from '@/models/entities/user.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; @@ -19,6 +19,7 @@ export class AntennaService { @Inject('antennaNotesRepository') private antennaNotesRepository: typeof AntennaNotes, + private idService: IdService, private globalEventServie: GlobalEventService, ) { } @@ -28,7 +29,7 @@ export class AntennaService { const read = !antenna.notify || (antenna.userId === noteUser.id); this.antennaNotesRepository.insert({ - id: genId(), + id: this.idService.genId(), antennaId: antenna.id, noteId: note.id, read: read, diff --git a/packages/backend/src/services/CreateNotificationService.ts b/packages/backend/src/services/CreateNotificationService.ts index 22be5f44467c..083c180ab15d 100644 --- a/packages/backend/src/services/CreateNotificationService.ts +++ b/packages/backend/src/services/CreateNotificationService.ts @@ -3,7 +3,7 @@ import type { Mutings, Notifications, UserProfiles , Users } from '@/models/inde import type { User } from '@/models/entities/user.js'; import type { Notification } from '@/models/entities/notification.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; @Injectable() export class CreateNotificationService { @@ -20,6 +20,7 @@ export class CreateNotificationService { @Inject('mutingsRepository') private mutingsRepository: typeof Mutings, + private idService: IdService, private globalEventServie: GlobalEventService, ) { } @@ -39,7 +40,7 @@ export class CreateNotificationService { // Create notification const notification = await this.notificationsRepository.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), notifieeId: notifieeId, type: type, diff --git a/packages/backend/src/services/CreateSystemUserService.ts b/packages/backend/src/services/CreateSystemUserService.ts index 3321fdd88e53..f0fe5e1e3db1 100644 --- a/packages/backend/src/services/CreateSystemUserService.ts +++ b/packages/backend/src/services/CreateSystemUserService.ts @@ -6,7 +6,7 @@ import { IsNull } from 'typeorm'; import { genRsaKeyPair } from '@/misc/gen-key-pair.js'; import { User } from '@/models/entities/user.js'; import { UserProfile } from '@/models/entities/user-profile.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { UserKeypair } from '@/models/entities/user-keypair.js'; import { UsedUsername } from '@/models/entities/used-username.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; @@ -18,6 +18,8 @@ export class CreateSystemUserService { constructor( @Inject(DI_SYMBOLS.db) private db: DataSource, + + private idService: IdService, ) { } @@ -45,7 +47,7 @@ export class CreateSystemUserService { if (exist) throw new Error('the user is already exists'); account = await transactionalEntityManager.insert(User, { - id: genId(), + id: this.idService.genId(), createdAt: new Date(), username: username, usernameLower: username.toLowerCase(), diff --git a/packages/backend/src/services/CustomEmojiService.ts b/packages/backend/src/services/CustomEmojiService.ts index 497dcd6b04fc..620a9d84d8f2 100644 --- a/packages/backend/src/services/CustomEmojiService.ts +++ b/packages/backend/src/services/CustomEmojiService.ts @@ -3,7 +3,7 @@ import { Emojis } from '@/models/index.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Config } from '@/config/types.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import type { Emoji } from '@/models/entities/emoji.js'; import type { DataSource } from 'typeorm'; @@ -20,6 +20,7 @@ export class CustomEmojiService { @Inject('emojisRepository') private emojisRepository: typeof Emojis, + private idService: IdService, private globalEventServie: GlobalEventService, ) { } @@ -32,7 +33,7 @@ export class CustomEmojiService { host: string | null; }): Promise { const emoji = await this.emojisRepository.insert({ - id: genId(), + id: this.idService.genId(), updatedAt: new Date(), name: data.name, category: data.category, diff --git a/packages/backend/src/services/DriveService.ts b/packages/backend/src/services/DriveService.ts index 9e39304bdb3c..06c0142a207d 100644 --- a/packages/backend/src/services/DriveService.ts +++ b/packages/backend/src/services/DriveService.ts @@ -11,7 +11,7 @@ import Logger from '@/Logger.js'; import type { IRemoteUser, User } from '@/models/entities/user.js'; import type { MetaService } from '@/services/MetaService.js'; import { DriveFile } from '@/models/entities/drive-file.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; @@ -85,6 +85,7 @@ export class DriveService { @Inject('driveFilesRepository') private driveFilesRepository: typeof DriveFiles, + private idService: IdService, private metaService: MetaService, private downloadService: DownloadService, private internalStorageService: InternalStorageService, @@ -503,7 +504,7 @@ export class DriveService { const folder = await fetchFolder(); let file = new DriveFile(); - file.id = genId(); + file.id = this.idService.genId(); file.createdAt = new Date(); file.userId = user ? user.id : null; file.userHost = user ? user.host : null; diff --git a/packages/backend/src/services/EmailService.ts b/packages/backend/src/services/EmailService.ts index 0fa8079683bd..19385d4ef5ac 100644 --- a/packages/backend/src/services/EmailService.ts +++ b/packages/backend/src/services/EmailService.ts @@ -3,18 +3,19 @@ import { Inject, Injectable } from '@nestjs/common'; import type { MetaService } from '@/services/MetaService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Config } from '@/config/types.js'; -import Logger from './logger.js'; - -export const logger = new Logger('email'); +import Logger from '@/logger.js'; @Injectable() export class EmailService { + #logger: Logger; + constructor( @Inject(DI_SYMBOLS.config) private config: Config, private metaService: MetaService, ) { + this.#logger = new Logger('email'); } public async sendEmail(to: string, subject: string, html: string, text: string) { @@ -126,9 +127,9 @@ export class EmailService { `, }); - logger.info(`Message sent: ${info.messageId}`); + this.#logger.info(`Message sent: ${info.messageId}`); } catch (err) { - logger.error(err as Error); + this.#logger.error(err as Error); throw err; } } diff --git a/packages/backend/src/services/FederatedInstanceService.ts b/packages/backend/src/services/FederatedInstanceService.ts index d9a15406c80c..31d52e470bab 100644 --- a/packages/backend/src/services/FederatedInstanceService.ts +++ b/packages/backend/src/services/FederatedInstanceService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Instances } from '@/models/index.js'; import type { Instance } from '@/models/entities/instance'; import { Cache } from '@/misc/cache.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { toPuny } from '@/misc/convert-host.js'; @Injectable() @@ -12,6 +12,8 @@ export class FederatedInstanceService { constructor( @Inject('instancesRepository') private instancesRepository: typeof Instances, + + private idService: IdService, ) { this.#cache = new Cache(1000 * 60 * 60); } @@ -26,7 +28,7 @@ export class FederatedInstanceService { if (index == null) { const i = await this.instancesRepository.insert({ - id: genId(), + id: this.idService.genId(), host, caughtAt: new Date(), lastCommunicatedAt: new Date(), diff --git a/packages/backend/src/services/HashtagService.ts b/packages/backend/src/services/HashtagService.ts index bb826729a2ab..7f08b4882008 100644 --- a/packages/backend/src/services/HashtagService.ts +++ b/packages/backend/src/services/HashtagService.ts @@ -3,7 +3,7 @@ import { DI_SYMBOLS } from '@/di-symbols.js'; import { Hashtags, Users } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import type { Hashtag } from '@/models/entities/hashtag.js'; import type HashtagChart from '@/services/chart/charts/hashtag.js'; @@ -16,6 +16,7 @@ export class HashtagService { @Inject('hashtagsRepository') private hashtagsRepository: typeof Hashtags, + private idService: IdService, private hashtagChart: HashtagChart, ) { } @@ -102,7 +103,7 @@ export class HashtagService { } else { if (isUserAttached) { Hashtags.insert({ - id: genId(), + id: this.idService.genId(), name: tag, mentionedUserIds: [], mentionedUsersCount: 0, @@ -119,7 +120,7 @@ export class HashtagService { } as Hashtag); } else { Hashtags.insert({ - id: genId(), + id: this.idService.genId(), name: tag, mentionedUserIds: [user.id], mentionedUsersCount: 1, diff --git a/packages/backend/src/services/IdService.ts b/packages/backend/src/services/IdService.ts new file mode 100644 index 000000000000..ac76fd4afd74 --- /dev/null +++ b/packages/backend/src/services/IdService.ts @@ -0,0 +1,33 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { ulid } from 'ulid'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; +import { genAid } from '@/misc/id/aid.js'; +import { genMeid } from '@/misc/id/meid.js'; +import { genMeidg } from '@/misc/id/meidg.js'; +import { genObjectId } from '@/misc/id/object-id.js'; + +@Injectable() +export class IdService { + #metohd: string; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + ) { + this.#metohd = config.id.toLowerCase(); + } + + public genId(date?: Date): string { + if (!date || (date > new Date())) date = new Date(); + + switch (this.#metohd) { + case 'aid': return genAid(date); + case 'meid': return genMeid(date); + case 'meidg': return genMeidg(date); + case 'ulid': return ulid(date.getTime()); + case 'objectid': return genObjectId(date); + default: throw new Error('unrecognized id generation method'); + } + } +} diff --git a/packages/backend/src/services/ModerationLogService.ts b/packages/backend/src/services/ModerationLogService.ts index 83f4ce6c47f4..7551090e8cab 100644 --- a/packages/backend/src/services/ModerationLogService.ts +++ b/packages/backend/src/services/ModerationLogService.ts @@ -2,19 +2,21 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { ModerationLogs } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; @Injectable() export class ModerationLogService { constructor( @Inject('moderationLogsRepository') private moderationLogsRepository: typeof ModerationLogs, + + private idService: IdService, ) { } public async insertModerationLog(moderator: { id: User['id'] }, type: string, info?: Record) { await this.moderationLogsRepository.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), userId: moderator.id, type: type, diff --git a/packages/backend/src/services/NoteCreateService.ts b/packages/backend/src/services/NoteCreateService.ts index 19a0d87eb33c..7f9b4d69bda0 100644 --- a/packages/backend/src/services/NoteCreateService.ts +++ b/packages/backend/src/services/NoteCreateService.ts @@ -18,7 +18,7 @@ import renderAnnounce from '@/services/remote/activitypub/renderer/announce.js'; import renderCreate from '@/services/remote/activitypub/renderer/create.js'; import renderNote from '@/services/remote/activitypub/renderer/note.js'; import DeliverManager from '@/services/remote/activitypub/deliver-manager.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import type { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; import type { IPoll } from '@/models/entities/poll.js'; import { Poll } from '@/models/entities/poll.js'; @@ -145,6 +145,7 @@ export class NoteCreateService { @Inject('notesRepository') private notesRepository: typeof Notes, + private idService: IdService, private globalEventServie: GlobalEventService, private queueService: QueueService, private createNotificationService: CreateNotificationService, @@ -412,7 +413,7 @@ export class NoteCreateService { checkWordMute(note, { id: u.userId }, u.mutedWords).then(shouldMute => { if (shouldMute) { MutedNotes.insert({ - id: genId(), + id: this.idService.genId(), userId: u.userId, noteId: note.id, reason: 'word', diff --git a/packages/backend/src/services/NotePiningService.ts b/packages/backend/src/services/NotePiningService.ts index 91202f621cfa..5883b9ab9c2a 100644 --- a/packages/backend/src/services/NotePiningService.ts +++ b/packages/backend/src/services/NotePiningService.ts @@ -5,7 +5,7 @@ import { Notes, UserNotePinings } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import type { User } from '@/models/entities/user.js'; import type { Note } from '@/models/entities/note.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import type { UserNotePining } from '@/models/entities/user-note-pining.js'; import type { RelayService } from '@/services/RelayService.js'; import type { Config } from '@/config/types.js'; @@ -25,6 +25,7 @@ export class NotePiningService { @Inject('userNotePiningsRepository') private userNotePiningsRepository: typeof UserNotePinings, + private idService: IdService, private relayService: RelayService, ) { } @@ -56,7 +57,7 @@ export class NotePiningService { } await this.userNotePiningsRepository.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), userId: user.id, noteId: note.id, diff --git a/packages/backend/src/services/NoteReadService.ts b/packages/backend/src/services/NoteReadService.ts index 740a5511a4d4..9a427ad17d30 100644 --- a/packages/backend/src/services/NoteReadService.ts +++ b/packages/backend/src/services/NoteReadService.ts @@ -6,7 +6,7 @@ import type { User } from '@/models/entities/user.js'; import type { Channel } from '@/models/entities/channel.js'; import type { Packed } from '@/misc/schema.js'; import type { Note } from '@/models/entities/note.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; @Injectable() @@ -18,6 +18,7 @@ export class NoteReadService { @Inject('noteUnreadsRepository') private noteUnreadsRepository: typeof NoteUnreads, + private idService: IdService, private globalEventServie: GlobalEventService, ) { } @@ -43,7 +44,7 @@ export class NoteReadService { if (threadMute) return; const unread = { - id: genId(), + id: this.idService.genId(), noteId: note.id, userId: userId, isSpecified: params.isSpecified, diff --git a/packages/backend/src/services/PollService.ts b/packages/backend/src/services/PollService.ts index a72b7065f7c0..8c3900612478 100644 --- a/packages/backend/src/services/PollService.ts +++ b/packages/backend/src/services/PollService.ts @@ -6,7 +6,7 @@ import { Polls , PollVotes } from '@/models/index.js'; import type { Note } from '@/models/entities/note.js'; import type { RelayService } from '@/services/RelayService.js'; import type { CacheableUser } from '@/models/entities/user.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; import type { CreateNotificationService } from '@/services/CreateNotificationService.js'; import renderUpdate from '@/services/remote/activitypub/renderer/update.js'; @@ -31,6 +31,7 @@ export class PollService { @Inject('blockingsRepository') private blockingsRepository: typeof Blockings, + private idService: IdService, private relayService: RelayService, private globalEventServie: GlobalEventService, private createNotificationService: CreateNotificationService, @@ -72,7 +73,7 @@ export class PollService { // Create vote await PollVotes.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), noteId: note.id, userId: user.id, diff --git a/packages/backend/src/services/ReactionService.ts b/packages/backend/src/services/ReactionService.ts index 0b90e6d689dc..5ed8d2555a2b 100644 --- a/packages/backend/src/services/ReactionService.ts +++ b/packages/backend/src/services/ReactionService.ts @@ -6,7 +6,7 @@ import type { Blockings, Emojis, NoteReactions , Users , Notes } from '@/models/ import { IdentifiableError } from '@/misc/identifiable-error.js'; import type { IRemoteUser, User } from '@/models/entities/user.js'; import type { Note } from '@/models/entities/note.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import type { NoteReaction } from '@/models/entities/note-reaction.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; @@ -33,6 +33,7 @@ export class ReactionService { @Inject('emojisRepository') private emojisRepository: typeof Emojis, + private idService: IdService, private globalEventServie: GlobalEventService, private createNotificationService: CreateNotificationService, private perUserReactionsChart: PerUserReactionsChart, @@ -60,7 +61,7 @@ export class ReactionService { reaction = await toDbReaction(reaction, user.host); const record: NoteReaction = { - id: genId(), + id: this.idService.genId(), createdAt: new Date(), noteId: note.id, userId: user.id, diff --git a/packages/backend/src/services/RelayService.ts b/packages/backend/src/services/RelayService.ts index acf2c3964020..6e9b1cd5fccc 100644 --- a/packages/backend/src/services/RelayService.ts +++ b/packages/backend/src/services/RelayService.ts @@ -5,7 +5,7 @@ import { renderActivity, attachLdSignature } from '@/services/remote/activitypub import renderUndo from '@/services/remote/activitypub/renderer/undo.js'; import type { ILocalUser, User } from '@/models/entities/user.js'; import type { Relays, Users } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { Cache } from '@/misc/cache.js'; import type { Relay } from '@/models/entities/relay.js'; import type { QueueService } from '@/queue/queue.service.js'; @@ -24,6 +24,7 @@ export class RelayService { @Inject('relaysRepository') private relaysRepository: typeof Relays, + private idService: IdService, private queueService: QueueService, private createSystemUserService: CreateSystemUserService, ) { @@ -44,7 +45,7 @@ export class RelayService { async addRelay(inbox: string): Promise { const relay = await this.relaysRepository.insert({ - id: genId(), + id: this.idService.genId(), inbox, status: 'requesting', }).then(x => this.relaysRepository.findOneByOrFail(x.identifiers[0])); diff --git a/packages/backend/src/services/UserBlockingService.ts b/packages/backend/src/services/UserBlockingService.ts index 061a14caac78..d719d281d2a2 100644 --- a/packages/backend/src/services/UserBlockingService.ts +++ b/packages/backend/src/services/UserBlockingService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { FollowRequests , Followings , UserLists , UserListJoinings , Users , Blockings } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import type { CacheableUser, User } from '@/models/entities/user.js'; import type { Blocking } from '@/models/entities/blocking.js'; import type { QueueService } from '@/queue/queue.service.js'; @@ -36,6 +36,7 @@ export class UserBlockingService { @Inject('userListJoiningsRepository') private userListJoiningsRepository: typeof UserListJoinings, + private idService: IdService, private queueService: QueueService, private globalEventServie: GlobalEventService, private webhookService: WebhookService, @@ -53,7 +54,7 @@ export class UserBlockingService { ]); const blocking = { - id: genId(), + id: this.idService.genId(), createdAt: new Date(), blocker, blockerId: blocker.id, diff --git a/packages/backend/src/services/UserFollowingService.ts b/packages/backend/src/services/UserFollowingService.ts index 3231d2e1323b..f150482df5ff 100644 --- a/packages/backend/src/services/UserFollowingService.ts +++ b/packages/backend/src/services/UserFollowingService.ts @@ -9,7 +9,7 @@ import { IdentifiableError } from '@/misc/identifiable-error.js'; import type { QueueService } from '@/queue/queue.service.js'; import type PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import renderUndo from '@/services/remote/activitypub/renderer/undo.js'; import type { Packed } from '@/misc/schema.js'; @@ -55,6 +55,7 @@ export class UserFollowingService { @Inject('instancesRepository') private instancesRepository: typeof Instances, + private idService: IdService, private queueService: QueueService, private globalEventServie: GlobalEventService, private createNotificationService: CreateNotificationService, @@ -152,7 +153,7 @@ export class UserFollowingService { let alreadyFollowed = false as boolean; await this.followingsRepository.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), followerId: follower.id, followeeId: followee.id, @@ -359,7 +360,7 @@ export class UserFollowingService { if (blocked != null) throw new Error('blocked'); const followRequest = await this.followRequestsRepository.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), followerId: follower.id, followeeId: followee.id, diff --git a/packages/backend/src/services/UserListService.ts b/packages/backend/src/services/UserListService.ts index 285fd18bb3c8..6c3970703021 100644 --- a/packages/backend/src/services/UserListService.ts +++ b/packages/backend/src/services/UserListService.ts @@ -4,7 +4,7 @@ import { Users } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; import type { UserList } from '@/models/entities/user-list.js'; import type { UserListJoining } from '@/models/entities/user-list-joining.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import type { UserFollowingService } from '@/services/UserFollowingService.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; @@ -17,6 +17,7 @@ export class UserListService { @Inject('userListJoiningsRepository') private userListJoiningsRepository: typeof UserListJoinings, + private idService: IdService, private userFollowingService: UserFollowingService, private globalEventServie: GlobalEventService, ) { @@ -24,7 +25,7 @@ export class UserListService { public async push(target: User, list: UserList) { await this.userListJoiningsRepository.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), userId: target.id, userListId: list.id, diff --git a/packages/backend/src/services/UserMutingService.ts b/packages/backend/src/services/UserMutingService.ts new file mode 100644 index 000000000000..6f3e6de4f1e2 --- /dev/null +++ b/packages/backend/src/services/UserMutingService.ts @@ -0,0 +1,32 @@ + +import { Inject, Injectable } from '@nestjs/common'; +import type { Users , Mutings } from '@/models/index.js'; +import type { IdService } from '@/services/IdService.js'; +import type { QueueService } from '@/queue/queue.service.js'; +import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import type { User } from '@/models/entities/user.js'; + +@Injectable() +export class UserMutingService { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('mutingsRepository') + private mutingsRepository: typeof Mutings, + + private idService: IdService, + private queueService: QueueService, + private globalEventServie: GlobalEventService, + ) { + } + + public async mute(user: User, target: User): Promise { + await this.mutingsRepository.insert({ + id: this.idService.genId(), + createdAt: new Date(), + muterId: user.id, + muteeId: target.id, + }); + } +} diff --git a/packages/backend/src/services/messages/create.ts b/packages/backend/src/services/messages/create.ts index 1732ef715bbb..5231f153e378 100644 --- a/packages/backend/src/services/messages/create.ts +++ b/packages/backend/src/services/messages/create.ts @@ -3,7 +3,7 @@ import type { CacheableUser, User } from '@/models/entities/user.js'; import type { UserGroup } from '@/models/entities/user-group.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import { MessagingMessages, UserGroupJoinings, Mutings, Users } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import type { MessagingMessage } from '@/models/entities/messaging-message.js'; import { publishMessagingStream, publishMessagingIndexStream, publishMainStream, publishGroupMessagingStream } from '@/services/stream.js'; import { pushNotification } from '@/services/push-notification.js'; @@ -15,7 +15,7 @@ import { deliver } from '@/queue/index.js'; export async function createMessage(user: { id: User['id']; host: User['host']; }, recipientUser: CacheableUser | undefined, recipientGroup: UserGroup | undefined, text: string | null | undefined, file: DriveFile | null, uri?: string) { const message = { - id: genId(), + id: this.idService.genId(), createdAt: new Date(), fileId: file ? file.id : null, recipientId: recipientUser ? recipientUser.id : null, diff --git a/packages/backend/src/services/remote/activitypub/ApInboxService.ts b/packages/backend/src/services/remote/activitypub/ApInboxService.ts index 8f5adeeb9ae7..f08432e409a3 100644 --- a/packages/backend/src/services/remote/activitypub/ApInboxService.ts +++ b/packages/backend/src/services/remote/activitypub/ApInboxService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import { Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; import type { Config } from '@/config/types.js'; import type { CacheableRemoteUser } from '@/models/entities/user.js'; import type { UserFollowingService } from '@/services/UserFollowingService.js'; @@ -17,6 +17,7 @@ import type { AppLockService } from '@/services/AppLockService.js'; import { extractDbHost, isSelfHost } from '@/misc/convert-host.js'; import type Logger from '@/this.#logger.js'; import type { MetaService } from '@/services/MetaService.js'; +import type { IdService } from '@/services/IdService.js'; import { createNote, fetchNote } from './models/note.js'; import { updatePerson } from './models/person.js'; import { updateQuestion } from './models/question.js'; @@ -37,6 +38,7 @@ export class ApInboxService { @Inject('usersRepository') private usersRepository: typeof Users, + private idService: IdService, private metaService: MetaService, private userFollowingService: UserFollowingService, private reactionService: ReactionService, @@ -298,7 +300,7 @@ export class ApInboxService { return 'skip: ブロックしようとしているユーザーはローカルユーザーではありません'; } - await this.userBlockingService.block(await Users.findOneByOrFail({ id: actor.id }), await Users.findOneByOrFail({ id: blockee.id })); + await this.userBlockingService.block(await this.usersRepository.findOneByOrFail({ id: actor.id }), await this.usersRepository.findOneByOrFail({ id: blockee.id })); return 'ok'; } @@ -419,14 +421,14 @@ export class ApInboxService { return `skip: delete actor ${actor.uri} !== ${uri}`; } - const user = await Users.findOneByOrFail({ id: actor.id }); + const user = await this.usersRepository.findOneByOrFail({ id: actor.id }); if (user.isDeleted) { this.#logger.info('skip: already deleted'); } const job = await createDeleteAccountJob(actor); - await Users.update(actor.id, { + await this.usersRepository.update(actor.id, { isDeleted: true, }); @@ -471,13 +473,13 @@ export class ApInboxService { const uris = getApIds(activity.object); const userIds = uris.filter(uri => uri.startsWith(this.config.url + '/users/')).map(uri => uri.split('/').pop()!); - const users = await Users.findBy({ + const users = await this.usersRepository.findBy({ id: In(userIds), }); if (users.length < 1) return 'skip'; await AbuseUserReports.insert({ - id: genId(), + id: this.idService.genId(), createdAt: new Date(), targetUserId: users[0].id, targetUserHost: users[0].host, @@ -515,7 +517,7 @@ export class ApInboxService { return 'skip: follower not found'; } - if (!Users.isLocalUser(follower)) { + if (!this.usersRepository.isLocalUser(follower)) { return 'skip: follower is not a local user'; } @@ -617,7 +619,7 @@ export class ApInboxService { return 'skip: ブロック解除しようとしているユーザーはローカルユーザーではありません'; } - await this.userBlockingService.unblock(await Users.findOneByOrFail({ id: actor.id }), blockee); + await this.userBlockingService.unblock(await this.usersRepository.findOneByOrFail({ id: actor.id }), blockee); return 'ok'; } diff --git a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts index f78494f490d2..848f918b89d4 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts @@ -352,7 +352,7 @@ export class ApNoteService { logger.info(`register emoji host=${host}, name=${name}`); return await Emojis.insert({ - id: genId(), + id: this.idService.genId(), host, name, uri: tag.id, diff --git a/packages/backend/src/services/remote/activitypub/models/person.ts b/packages/backend/src/services/remote/activitypub/models/person.ts index 74e5eeb6da98..66155cce43a4 100644 --- a/packages/backend/src/services/remote/activitypub/models/person.ts +++ b/packages/backend/src/services/remote/activitypub/models/person.ts @@ -10,7 +10,7 @@ import type { IRemoteUser, CacheableUser } from '@/models/entities/user.js'; import { User } from '@/models/entities/user.js'; import type { Emoji } from '@/models/entities/emoji.js'; import { UserNotePining } from '@/models/entities/user-note-pining.js'; -import { genId } from '@/misc/gen-id.js'; +import type { IdService } from '@/services/IdService.js'; import { instanceChart, usersChart } from '@/services/chart/index.js'; import { UserPublickey } from '@/models/entities/user-publickey.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; @@ -167,7 +167,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise { user = await transactionalEntityManager.save(new User({ - id: genId(), + id: this.idService.genId(), avatarId: null, bannerId: null, createdAt: new Date(), From 2477ca82d96668fd274693ce84e30e1620473d70 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 20:58:13 +0900 Subject: [PATCH 063/180] Update ApInboxService.ts --- .../remote/activitypub/ApInboxService.ts | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/packages/backend/src/services/remote/activitypub/ApInboxService.ts b/packages/backend/src/services/remote/activitypub/ApInboxService.ts index f08432e409a3..cdda427df1bc 100644 --- a/packages/backend/src/services/remote/activitypub/ApInboxService.ts +++ b/packages/backend/src/services/remote/activitypub/ApInboxService.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Users } from '@/models/index.js'; +import type { Followings, Notes , Users } from '@/models/index.js'; + import type { Config } from '@/config/types.js'; import type { CacheableRemoteUser } from '@/models/entities/user.js'; import type { UserFollowingService } from '@/services/UserFollowingService.js'; @@ -15,13 +16,13 @@ import type { NoteCreateService } from '@/services/NoteCreateService.js'; import { concat, toArray, toSingle, unique } from '@/prelude/array.js'; import type { AppLockService } from '@/services/AppLockService.js'; import { extractDbHost, isSelfHost } from '@/misc/convert-host.js'; -import type Logger from '@/this.#logger.js'; +import type Logger from '@/logger.js'; import type { MetaService } from '@/services/MetaService.js'; import type { IdService } from '@/services/IdService.js'; -import { createNote, fetchNote } from './models/note.js'; import { updatePerson } from './models/person.js'; import { updateQuestion } from './models/question.js'; import { getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isPost, isRead, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js'; +import type { ApNoteService } from './models/ApNoteService.js'; import type { ApLoggerService } from './ApLoggerService.js'; import type { ApDbResolverService } from './ApDbResolverService.js'; import type { ApResolverService, Resolver } from './ApResolverService.js'; @@ -38,6 +39,12 @@ export class ApInboxService { @Inject('usersRepository') private usersRepository: typeof Users, + @Inject('notesRepository') + private notesRepository: typeof Notes, + + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + private idService: IdService, private metaService: MetaService, private userFollowingService: UserFollowingService, @@ -51,6 +58,7 @@ export class ApInboxService { private apResolverService: ApResolverService, private apDbResolverService: ApDbResolverService, private apLoggerService: ApLoggerService, + private apNoteService: ApNoteService, ) { this.#logger = this.apLoggerService.logger; } @@ -127,10 +135,10 @@ export class ApInboxService { async #like(actor: CacheableRemoteUser, activity: ILike): Promise { const targetUri = getApId(activity.object); - const note = await fetchNote(targetUri); + const note = await this.apNoteService.fetchNote(targetUri); if (!note) return `skip: target note not found ${targetUri}`; - await extractEmojis(activity.tag || [], actor.host).catch(() => null); + await this.apNoteService.extractEmojis(activity.tag || [], actor.host).catch(() => null); return await this.reactionService.create(actor, note, activity._misskey_reaction || activity.content || activity.name).catch(e => { if (e.id === '51c42bb4-931a-456b-bff7-e5a8a70dd298') { @@ -213,7 +221,7 @@ export class ApInboxService { } if (activity.target === actor.featured) { - const note = await resolveNote(activity.object); + const note = await this.apNoteService.resolveNote(activity.object); if (note == null) throw new Error('note not found'); await this.notePiningService.addPinned(actor, note.id); return; @@ -247,7 +255,7 @@ export class ApInboxService { try { // 既に同じURIを持つものが登録されていないかチェック - const exist = await fetchNote(uri); + const exist = await this.apNoteService.fetchNote(uri); if (exist) { return; } @@ -255,7 +263,7 @@ export class ApInboxService { // Announce対象をresolve let renote; try { - renote = await resolveNote(targetUri); + renote = await this.apNoteService.resolveNote(targetUri); } catch (e) { // 対象が4xxならスキップ if (e instanceof StatusError) { @@ -269,7 +277,7 @@ export class ApInboxService { throw e; } - if (!await Notes.isVisibleForMe(renote, actor.id)) return 'skip: invalid actor for this activity'; + if (!await this.notesRepository.isVisibleForMe(renote, actor.id)) return 'skip: invalid actor for this activity'; this.#logger.info(`Creating the (Re)Note: ${uri}`); @@ -357,10 +365,10 @@ export class ApInboxService { const unlock = await this.appLockService.getApLock(uri); try { - const exist = await fetchNote(note); + const exist = await this.apNoteService.fetchNote(note); if (exist) return 'skip: note exists'; - await createNote(note, resolver, silent); + await this.apNoteService.createNote(note, resolver, silent); return 'ok'; } catch (e) { if (e instanceof StatusError && e.isClientError) { @@ -541,7 +549,7 @@ export class ApInboxService { } if (activity.target === actor.featured) { - const note = await resolveNote(activity.object); + const note = await this.apNoteService.resolveNote(activity.object); if (note == null) throw new Error('note not found'); await this.notePiningService.removePinned(actor, note.id); return; @@ -581,7 +589,7 @@ export class ApInboxService { return 'skip: follower not found'; } - const following = await Followings.findOneBy({ + const following = await this.followingsRepository.findOneBy({ followerId: follower.id, followeeId: actor.id, }); @@ -597,7 +605,7 @@ export class ApInboxService { async #undoAnnounce(actor: CacheableRemoteUser, activity: IAnnounce): Promise { const uri = getApId(activity); - const note = await Notes.findOneBy({ + const note = await this.notesRepository.findOneBy({ uri, userId: actor.id, }); @@ -638,7 +646,7 @@ export class ApInboxService { followeeId: followee.id, }); - const following = await Followings.findOneBy({ + const following = await this.followingsRepository.findOneBy({ followerId: actor.id, followeeId: followee.id, }); @@ -659,7 +667,7 @@ export class ApInboxService { async #undoLike(actor: CacheableRemoteUser, activity: ILike): Promise { const targetUri = getApId(activity.object); - const note = await fetchNote(targetUri); + const note = await this.apNoteService.fetchNote(targetUri); if (!note) return `skip: target note not found ${targetUri}`; await this.reactionService.delete(actor, note).catch(e => { From a5d879083cd2a948e492025e589c72e4a4da00bc Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 21:03:22 +0900 Subject: [PATCH 064/180] wip --- .../index.ts => ObjectStorageQueueService.ts} | 4 +- .../CleanRemoteFilesProcessorService.ts | 69 +++++++++++++++++++ .../processors/DeleteFileProcessorService.ts | 31 +++++++++ .../object-storage/clean-remote-files.ts | 50 -------------- .../processors/object-storage/delete-file.ts | 11 --- 5 files changed, 102 insertions(+), 63 deletions(-) rename packages/backend/src/queue/{processors/object-storage/index.ts => ObjectStorageQueueService.ts} (80%) create mode 100644 packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts create mode 100644 packages/backend/src/queue/processors/DeleteFileProcessorService.ts delete mode 100644 packages/backend/src/queue/processors/object-storage/clean-remote-files.ts delete mode 100644 packages/backend/src/queue/processors/object-storage/delete-file.ts diff --git a/packages/backend/src/queue/processors/object-storage/index.ts b/packages/backend/src/queue/ObjectStorageQueueService.ts similarity index 80% rename from packages/backend/src/queue/processors/object-storage/index.ts rename to packages/backend/src/queue/ObjectStorageQueueService.ts index ae6c481feaf1..b6c2357e0350 100644 --- a/packages/backend/src/queue/processors/object-storage/index.ts +++ b/packages/backend/src/queue/ObjectStorageQueueService.ts @@ -1,7 +1,7 @@ -import Bull from 'bull'; -import { ObjectStorageJobData } from '@/queue/types.js'; +import type { ObjectStorageJobData } from '@/queue/types.js'; import deleteFile from './delete-file.js'; import cleanRemoteFiles from './clean-remote-files.js'; +import type Bull from 'bull'; const jobs = { deleteFile, diff --git a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts new file mode 100644 index 000000000000..f55b27d4cf93 --- /dev/null +++ b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts @@ -0,0 +1,69 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { IsNull, MoreThan, Not } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { DriveFiles } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import type { DriveService } from '@/services/DriveService.js'; +import type Bull from 'bull'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; + +@Injectable() +export class CleanRemoteFilesProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private driveService: DriveService, + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('clean-remote-files'); + } + + public async process(job: Bull.Job>, done: () => void): Promise { + this.#logger.info('Deleting cached remote files...'); + + let deletedCount = 0; + let cursor: any = null; + + while (true) { + const files = await this.driveFilesRepository.find({ + where: { + userHost: Not(IsNull()), + isLink: false, + ...(cursor ? { id: MoreThan(cursor) } : {}), + }, + take: 8, + order: { + id: 1, + }, + }); + + if (files.length === 0) { + job.progress(100); + break; + } + + cursor = files[files.length - 1].id; + + await Promise.all(files.map(file => this.driveService.deleteFileSync(file, true))); + + deletedCount += 8; + + const total = await this.driveFilesRepository.countBy({ + userHost: Not(IsNull()), + isLink: false, + }); + + job.progress(deletedCount / total); + } + + this.#logger.succ('All cahced remote files has been deleted.'); + done(); + } +} diff --git a/packages/backend/src/queue/processors/DeleteFileProcessorService.ts b/packages/backend/src/queue/processors/DeleteFileProcessorService.ts new file mode 100644 index 000000000000..60bf39e8c775 --- /dev/null +++ b/packages/backend/src/queue/processors/DeleteFileProcessorService.ts @@ -0,0 +1,31 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import type { DriveService } from '@/services/DriveService.js'; +import type Bull from 'bull'; +import type { ObjectStorageFileJobData } from '../types.js'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; + +@Injectable() +export class DeleteFileProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + private driveService: DriveService, + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('delete-file'); + } + + public async process(job: Bull.Job): Promise { + const key: string = job.data.key; + + await this.driveService.deleteObjectStorageFile(key); + + return 'Success'; + } +} diff --git a/packages/backend/src/queue/processors/object-storage/clean-remote-files.ts b/packages/backend/src/queue/processors/object-storage/clean-remote-files.ts deleted file mode 100644 index 77da162f6e75..000000000000 --- a/packages/backend/src/queue/processors/object-storage/clean-remote-files.ts +++ /dev/null @@ -1,50 +0,0 @@ -import Bull from 'bull'; - -import { queueLogger } from '../../logger.js'; -import { deleteFileSync } from '@/services/drive/delete-file.js'; -import { DriveFiles } from '@/models/index.js'; -import { MoreThan, Not, IsNull } from 'typeorm'; - -const logger = queueLogger.createSubLogger('clean-remote-files'); - -export default async function cleanRemoteFiles(job: Bull.Job>, done: any): Promise { - logger.info(`Deleting cached remote files...`); - - let deletedCount = 0; - let cursor: any = null; - - while (true) { - const files = await DriveFiles.find({ - where: { - userHost: Not(IsNull()), - isLink: false, - ...(cursor ? { id: MoreThan(cursor) } : {}), - }, - take: 8, - order: { - id: 1, - }, - }); - - if (files.length === 0) { - job.progress(100); - break; - } - - cursor = files[files.length - 1].id; - - await Promise.all(files.map(file => deleteFileSync(file, true))); - - deletedCount += 8; - - const total = await DriveFiles.countBy({ - userHost: Not(IsNull()), - isLink: false, - }); - - job.progress(deletedCount / total); - } - - logger.succ(`All cahced remote files has been deleted.`); - done(); -} diff --git a/packages/backend/src/queue/processors/object-storage/delete-file.ts b/packages/backend/src/queue/processors/object-storage/delete-file.ts deleted file mode 100644 index c271e3ddd4db..000000000000 --- a/packages/backend/src/queue/processors/object-storage/delete-file.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ObjectStorageFileJobData } from '@/queue/types.js'; -import Bull from 'bull'; -import { deleteObjectStorageFile } from '@/services/drive/delete-file.js'; - -export default async (job: Bull.Job) => { - const key: string = job.data.key; - - await deleteObjectStorageFile(key); - - return 'Success'; -}; From 20dc75278eed8cde137ee8629d788f06dbb62529 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 21:30:14 +0900 Subject: [PATCH 065/180] wip --- .../system/index.ts => SystemQueueService.ts} | 2 +- .../CheckExpiredMutingsProcessorService.ts | 50 +++++ .../processors/CleanChartsProcessorService.ts | 68 +++++++ .../queue/processors/CleanProcessorService.ts | 36 ++++ .../processors/DeliverProcessorService.ts | 129 ++++++++++++ .../queue/processors/InboxProcessorService.ts | 190 ++++++++++++++++++ .../ResyncChartsProcessorService.ts | 61 ++++++ .../processors/TickChartsProcessorService.ts | 68 +++++++ .../WebhookDeliverProcessorService.ts | 79 ++++++++ .../backend/src/queue/processors/deliver.ts | 98 --------- .../backend/src/queue/processors/inbox.ts | 157 --------------- .../system/check-expired-mutings.ts | 30 --- .../queue/processors/system/clean-charts.ts | 28 --- .../src/queue/processors/system/clean.ts | 18 -- .../queue/processors/system/resync-charts.ts | 21 -- .../queue/processors/system/tick-charts.ts | 28 --- .../src/queue/processors/webhook-deliver.ts | 59 ------ 17 files changed, 682 insertions(+), 440 deletions(-) rename packages/backend/src/queue/{processors/system/index.ts => SystemQueueService.ts} (95%) create mode 100644 packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts create mode 100644 packages/backend/src/queue/processors/CleanChartsProcessorService.ts create mode 100644 packages/backend/src/queue/processors/CleanProcessorService.ts create mode 100644 packages/backend/src/queue/processors/DeliverProcessorService.ts create mode 100644 packages/backend/src/queue/processors/InboxProcessorService.ts create mode 100644 packages/backend/src/queue/processors/ResyncChartsProcessorService.ts create mode 100644 packages/backend/src/queue/processors/TickChartsProcessorService.ts create mode 100644 packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts delete mode 100644 packages/backend/src/queue/processors/deliver.ts delete mode 100644 packages/backend/src/queue/processors/inbox.ts delete mode 100644 packages/backend/src/queue/processors/system/check-expired-mutings.ts delete mode 100644 packages/backend/src/queue/processors/system/clean-charts.ts delete mode 100644 packages/backend/src/queue/processors/system/clean.ts delete mode 100644 packages/backend/src/queue/processors/system/resync-charts.ts delete mode 100644 packages/backend/src/queue/processors/system/tick-charts.ts delete mode 100644 packages/backend/src/queue/processors/webhook-deliver.ts diff --git a/packages/backend/src/queue/processors/system/index.ts b/packages/backend/src/queue/SystemQueueService.ts similarity index 95% rename from packages/backend/src/queue/processors/system/index.ts rename to packages/backend/src/queue/SystemQueueService.ts index 9527d40b0f23..c442a233b91d 100644 --- a/packages/backend/src/queue/processors/system/index.ts +++ b/packages/backend/src/queue/SystemQueueService.ts @@ -1,9 +1,9 @@ -import Bull from 'bull'; import { tickCharts } from './tick-charts.js'; import { resyncCharts } from './resync-charts.js'; import { cleanCharts } from './clean-charts.js'; import { checkExpiredMutings } from './check-expired-mutings.js'; import { clean } from './clean.js'; +import type Bull from 'bull'; const jobs = { tickCharts, diff --git a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts new file mode 100644 index 000000000000..075385fac4ed --- /dev/null +++ b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts @@ -0,0 +1,50 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { In, MoreThan } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Mutings } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import type Bull from 'bull'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; + +@Injectable() +export class CheckExpiredMutingsProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('mutingsRepository') + private mutingsRepository: typeof Mutings, + + private globalEventService: GlobalEventService, + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('check-expired-mutings'); + } + + public async process(job: Bull.Job>, done: () => void): Promise { + this.#logger.info('Checking expired mutings...'); + + const expired = await this.mutingsRepository.createQueryBuilder('muting') + .where('muting.expiresAt IS NOT NULL') + .andWhere('muting.expiresAt < :now', { now: new Date() }) + .innerJoinAndSelect('muting.mutee', 'mutee') + .getMany(); + + if (expired.length > 0) { + await this.mutingsRepository.delete({ + id: In(expired.map(m => m.id)), + }); + + for (const m of expired) { + this.globalEventService.publishUserEvent(m.muterId, 'unmute', m.mutee!); + } + } + + this.#logger.succ('All expired mutings checked.'); + done(); + } +} diff --git a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts new file mode 100644 index 000000000000..03a8f6ad3ddb --- /dev/null +++ b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts @@ -0,0 +1,68 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { In, MoreThan } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import type FederationChart from '@/services/chart/charts/federation.js'; +import type NotesChart from '@/services/chart/charts/notes.js'; +import type UsersChart from '@/services/chart/charts/users.js'; +import type ActiveUsersChart from '@/services/chart/charts/active-users.js'; +import type InstanceChart from '@/services/chart/charts/instance.js'; +import type PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; +import type DriveChart from '@/services/chart/charts/drive.js'; +import type PerUserReactionsChart from '@/services/chart/charts/per-user-reactions.js'; +import type HashtagChart from '@/services/chart/charts/hashtag.js'; +import type PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; +import type PerUserDriveChart from '@/services/chart/charts/per-user-drive.js'; +import type ApRequestChart from '@/services/chart/charts/ap-request.js'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; +import type Bull from 'bull'; + +@Injectable() +export class CleanChartsProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + private federationChart: FederationChart, + private notesChart: NotesChart, + private usersChart: UsersChart, + private activeUsersChart: ActiveUsersChart, + private instanceChart: InstanceChart, + private perUserNotesChart: PerUserNotesChart, + private driveChart: DriveChart, + private perUserReactionsChart: PerUserReactionsChart, + private hashtagChart: HashtagChart, + private perUserFollowingChart: PerUserFollowingChart, + private perUserDriveChart: PerUserDriveChart, + private apRequestChart: ApRequestChart, + + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('clean-charts'); + } + + public async process(job: Bull.Job>, done: () => void): Promise { + this.#logger.info('Clean charts...'); + + await Promise.all([ + this.federationChart.clean(), + this.notesChart.clean(), + this.usersChart.clean(), + this.activeUsersChart.clean(), + this.instanceChart.clean(), + this.perUserNotesChart.clean(), + this.driveChart.clean(), + this.perUserReactionsChart.clean(), + this.hashtagChart.clean(), + this.perUserFollowingChart.clean(), + this.perUserDriveChart.clean(), + this.apRequestChart.clean(), + ]); + + this.#logger.succ('All charts successfully cleaned.'); + done(); + } +} diff --git a/packages/backend/src/queue/processors/CleanProcessorService.ts b/packages/backend/src/queue/processors/CleanProcessorService.ts new file mode 100644 index 000000000000..d08289219e49 --- /dev/null +++ b/packages/backend/src/queue/processors/CleanProcessorService.ts @@ -0,0 +1,36 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { In, LessThan, MoreThan } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { UserIps } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import type Bull from 'bull'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; + +@Injectable() +export class CleanProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('userIpsRepository') + private userIpsRepository: typeof UserIps, + + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('clean'); + } + + public async process(job: Bull.Job>, done: () => void): Promise { + this.#logger.info('Cleaning...'); + + this.userIpsRepository.delete({ + createdAt: LessThan(new Date(Date.now() - (1000 * 60 * 60 * 24 * 90))), + }); + + this.#logger.succ('Cleaned.'); + done(); + } +} diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts new file mode 100644 index 000000000000..12eff50877fd --- /dev/null +++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts @@ -0,0 +1,129 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { MoreThan } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { DriveFiles , Instances } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import type { MetaService } from '@/services/MetaService.js'; +import { toPuny } from '@/misc/convert-host.js'; +import { StatusError } from '@/services/HttpRequestService.js'; +import type { ApRequestService } from '@/services/remote/activitypub/ApRequestService.js'; +import type { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; +import type { FetchInstanceMetadataService } from '@/services/FetchInstanceMetadataService.js'; +import { Cache } from '@/misc/cache.js'; +import type { Instance } from '@/models/entities/instance.js'; +import type InstanceChart from '@/services/chart/charts/instance.js'; +import type ApRequestChart from '@/services/chart/charts/ap-request.js'; +import type FederationChart from '@/services/chart/charts/federation.js'; +import type Bull from 'bull'; +import type { DeliverJobData } from '../types.js'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; + +@Injectable() +export class DeliverProcessorService { + #logger: Logger; + #suspendedHostsCache: Cache; + #latest: string | null; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('instancesRepository') + private instancesRepository: typeof Instances, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private metaService: MetaService, + private federatedInstanceService: FederatedInstanceService, + private fetchInstanceMetadataService: FetchInstanceMetadataService, + private apRequestService: ApRequestService, + private instanceChart: InstanceChart, + private apRequestChart: ApRequestChart, + private federationChart: FederationChart, + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('deliver'); + this.#suspendedHostsCache = new Cache(1000 * 60 * 60); + this.#latest = null; + } + + public async process(job: Bull.Job): Promise { + const { host } = new URL(job.data.to); + + // ブロックしてたら中断 + const meta = await this.metaService.fetch(); + if (meta.blockedHosts.includes(toPuny(host))) { + return 'skip (blocked)'; + } + + // isSuspendedなら中断 + let suspendedHosts = this.#suspendedHostsCache.get(null); + if (suspendedHosts == null) { + suspendedHosts = await this.instancesRepository.find({ + where: { + isSuspended: true, + }, + }); + this.#suspendedHostsCache.set(null, suspendedHosts); + } + if (suspendedHosts.map(x => x.host).includes(toPuny(host))) { + return 'skip (suspended)'; + } + + try { + if (this.#latest !== (this.#latest = JSON.stringify(job.data.content, null, 2))) { + this.#logger.debug(`delivering ${this.#latest}`); + } + + await this.apRequestService.signedPost(job.data.user, job.data.to, job.data.content); + + // Update stats + this.federatedInstanceService.registerOrFetchInstanceDoc(host).then(i => { + this.instancesRepository.update(i.id, { + latestRequestSentAt: new Date(), + latestStatus: 200, + lastCommunicatedAt: new Date(), + isNotResponding: false, + }); + + this.fetchInstanceMetadataService.fetchInstanceMetadata(i); + + this.instanceChart.requestSent(i.host, true); + this.apRequestChart.deliverSucc(); + this.federationChart.deliverd(i.host, true); + }); + + return 'Success'; + } catch (res) { + // Update stats + this.federatedInstanceService.registerOrFetchInstanceDoc(host).then(i => { + this.instancesRepository.update(i.id, { + latestRequestSentAt: new Date(), + latestStatus: res instanceof StatusError ? res.statusCode : null, + isNotResponding: true, + }); + + this.instanceChart.requestSent(i.host, false); + this.apRequestChart.deliverFail(); + this.federationChart.deliverd(i.host, false); + }); + + if (res instanceof StatusError) { + // 4xx + if (res.isClientError) { + // HTTPステータスコード4xxはクライアントエラーであり、それはつまり + // 何回再送しても成功することはないということなのでエラーにはしないでおく + return `${res.statusCode} ${res.statusMessage}`; + } + + // 5xx etc. + throw `${res.statusCode} ${res.statusMessage}`; + } else { + // DNS error, socket error, timeout ... + throw res; + } + } + } +} diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts new file mode 100644 index 000000000000..54728538e1e1 --- /dev/null +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -0,0 +1,190 @@ +import { URL } from 'node:url'; +import { Inject, Injectable } from '@nestjs/common'; +import { MoreThan } from 'typeorm'; +import httpSignature from '@peertube/http-signature'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { Instances } from '@/models/index.js'; +import type { DriveFiles } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import type { MetaService } from '@/services/MetaService.js'; +import { extractDbHost, toPuny } from '@/misc/convert-host.js'; +import { StatusError } from '@/services/HttpRequestService.js'; +import type { ApRequestService } from '@/services/remote/activitypub/ApRequestService.js'; +import type { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; +import type { FetchInstanceMetadataService } from '@/services/FetchInstanceMetadataService.js'; +import { Cache } from '@/misc/cache.js'; +import type { Instance } from '@/models/entities/instance.js'; +import type InstanceChart from '@/services/chart/charts/instance.js'; +import type ApRequestChart from '@/services/chart/charts/ap-request.js'; +import type FederationChart from '@/services/chart/charts/federation.js'; +import { LdSignature } from '@/services/remote/activitypub/misc/ld-signature.js'; +import { getApId } from '@/services/remote/activitypub/type.js'; +import type { CacheableRemoteUser } from '@/models/entities/user.js'; +import type { UserPublickey } from '@/models/entities/user-publickey.js'; +import type { ApDbResolverService } from '@/services/remote/activitypub/ApDbResolverService.js'; +import type Bull from 'bull'; +import type { DeliverJobData, InboxJobData } from '../types.js'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; + +// ユーザーのinboxにアクティビティが届いた時の処理 +@Injectable() +export class InboxProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('instancesRepository') + private instancesRepository: typeof Instances, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private metaService: MetaService, + private federatedInstanceService: FederatedInstanceService, + private fetchInstanceMetadataService: FetchInstanceMetadataService, + private apRequestService: ApRequestService, + private instanceChart: InstanceChart, + private apRequestChart: ApRequestChart, + private apDbResolverService: ApDbResolverService, + private federationChart: FederationChart, + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('inbox'); + } + + public async process(job: Bull.Job): Promise { + const signature = job.data.signature; // HTTP-signature + const activity = job.data.activity; + + //#region Log + const info = Object.assign({}, activity) as any; + delete info['@context']; + this.#logger.debug(JSON.stringify(info, null, 2)); + //#endregion + + const host = toPuny(new URL(signature.keyId).hostname); + + // ブロックしてたら中断 + const meta = await this.metaService.fetch(); + if (meta.blockedHosts.includes(host)) { + return `Blocked request: ${host}`; + } + + const keyIdLower = signature.keyId.toLowerCase(); + if (keyIdLower.startsWith('acct:')) { + return `Old keyId is no longer supported. ${keyIdLower}`; + } + + // HTTP-Signature keyIdを元にDBから取得 + let authUser: { + user: CacheableRemoteUser; + key: UserPublickey | null; + } | null = await this.apDbResolverService.getAuthUserFromKeyId(signature.keyId); + + // keyIdでわからなければ、activity.actorを元にDBから取得 || activity.actorを元にリモートから取得 + if (authUser == null) { + try { + authUser = await this.apDbResolverService.getAuthUserFromApId(getApId(activity.actor)); + } catch (e) { + // 対象が4xxならスキップ + if (e instanceof StatusError) { + if (e.isClientError) { + return `skip: Ignored deleted actors on both ends ${activity.actor} - ${e.statusCode}`; + } + throw `Error in actor ${activity.actor} - ${e.statusCode || e}`; + } + } + } + + // それでもわからなければ終了 + if (authUser == null) { + return 'skip: failed to resolve user'; + } + + // publicKey がなくても終了 + if (authUser.key == null) { + return 'skip: failed to resolve user publicKey'; + } + + // HTTP-Signatureの検証 + const httpSignatureValidated = httpSignature.verifySignature(signature, authUser.key.keyPem); + + // また、signatureのsignerは、activity.actorと一致する必要がある + if (!httpSignatureValidated || authUser.user.uri !== activity.actor) { + // 一致しなくても、でもLD-Signatureがありそうならそっちも見る + if (activity.signature) { + if (activity.signature.type !== 'RsaSignature2017') { + return `skip: unsupported LD-signature type ${activity.signature.type}`; + } + + // activity.signature.creator: https://example.oom/users/user#main-key + // みたいになっててUserを引っ張れば公開キーも入ることを期待する + if (activity.signature.creator) { + const candicate = activity.signature.creator.replace(/#.*/, ''); + await resolvePerson(candicate).catch(() => null); + } + + // keyIdからLD-Signatureのユーザーを取得 + authUser = await dbResolver.getAuthUserFromKeyId(activity.signature.creator); + if (authUser == null) { + return 'skip: LD-Signatureのユーザーが取得できませんでした'; + } + + if (authUser.key == null) { + return 'skip: LD-SignatureのユーザーはpublicKeyを持っていませんでした'; + } + + // LD-Signature検証 + const ldSignature = new LdSignature(); + const verified = await ldSignature.verifyRsaSignature2017(activity, authUser.key.keyPem).catch(() => false); + if (!verified) { + return 'skip: LD-Signatureの検証に失敗しました'; + } + + // もう一度actorチェック + if (authUser.user.uri !== activity.actor) { + return `skip: LD-Signature user(${authUser.user.uri}) !== activity.actor(${activity.actor})`; + } + + // ブロックしてたら中断 + const ldHost = extractDbHost(authUser.user.uri); + if (meta.blockedHosts.includes(ldHost)) { + return `Blocked request: ${ldHost}`; + } + } else { + return `skip: http-signature verification failed and no LD-Signature. keyId=${signature.keyId}`; + } + } + + // activity.idがあればホストが署名者のホストであることを確認する + if (typeof activity.id === 'string') { + const signerHost = extractDbHost(authUser.user.uri!); + const activityIdHost = extractDbHost(activity.id); + if (signerHost !== activityIdHost) { + return `skip: signerHost(${signerHost}) !== activity.id host(${activityIdHost}`; + } + } + + // Update stats + this.federatedInstanceService.registerOrFetchInstanceDoc(authUser.user.host).then(i => { + Instances.update(i.id, { + latestRequestReceivedAt: new Date(), + lastCommunicatedAt: new Date(), + isNotResponding: false, + }); + + this.fetchInstanceMetadataService.fetchInstanceMetadata(i); + + this.instanceChart.requestReceived(i.host); + this.apRequestChart.inbox(); + this.federationChart.inbox(i.host); + }); + + // アクティビティを処理 + await perform(authUser.user, activity); + return 'ok'; + } +} diff --git a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts new file mode 100644 index 000000000000..f97f251c1686 --- /dev/null +++ b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts @@ -0,0 +1,61 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { In, MoreThan } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import type FederationChart from '@/services/chart/charts/federation.js'; +import type NotesChart from '@/services/chart/charts/notes.js'; +import type UsersChart from '@/services/chart/charts/users.js'; +import type ActiveUsersChart from '@/services/chart/charts/active-users.js'; +import type InstanceChart from '@/services/chart/charts/instance.js'; +import type PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; +import type DriveChart from '@/services/chart/charts/drive.js'; +import type PerUserReactionsChart from '@/services/chart/charts/per-user-reactions.js'; +import type HashtagChart from '@/services/chart/charts/hashtag.js'; +import type PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; +import type PerUserDriveChart from '@/services/chart/charts/per-user-drive.js'; +import type ApRequestChart from '@/services/chart/charts/ap-request.js'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; +import type Bull from 'bull'; + +@Injectable() +export class ResyncChartsProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + private federationChart: FederationChart, + private notesChart: NotesChart, + private usersChart: UsersChart, + private activeUsersChart: ActiveUsersChart, + private instanceChart: InstanceChart, + private perUserNotesChart: PerUserNotesChart, + private driveChart: DriveChart, + private perUserReactionsChart: PerUserReactionsChart, + private hashtagChart: HashtagChart, + private perUserFollowingChart: PerUserFollowingChart, + private perUserDriveChart: PerUserDriveChart, + private apRequestChart: ApRequestChart, + + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('resync-charts'); + } + + public async process(job: Bull.Job>, done: () => void): Promise { + this.#logger.info('Resync charts...'); + + // TODO: ユーザーごとのチャートも更新する + // TODO: インスタンスごとのチャートも更新する + await Promise.all([ + this.driveChart.resync(), + this.notesChart.resync(), + this.usersChart.resync(), + ]); + + this.#logger.succ('All charts successfully resynced.'); + done(); + } +} diff --git a/packages/backend/src/queue/processors/TickChartsProcessorService.ts b/packages/backend/src/queue/processors/TickChartsProcessorService.ts new file mode 100644 index 000000000000..10beee62096d --- /dev/null +++ b/packages/backend/src/queue/processors/TickChartsProcessorService.ts @@ -0,0 +1,68 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { In, MoreThan } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import type FederationChart from '@/services/chart/charts/federation.js'; +import type NotesChart from '@/services/chart/charts/notes.js'; +import type UsersChart from '@/services/chart/charts/users.js'; +import type ActiveUsersChart from '@/services/chart/charts/active-users.js'; +import type InstanceChart from '@/services/chart/charts/instance.js'; +import type PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; +import type DriveChart from '@/services/chart/charts/drive.js'; +import type PerUserReactionsChart from '@/services/chart/charts/per-user-reactions.js'; +import type HashtagChart from '@/services/chart/charts/hashtag.js'; +import type PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; +import type PerUserDriveChart from '@/services/chart/charts/per-user-drive.js'; +import type ApRequestChart from '@/services/chart/charts/ap-request.js'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; +import type Bull from 'bull'; + +@Injectable() +export class TickChartsProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + private federationChart: FederationChart, + private notesChart: NotesChart, + private usersChart: UsersChart, + private activeUsersChart: ActiveUsersChart, + private instanceChart: InstanceChart, + private perUserNotesChart: PerUserNotesChart, + private driveChart: DriveChart, + private perUserReactionsChart: PerUserReactionsChart, + private hashtagChart: HashtagChart, + private perUserFollowingChart: PerUserFollowingChart, + private perUserDriveChart: PerUserDriveChart, + private apRequestChart: ApRequestChart, + + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('tick-charts'); + } + + public async process(job: Bull.Job>, done: () => void): Promise { + this.#logger.info('Tick charts...'); + + await Promise.all([ + this.federationChart.tick(false), + this.notesChart.tick(false), + this.usersChart.tick(false), + this.activeUsersChart.tick(false), + this.instanceChart.tick(false), + this.perUserNotesChart.tick(false), + this.driveChart.tick(false), + this.perUserReactionsChart.tick(false), + this.hashtagChart.tick(false), + this.perUserFollowingChart.tick(false), + this.perUserDriveChart.tick(false), + this.apRequestChart.tick(false), + ]); + + this.#logger.succ('All charts successfully ticked.'); + done(); + } +} diff --git a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts new file mode 100644 index 000000000000..57722e9bed19 --- /dev/null +++ b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts @@ -0,0 +1,79 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { IsNull, MoreThan } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Webhooks } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import type { HttpRequestService } from '@/services/HttpRequestService.js'; +import { StatusError } from '@/services/HttpRequestService.js'; +import type Bull from 'bull'; +import type { WebhookDeliverJobData } from '../types.js'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; + +@Injectable() +export class WebhookDeliverProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('webhooksRepository') + private webhooksRepository: typeof Webhooks, + + private httpRequestService: HttpRequestService, + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('webhook'); + } + + public async process(job: Bull.Job): Promise { + try { + this.#logger.debug(`delivering ${job.data.webhookId}`); + + const res = await this.httpRequestService.getResponse({ + url: job.data.to, + method: 'POST', + headers: { + 'User-Agent': 'Misskey-Hooks', + 'X-Misskey-Host': this.config.host, + 'X-Misskey-Hook-Id': job.data.webhookId, + 'X-Misskey-Hook-Secret': job.data.secret, + }, + body: JSON.stringify({ + hookId: job.data.webhookId, + userId: job.data.userId, + eventId: job.data.eventId, + createdAt: job.data.createdAt, + type: job.data.type, + body: job.data.content, + }), + }); + + this.webhooksRepository.update({ id: job.data.webhookId }, { + latestSentAt: new Date(), + latestStatus: res.status, + }); + + return 'Success'; + } catch (res) { + this.webhooksRepository.update({ id: job.data.webhookId }, { + latestSentAt: new Date(), + latestStatus: res instanceof StatusError ? res.statusCode : 1, + }); + + if (res instanceof StatusError) { + // 4xx + if (res.isClientError) { + return `${res.statusCode} ${res.statusMessage}`; + } + + // 5xx etc. + throw `${res.statusCode} ${res.statusMessage}`; + } else { + // DNS error, socket error, timeout ... + throw res; + } + } + } +} diff --git a/packages/backend/src/queue/processors/deliver.ts b/packages/backend/src/queue/processors/deliver.ts deleted file mode 100644 index 935bec837273..000000000000 --- a/packages/backend/src/queue/processors/deliver.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { URL } from 'node:url'; -import request from '@/services/remote/activitypub/request.js'; -import { registerOrFetchInstanceDoc } from '@/services/register-or-fetch-instance-doc.js'; -import Logger from '@/services/logger.js'; -import { Instances } from '@/models/index.js'; -import { apRequestChart, federationChart, instanceChart } from '@/services/chart/index.js'; -import { fetchInstanceMetadata } from '@/services/fetch-instance-metadata.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import { toPuny } from '@/misc/convert-host.js'; -import { Cache } from '@/misc/cache.js'; -import type { Instance } from '@/models/entities/instance.js'; -import { StatusError } from '@/misc/fetch.js'; -import type { DeliverJobData } from '../types.js'; -import type Bull from 'bull'; - -const logger = new Logger('deliver'); - -let latest: string | null = null; - -const suspendedHostsCache = new Cache(1000 * 60 * 60); - -export default async (job: Bull.Job) => { - const { host } = new URL(job.data.to); - - // ブロックしてたら中断 - const meta = await fetchMeta(); - if (meta.blockedHosts.includes(toPuny(host))) { - return 'skip (blocked)'; - } - - // isSuspendedなら中断 - let suspendedHosts = suspendedHostsCache.get(null); - if (suspendedHosts == null) { - suspendedHosts = await Instances.find({ - where: { - isSuspended: true, - }, - }); - suspendedHostsCache.set(null, suspendedHosts); - } - if (suspendedHosts.map(x => x.host).includes(toPuny(host))) { - return 'skip (suspended)'; - } - - try { - if (latest !== (latest = JSON.stringify(job.data.content, null, 2))) { - logger.debug(`delivering ${latest}`); - } - - await request(job.data.user, job.data.to, job.data.content); - - // Update stats - registerOrFetchInstanceDoc(host).then(i => { - Instances.update(i.id, { - latestRequestSentAt: new Date(), - latestStatus: 200, - lastCommunicatedAt: new Date(), - isNotResponding: false, - }); - - fetchInstanceMetadata(i); - - instanceChart.requestSent(i.host, true); - apRequestChart.deliverSucc(); - federationChart.deliverd(i.host, true); - }); - - return 'Success'; - } catch (res) { - // Update stats - registerOrFetchInstanceDoc(host).then(i => { - Instances.update(i.id, { - latestRequestSentAt: new Date(), - latestStatus: res instanceof StatusError ? res.statusCode : null, - isNotResponding: true, - }); - - instanceChart.requestSent(i.host, false); - apRequestChart.deliverFail(); - federationChart.deliverd(i.host, false); - }); - - if (res instanceof StatusError) { - // 4xx - if (res.isClientError) { - // HTTPステータスコード4xxはクライアントエラーであり、それはつまり - // 何回再送しても成功することはないということなのでエラーにはしないでおく - return `${res.statusCode} ${res.statusMessage}`; - } - - // 5xx etc. - throw `${res.statusCode} ${res.statusMessage}`; - } else { - // DNS error, socket error, timeout ... - throw res; - } - } -}; diff --git a/packages/backend/src/queue/processors/inbox.ts b/packages/backend/src/queue/processors/inbox.ts deleted file mode 100644 index 3f7125ca598a..000000000000 --- a/packages/backend/src/queue/processors/inbox.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { URL } from 'node:url'; -import httpSignature from '@peertube/http-signature'; -import perform from '@/services/remote/activitypub/perform.js'; -import Logger from '@/services/logger.js'; -import { registerOrFetchInstanceDoc } from '@/services/register-or-fetch-instance-doc.js'; -import { Instances } from '@/models/index.js'; -import { apRequestChart, federationChart, instanceChart } from '@/services/chart/index.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import { toPuny, extractDbHost } from '@/misc/convert-host.js'; -import { getApId } from '@/services/remote/activitypub/type.js'; -import { fetchInstanceMetadata } from '@/services/fetch-instance-metadata.js'; -import DbResolver from '@/services/remote/activitypub/db-resolver.js'; -import { resolvePerson } from '@/services/remote/activitypub/models/person.js'; -import { LdSignature } from '@/services/remote/activitypub/misc/ld-signature.js'; -import { StatusError } from '@/misc/fetch.js'; -import type { CacheableRemoteUser } from '@/models/entities/user.js'; -import type { UserPublickey } from '@/models/entities/user-publickey.js'; -import type { InboxJobData } from '../types.js'; -import type Bull from 'bull'; - -const logger = new Logger('inbox'); - -// ユーザーのinboxにアクティビティが届いた時の処理 -export default async (job: Bull.Job): Promise => { - const signature = job.data.signature; // HTTP-signature - const activity = job.data.activity; - - //#region Log - const info = Object.assign({}, activity) as any; - delete info['@context']; - logger.debug(JSON.stringify(info, null, 2)); - //#endregion - - const host = toPuny(new URL(signature.keyId).hostname); - - // ブロックしてたら中断 - const meta = await fetchMeta(); - if (meta.blockedHosts.includes(host)) { - return `Blocked request: ${host}`; - } - - const keyIdLower = signature.keyId.toLowerCase(); - if (keyIdLower.startsWith('acct:')) { - return `Old keyId is no longer supported. ${keyIdLower}`; - } - - const dbResolver = new DbResolver(); - - // HTTP-Signature keyIdを元にDBから取得 - let authUser: { - user: CacheableRemoteUser; - key: UserPublickey | null; - } | null = await dbResolver.getAuthUserFromKeyId(signature.keyId); - - // keyIdでわからなければ、activity.actorを元にDBから取得 || activity.actorを元にリモートから取得 - if (authUser == null) { - try { - authUser = await dbResolver.getAuthUserFromApId(getApId(activity.actor)); - } catch (e) { - // 対象が4xxならスキップ - if (e instanceof StatusError) { - if (e.isClientError) { - return `skip: Ignored deleted actors on both ends ${activity.actor} - ${e.statusCode}`; - } - throw `Error in actor ${activity.actor} - ${e.statusCode || e}`; - } - } - } - - // それでもわからなければ終了 - if (authUser == null) { - return 'skip: failed to resolve user'; - } - - // publicKey がなくても終了 - if (authUser.key == null) { - return 'skip: failed to resolve user publicKey'; - } - - // HTTP-Signatureの検証 - const httpSignatureValidated = httpSignature.verifySignature(signature, authUser.key.keyPem); - - // また、signatureのsignerは、activity.actorと一致する必要がある - if (!httpSignatureValidated || authUser.user.uri !== activity.actor) { - // 一致しなくても、でもLD-Signatureがありそうならそっちも見る - if (activity.signature) { - if (activity.signature.type !== 'RsaSignature2017') { - return `skip: unsupported LD-signature type ${activity.signature.type}`; - } - - // activity.signature.creator: https://example.oom/users/user#main-key - // みたいになっててUserを引っ張れば公開キーも入ることを期待する - if (activity.signature.creator) { - const candicate = activity.signature.creator.replace(/#.*/, ''); - await resolvePerson(candicate).catch(() => null); - } - - // keyIdからLD-Signatureのユーザーを取得 - authUser = await dbResolver.getAuthUserFromKeyId(activity.signature.creator); - if (authUser == null) { - return 'skip: LD-Signatureのユーザーが取得できませんでした'; - } - - if (authUser.key == null) { - return 'skip: LD-SignatureのユーザーはpublicKeyを持っていませんでした'; - } - - // LD-Signature検証 - const ldSignature = new LdSignature(); - const verified = await ldSignature.verifyRsaSignature2017(activity, authUser.key.keyPem).catch(() => false); - if (!verified) { - return 'skip: LD-Signatureの検証に失敗しました'; - } - - // もう一度actorチェック - if (authUser.user.uri !== activity.actor) { - return `skip: LD-Signature user(${authUser.user.uri}) !== activity.actor(${activity.actor})`; - } - - // ブロックしてたら中断 - const ldHost = extractDbHost(authUser.user.uri); - if (meta.blockedHosts.includes(ldHost)) { - return `Blocked request: ${ldHost}`; - } - } else { - return `skip: http-signature verification failed and no LD-Signature. keyId=${signature.keyId}`; - } - } - - // activity.idがあればホストが署名者のホストであることを確認する - if (typeof activity.id === 'string') { - const signerHost = extractDbHost(authUser.user.uri!); - const activityIdHost = extractDbHost(activity.id); - if (signerHost !== activityIdHost) { - return `skip: signerHost(${signerHost}) !== activity.id host(${activityIdHost}`; - } - } - - // Update stats - registerOrFetchInstanceDoc(authUser.user.host).then(i => { - Instances.update(i.id, { - latestRequestReceivedAt: new Date(), - lastCommunicatedAt: new Date(), - isNotResponding: false, - }); - - fetchInstanceMetadata(i); - - instanceChart.requestReceived(i.host); - apRequestChart.inbox(); - federationChart.inbox(i.host); - }); - - // アクティビティを処理 - await perform(authUser.user, activity); - return 'ok'; -}; diff --git a/packages/backend/src/queue/processors/system/check-expired-mutings.ts b/packages/backend/src/queue/processors/system/check-expired-mutings.ts deleted file mode 100644 index 621269e7e1a3..000000000000 --- a/packages/backend/src/queue/processors/system/check-expired-mutings.ts +++ /dev/null @@ -1,30 +0,0 @@ -import Bull from 'bull'; -import { In } from 'typeorm'; -import { Mutings } from '@/models/index.js'; -import { queueLogger } from '../../logger.js'; -import { publishUserEvent } from '@/services/stream.js'; - -const logger = queueLogger.createSubLogger('check-expired-mutings'); - -export async function checkExpiredMutings(job: Bull.Job>, done: any): Promise { - logger.info(`Checking expired mutings...`); - - const expired = await Mutings.createQueryBuilder('muting') - .where('muting.expiresAt IS NOT NULL') - .andWhere('muting.expiresAt < :now', { now: new Date() }) - .innerJoinAndSelect('muting.mutee', 'mutee') - .getMany(); - - if (expired.length > 0) { - await Mutings.delete({ - id: In(expired.map(m => m.id)), - }); - - for (const m of expired) { - publishUserEvent(m.muterId, 'unmute', m.mutee!); - } - } - - logger.succ(`All expired mutings checked.`); - done(); -} diff --git a/packages/backend/src/queue/processors/system/clean-charts.ts b/packages/backend/src/queue/processors/system/clean-charts.ts deleted file mode 100644 index c9169d5acfd6..000000000000 --- a/packages/backend/src/queue/processors/system/clean-charts.ts +++ /dev/null @@ -1,28 +0,0 @@ -import Bull from 'bull'; - -import { queueLogger } from '../../logger.js'; -import { activeUsersChart, driveChart, federationChart, hashtagChart, instanceChart, notesChart, perUserDriveChart, perUserFollowingChart, perUserNotesChart, perUserReactionsChart, usersChart, apRequestChart } from '@/services/chart/index.js'; - -const logger = queueLogger.createSubLogger('clean-charts'); - -export async function cleanCharts(job: Bull.Job>, done: any): Promise { - logger.info(`Clean charts...`); - - await Promise.all([ - federationChart.clean(), - notesChart.clean(), - usersChart.clean(), - activeUsersChart.clean(), - instanceChart.clean(), - perUserNotesChart.clean(), - driveChart.clean(), - perUserReactionsChart.clean(), - hashtagChart.clean(), - perUserFollowingChart.clean(), - perUserDriveChart.clean(), - apRequestChart.clean(), - ]); - - logger.succ(`All charts successfully cleaned.`); - done(); -} diff --git a/packages/backend/src/queue/processors/system/clean.ts b/packages/backend/src/queue/processors/system/clean.ts deleted file mode 100644 index c4f978d7c931..000000000000 --- a/packages/backend/src/queue/processors/system/clean.ts +++ /dev/null @@ -1,18 +0,0 @@ -import Bull from 'bull'; -import { LessThan } from 'typeorm'; -import { UserIps } from '@/models/index.js'; - -import { queueLogger } from '../../logger.js'; - -const logger = queueLogger.createSubLogger('clean'); - -export async function clean(job: Bull.Job>, done: any): Promise { - logger.info('Cleaning...'); - - UserIps.delete({ - createdAt: LessThan(new Date(Date.now() - (1000 * 60 * 60 * 24 * 90))), - }); - - logger.succ('Cleaned.'); - done(); -} diff --git a/packages/backend/src/queue/processors/system/resync-charts.ts b/packages/backend/src/queue/processors/system/resync-charts.ts deleted file mode 100644 index 20012513aff1..000000000000 --- a/packages/backend/src/queue/processors/system/resync-charts.ts +++ /dev/null @@ -1,21 +0,0 @@ -import Bull from 'bull'; - -import { queueLogger } from '../../logger.js'; -import { driveChart, notesChart, usersChart } from '@/services/chart/index.js'; - -const logger = queueLogger.createSubLogger('resync-charts'); - -export async function resyncCharts(job: Bull.Job>, done: any): Promise { - logger.info(`Resync charts...`); - - // TODO: ユーザーごとのチャートも更新する - // TODO: インスタンスごとのチャートも更新する - await Promise.all([ - driveChart.resync(), - notesChart.resync(), - usersChart.resync(), - ]); - - logger.succ(`All charts successfully resynced.`); - done(); -} diff --git a/packages/backend/src/queue/processors/system/tick-charts.ts b/packages/backend/src/queue/processors/system/tick-charts.ts deleted file mode 100644 index 13403f8f73ae..000000000000 --- a/packages/backend/src/queue/processors/system/tick-charts.ts +++ /dev/null @@ -1,28 +0,0 @@ -import Bull from 'bull'; - -import { queueLogger } from '../../logger.js'; -import { activeUsersChart, driveChart, federationChart, hashtagChart, instanceChart, notesChart, perUserDriveChart, perUserFollowingChart, perUserNotesChart, perUserReactionsChart, usersChart, apRequestChart } from '@/services/chart/index.js'; - -const logger = queueLogger.createSubLogger('tick-charts'); - -export async function tickCharts(job: Bull.Job>, done: any): Promise { - logger.info(`Tick charts...`); - - await Promise.all([ - federationChart.tick(false), - notesChart.tick(false), - usersChart.tick(false), - activeUsersChart.tick(false), - instanceChart.tick(false), - perUserNotesChart.tick(false), - driveChart.tick(false), - perUserReactionsChart.tick(false), - hashtagChart.tick(false), - perUserFollowingChart.tick(false), - perUserDriveChart.tick(false), - apRequestChart.tick(false), - ]); - - logger.succ(`All charts successfully ticked.`); - done(); -} diff --git a/packages/backend/src/queue/processors/webhook-deliver.ts b/packages/backend/src/queue/processors/webhook-deliver.ts deleted file mode 100644 index d49206f68f70..000000000000 --- a/packages/backend/src/queue/processors/webhook-deliver.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { URL } from 'node:url'; -import Bull from 'bull'; -import Logger from '@/services/logger.js'; -import { WebhookDeliverJobData } from '../types.js'; -import { getResponse, StatusError } from '@/misc/fetch.js'; -import { Webhooks } from '@/models/index.js'; -import config from '@/config/index.js'; - -const logger = new Logger('webhook'); - -export default async (job: Bull.Job) => { - try { - logger.debug(`delivering ${job.data.webhookId}`); - - const res = await getResponse({ - url: job.data.to, - method: 'POST', - headers: { - 'User-Agent': 'Misskey-Hooks', - 'X-Misskey-Host': config.host, - 'X-Misskey-Hook-Id': job.data.webhookId, - 'X-Misskey-Hook-Secret': job.data.secret, - }, - body: JSON.stringify({ - hookId: job.data.webhookId, - userId: job.data.userId, - eventId: job.data.eventId, - createdAt: job.data.createdAt, - type: job.data.type, - body: job.data.content, - }), - }); - - Webhooks.update({ id: job.data.webhookId }, { - latestSentAt: new Date(), - latestStatus: res.status, - }); - - return 'Success'; - } catch (res) { - Webhooks.update({ id: job.data.webhookId }, { - latestSentAt: new Date(), - latestStatus: res instanceof StatusError ? res.statusCode : 1, - }); - - if (res instanceof StatusError) { - // 4xx - if (res.isClientError) { - return `${res.statusCode} ${res.statusMessage}`; - } - - // 5xx etc. - throw `${res.statusCode} ${res.statusMessage}`; - } else { - // DNS error, socket error, timeout ... - throw res; - } - } -}; From 70b637a11ab6148c7a05eff80a175b84c7c76235 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 21:47:43 +0900 Subject: [PATCH 066/180] wip --- .../src/queue/SystemQueueProcessorsService.ts | 38 +++++++++++++ .../backend/src/queue/SystemQueueService.ts | 20 ------- packages/backend/src/queue/index.ts | 4 +- .../EndedPollNotificationProcessorService.ts | 56 +++++++++++++++++++ .../processors/ended-poll-notification.ts | 33 ----------- packages/backend/src/queue/queue.module.ts | 7 ++- packages/backend/src/queue/queue.service.ts | 12 ++-- 7 files changed, 108 insertions(+), 62 deletions(-) create mode 100644 packages/backend/src/queue/SystemQueueProcessorsService.ts delete mode 100644 packages/backend/src/queue/SystemQueueService.ts create mode 100644 packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts delete mode 100644 packages/backend/src/queue/processors/ended-poll-notification.ts diff --git a/packages/backend/src/queue/SystemQueueProcessorsService.ts b/packages/backend/src/queue/SystemQueueProcessorsService.ts new file mode 100644 index 000000000000..2370b61b5814 --- /dev/null +++ b/packages/backend/src/queue/SystemQueueProcessorsService.ts @@ -0,0 +1,38 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; +import type { TickChartsProcessorService } from './processors/TickChartsProcessorService.js'; +import type { ResyncChartsProcessorService } from './processors/ResyncChartsProcessorService.js'; +import type { CleanChartsProcessorService } from './processors/CleanChartsProcessorService.js'; +import type { CheckExpiredMutingsProcessorService } from './processors/CheckExpiredMutingsProcessorService.js'; +import type { CleanProcessorService } from './processors/CleanProcessorService.js'; +import type Bull from 'bull'; + +@Injectable() +export class SystemQueueProcessorsService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + private tickChartsProcessorService: TickChartsProcessorService, + private resyncChartsProcessorService: ResyncChartsProcessorService, + private cleanChartsProcessorService: CleanChartsProcessorService, + private checkExpiredMutingsProcessorService: CheckExpiredMutingsProcessorService, + private cleanProcessorService: CleanProcessorService, + ) { + } + + public start(dbQueue: Bull.Queue>) { + const jobs = { + tickCharts: this.tickChartsProcessorService.process, + resyncCharts: this.resyncChartsProcessorService.process, + cleanCharts: this.cleanChartsProcessorService.process, + checkExpiredMutings: this.checkExpiredMutingsProcessorService.process, + clean: this.cleanProcessorService.process, + } as Record> | Bull.ProcessPromiseFunction>>; + + for (const [k, v] of Object.entries(jobs)) { + dbQueue.process(k, v); + } + } +} diff --git a/packages/backend/src/queue/SystemQueueService.ts b/packages/backend/src/queue/SystemQueueService.ts deleted file mode 100644 index c442a233b91d..000000000000 --- a/packages/backend/src/queue/SystemQueueService.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { tickCharts } from './tick-charts.js'; -import { resyncCharts } from './resync-charts.js'; -import { cleanCharts } from './clean-charts.js'; -import { checkExpiredMutings } from './check-expired-mutings.js'; -import { clean } from './clean.js'; -import type Bull from 'bull'; - -const jobs = { - tickCharts, - resyncCharts, - cleanCharts, - checkExpiredMutings, - clean, -} as Record> | Bull.ProcessPromiseFunction>>; - -export default function(dbQueue: Bull.Queue>) { - for (const [k, v] of Object.entries(jobs)) { - dbQueue.process(k, v); - } -} diff --git a/packages/backend/src/queue/index.ts b/packages/backend/src/queue/index.ts index 80e1c7b3ab62..af95e11fbfe5 100644 --- a/packages/backend/src/queue/index.ts +++ b/packages/backend/src/queue/index.ts @@ -11,6 +11,7 @@ import { endedPollNotification } from './processors/ended-poll-notification.js'; import { queueLogger } from './logger.js'; import { getJobInfo } from './get-job-info.js'; import { QueueService } from './queue.service.js'; +import { SystemQueueProcessorsService } from './SystemQueueProcessorsService.js'; import type { INestApplicationContext } from '@nestjs/common'; export default function(app: INestApplicationContext) { @@ -22,6 +23,7 @@ export default function(app: INestApplicationContext) { const objectStorageQueue = queueService.objectStorageQueue; const webhookDeliverQueue = queueService.webhookDeliverQueue; const endedPollNotificationQueue = queueService.endedPollNotificationQueue; + const systemQueueProcessorsService = app.get(SystemQueueProcessorsService); function renderError(e: Error): any { return { @@ -123,5 +125,5 @@ export default function(app: INestApplicationContext) { removeOnComplete: true, }); - processSystemQueue(systemQueue); + systemQueueProcessorsService.start(systemQueue); } diff --git a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts new file mode 100644 index 000000000000..13972f8f4463 --- /dev/null +++ b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts @@ -0,0 +1,56 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { MoreThan } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { PollVotes , Notes } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type Logger from '@/logger.js'; +import type { CreateNotificationService } from '@/services/CreateNotificationService.js'; +import type Bull from 'bull'; +import type { EndedPollNotificationJobData } from '../types.js'; +import type { QueueLoggerService } from '../QueueLoggerService.js'; + +@Injectable() +export class EndedPollNotificationProcessorService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + + @Inject('pollVotesRepository') + private pollVotesRepository: typeof PollVotes, + + private createNotificationService: CreateNotificationService, + private queueLoggerService: QueueLoggerService, + ) { + this.queueLoggerService.logger.createSubLogger('ended-poll-notification'); + } + + public async process(job: Bull.Job, done: () => void): Promise { + const note = await this.notesRepository.findOneBy({ id: job.data.noteId }); + if (note == null || !note.hasPoll) { + done(); + return; + } + + const votes = await this.pollVotesRepository.createQueryBuilder('vote') + .select('vote.userId') + .where('vote.noteId = :noteId', { noteId: note.id }) + .innerJoinAndSelect('vote.user', 'user') + .andWhere('user.host IS NULL') + .getMany(); + + const userIds = [...new Set([note.userId, ...votes.map(v => v.userId)])]; + + for (const userId of userIds) { + this.createNotificationService.createNotification(userId, 'pollEnded', { + noteId: note.id, + }); + } + + done(); + } +} diff --git a/packages/backend/src/queue/processors/ended-poll-notification.ts b/packages/backend/src/queue/processors/ended-poll-notification.ts deleted file mode 100644 index 6151c96ad638..000000000000 --- a/packages/backend/src/queue/processors/ended-poll-notification.ts +++ /dev/null @@ -1,33 +0,0 @@ -import Bull from 'bull'; -import { In } from 'typeorm'; -import { Notes, Polls, PollVotes } from '@/models/index.js'; -import { queueLogger } from '../logger.js'; -import { EndedPollNotificationJobData } from '@/queue/types.js'; -import { createNotification } from '@/services/create-notification.js'; - -const logger = queueLogger.createSubLogger('ended-poll-notification'); - -export async function endedPollNotification(job: Bull.Job, done: any): Promise { - const note = await Notes.findOneBy({ id: job.data.noteId }); - if (note == null || !note.hasPoll) { - done(); - return; - } - - const votes = await PollVotes.createQueryBuilder('vote') - .select('vote.userId') - .where('vote.noteId = :noteId', { noteId: note.id }) - .innerJoinAndSelect('vote.user', 'user') - .andWhere('user.host IS NULL') - .getMany(); - - const userIds = [...new Set([note.userId, ...votes.map(v => v.userId)])]; - - for (const userId of userIds) { - createNotification(userId, 'pollEnded', { - noteId: note.id, - }); - } - - done(); -} diff --git a/packages/backend/src/queue/queue.module.ts b/packages/backend/src/queue/queue.module.ts index 845f9b30e21c..705f03fc8a3b 100644 --- a/packages/backend/src/queue/queue.module.ts +++ b/packages/backend/src/queue/queue.module.ts @@ -1,5 +1,6 @@ import { Module } from '@nestjs/common'; -import config from '@/config/index.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { Config } from '@/config/types.js'; import { initialize as initializeQueue } from './initialize.js'; import type Bull from 'bull'; import type { DeliverJobData, InboxJobData, DbJobData, ObjectStorageJobData, EndedPollNotificationJobData, WebhookDeliverJobData } from './types.js'; @@ -18,8 +19,8 @@ export type WebhookDeliverQueue = Bull.Queue; providers: [ { provide: 'queue:system', useValue: initializeQueue('system') }, { provide: 'queue:endedPollNotification', useValue: initializeQueue('endedPollNotification') }, - { provide: 'queue:deliver', useValue: initializeQueue('deliver', config.deliverJobPerSec || 128) }, - { provide: 'queue:inbox', useValue: initializeQueue('inbox', config.inboxJobPerSec || 16) }, + { provide: 'queue:deliver', useFactory: (config: Config) => initializeQueue('deliver', config.deliverJobPerSec || 128), inject: [DI_SYMBOLS.config] }, + { provide: 'queue:inbox', useFactory: (config: Config) => initializeQueue('inbox', config.inboxJobPerSec || 16), inject: [DI_SYMBOLS.config] }, { provide: 'queue:db', useValue: initializeQueue('db') }, { provide: 'queue:objectStorage', useValue: initializeQueue('objectStorage') }, { provide: 'queue:webhookDeliver', useValue: initializeQueue('webhookDeliver', 64) }, diff --git a/packages/backend/src/queue/queue.service.ts b/packages/backend/src/queue/queue.service.ts index 42a8f4f5fa8e..9ffe7c5febfc 100644 --- a/packages/backend/src/queue/queue.service.ts +++ b/packages/backend/src/queue/queue.service.ts @@ -1,11 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { v4 as uuid } from 'uuid'; -import config from '@/config/index.js'; import type { IActivity } from '@/services/remote/activitypub/type.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; - import type { Webhook, webhookEventTypes } from '@/models/entities/webhook.js'; -import { envOption } from '../env.js'; +import type { Config } from '@/config/types.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from './queue.module.js'; import type { ThinUser } from './types.js'; import type httpSignature from '@peertube/http-signature'; @@ -13,6 +12,9 @@ import type httpSignature from '@peertube/http-signature'; @Injectable() export class QueueService { constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + @Inject('queue:system') public systemQueue: SystemQueue, @Inject('queue:endedPollNotification') public endedPollNotificationQueue: EndedPollNotificationQueue, @Inject('queue:deliver') public deliverQueue: DeliverQueue, @@ -35,7 +37,7 @@ export class QueueService { }; return this.deliverQueue.add(data, { - attempts: config.deliverJobMaxAttempts || 12, + attempts: this.config.deliverJobMaxAttempts || 12, timeout: 1 * 60 * 1000, // 1min backoff: { type: 'apBackoff', @@ -52,7 +54,7 @@ export class QueueService { }; return this.inboxQueue.add(data, { - attempts: config.inboxJobMaxAttempts || 8, + attempts: this.config.inboxJobMaxAttempts || 8, timeout: 5 * 60 * 1000, // 5min backoff: { type: 'apBackoff', From fee749b0f481f0a75198731dfef819ecf2ff20c9 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 22:02:29 +0900 Subject: [PATCH 067/180] wip --- .../src/queue/DbQueueProcessorsService.ts | 63 +++++++++++++++++++ packages/backend/src/queue/DbQueueService.ts | 37 ----------- .../ObjectStorageQueueProcessorsService.ts | 30 +++++++++ .../src/queue/ObjectStorageQueueService.ts | 15 ----- packages/backend/src/queue/index.ts | 39 +++++++----- packages/backend/src/queue/queue.service.ts | 2 +- 6 files changed, 117 insertions(+), 69 deletions(-) create mode 100644 packages/backend/src/queue/DbQueueProcessorsService.ts delete mode 100644 packages/backend/src/queue/DbQueueService.ts create mode 100644 packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts delete mode 100644 packages/backend/src/queue/ObjectStorageQueueService.ts diff --git a/packages/backend/src/queue/DbQueueProcessorsService.ts b/packages/backend/src/queue/DbQueueProcessorsService.ts new file mode 100644 index 000000000000..707895b89349 --- /dev/null +++ b/packages/backend/src/queue/DbQueueProcessorsService.ts @@ -0,0 +1,63 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { DbJobData } from '@/queue/types.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; +import type { DeleteDriveFilesProcessorService } from './processors/DeleteDriveFilesProcessorService.js'; +import type { ExportCustomEmojisProcessorService } from './processors/ExportCustomEmojisProcessorService.js'; +import type { ExportNotesProcessorService } from './processors/ExportNotesProcessorService.js'; +import type { ExportFollowingProcessorService } from './processors/ExportFollowingProcessorService.js'; +import type { ExportMutingProcessorService } from './processors/ExportMutingProcessorService.js'; +import type { ExportBlockingProcessorService } from './processors/ExportBlockingProcessorService.js'; +import type { ExportUserListsProcessorService } from './processors/ExportUserListsProcessorService.js'; +import type { ImportFollowingProcessorService } from './processors/ImportFollowingProcessorService.js'; +import type { ImportMutingProcessorService } from './processors/ImportMutingProcessorService.js'; +import type { ImportBlockingProcessorService } from './processors/ImportBlockingProcessorService.js'; +import type { ImportUserListsProcessorService } from './processors/ImportUserListsProcessorService.js'; +import type { ImportCustomEmojisProcessorService } from './processors/ImportCustomEmojisProcessorService.js'; +import type { DeleteAccountProcessorService } from './processors/DeleteAccountProcessorService.js'; +import type Bull from 'bull'; + +@Injectable() +export class DbQueueProcessorsService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + private deleteDriveFilesProcessorService: DeleteDriveFilesProcessorService, + private exportCustomEmojisProcessorService: ExportCustomEmojisProcessorService, + private exportNotesProcessorService: ExportNotesProcessorService, + private exportFollowingProcessorService: ExportFollowingProcessorService, + private exportMutingProcessorService: ExportMutingProcessorService, + private exportBlockingProcessorService: ExportBlockingProcessorService, + private exportUserListsProcessorService: ExportUserListsProcessorService, + private importFollowingProcessorService: ImportFollowingProcessorService, + private importMutingProcessorService: ImportMutingProcessorService, + private importBlockingProcessorService: ImportBlockingProcessorService, + private importUserListsProcessorService: ImportUserListsProcessorService, + private importCustomEmojisProcessorService: ImportCustomEmojisProcessorService, + private deleteAccountProcessorService: DeleteAccountProcessorService, + ) { + } + + public start(dbQueue: Bull.Queue) { + const jobs = { + deleteDriveFiles: this.deleteDriveFilesProcessorService.process, + exportCustomEmojis: this.exportCustomEmojisProcessorService.process, + exportNotes: this.exportNotesProcessorService.process, + exportFollowing: this.exportFollowingProcessorService.process, + exportMuting: this.exportMutingProcessorService.process, + exportBlocking: this.exportBlockingProcessorService.process, + exportUserLists: this.exportUserListsProcessorService.process, + importFollowing: this.importFollowingProcessorService.process, + importMuting: this.importMutingProcessorService.process, + importBlocking: this.importBlockingProcessorService.process, + importUserLists: this.importUserListsProcessorService.process, + importCustomEmojis: this.importCustomEmojisProcessorService.process, + deleteAccount: this.deleteAccountProcessorService.process, + } as Record>>; + + for (const [k, v] of Object.entries(jobs)) { + dbQueue.process(k, v); + } + } +} diff --git a/packages/backend/src/queue/DbQueueService.ts b/packages/backend/src/queue/DbQueueService.ts deleted file mode 100644 index 2293341b5b38..000000000000 --- a/packages/backend/src/queue/DbQueueService.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { DbJobData } from '@/queue/types.js'; -import { deleteDriveFiles } from './delete-drive-files.js'; -import { exportCustomEmojis } from './export-custom-emojis.js'; -import { exportNotes } from './export-notes.js'; -import { exportFollowing } from './export-following.js'; -import { exportMute } from './export-mute.js'; -import { exportBlocking } from './export-blocking.js'; -import { exportUserLists } from './export-user-lists.js'; -import { importFollowing } from './import-following.js'; -import { importUserLists } from './import-user-lists.js'; -import { deleteAccount } from './delete-account.js'; -import { importMuting } from './import-muting.js'; -import { importBlocking } from './import-blocking.js'; -import { importCustomEmojis } from './import-custom-emojis.js'; -import type Bull from 'bull'; - -const jobs = { - deleteDriveFiles, - exportCustomEmojis, - exportNotes, - exportFollowing, - exportMute, - exportBlocking, - exportUserLists, - importFollowing, - importMuting, - importBlocking, - importUserLists, - importCustomEmojis, - deleteAccount, -} as Record | Bull.ProcessPromiseFunction>; - -export default function(dbQueue: Bull.Queue) { - for (const [k, v] of Object.entries(jobs)) { - dbQueue.process(k, v); - } -} diff --git a/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts b/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts new file mode 100644 index 000000000000..f37f92235fbe --- /dev/null +++ b/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts @@ -0,0 +1,30 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { ObjectStorageJobData } from '@/queue/types.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; +import type { CleanRemoteFilesProcessorService } from './processors/CleanRemoteFilesProcessorService.js'; +import type { DeleteFileProcessorService } from './processors/DeleteFileProcessorService.js'; +import type Bull from 'bull'; + +@Injectable() +export class ObjectStorageQueueProcessorsService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + private deleteFileProcessorService: DeleteFileProcessorService, + private cleanRemoteFilesProcessorService: CleanRemoteFilesProcessorService, + ) { + } + + public start(q: Bull.Queue) { + const jobs = { + deleteFile: this.deleteFileProcessorService.process, + cleanRemoteFiles: this.cleanRemoteFilesProcessorService.process, + } as Record>>; + + for (const [k, v] of Object.entries(jobs)) { + q.process(k, 16, v); + } + } +} diff --git a/packages/backend/src/queue/ObjectStorageQueueService.ts b/packages/backend/src/queue/ObjectStorageQueueService.ts deleted file mode 100644 index b6c2357e0350..000000000000 --- a/packages/backend/src/queue/ObjectStorageQueueService.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { ObjectStorageJobData } from '@/queue/types.js'; -import deleteFile from './delete-file.js'; -import cleanRemoteFiles from './clean-remote-files.js'; -import type Bull from 'bull'; - -const jobs = { - deleteFile, - cleanRemoteFiles, -} as Record | Bull.ProcessPromiseFunction>; - -export default function(q: Bull.Queue) { - for (const [k, v] of Object.entries(jobs)) { - q.process(k, 16, v); - } -} diff --git a/packages/backend/src/queue/index.ts b/packages/backend/src/queue/index.ts index af95e11fbfe5..169fd4cec9b6 100644 --- a/packages/backend/src/queue/index.ts +++ b/packages/backend/src/queue/index.ts @@ -1,20 +1,21 @@ -import config from '@/config/index.js'; - -import processDeliver from './processors/deliver.js'; -import processInbox from './processors/inbox.js'; -import processDb from './processors/db/index.js'; -import processObjectStorage from './processors/object-storage/index.js'; -import processSystemQueue from './processors/system/index.js'; -import processWebhookDeliver from './processors/webhook-deliver.js'; -import { endedPollNotification } from './processors/ended-poll-notification.js'; -import { queueLogger } from './logger.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import { getJobInfo } from './get-job-info.js'; import { QueueService } from './queue.service.js'; import { SystemQueueProcessorsService } from './SystemQueueProcessorsService.js'; +import { ObjectStorageQueueProcessorsService } from './ObjectStorageQueueProcessorsService.js'; +import { DbQueueProcessorsService } from './DbQueueProcessorsService.js'; +import { WebhookDeliverProcessorService } from './processors/WebhookDeliverProcessorService.js'; +import { EndedPollNotificationProcessorService } from './processors/EndedPollNotificationProcessorService.js'; +import { DeliverProcessorService } from './processors/DeliverProcessorService.js'; +import { InboxProcessorService } from './processors/InboxProcessorService.js'; +import { QueueLoggerService } from './QueueLoggerService.js'; import type { INestApplicationContext } from '@nestjs/common'; export default function(app: INestApplicationContext) { + const config = app.get(DI_SYMBOLS.config); + const queueLoggerService = app.get(QueueLoggerService); + const queueLogger = queueLoggerService.logger; const queueService = app.get(QueueService); const systemQueue = queueService.systemQueue; const deliverQueue = queueService.deliverQueue; @@ -24,6 +25,12 @@ export default function(app: INestApplicationContext) { const webhookDeliverQueue = queueService.webhookDeliverQueue; const endedPollNotificationQueue = queueService.endedPollNotificationQueue; const systemQueueProcessorsService = app.get(SystemQueueProcessorsService); + const objectStorageQueueProcessorsService = app.get(ObjectStorageQueueProcessorsService); + const dbQueueProcessorsService = app.get(DbQueueProcessorsService); + const webhookDeliverProcessorService = app.get(WebhookDeliverProcessorService); + const endedPollNotificationProcessorService = app.get(EndedPollNotificationProcessorService); + const deliverProcessorService = app.get(DeliverProcessorService); + const inboxProcessorService = app.get(InboxProcessorService); function renderError(e: Error): any { return { @@ -88,12 +95,12 @@ export default function(app: INestApplicationContext) { .on('error', (job: any, err: Error) => webhookLogger.error(`error ${err}`, { job, e: renderError(err) })) .on('stalled', (job) => webhookLogger.warn(`stalled ${getJobInfo(job)} to=${job.data.to}`)); - deliverQueue.process(config.deliverJobConcurrency || 128, processDeliver); - inboxQueue.process(config.inboxJobConcurrency || 16, processInbox); - endedPollNotificationQueue.process(endedPollNotification); - webhookDeliverQueue.process(64, processWebhookDeliver); - processDb(dbQueue); - processObjectStorage(objectStorageQueue); + deliverQueue.process(config.deliverJobConcurrency || 128, deliverProcessorService.process); + inboxQueue.process(config.inboxJobConcurrency || 16, inboxProcessorService.process); + endedPollNotificationQueue.process(endedPollNotificationProcessorService.process); + webhookDeliverQueue.process(64, webhookDeliverProcessorService.process); + dbQueueProcessorsService.start(dbQueue); + objectStorageQueueProcessorsService.start(objectStorageQueue); systemQueue.add('tickCharts', { }, { diff --git a/packages/backend/src/queue/queue.service.ts b/packages/backend/src/queue/queue.service.ts index 9ffe7c5febfc..46afff09eea0 100644 --- a/packages/backend/src/queue/queue.service.ts +++ b/packages/backend/src/queue/queue.service.ts @@ -103,7 +103,7 @@ export class QueueService { } public createExportMuteJob(user: ThinUser) { - return this.dbQueue.add('exportMute', { + return this.dbQueue.add('exportMuting', { user: user, }, { removeOnComplete: true, From fd087b5e0b68891efb8d2f323e7da36e450dc6ce Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 23:10:38 +0900 Subject: [PATCH 068/180] wip --- .../services/FetchInstanceMetadataService.ts | 31 +++---- .../activitypub/models/ApImageService.ts | 90 +++++++++++++++++++ .../remote/activitypub/models/image.ts | 68 -------------- 3 files changed, 106 insertions(+), 83 deletions(-) create mode 100644 packages/backend/src/services/remote/activitypub/models/ApImageService.ts delete mode 100644 packages/backend/src/services/remote/activitypub/models/image.ts diff --git a/packages/backend/src/services/FetchInstanceMetadataService.ts b/packages/backend/src/services/FetchInstanceMetadataService.ts index 2ade526a3aa0..8ffab0150241 100644 --- a/packages/backend/src/services/FetchInstanceMetadataService.ts +++ b/packages/backend/src/services/FetchInstanceMetadataService.ts @@ -3,11 +3,11 @@ import { Inject, Injectable } from '@nestjs/common'; import { JSDOM } from 'jsdom'; import fetch from 'node-fetch'; import tinycolor from 'tinycolor2'; -import { getJson, getHtml, getAgentByUrl } from '@/misc/fetch.js'; import type { Instance } from '@/models/entities/instance.js'; import type { Instances } from '@/models/index.js'; import type { AppLockService } from '@/services/AppLockService.js'; -import Logger from './logger.js'; +import Logger from '@/logger.js'; +import type { HttpRequestService } from './HttpRequestService'; import type { DOMWindow } from 'jsdom'; const logger = new Logger('metadata', 'cyan'); @@ -37,6 +37,7 @@ export class FetchInstanceMetadataService { private instancesRepository: typeof Instances, private appLockService: AppLockService, + private httpRequestService: HttpRequestService, ) { } @@ -103,12 +104,12 @@ export class FetchInstanceMetadataService { logger.info(`Fetching nodeinfo of ${instance.host} ...`); try { - const wellknown = await getJson('https://' + instance.host + '/.well-known/nodeinfo') - .catch(e => { - if (e.statusCode === 404) { + const wellknown = await this.httpRequestService.getJson('https://' + instance.host + '/.well-known/nodeinfo') + .catch(err => { + if (err.statusCode === 404) { throw 'No nodeinfo provided'; } else { - throw e.statusCode || e.message; + throw err.statusCode ?? err.message; } }) as Record; @@ -127,18 +128,18 @@ export class FetchInstanceMetadataService { throw 'No nodeinfo link provided'; } - const info = await getJson(link.href) - .catch(e => { - throw e.statusCode || e.message; + const info = await this.httpRequestService.getJson(link.href) + .catch(err => { + throw err.statusCode ?? err.message; }); logger.succ(`Successfuly fetched nodeinfo of ${instance.host}`); return info as NodeInfo; - } catch (e) { - logger.error(`Failed to fetch nodeinfo of ${instance.host}: ${e}`); + } catch (err) { + logger.error(`Failed to fetch nodeinfo of ${instance.host}: ${err}`); - throw e; + throw err; } } @@ -147,7 +148,7 @@ export class FetchInstanceMetadataService { const url = 'https://' + instance.host; - const html = await getHtml(url); + const html = await this.httpRequestService.getHtml(url); const { window } = new JSDOM(html); const doc = window.document; @@ -160,7 +161,7 @@ export class FetchInstanceMetadataService { const manifestUrl = url + '/manifest.json'; - const manifest = await getJson(manifestUrl) as Record; + const manifest = await this.httpRequestService.getJson(manifestUrl) as Record; return manifest; } @@ -182,7 +183,7 @@ export class FetchInstanceMetadataService { const favicon = await fetch(faviconUrl, { // TODO //timeout: 10000, - agent: getAgentByUrl, + agent: url => this.httpRequestService.getAgentByUrl(url), }); if (favicon.ok) { diff --git a/packages/backend/src/services/remote/activitypub/models/ApImageService.ts b/packages/backend/src/services/remote/activitypub/models/ApImageService.ts new file mode 100644 index 000000000000..4e2e82c197ef --- /dev/null +++ b/packages/backend/src/services/remote/activitypub/models/ApImageService.ts @@ -0,0 +1,90 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { DriveFiles } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type { CacheableRemoteUser } from '@/models/entities/user.js'; +import type { DriveFile } from '@/models/entities/drive-file.js'; +import type { MetaService } from '@/services/MetaService.js'; +import { truncate } from '@/misc/truncate.js'; +import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js'; +import type { DriveService } from '@/services/DriveService.js'; +import type Logger from '@/logger.js'; +import type { ApResolverService } from '../ApResolverService.js'; +import type { ApLoggerService } from '../ApLoggerService.js'; + +@Injectable() +export class UserSuspendService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private metaService: MetaService, + private apResolverService: ApResolverService, + private driveService: DriveService, + private apLoggerService: ApLoggerService, + ) { + this.#logger = this.apLoggerService.logger; + } + + /** + * Imageを作成します。 + */ + public async createImage(actor: CacheableRemoteUser, value: any): Promise { + // 投稿者が凍結されていたらスキップ + if (actor.isSuspended) { + throw new Error('actor has been suspended'); + } + + const image = await this.apResolverService.createResolver().resolve(value) as any; + + if (image.url == null) { + throw new Error('invalid image: url not privided'); + } + + this.#logger.info(`Creating the Image: ${image.url}`); + + const instance = await this.metaService.fetch(); + + let file = await this.driveService.uploadFromUrl({ + url: image.url, + user: actor, + uri: image.url, + sensitive: image.sensitive, + isLink: !instance.cacheRemoteFiles, + comment: truncate(image.name, DB_MAX_IMAGE_COMMENT_LENGTH), + }); + + if (file.isLink) { + // URLが異なっている場合、同じ画像が以前に異なるURLで登録されていたということなので、 + // URLを更新する + if (file.url !== image.url) { + await this.driveFilesRepository.update({ id: file.id }, { + url: image.url, + uri: image.url, + }); + + file = await this.driveFilesRepository.findOneByOrFail({ id: file.id }); + } + } + + return file; + } + + /** + * Imageを解決します。 + * + * Misskeyに対象のImageが登録されていればそれを返し、そうでなければ + * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 + */ + public async resolveImage(actor: CacheableRemoteUser, value: any): Promise { + // TODO + + // リモートサーバーからフェッチしてきて登録 + return await this.createImage(actor, value); + } +} diff --git a/packages/backend/src/services/remote/activitypub/models/image.ts b/packages/backend/src/services/remote/activitypub/models/image.ts deleted file mode 100644 index 102b7b134632..000000000000 --- a/packages/backend/src/services/remote/activitypub/models/image.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { uploadFromUrl } from '@/services/drive/upload-from-url.js'; -import { CacheableRemoteUser, IRemoteUser } from '@/models/entities/user.js'; -import Resolver from '../resolver.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import { apLogger } from '../logger.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; -import { DriveFiles, Users } from '@/models/index.js'; -import { truncate } from '@/misc/truncate.js'; -import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js'; - -const logger = apLogger; - -/** - * Imageを作成します。 - */ -export async function createImage(actor: CacheableRemoteUser, value: any): Promise { - // 投稿者が凍結されていたらスキップ - if (actor.isSuspended) { - throw new Error('actor has been suspended'); - } - - const image = await new Resolver().resolve(value) as any; - - if (image.url == null) { - throw new Error('invalid image: url not privided'); - } - - logger.info(`Creating the Image: ${image.url}`); - - const instance = await fetchMeta(); - - let file = await uploadFromUrl({ - url: image.url, - user: actor, - uri: image.url, - sensitive: image.sensitive, - isLink: !instance.cacheRemoteFiles, - comment: truncate(image.name, DB_MAX_IMAGE_COMMENT_LENGTH) - }); - - if (file.isLink) { - // URLが異なっている場合、同じ画像が以前に異なるURLで登録されていたということなので、 - // URLを更新する - if (file.url !== image.url) { - await DriveFiles.update({ id: file.id }, { - url: image.url, - uri: image.url, - }); - - file = await DriveFiles.findOneByOrFail({ id: file.id }); - } - } - - return file; -} - -/** - * Imageを解決します。 - * - * Misskeyに対象のImageが登録されていればそれを返し、そうでなければ - * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 - */ -export async function resolveImage(actor: CacheableRemoteUser, value: any): Promise { - // TODO - - // リモートサーバーからフェッチしてきて登録 - return await createImage(actor, value); -} From add33c630e702847ce5efb67066b877bd4863197 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 23:38:06 +0900 Subject: [PATCH 069/180] wip --- .../remote/activitypub/ApMfmService.ts | 22 + .../remote/activitypub/misc/html-to-mfm.ts | 9 - .../activitypub/models/ApImageService.ts | 2 +- .../activitypub/models/ApMentionService.ts | 39 ++ .../activitypub/models/ApNoteService.ts | 52 +- .../activitypub/models/ApPersonService.ts | 527 ++++++++++++++++++ .../remote/activitypub/models/mention.ts | 24 - .../remote/activitypub/models/person.ts | 506 ----------------- 8 files changed, 622 insertions(+), 559 deletions(-) create mode 100644 packages/backend/src/services/remote/activitypub/ApMfmService.ts delete mode 100644 packages/backend/src/services/remote/activitypub/misc/html-to-mfm.ts create mode 100644 packages/backend/src/services/remote/activitypub/models/ApMentionService.ts create mode 100644 packages/backend/src/services/remote/activitypub/models/ApPersonService.ts delete mode 100644 packages/backend/src/services/remote/activitypub/models/mention.ts delete mode 100644 packages/backend/src/services/remote/activitypub/models/person.ts diff --git a/packages/backend/src/services/remote/activitypub/ApMfmService.ts b/packages/backend/src/services/remote/activitypub/ApMfmService.ts new file mode 100644 index 000000000000..219a6dbad422 --- /dev/null +++ b/packages/backend/src/services/remote/activitypub/ApMfmService.ts @@ -0,0 +1,22 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; +import type { MfmService } from '@/services/MfmService'; +import type { IObject } from './type'; + +@Injectable() +export class ApMfmService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + private mfmService: MfmService, + ) { + } + + public htmlToMfm(html: string, tag?: IObject | IObject[]) { + const hashtagNames = extractApHashtagObjects(tag).map(x => x.name).filter((x): x is string => x != null); + + return this.mfmService.fromHtml(html, hashtagNames); + } +} diff --git a/packages/backend/src/services/remote/activitypub/misc/html-to-mfm.ts b/packages/backend/src/services/remote/activitypub/misc/html-to-mfm.ts deleted file mode 100644 index 829c5d781b45..000000000000 --- a/packages/backend/src/services/remote/activitypub/misc/html-to-mfm.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { extractApHashtagObjects } from '../models/tag.js'; -import { fromHtml } from '../../../../mfm/from-html.js'; -import type { IObject } from '../type.js'; - -export function htmlToMfm(html: string, tag?: IObject | IObject[]) { - const hashtagNames = extractApHashtagObjects(tag).map(x => x.name).filter((x): x is string => x != null); - - return fromHtml(html, hashtagNames); -} diff --git a/packages/backend/src/services/remote/activitypub/models/ApImageService.ts b/packages/backend/src/services/remote/activitypub/models/ApImageService.ts index 4e2e82c197ef..adc674aa8a03 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApImageService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApImageService.ts @@ -13,7 +13,7 @@ import type { ApResolverService } from '../ApResolverService.js'; import type { ApLoggerService } from '../ApLoggerService.js'; @Injectable() -export class UserSuspendService { +export class ApImageService { #logger: Logger; constructor( diff --git a/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts b/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts new file mode 100644 index 000000000000..7cea3868bdfc --- /dev/null +++ b/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts @@ -0,0 +1,39 @@ +import { Inject, Injectable } from '@nestjs/common'; +import promiseLimit from 'promise-limit'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Users } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import { toArray, unique } from '@/prelude/array.js'; +import type { CacheableUser } from '@/models/entities/user.js'; +import { isMention } from '../type.js'; +import type { ApResolverService } from '../ApResolverService.js'; +import type { IObject , IApMention } from '../type.js'; + +@Injectable() +export class ApMentionService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + private apResolverService: ApResolverService, + ) { + } + + public async extractApMentions(tags: IObject | IObject[] | null | undefined) { + const hrefs = unique(this.extractApMentionObjects(tags).map(x => x.href as string)); + + const resolver = this.apResolverService.createResolver(); + + const limit = promiseLimit(2); + const mentionedUsers = (await Promise.all( + hrefs.map(x => limit(() => resolvePerson(x, resolver).catch(() => null))), + )).filter((x): x is CacheableUser => x != null); + + return mentionedUsers; + } + + public extractApMentionObjects(tags: IObject | IObject[] | null | undefined): IApMention[] { + if (tags == null) return []; + return toArray(tags).filter(isMention); + } +} diff --git a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts index 848f918b89d4..08554781588e 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Users } from '@/models/index.js'; +import type { Emojis, Users } from '@/models/index.js'; import type { Config } from '@/config/types.js'; import type { CacheableRemoteUser } from '@/models/entities/user.js'; import type { MfmService } from '@/services/MfmService.js'; @@ -14,13 +14,20 @@ import type { MetaService } from '@/services/MetaService.js'; import type { AppLockService } from '@/services/AppLockService.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import type { NoteCreateService } from '@/services/NoteCreateService.js'; +import type Logger from '@/logger.js'; +import type { IdService } from '@/services/IdService.js'; import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js'; +import type { ApPersonService } from './ApPersonService.js'; +import type { ApLoggerService } from '../ApLoggerService.js'; +import type { ApMfmService } from '../ApMfmService.js'; import type { ApDbResolverService } from '../ApDbResolverService.js'; import type { ApResolverService, Resolver } from '../ApResolverService.js'; import type { IObject, IPost } from '../type.js'; @Injectable() export class ApNoteService { + #logger: Logger; + constructor( @Inject(DI_SYMBOLS.config) private config: Config, @@ -28,13 +35,20 @@ export class ApNoteService { @Inject('usersRepository') private usersRepository: typeof Users, - private mfmService: MfmService, + @Inject('emojisRepository') + private emojisRepository: typeof Emojis, + + private idService: IdService, + private apMfmService: ApMfmService, private apResolverService: ApResolverService, + private apPersonService: ApPersonService, private metaService: MetaService, private appLockService: AppLockService, private noteCreateService: NoteCreateService, private apDbResolverService: ApDbResolverService, + private apLoggerService: ApLoggerService, ) { + this.#logger = this.apLoggerService.logger; } public validateNote(object: any, uri: string) { @@ -79,7 +93,7 @@ export class ApNoteService { const entryUri = getApId(value); const err = this.validateNote(object, entryUri); if (err) { - logger.error(`${err.message}`, { + this.#logger.error(`${err.message}`, { resolver: { history: resolver.getHistory(), }, @@ -91,12 +105,12 @@ export class ApNoteService { const note: IPost = object; - logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`); + this.#logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`); - logger.info(`Creating the Note: ${note.id}`); + this.#logger.info(`Creating the Note: ${note.id}`); // 投稿者をフェッチ - const actor = await resolvePerson(getOneApId(note.attributedTo), resolver) as CacheableRemoteUser; + const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as CacheableRemoteUser; // 投稿者が凍結されていたらスキップ if (actor.isSuspended) { @@ -137,7 +151,7 @@ export class ApNoteService { const reply: Note | null = note.inReplyTo ? await this.resolveNote(note.inReplyTo, resolver).then(x => { if (x == null) { - logger.warn('Specified inReplyTo, but nout found'); + this.#logger.warn('Specified inReplyTo, but nout found'); throw new Error('inReplyTo not found'); } else { return x; @@ -154,7 +168,7 @@ export class ApNoteService { } } - logger.warn(`Error in inReplyTo ${note.inReplyTo} - ${err.statusCode ?? err}`); + this.#logger.warn(`Error in inReplyTo ${note.inReplyTo} - ${err.statusCode ?? err}`); throw err; }) : null; @@ -171,7 +185,7 @@ export class ApNoteService { }> => { if (typeof uri !== 'string' || !uri.match(/^https?:/)) return { status: 'permerror' }; try { - const res = await resolveNote(uri); + const res = await this.resolveNote(uri); if (res) { return { status: 'ok', @@ -209,7 +223,7 @@ export class ApNoteService { } else if (typeof note._misskey_content !== 'undefined') { text = note._misskey_content; } else if (typeof note.content === 'string') { - text = this.mfmService.fromHtml(note.content, note.tag); + text = this.apMfmService.htmlToMfm(note.content, note.tag); } // vote @@ -218,9 +232,9 @@ export class ApNoteService { const tryCreateVote = async (name: string, index: number): Promise => { if (poll.expiresAt && Date.now() > new Date(poll.expiresAt).getTime()) { - logger.warn(`vote to expired poll from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`); + this.#logger.warn(`vote to expired poll from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`); } else if (index >= 0) { - logger.info(`vote from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`); + this.#logger.info(`vote from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`); await vote(actor, reply, index); // リモートフォロワーにUpdate配信 @@ -235,7 +249,7 @@ export class ApNoteService { } const emojis = await this.extractEmojis(note.tag || [], actor.host).catch(e => { - logger.info(`extractEmojis: ${e}`); + this.#logger.info(`extractEmojis: ${e}`); return [] as Emoji[]; }); @@ -319,7 +333,7 @@ export class ApNoteService { const name = tag.name!.replace(/^:/, '').replace(/:$/, ''); tag.icon = toSingle(tag.icon); - const exists = await Emojis.findOneBy({ + const exists = await this.emojisRepository.findOneBy({ host, name, }); @@ -330,7 +344,7 @@ export class ApNoteService { || (tag.updated != null && exists.updatedAt != null && new Date(tag.updated) > exists.updatedAt) || (tag.icon!.url !== exists.originalUrl) ) { - await Emojis.update({ + await this.emojisRepository.update({ host, name, }, { @@ -340,7 +354,7 @@ export class ApNoteService { updatedAt: new Date(), }); - return await Emojis.findOneBy({ + return await this.emojisRepository.findOneBy({ host, name, }) as Emoji; @@ -349,9 +363,9 @@ export class ApNoteService { return exists; } - logger.info(`register emoji host=${host}, name=${name}`); + this.#logger.info(`register emoji host=${host}, name=${name}`); - return await Emojis.insert({ + return await this.emojisRepository.insert({ id: this.idService.genId(), host, name, @@ -360,7 +374,7 @@ export class ApNoteService { publicUrl: tag.icon!.url, updatedAt: new Date(), aliases: [], - } as Partial).then(x => Emojis.findOneByOrFail(x.identifiers[0])); + } as Partial).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0])); })); } } diff --git a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts new file mode 100644 index 000000000000..ff5319e8856b --- /dev/null +++ b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts @@ -0,0 +1,527 @@ +import { Inject, Injectable } from '@nestjs/common'; +import promiseLimit from 'promise-limit'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { Users } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type { CacheableUser, IRemoteUser } from '@/models/entities/user.js'; +import { User } from '@/models/entities/user.js'; +import { toPuny } from '@/misc/convert-host.js'; +import { truncate } from '@/misc/truncate.js'; +import type { UserCacheService } from '@/services/UserCacheService.js'; +import { StatusError } from '@/services/HttpRequestService.js'; +import { normalizeForSearch } from '@/misc/normalize-for-search.js'; +import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; +import type Logger from '@/logger.js'; +import type { Note } from '@/models/entities/note.js'; +import type { IdService } from '@/services/IdService.js'; +import type { MfmService } from '@/services/MfmService.js'; +import type { Emoji } from '@/models/entities/emoji.js'; +import { toArray } from '@/prelude/array.js'; +import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import type { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; +import type { FetchInstanceMetadataService } from '@/services/FetchInstanceMetadataService.js'; +import { getApId, getApType, getOneApHrefNullable, isActor, isCollection, isCollectionOrOrderedCollection, isPropertyValue } from '../type.js'; +import type { ApMfmService } from '../ApMfmService.js'; +import type { Resolver , ApResolverService } from '../ApResolverService.js'; +import type { ApImageService } from './ApImageService.js'; +import type { DataSource } from 'typeorm'; +import type { IActor, IObject } from '../type.js'; +import type { ApLoggerService } from '../ApLoggerService.js'; + +const nameLength = 128; +const summaryLength = 2048; + +const services: { + [x: string]: (id: string, username: string) => any +} = { + 'misskey:authentication:twitter': (userId, screenName) => ({ userId, screenName }), + 'misskey:authentication:github': (id, login) => ({ id, login }), + 'misskey:authentication:discord': (id, name) => $discord(id, name), +}; + +const $discord = (id: string, name: string) => { + if (typeof name !== 'string') { + name = 'unknown#0000'; + } + const [username, discriminator] = name.split('#'); + return { id, username, discriminator }; +}; + +function addService(target: { [x: string]: any }, source: IApPropertyValue) { + const service = services[source.name]; + + if (typeof source.value !== 'string') { + source.value = 'unknown'; + } + + const [id, username] = source.value.split('@'); + + if (service) { + target[source.name.split(':')[2]] = service(id, username); + } +} + +/** + * Validate and convert to actor object + * @param x Fetched object + * @param uri Fetch target URI + */ +function validateActor(x: IObject, uri: string): IActor { + const expectHost = toPuny(new URL(uri).hostname); + + if (x == null) { + throw new Error('invalid Actor: object is null'); + } + + if (!isActor(x)) { + throw new Error(`invalid Actor type '${x.type}'`); + } + + if (!(typeof x.id === 'string' && x.id.length > 0)) { + throw new Error('invalid Actor: wrong id'); + } + + if (!(typeof x.inbox === 'string' && x.inbox.length > 0)) { + throw new Error('invalid Actor: wrong inbox'); + } + + if (!(typeof x.preferredUsername === 'string' && x.preferredUsername.length > 0 && x.preferredUsername.length <= 128 && /^\w([\w-.]*\w)?$/.test(x.preferredUsername))) { + throw new Error('invalid Actor: wrong username'); + } + + // These fields are only informational, and some AP software allows these + // fields to be very long. If they are too long, we cut them off. This way + // we can at least see these users and their activities. + if (x.name) { + if (!(typeof x.name === 'string' && x.name.length > 0)) { + throw new Error('invalid Actor: wrong name'); + } + x.name = truncate(x.name, nameLength); + } + if (x.summary) { + if (!(typeof x.summary === 'string' && x.summary.length > 0)) { + throw new Error('invalid Actor: wrong summary'); + } + x.summary = truncate(x.summary, summaryLength); + } + + const idHost = toPuny(new URL(x.id!).hostname); + if (idHost !== expectHost) { + throw new Error('invalid Actor: id has different host'); + } + + if (x.publicKey) { + if (typeof x.publicKey.id !== 'string') { + throw new Error('invalid Actor: publicKey.id is not a string'); + } + + const publicKeyIdHost = toPuny(new URL(x.publicKey.id).hostname); + if (publicKeyIdHost !== expectHost) { + throw new Error('invalid Actor: publicKey.id has different host'); + } + } + + return x; +} + +@Injectable() +export class ApPersonService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject(DI_SYMBOLS.db) + private db: DataSource, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + private idService: IdService, + private globalEventService: GlobalEventService, + private federatedInstanceService: FederatedInstanceService, + private fetchInstanceMetadataService: FetchInstanceMetadataService, + private userCacheService: UserCacheService, + private apResolverService: ApResolverService, + private apImageService: ApImageService, + private apMfmService: ApMfmService, + private mfmService: MfmService, + private apLoggerService: ApLoggerService, + ) { + this.#logger = this.apLoggerService.logger; + } + + /** + * Personをフェッチします。 + * + * Misskeyに対象のPersonが登録されていればそれを返します。 + */ + public async fetchPerson(uri: string, resolver?: Resolver): Promise { + if (typeof uri !== 'string') throw new Error('uri is not string'); + + const cached = this.userCacheService.uriPersonCache.get(uri); + if (cached) return cached; + + // URIがこのサーバーを指しているならデータベースからフェッチ + if (uri.startsWith(this.config.url + '/')) { + const id = uri.split('/').pop(); + const u = await Users.findOneBy({ id }); + if (u) this.userCacheService.uriPersonCache.set(uri, u); + return u; + } + + //#region このサーバーに既に登録されていたらそれを返す + const exist = await Users.findOneBy({ uri }); + + if (exist) { + this.userCacheService.uriPersonCache.set(uri, exist); + return exist; + } + //#endregion + + return null; + } + + /** + * Personを作成します。 + */ + public async createPerson(uri: string, resolver?: Resolver): Promise { + if (typeof uri !== 'string') throw new Error('uri is not string'); + + if (uri.startsWith(this.config.url)) { + throw new StatusError('cannot resolve local user', 400, 'cannot resolve local user'); + } + + if (resolver == null) resolver = this.apResolverService.createResolver(); + + const object = await resolver.resolve(uri) as any; + + const person = validateActor(object, uri); + + this.#logger.info(`Creating the Person: ${person.id}`); + + const host = toPuny(new URL(object.id).hostname); + + const { fields } = this.analyzeAttachments(person.attachment || []); + + const tags = extractApHashtags(person.tag).map(tag => normalizeForSearch(tag)).splice(0, 32); + + const isBot = getApType(object) === 'Service'; + + const bday = person['vcard:bday']?.match(/^\d{4}-\d{2}-\d{2}/); + + // Create user + let user: IRemoteUser; + try { + // Start transaction + await this.db.transaction(async transactionalEntityManager => { + user = await transactionalEntityManager.save(new User({ + id: this.idService.genId(), + avatarId: null, + bannerId: null, + createdAt: new Date(), + lastFetchedAt: new Date(), + name: truncate(person.name, nameLength), + isLocked: !!person.manuallyApprovesFollowers, + isExplorable: !!person.discoverable, + username: person.preferredUsername, + usernameLower: person.preferredUsername!.toLowerCase(), + host, + inbox: person.inbox, + sharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined), + followersUri: person.followers ? getApId(person.followers) : undefined, + featured: person.featured ? getApId(person.featured) : undefined, + uri: person.id, + tags, + isBot, + isCat: (person as any).isCat === true, + showTimelineReplies: false, + })) as IRemoteUser; + + await transactionalEntityManager.save(new UserProfile({ + userId: user.id, + description: person.summary ? this.apMfmService.htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null, + url: getOneApHrefNullable(person.url), + fields, + birthday: bday ? bday[0] : null, + location: person['vcard:Address'] || null, + userHost: host, + })); + + if (person.publicKey) { + await transactionalEntityManager.save(new UserPublickey({ + userId: user.id, + keyId: person.publicKey.id, + keyPem: person.publicKey.publicKeyPem, + })); + } + }); + } catch (e) { + // duplicate key error + if (isDuplicateKeyValueError(e)) { + // /users/@a => /users/:id のように入力がaliasなときにエラーになることがあるのを対応 + const u = await Users.findOneBy({ + uri: person.id, + }); + + if (u) { + user = u as IRemoteUser; + } else { + throw new Error('already registered'); + } + } else { + this.#logger.error(e instanceof Error ? e : new Error(e as string)); + throw e; + } + } + + // Register host + this.federatedInstanceService.registerOrFetchInstanceDoc(host).then(i => { + Instances.increment({ id: i.id }, 'usersCount', 1); + instanceChart.newUser(i.host); + this.fetchInstanceMetadataService.fetchInstanceMetadata(i); + }); + + usersChart.update(user!, true); + + // ハッシュタグ更新 + updateUsertags(user!, tags); + + //#region アバターとヘッダー画像をフェッチ + const [avatar, banner] = await Promise.all([ + person.icon, + person.image, + ].map(img => + img == null + ? Promise.resolve(null) + : this.apImageService.resolveImage(user!, img).catch(() => null), + )); + + const avatarId = avatar ? avatar.id : null; + const bannerId = banner ? banner.id : null; + + await Users.update(user!.id, { + avatarId, + bannerId, + }); + + user!.avatarId = avatarId; + user!.bannerId = bannerId; + //#endregion + + //#region カスタム絵文字取得 + const emojis = await extractEmojis(person.tag || [], host).catch(e => { + this.#logger.info(`extractEmojis: ${e}`); + return [] as Emoji[]; + }); + + const emojiNames = emojis.map(emoji => emoji.name); + + await Users.update(user!.id, { + emojis: emojiNames, + }); + //#endregion + + await this.updateFeatured(user!.id).catch(err => logger.error(err)); + + return user!; + } + + /** + * Personの情報を更新します。 + * Misskeyに対象のPersonが登録されていなければ無視します。 + * @param uri URI of Person + * @param resolver Resolver + * @param hint Hint of Person object (この値が正当なPersonの場合、Remote resolveをせずに更新に利用します) + */ + public async updatePerson(uri: string, resolver?: Resolver | null, hint?: IObject): Promise { + if (typeof uri !== 'string') throw new Error('uri is not string'); + + // URIがこのサーバーを指しているならスキップ + if (uri.startsWith(this.config.url + '/')) { + return; + } + + //#region このサーバーに既に登録されているか + const exist = await Users.findOneBy({ uri }) as IRemoteUser; + + if (exist == null) { + return; + } + //#endregion + + if (resolver == null) resolver = this.apResolverService.createResolver(); + + const object = hint || await resolver.resolve(uri); + + const person = validateActor(object, uri); + + this.#logger.info(`Updating the Person: ${person.id}`); + + // アバターとヘッダー画像をフェッチ + const [avatar, banner] = await Promise.all([ + person.icon, + person.image, + ].map(img => + img == null + ? Promise.resolve(null) + : this.apImageService.resolveImage(exist, img).catch(() => null), + )); + + // カスタム絵文字取得 + const emojis = await extractEmojis(person.tag || [], exist.host).catch(e => { + this.#logger.info(`extractEmojis: ${e}`); + return [] as Emoji[]; + }); + + const emojiNames = emojis.map(emoji => emoji.name); + + const { fields } = analyzeAttachments(person.attachment || []); + + const tags = extractApHashtags(person.tag).map(tag => normalizeForSearch(tag)).splice(0, 32); + + const bday = person['vcard:bday']?.match(/^\d{4}-\d{2}-\d{2}/); + + const updates = { + lastFetchedAt: new Date(), + inbox: person.inbox, + sharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined), + followersUri: person.followers ? getApId(person.followers) : undefined, + featured: person.featured, + emojis: emojiNames, + name: truncate(person.name, nameLength), + tags, + isBot: getApType(object) === 'Service', + isCat: (person as any).isCat === true, + isLocked: !!person.manuallyApprovesFollowers, + isExplorable: !!person.discoverable, + } as Partial; + + if (avatar) { + updates.avatarId = avatar.id; + } + + if (banner) { + updates.bannerId = banner.id; + } + + // Update user + await Users.update(exist.id, updates); + + if (person.publicKey) { + await UserPublickeys.update({ userId: exist.id }, { + keyId: person.publicKey.id, + keyPem: person.publicKey.publicKeyPem, + }); + } + + await UserProfiles.update({ userId: exist.id }, { + url: getOneApHrefNullable(person.url), + fields, + description: person.summary ? this.apMfmService.htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null, + birthday: bday ? bday[0] : null, + location: person['vcard:Address'] || null, + }); + + this.globalEventService.publishInternalEvent('remoteUserUpdated', { id: exist.id }); + + // ハッシュタグ更新 + updateUsertags(exist, tags); + + // 該当ユーザーが既にフォロワーになっていた場合はFollowingもアップデートする + await Followings.update({ + followerId: exist.id, + }, { + followerSharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined), + }); + + await this.updateFeatured(exist.id).catch(err => logger.error(err)); + } + + /** + * Personを解決します。 + * + * Misskeyに対象のPersonが登録されていればそれを返し、そうでなければ + * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 + */ + public async resolvePerson(uri: string, resolver?: Resolver): Promise { + if (typeof uri !== 'string') throw new Error('uri is not string'); + + //#region このサーバーに既に登録されていたらそれを返す + const exist = await this.fetchPerson(uri); + + if (exist) { + return exist; + } + //#endregion + + // リモートサーバーからフェッチしてきて登録 + if (resolver == null) resolver = this.apResolverService.createResolver(); + return await this.createPerson(uri, resolver); + } + + public analyzeAttachments(attachments: IObject | IObject[] | undefined) { + const fields: { + name: string, + value: string + }[] = []; + const services: { [x: string]: any } = {}; + + if (Array.isArray(attachments)) { + for (const attachment of attachments.filter(isPropertyValue)) { + if (isPropertyValue(attachment.identifier)) { + addService(services, attachment.identifier); + } else { + fields.push({ + name: attachment.name, + value: this.mfmService.fromHtml(attachment.value), + }); + } + } + } + + return { fields, services }; + } + + public async updateFeatured(userId: User['id']) { + const user = await Users.findOneByOrFail({ id: userId }); + if (!Users.isRemoteUser(user)) return; + if (!user.featured) return; + + this.#logger.info(`Updating the featured: ${user.uri}`); + + const resolver = this.apResolverService.createResolver(); + + // Resolve to (Ordered)Collection Object + const collection = await resolver.resolveCollection(user.featured); + if (!isCollectionOrOrderedCollection(collection)) throw new Error('Object is not Collection or OrderedCollection'); + + // Resolve to Object(may be Note) arrays + const unresolvedItems = isCollection(collection) ? collection.items : collection.orderedItems; + const items = await Promise.all(toArray(unresolvedItems).map(x => resolver.resolve(x))); + + // Resolve and regist Notes + const limit = promiseLimit(2); + const featuredNotes = await Promise.all(items + .filter(item => getApType(item) === 'Note') // TODO: Noteでなくてもいいかも + .slice(0, 5) + .map(item => limit(() => resolveNote(item, resolver)))); + + await this.db.transaction(async transactionalEntityManager => { + await transactionalEntityManager.delete(UserNotePining, { userId: user.id }); + + // とりあえずidを別の時間で生成して順番を維持 + let td = 0; + for (const note of featuredNotes.filter(note => note != null)) { + td -= 1000; + transactionalEntityManager.insert(UserNotePining, { + id: this.idService.genId(new Date(Date.now() + td)), + createdAt: new Date(), + userId: user.id, + noteId: note!.id, + }); + } + }); + } +} diff --git a/packages/backend/src/services/remote/activitypub/models/mention.ts b/packages/backend/src/services/remote/activitypub/models/mention.ts deleted file mode 100644 index 13f77424ec40..000000000000 --- a/packages/backend/src/services/remote/activitypub/models/mention.ts +++ /dev/null @@ -1,24 +0,0 @@ -import promiseLimit from 'promise-limit'; -import { toArray, unique } from '@/prelude/array.js'; -import { CacheableUser, User } from '@/models/entities/user.js'; -import { IObject, isMention, IApMention } from '../type.js'; -import Resolver from '../resolver.js'; -import { resolvePerson } from './person.js'; - -export async function extractApMentions(tags: IObject | IObject[] | null | undefined) { - const hrefs = unique(extractApMentionObjects(tags).map(x => x.href as string)); - - const resolver = new Resolver(); - - const limit = promiseLimit(2); - const mentionedUsers = (await Promise.all( - hrefs.map(x => limit(() => resolvePerson(x, resolver).catch(() => null))), - )).filter((x): x is CacheableUser => x != null); - - return mentionedUsers; -} - -export function extractApMentionObjects(tags: IObject | IObject[] | null | undefined): IApMention[] { - if (tags == null) return []; - return toArray(tags).filter(isMention); -} diff --git a/packages/backend/src/services/remote/activitypub/models/person.ts b/packages/backend/src/services/remote/activitypub/models/person.ts deleted file mode 100644 index 66155cce43a4..000000000000 --- a/packages/backend/src/services/remote/activitypub/models/person.ts +++ /dev/null @@ -1,506 +0,0 @@ -import { URL } from 'node:url'; -import promiseLimit from 'promise-limit'; - -import config from '@/config/index.js'; -import { registerOrFetchInstanceDoc } from '@/services/register-or-fetch-instance-doc.js'; -import type { Note } from '@/models/entities/note.js'; -import { updateUsertags } from '@/services/update-hashtag.js'; -import { Users, Instances, DriveFiles, Followings, UserProfiles, UserPublickeys } from '@/models/index.js'; -import type { IRemoteUser, CacheableUser } from '@/models/entities/user.js'; -import { User } from '@/models/entities/user.js'; -import type { Emoji } from '@/models/entities/emoji.js'; -import { UserNotePining } from '@/models/entities/user-note-pining.js'; -import type { IdService } from '@/services/IdService.js'; -import { instanceChart, usersChart } from '@/services/chart/index.js'; -import { UserPublickey } from '@/models/entities/user-publickey.js'; -import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; -import { toPuny } from '@/misc/convert-host.js'; -import { UserProfile } from '@/models/entities/user-profile.js'; -import { toArray } from '@/prelude/array.js'; -import { fetchInstanceMetadata } from '@/services/fetch-instance-metadata.js'; -import { normalizeForSearch } from '@/misc/normalize-for-search.js'; -import { truncate } from '@/misc/truncate.js'; -import { StatusError } from '@/misc/fetch.js'; -import { uriPersonCache } from '@/services/user-cache.js'; -import { publishInternalEvent } from '@/services/stream.js'; -import { db } from '@/db/postgre.js'; -import { apLogger } from '../logger.js'; -import { htmlToMfm } from '../misc/html-to-mfm.js'; -import { fromHtml } from '../../../../mfm/from-html.js'; -import { isCollectionOrOrderedCollection, isCollection, getApId, getOneApHrefNullable, isPropertyValue, getApType, isActor } from '../type.js'; -import Resolver from '../resolver.js'; -import { extractApHashtags } from './tag.js'; -import { resolveNote, extractEmojis } from './note.js'; -import { resolveImage } from './image.js'; -import type { IActor, IObject, IApPropertyValue } from '../type.js'; - -const logger = apLogger; - -const nameLength = 128; -const summaryLength = 2048; - -/** - * Validate and convert to actor object - * @param x Fetched object - * @param uri Fetch target URI - */ -function validateActor(x: IObject, uri: string): IActor { - const expectHost = toPuny(new URL(uri).hostname); - - if (x == null) { - throw new Error('invalid Actor: object is null'); - } - - if (!isActor(x)) { - throw new Error(`invalid Actor type '${x.type}'`); - } - - if (!(typeof x.id === 'string' && x.id.length > 0)) { - throw new Error('invalid Actor: wrong id'); - } - - if (!(typeof x.inbox === 'string' && x.inbox.length > 0)) { - throw new Error('invalid Actor: wrong inbox'); - } - - if (!(typeof x.preferredUsername === 'string' && x.preferredUsername.length > 0 && x.preferredUsername.length <= 128 && /^\w([\w-.]*\w)?$/.test(x.preferredUsername))) { - throw new Error('invalid Actor: wrong username'); - } - - // These fields are only informational, and some AP software allows these - // fields to be very long. If they are too long, we cut them off. This way - // we can at least see these users and their activities. - if (x.name) { - if (!(typeof x.name === 'string' && x.name.length > 0)) { - throw new Error('invalid Actor: wrong name'); - } - x.name = truncate(x.name, nameLength); - } - if (x.summary) { - if (!(typeof x.summary === 'string' && x.summary.length > 0)) { - throw new Error('invalid Actor: wrong summary'); - } - x.summary = truncate(x.summary, summaryLength); - } - - const idHost = toPuny(new URL(x.id!).hostname); - if (idHost !== expectHost) { - throw new Error('invalid Actor: id has different host'); - } - - if (x.publicKey) { - if (typeof x.publicKey.id !== 'string') { - throw new Error('invalid Actor: publicKey.id is not a string'); - } - - const publicKeyIdHost = toPuny(new URL(x.publicKey.id).hostname); - if (publicKeyIdHost !== expectHost) { - throw new Error('invalid Actor: publicKey.id has different host'); - } - } - - return x; -} - -/** - * Personをフェッチします。 - * - * Misskeyに対象のPersonが登録されていればそれを返します。 - */ -export async function fetchPerson(uri: string, resolver?: Resolver): Promise { - if (typeof uri !== 'string') throw new Error('uri is not string'); - - const cached = uriPersonCache.get(uri); - if (cached) return cached; - - // URIがこのサーバーを指しているならデータベースからフェッチ - if (uri.startsWith(config.url + '/')) { - const id = uri.split('/').pop(); - const u = await Users.findOneBy({ id }); - if (u) uriPersonCache.set(uri, u); - return u; - } - - //#region このサーバーに既に登録されていたらそれを返す - const exist = await Users.findOneBy({ uri }); - - if (exist) { - uriPersonCache.set(uri, exist); - return exist; - } - //#endregion - - return null; -} - -/** - * Personを作成します。 - */ -export async function createPerson(uri: string, resolver?: Resolver): Promise { - if (typeof uri !== 'string') throw new Error('uri is not string'); - - if (uri.startsWith(config.url)) { - throw new StatusError('cannot resolve local user', 400, 'cannot resolve local user'); - } - - if (resolver == null) resolver = new Resolver(); - - const object = await resolver.resolve(uri) as any; - - const person = validateActor(object, uri); - - logger.info(`Creating the Person: ${person.id}`); - - const host = toPuny(new URL(object.id).hostname); - - const { fields } = analyzeAttachments(person.attachment || []); - - const tags = extractApHashtags(person.tag).map(tag => normalizeForSearch(tag)).splice(0, 32); - - const isBot = getApType(object) === 'Service'; - - const bday = person['vcard:bday']?.match(/^\d{4}-\d{2}-\d{2}/); - - // Create user - let user: IRemoteUser; - try { - // Start transaction - await db.transaction(async transactionalEntityManager => { - user = await transactionalEntityManager.save(new User({ - id: this.idService.genId(), - avatarId: null, - bannerId: null, - createdAt: new Date(), - lastFetchedAt: new Date(), - name: truncate(person.name, nameLength), - isLocked: !!person.manuallyApprovesFollowers, - isExplorable: !!person.discoverable, - username: person.preferredUsername, - usernameLower: person.preferredUsername!.toLowerCase(), - host, - inbox: person.inbox, - sharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined), - followersUri: person.followers ? getApId(person.followers) : undefined, - featured: person.featured ? getApId(person.featured) : undefined, - uri: person.id, - tags, - isBot, - isCat: (person as any).isCat === true, - showTimelineReplies: false, - })) as IRemoteUser; - - await transactionalEntityManager.save(new UserProfile({ - userId: user.id, - description: person.summary ? htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null, - url: getOneApHrefNullable(person.url), - fields, - birthday: bday ? bday[0] : null, - location: person['vcard:Address'] || null, - userHost: host, - })); - - if (person.publicKey) { - await transactionalEntityManager.save(new UserPublickey({ - userId: user.id, - keyId: person.publicKey.id, - keyPem: person.publicKey.publicKeyPem, - })); - } - }); - } catch (e) { - // duplicate key error - if (isDuplicateKeyValueError(e)) { - // /users/@a => /users/:id のように入力がaliasなときにエラーになることがあるのを対応 - const u = await Users.findOneBy({ - uri: person.id, - }); - - if (u) { - user = u as IRemoteUser; - } else { - throw new Error('already registered'); - } - } else { - logger.error(e instanceof Error ? e : new Error(e as string)); - throw e; - } - } - - // Register host - registerOrFetchInstanceDoc(host).then(i => { - Instances.increment({ id: i.id }, 'usersCount', 1); - instanceChart.newUser(i.host); - fetchInstanceMetadata(i); - }); - - usersChart.update(user!, true); - - // ハッシュタグ更新 - updateUsertags(user!, tags); - - //#region アバターとヘッダー画像をフェッチ - const [avatar, banner] = await Promise.all([ - person.icon, - person.image, - ].map(img => - img == null - ? Promise.resolve(null) - : resolveImage(user!, img).catch(() => null), - )); - - const avatarId = avatar ? avatar.id : null; - const bannerId = banner ? banner.id : null; - - await Users.update(user!.id, { - avatarId, - bannerId, - }); - - user!.avatarId = avatarId; - user!.bannerId = bannerId; - //#endregion - - //#region カスタム絵文字取得 - const emojis = await extractEmojis(person.tag || [], host).catch(e => { - logger.info(`extractEmojis: ${e}`); - return [] as Emoji[]; - }); - - const emojiNames = emojis.map(emoji => emoji.name); - - await Users.update(user!.id, { - emojis: emojiNames, - }); - //#endregion - - await updateFeatured(user!.id).catch(err => logger.error(err)); - - return user!; -} - -/** - * Personの情報を更新します。 - * Misskeyに対象のPersonが登録されていなければ無視します。 - * @param uri URI of Person - * @param resolver Resolver - * @param hint Hint of Person object (この値が正当なPersonの場合、Remote resolveをせずに更新に利用します) - */ -export async function updatePerson(uri: string, resolver?: Resolver | null, hint?: IObject): Promise { - if (typeof uri !== 'string') throw new Error('uri is not string'); - - // URIがこのサーバーを指しているならスキップ - if (uri.startsWith(config.url + '/')) { - return; - } - - //#region このサーバーに既に登録されているか - const exist = await Users.findOneBy({ uri }) as IRemoteUser; - - if (exist == null) { - return; - } - //#endregion - - if (resolver == null) resolver = new Resolver(); - - const object = hint || await resolver.resolve(uri); - - const person = validateActor(object, uri); - - logger.info(`Updating the Person: ${person.id}`); - - // アバターとヘッダー画像をフェッチ - const [avatar, banner] = await Promise.all([ - person.icon, - person.image, - ].map(img => - img == null - ? Promise.resolve(null) - : resolveImage(exist, img).catch(() => null), - )); - - // カスタム絵文字取得 - const emojis = await extractEmojis(person.tag || [], exist.host).catch(e => { - logger.info(`extractEmojis: ${e}`); - return [] as Emoji[]; - }); - - const emojiNames = emojis.map(emoji => emoji.name); - - const { fields } = analyzeAttachments(person.attachment || []); - - const tags = extractApHashtags(person.tag).map(tag => normalizeForSearch(tag)).splice(0, 32); - - const bday = person['vcard:bday']?.match(/^\d{4}-\d{2}-\d{2}/); - - const updates = { - lastFetchedAt: new Date(), - inbox: person.inbox, - sharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined), - followersUri: person.followers ? getApId(person.followers) : undefined, - featured: person.featured, - emojis: emojiNames, - name: truncate(person.name, nameLength), - tags, - isBot: getApType(object) === 'Service', - isCat: (person as any).isCat === true, - isLocked: !!person.manuallyApprovesFollowers, - isExplorable: !!person.discoverable, - } as Partial; - - if (avatar) { - updates.avatarId = avatar.id; - } - - if (banner) { - updates.bannerId = banner.id; - } - - // Update user - await Users.update(exist.id, updates); - - if (person.publicKey) { - await UserPublickeys.update({ userId: exist.id }, { - keyId: person.publicKey.id, - keyPem: person.publicKey.publicKeyPem, - }); - } - - await UserProfiles.update({ userId: exist.id }, { - url: getOneApHrefNullable(person.url), - fields, - description: person.summary ? htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null, - birthday: bday ? bday[0] : null, - location: person['vcard:Address'] || null, - }); - - publishInternalEvent('remoteUserUpdated', { id: exist.id }); - - // ハッシュタグ更新 - updateUsertags(exist, tags); - - // 該当ユーザーが既にフォロワーになっていた場合はFollowingもアップデートする - await Followings.update({ - followerId: exist.id, - }, { - followerSharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined), - }); - - await updateFeatured(exist.id).catch(err => logger.error(err)); -} - -/** - * Personを解決します。 - * - * Misskeyに対象のPersonが登録されていればそれを返し、そうでなければ - * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 - */ -export async function resolvePerson(uri: string, resolver?: Resolver): Promise { - if (typeof uri !== 'string') throw new Error('uri is not string'); - - //#region このサーバーに既に登録されていたらそれを返す - const exist = await fetchPerson(uri); - - if (exist) { - return exist; - } - //#endregion - - // リモートサーバーからフェッチしてきて登録 - if (resolver == null) resolver = new Resolver(); - return await createPerson(uri, resolver); -} - -const services: { - [x: string]: (id: string, username: string) => any - } = { - 'misskey:authentication:twitter': (userId, screenName) => ({ userId, screenName }), - 'misskey:authentication:github': (id, login) => ({ id, login }), - 'misskey:authentication:discord': (id, name) => $discord(id, name), - }; - -const $discord = (id: string, name: string) => { - if (typeof name !== 'string') { - name = 'unknown#0000'; - } - const [username, discriminator] = name.split('#'); - return { id, username, discriminator }; -}; - -function addService(target: { [x: string]: any }, source: IApPropertyValue) { - const service = services[source.name]; - - if (typeof source.value !== 'string') { - source.value = 'unknown'; - } - - const [id, username] = source.value.split('@'); - - if (service) { - target[source.name.split(':')[2]] = service(id, username); - } -} - -export function analyzeAttachments(attachments: IObject | IObject[] | undefined) { - const fields: { - name: string, - value: string - }[] = []; - const services: { [x: string]: any } = {}; - - if (Array.isArray(attachments)) { - for (const attachment of attachments.filter(isPropertyValue)) { - if (isPropertyValue(attachment.identifier)) { - addService(services, attachment.identifier); - } else { - fields.push({ - name: attachment.name, - value: fromHtml(attachment.value), - }); - } - } - } - - return { fields, services }; -} - -export async function updateFeatured(userId: User['id']) { - const user = await Users.findOneByOrFail({ id: userId }); - if (!Users.isRemoteUser(user)) return; - if (!user.featured) return; - - logger.info(`Updating the featured: ${user.uri}`); - - const resolver = new Resolver(); - - // Resolve to (Ordered)Collection Object - const collection = await resolver.resolveCollection(user.featured); - if (!isCollectionOrOrderedCollection(collection)) throw new Error('Object is not Collection or OrderedCollection'); - - // Resolve to Object(may be Note) arrays - const unresolvedItems = isCollection(collection) ? collection.items : collection.orderedItems; - const items = await Promise.all(toArray(unresolvedItems).map(x => resolver.resolve(x))); - - // Resolve and regist Notes - const limit = promiseLimit(2); - const featuredNotes = await Promise.all(items - .filter(item => getApType(item) === 'Note') // TODO: Noteでなくてもいいかも - .slice(0, 5) - .map(item => limit(() => resolveNote(item, resolver)))); - - await db.transaction(async transactionalEntityManager => { - await transactionalEntityManager.delete(UserNotePining, { userId: user.id }); - - // とりあえずidを別の時間で生成して順番を維持 - let td = 0; - for (const note of featuredNotes.filter(note => note != null)) { - td -= 1000; - transactionalEntityManager.insert(UserNotePining, { - id: genId(new Date(Date.now() + td)), - createdAt: new Date(), - userId: user.id, - noteId: note!.id, - }); - } - }); -} From 354e4b6afff7dd3d53f0af3472f9a4cffd7306e7 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 23:45:16 +0900 Subject: [PATCH 070/180] Update ApPersonService.ts --- .../activitypub/models/ApPersonService.ts | 71 ++++++++++++------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts index ff5319e8856b..a336cdde76a9 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import { Users } from '@/models/index.js'; +import type { Followings , Instances, UserProfiles, UserPublickeys, Users } from '@/models/index.js'; import type { Config } from '@/config/types.js'; import type { CacheableUser, IRemoteUser } from '@/models/entities/user.js'; import { User } from '@/models/entities/user.js'; @@ -20,7 +20,14 @@ import { toArray } from '@/prelude/array.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; import type { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; import type { FetchInstanceMetadataService } from '@/services/FetchInstanceMetadataService.js'; +import { UserProfile } from '@/models/entities/user-profile.js'; +import { UserPublickey } from '@/models/entities/user-publickey.js'; +import type UsersChart from '@/services/chart/charts/users.js'; +import type InstanceChart from '@/services/chart/charts/instance.js'; +import type { HashtagService } from '@/services/HashtagService.js'; +import { UserNotePining } from '@/models/entities/user-note-pining.js'; import { getApId, getApType, getOneApHrefNullable, isActor, isCollection, isCollectionOrOrderedCollection, isPropertyValue } from '../type.js'; +import type { ApNoteService } from './ApNoteService.js'; import type { ApMfmService } from '../ApMfmService.js'; import type { Resolver , ApResolverService } from '../ApResolverService.js'; import type { ApImageService } from './ApImageService.js'; @@ -138,15 +145,31 @@ export class ApPersonService { @Inject('usersRepository') private usersRepository: typeof Users, + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + @Inject('userPublickeysRepository') + private userPublickeysRepository: typeof UserPublickeys, + + @Inject('instancesRepository') + private instancesRepository: typeof Instances, + + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + private idService: IdService, private globalEventService: GlobalEventService, private federatedInstanceService: FederatedInstanceService, private fetchInstanceMetadataService: FetchInstanceMetadataService, private userCacheService: UserCacheService, private apResolverService: ApResolverService, + private apNoteService: ApNoteService, private apImageService: ApImageService, private apMfmService: ApMfmService, private mfmService: MfmService, + private hashtagService: HashtagService, + private usersChart: UsersChart, + private instanceChart: InstanceChart, private apLoggerService: ApLoggerService, ) { this.#logger = this.apLoggerService.logger; @@ -166,13 +189,13 @@ export class ApPersonService { // URIがこのサーバーを指しているならデータベースからフェッチ if (uri.startsWith(this.config.url + '/')) { const id = uri.split('/').pop(); - const u = await Users.findOneBy({ id }); + const u = await this.usersRepository.findOneBy({ id }); if (u) this.userCacheService.uriPersonCache.set(uri, u); return u; } //#region このサーバーに既に登録されていたらそれを返す - const exist = await Users.findOneBy({ uri }); + const exist = await this.usersRepository.findOneBy({ uri }); if (exist) { this.userCacheService.uriPersonCache.set(uri, exist); @@ -261,7 +284,7 @@ export class ApPersonService { // duplicate key error if (isDuplicateKeyValueError(e)) { // /users/@a => /users/:id のように入力がaliasなときにエラーになることがあるのを対応 - const u = await Users.findOneBy({ + const u = await this.usersRepository.findOneBy({ uri: person.id, }); @@ -278,15 +301,15 @@ export class ApPersonService { // Register host this.federatedInstanceService.registerOrFetchInstanceDoc(host).then(i => { - Instances.increment({ id: i.id }, 'usersCount', 1); - instanceChart.newUser(i.host); + this.instancesRepository.increment({ id: i.id }, 'usersCount', 1); + this.instanceChart.newUser(i.host); this.fetchInstanceMetadataService.fetchInstanceMetadata(i); }); - usersChart.update(user!, true); + this.usersChart.update(user!, true); // ハッシュタグ更新 - updateUsertags(user!, tags); + this.hashtagService.updateUsertags(user!, tags); //#region アバターとヘッダー画像をフェッチ const [avatar, banner] = await Promise.all([ @@ -301,7 +324,7 @@ export class ApPersonService { const avatarId = avatar ? avatar.id : null; const bannerId = banner ? banner.id : null; - await Users.update(user!.id, { + await this.usersRepository.update(user!.id, { avatarId, bannerId, }); @@ -311,19 +334,19 @@ export class ApPersonService { //#endregion //#region カスタム絵文字取得 - const emojis = await extractEmojis(person.tag || [], host).catch(e => { + const emojis = await this.apNoteService.extractEmojis(person.tag || [], host).catch(e => { this.#logger.info(`extractEmojis: ${e}`); return [] as Emoji[]; }); const emojiNames = emojis.map(emoji => emoji.name); - await Users.update(user!.id, { + await this.usersRepository.update(user!.id, { emojis: emojiNames, }); //#endregion - await this.updateFeatured(user!.id).catch(err => logger.error(err)); + await this.updateFeatured(user!.id).catch(err => this.#logger.error(err)); return user!; } @@ -344,7 +367,7 @@ export class ApPersonService { } //#region このサーバーに既に登録されているか - const exist = await Users.findOneBy({ uri }) as IRemoteUser; + const exist = await this.usersRepository.findOneBy({ uri }) as IRemoteUser; if (exist == null) { return; @@ -370,14 +393,14 @@ export class ApPersonService { )); // カスタム絵文字取得 - const emojis = await extractEmojis(person.tag || [], exist.host).catch(e => { + const emojis = await this.apNoteService.extractEmojis(person.tag || [], exist.host).catch(e => { this.#logger.info(`extractEmojis: ${e}`); return [] as Emoji[]; }); const emojiNames = emojis.map(emoji => emoji.name); - const { fields } = analyzeAttachments(person.attachment || []); + const { fields } = this.analyzeAttachments(person.attachment || []); const tags = extractApHashtags(person.tag).map(tag => normalizeForSearch(tag)).splice(0, 32); @@ -407,16 +430,16 @@ export class ApPersonService { } // Update user - await Users.update(exist.id, updates); + await this.usersRepository.update(exist.id, updates); if (person.publicKey) { - await UserPublickeys.update({ userId: exist.id }, { + await this.userPublickeysRepository.update({ userId: exist.id }, { keyId: person.publicKey.id, keyPem: person.publicKey.publicKeyPem, }); } - await UserProfiles.update({ userId: exist.id }, { + await this.userProfilesRepository.update({ userId: exist.id }, { url: getOneApHrefNullable(person.url), fields, description: person.summary ? this.apMfmService.htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null, @@ -427,16 +450,16 @@ export class ApPersonService { this.globalEventService.publishInternalEvent('remoteUserUpdated', { id: exist.id }); // ハッシュタグ更新 - updateUsertags(exist, tags); + this.hashtagService.updateUsertags(exist, tags); // 該当ユーザーが既にフォロワーになっていた場合はFollowingもアップデートする - await Followings.update({ + await this.followingsRepository.update({ followerId: exist.id, }, { followerSharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined), }); - await this.updateFeatured(exist.id).catch(err => logger.error(err)); + await this.updateFeatured(exist.id).catch(err => this.#logger.error(err)); } /** @@ -485,8 +508,8 @@ export class ApPersonService { } public async updateFeatured(userId: User['id']) { - const user = await Users.findOneByOrFail({ id: userId }); - if (!Users.isRemoteUser(user)) return; + const user = await this.usersRepository.findOneByOrFail({ id: userId }); + if (!this.usersRepository.isRemoteUser(user)) return; if (!user.featured) return; this.#logger.info(`Updating the featured: ${user.uri}`); @@ -506,7 +529,7 @@ export class ApPersonService { const featuredNotes = await Promise.all(items .filter(item => getApType(item) === 'Note') // TODO: Noteでなくてもいいかも .slice(0, 5) - .map(item => limit(() => resolveNote(item, resolver)))); + .map(item => limit(() => this.apNoteService.resolveNote(item, resolver)))); await this.db.transaction(async transactionalEntityManager => { await transactionalEntityManager.delete(UserNotePining, { userId: user.id }); From ec579d9f346991e01c8307430e3cc09829a9c128 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 23:46:21 +0900 Subject: [PATCH 071/180] Update ApPersonService.ts --- .../src/services/remote/activitypub/models/ApPersonService.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts index a336cdde76a9..6b47d8458f45 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts @@ -27,12 +27,13 @@ import type InstanceChart from '@/services/chart/charts/instance.js'; import type { HashtagService } from '@/services/HashtagService.js'; import { UserNotePining } from '@/models/entities/user-note-pining.js'; import { getApId, getApType, getOneApHrefNullable, isActor, isCollection, isCollectionOrOrderedCollection, isPropertyValue } from '../type.js'; +import { extractApHashtags } from './tag.js'; import type { ApNoteService } from './ApNoteService.js'; import type { ApMfmService } from '../ApMfmService.js'; import type { Resolver , ApResolverService } from '../ApResolverService.js'; import type { ApImageService } from './ApImageService.js'; import type { DataSource } from 'typeorm'; -import type { IActor, IObject } from '../type.js'; +import type { IActor, IObject , IApPropertyValue } from '../type.js'; import type { ApLoggerService } from '../ApLoggerService.js'; const nameLength = 128; From c444831d6f7ff6f5aae1f1b0df3c508206e79c09 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 13 Sep 2022 23:50:51 +0900 Subject: [PATCH 072/180] wip --- .../services/remote/activitypub/models/ApNoteService.ts | 9 +++++++-- .../remote/activitypub/models/ApPersonService.ts | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts index 08554781588e..78cbce957071 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { forwardRef, Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Emojis, Users } from '@/models/index.js'; @@ -17,7 +17,8 @@ import type { NoteCreateService } from '@/services/NoteCreateService.js'; import type Logger from '@/logger.js'; import type { IdService } from '@/services/IdService.js'; import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js'; -import type { ApPersonService } from './ApPersonService.js'; +// eslint-disable-next-line @typescript-eslint/consistent-type-imports +import { ApPersonService } from './ApPersonService.js'; import type { ApLoggerService } from '../ApLoggerService.js'; import type { ApMfmService } from '../ApMfmService.js'; import type { ApDbResolverService } from '../ApDbResolverService.js'; @@ -41,7 +42,11 @@ export class ApNoteService { private idService: IdService, private apMfmService: ApMfmService, private apResolverService: ApResolverService, + + // 循環参照のため / for circular dependency + @Inject(forwardRef(() => ApPersonService)) private apPersonService: ApPersonService, + private metaService: MetaService, private appLockService: AppLockService, private noteCreateService: NoteCreateService, diff --git a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts index 6b47d8458f45..8b7e987131cf 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { forwardRef, Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Followings , Instances, UserProfiles, UserPublickeys, Users } from '@/models/index.js'; @@ -28,7 +28,8 @@ import type { HashtagService } from '@/services/HashtagService.js'; import { UserNotePining } from '@/models/entities/user-note-pining.js'; import { getApId, getApType, getOneApHrefNullable, isActor, isCollection, isCollectionOrOrderedCollection, isPropertyValue } from '../type.js'; import { extractApHashtags } from './tag.js'; -import type { ApNoteService } from './ApNoteService.js'; +// eslint-disable-next-line @typescript-eslint/consistent-type-imports +import { ApNoteService } from './ApNoteService.js'; import type { ApMfmService } from '../ApMfmService.js'; import type { Resolver , ApResolverService } from '../ApResolverService.js'; import type { ApImageService } from './ApImageService.js'; @@ -164,7 +165,11 @@ export class ApPersonService { private fetchInstanceMetadataService: FetchInstanceMetadataService, private userCacheService: UserCacheService, private apResolverService: ApResolverService, + + // 循環参照のため / for circular dependency + @Inject(forwardRef(() => ApNoteService)) private apNoteService: ApNoteService, + private apImageService: ApImageService, private apMfmService: ApMfmService, private mfmService: MfmService, From 304fcb5eff99dbe3a0e01a1f66be20ecb0b32307 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 14 Sep 2022 00:01:32 +0900 Subject: [PATCH 073/180] wip --- packages/backend/src/services/RelayService.ts | 29 +++-- .../activitypub/models/ApNoteService.ts | 26 +++-- .../activitypub/models/ApQuestionService.ts | 108 ++++++++++++++++++ .../remote/activitypub/models/question.ts | 83 -------------- .../services/remote/activitypub/models/tag.ts | 3 +- 5 files changed, 141 insertions(+), 108 deletions(-) create mode 100644 packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts delete mode 100644 packages/backend/src/services/remote/activitypub/models/question.ts diff --git a/packages/backend/src/services/RelayService.ts b/packages/backend/src/services/RelayService.ts index 6e9b1cd5fccc..b045579351b3 100644 --- a/packages/backend/src/services/RelayService.ts +++ b/packages/backend/src/services/RelayService.ts @@ -1,8 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; -import { renderFollowRelay } from '@/services/remote/activitypub/renderer/follow-relay.js'; -import { renderActivity, attachLdSignature } from '@/services/remote/activitypub/renderer/index.js'; -import renderUndo from '@/services/remote/activitypub/renderer/undo.js'; import type { ILocalUser, User } from '@/models/entities/user.js'; import type { Relays, Users } from '@/models/index.js'; import type { IdService } from '@/services/IdService.js'; @@ -10,6 +7,7 @@ import { Cache } from '@/misc/cache.js'; import type { Relay } from '@/models/entities/relay.js'; import type { QueueService } from '@/queue/queue.service.js'; import type { CreateSystemUserService } from '@/services/CreateSystemUserService.js'; +import type { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; const ACTOR_USERNAME = 'relay.actor' as const; @@ -27,6 +25,7 @@ export class RelayService { private idService: IdService, private queueService: QueueService, private createSystemUserService: CreateSystemUserService, + private apRendererService: ApRendererService, ) { this.#relaysCache = new Cache(1000 * 60 * 10); } @@ -43,7 +42,7 @@ export class RelayService { return created as ILocalUser; } - async addRelay(inbox: string): Promise { + public async addRelay(inbox: string): Promise { const relay = await this.relaysRepository.insert({ id: this.idService.genId(), inbox, @@ -51,14 +50,14 @@ export class RelayService { }).then(x => this.relaysRepository.findOneByOrFail(x.identifiers[0])); const relayActor = await this.#getRelayActor(); - const follow = await renderFollowRelay(relay, relayActor); - const activity = renderActivity(follow); + const follow = await this.apRendererService.renderFollowRelay(relay, relayActor); + const activity = this.apRendererService.renderActivity(follow); this.queueService.deliver(relayActor, activity, relay.inbox); return relay; } - async removeRelay(inbox: string): Promise { + public async removeRelay(inbox: string): Promise { const relay = await this.relaysRepository.findOneBy({ inbox, }); @@ -68,20 +67,20 @@ export class RelayService { } const relayActor = await this.#getRelayActor(); - const follow = renderFollowRelay(relay, relayActor); - const undo = renderUndo(follow, relayActor); - const activity = renderActivity(undo); + const follow = this.apRendererService.renderFollowRelay(relay, relayActor); + const undo = this.apRendererService.renderUndo(follow, relayActor); + const activity = this.apRendererService.renderActivity(undo); this.queueService.deliver(relayActor, activity, relay.inbox); await this.relaysRepository.delete(relay.id); } - async listRelay(): Promise { + public async listRelay(): Promise { const relays = await this.relaysRepository.find(); return relays; } - async relayAccepted(id: string): Promise { + public async relayAccepted(id: string): Promise { const result = await this.relaysRepository.update(id, { status: 'accepted', }); @@ -89,7 +88,7 @@ export class RelayService { return JSON.stringify(result); } - async relayRejected(id: string): Promise { + public async relayRejected(id: string): Promise { const result = await this.relaysRepository.update(id, { status: 'rejected', }); @@ -97,7 +96,7 @@ export class RelayService { return JSON.stringify(result); } - async deliverToRelays(user: { id: User['id']; host: null; }, activity: any): Promise { + public async deliverToRelays(user: { id: User['id']; host: null; }, activity: any): Promise { if (activity == null) return; const relays = await this.#relaysCache.fetch(null, () => this.relaysRepository.findBy({ @@ -110,7 +109,7 @@ export class RelayService { const copy = JSON.parse(JSON.stringify(activity)); if (!copy.to) copy.to = ['https://www.w3.org/ns/activitystreams#Public']; - const signed = await attachLdSignature(copy, user); + const signed = await this.apRendererService.attachLdSignature(copy, user); for (const relay of relays) { this.queueService.deliver(user, signed, relay.inbox); diff --git a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts index 78cbce957071..b83ef232e950 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts @@ -1,10 +1,9 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Emojis, Users } from '@/models/index.js'; +import type { Polls , Emojis, Users } from '@/models/index.js'; import type { Config } from '@/config/types.js'; import type { CacheableRemoteUser } from '@/models/entities/user.js'; -import type { MfmService } from '@/services/MfmService.js'; import { extractDbHost, toPuny } from '@/misc/convert-host'; import type { Note } from '@/models/entities/note.js'; import { StatusError } from '@/services/HttpRequestService.js'; @@ -16,14 +15,19 @@ import type { DriveFile } from '@/models/entities/drive-file.js'; import type { NoteCreateService } from '@/services/NoteCreateService.js'; import type Logger from '@/logger.js'; import type { IdService } from '@/services/IdService.js'; +import type { PollService } from '@/services/PollService.js'; import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js'; // eslint-disable-next-line @typescript-eslint/consistent-type-imports import { ApPersonService } from './ApPersonService.js'; +import { extractApHashtags } from './tag.js'; +import type { ApMentionService } from './ApMentionService.js'; +import type { ApQuestionService } from './ApQuestionService.js'; import type { ApLoggerService } from '../ApLoggerService.js'; import type { ApMfmService } from '../ApMfmService.js'; import type { ApDbResolverService } from '../ApDbResolverService.js'; import type { ApResolverService, Resolver } from '../ApResolverService.js'; import type { IObject, IPost } from '../type.js'; +import type { ApImageService } from './ApImageService.js'; @Injectable() export class ApNoteService { @@ -33,8 +37,8 @@ export class ApNoteService { @Inject(DI_SYMBOLS.config) private config: Config, - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('pollsRepository') + private pollsRepository: typeof Polls, @Inject('emojisRepository') private emojisRepository: typeof Emojis, @@ -47,8 +51,12 @@ export class ApNoteService { @Inject(forwardRef(() => ApPersonService)) private apPersonService: ApPersonService, + private apMentionService: ApMentionService, + private apImageService: ApImageService, + private apQuestionService: ApQuestionService, private metaService: MetaService, private appLockService: AppLockService, + private pollService: PollService, private noteCreateService: NoteCreateService, private apDbResolverService: ApDbResolverService, private apLoggerService: ApLoggerService, @@ -136,7 +144,7 @@ export class ApNoteService { let isTalk = note._misskey_talk && visibility === 'specified'; - const apMentions = await extractApMentions(note.tag); + const apMentions = await this.apMentionService.extractApMentions(note.tag); const apHashtags = await extractApHashtags(note.tag); // 添付ファイル @@ -148,7 +156,7 @@ export class ApNoteService { note.attachment = Array.isArray(note.attachment) ? note.attachment : note.attachment ? [note.attachment] : []; const files = note.attachment .map(attach => attach.sensitive = note.sensitive) - ? (await Promise.all(note.attachment.map(x => limit(() => resolveImage(actor, x)) as Promise))) + ? (await Promise.all(note.attachment.map(x => limit(() => this.apImageService.resolveImage(actor, x)) as Promise))) .filter(image => image != null) : []; @@ -233,7 +241,7 @@ export class ApNoteService { // vote if (reply && reply.hasPoll) { - const poll = await Polls.findOneByOrFail({ noteId: reply.id }); + const poll = await this.pollsRepository.findOneByOrFail({ noteId: reply.id }); const tryCreateVote = async (name: string, index: number): Promise => { if (poll.expiresAt && Date.now() > new Date(poll.expiresAt).getTime()) { @@ -243,7 +251,7 @@ export class ApNoteService { await vote(actor, reply, index); // リモートフォロワーにUpdate配信 - deliverQuestionUpdate(reply.id); + this.pollService.deliverQuestionUpdate(reply.id); } return null; }; @@ -260,7 +268,7 @@ export class ApNoteService { const apEmojis = emojis.map(emoji => emoji.name); - const poll = await extractPollFromQuestion(note, resolver).catch(() => undefined); + const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined); if (isTalk) { for (const recipient of visibleUsers) { diff --git a/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts b/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts new file mode 100644 index 000000000000..e46cb040521d --- /dev/null +++ b/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts @@ -0,0 +1,108 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Notes , Polls } from '@/models/index.js'; +import type { Config } from '@/config/types.js'; +import type { IPoll } from '@/models/entities/poll.js'; +import type Logger from '@/logger.js'; +import { isQuestion } from '../type.js'; +import type { ApLoggerService } from '../ApLoggerService.js'; +import type { Resolver , ApResolverService } from '../ApResolverService.js'; +import type { IObject , IQuestion } from '../type.js'; + +@Injectable() +export class ApQuestionService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + + @Inject('pollsRepository') + private pollsRepository: typeof Polls, + + private apResolverService: ApResolverService, + private apLoggerService: ApLoggerService, + ) { + this.#logger = this.apLoggerService.logger; + } + + public async extractPollFromQuestion(source: string | IObject, resolver?: Resolver): Promise { + if (resolver == null) resolver = this.apResolverService.createResolver(); + + const question = await resolver.resolve(source); + + if (!isQuestion(question)) { + throw new Error('invalid type'); + } + + const multiple = !question.oneOf; + const expiresAt = question.endTime ? new Date(question.endTime) : question.closed ? new Date(question.closed) : null; + + if (multiple && !question.anyOf) { + throw new Error('invalid question'); + } + + const choices = question[multiple ? 'anyOf' : 'oneOf']! + .map((x, i) => x.name!); + + const votes = question[multiple ? 'anyOf' : 'oneOf']! + .map((x, i) => x.replies && x.replies.totalItems || x._misskey_votes || 0); + + return { + choices, + votes, + multiple, + expiresAt, + }; + } + + /** + * Update votes of Question + * @param uri URI of AP Question object + * @returns true if updated + */ + public async updateQuestion(value: any) { + const uri = typeof value === 'string' ? value : value.id; + + // URIがこのサーバーを指しているならスキップ + if (uri.startsWith(this.config.url + '/')) throw new Error('uri points local'); + + //#region このサーバーに既に登録されているか + const note = await this.notesRepository.findOneBy({ uri }); + if (note == null) throw new Error('Question is not registed'); + + const poll = await this.pollsRepository.findOneBy({ noteId: note.id }); + if (poll == null) throw new Error('Question is not registed'); + //#endregion + + // resolve new Question object + const resolver = this.apResolverService.createResolver(); + const question = await resolver.resolve(value) as IQuestion; + this.#logger.debug(`fetched question: ${JSON.stringify(question, null, 2)}`); + + if (question.type !== 'Question') throw new Error('object is not a Question'); + + const apChoices = question.oneOf || question.anyOf; + + let changed = false; + + for (const choice of poll.choices) { + const oldCount = poll.votes[poll.choices.indexOf(choice)]; + const newCount = apChoices!.filter(ap => ap.name === choice)[0].replies!.totalItems; + + if (oldCount !== newCount) { + changed = true; + poll.votes[poll.choices.indexOf(choice)] = newCount; + } + } + + await this.pollsRepository.update({ noteId: note.id }, { + votes: poll.votes, + }); + + return changed; + } +} diff --git a/packages/backend/src/services/remote/activitypub/models/question.ts b/packages/backend/src/services/remote/activitypub/models/question.ts deleted file mode 100644 index f0321fdf2faa..000000000000 --- a/packages/backend/src/services/remote/activitypub/models/question.ts +++ /dev/null @@ -1,83 +0,0 @@ -import config from '@/config/index.js'; -import Resolver from '../resolver.js'; -import { IObject, IQuestion, isQuestion } from '../type.js'; -import { apLogger } from '../logger.js'; -import { Notes, Polls } from '@/models/index.js'; -import { IPoll } from '@/models/entities/poll.js'; - -export async function extractPollFromQuestion(source: string | IObject, resolver?: Resolver): Promise { - if (resolver == null) resolver = new Resolver(); - - const question = await resolver.resolve(source); - - if (!isQuestion(question)) { - throw new Error('invalid type'); - } - - const multiple = !question.oneOf; - const expiresAt = question.endTime ? new Date(question.endTime) : question.closed ? new Date(question.closed) : null; - - if (multiple && !question.anyOf) { - throw new Error('invalid question'); - } - - const choices = question[multiple ? 'anyOf' : 'oneOf']! - .map((x, i) => x.name!); - - const votes = question[multiple ? 'anyOf' : 'oneOf']! - .map((x, i) => x.replies && x.replies.totalItems || x._misskey_votes || 0); - - return { - choices, - votes, - multiple, - expiresAt, - }; -} - -/** - * Update votes of Question - * @param uri URI of AP Question object - * @returns true if updated - */ -export async function updateQuestion(value: any) { - const uri = typeof value === 'string' ? value : value.id; - - // URIがこのサーバーを指しているならスキップ - if (uri.startsWith(config.url + '/')) throw new Error('uri points local'); - - //#region このサーバーに既に登録されているか - const note = await Notes.findOneBy({ uri }); - if (note == null) throw new Error('Question is not registed'); - - const poll = await Polls.findOneBy({ noteId: note.id }); - if (poll == null) throw new Error('Question is not registed'); - //#endregion - - // resolve new Question object - const resolver = new Resolver(); - const question = await resolver.resolve(value) as IQuestion; - apLogger.debug(`fetched question: ${JSON.stringify(question, null, 2)}`); - - if (question.type !== 'Question') throw new Error('object is not a Question'); - - const apChoices = question.oneOf || question.anyOf; - - let changed = false; - - for (const choice of poll.choices) { - const oldCount = poll.votes[poll.choices.indexOf(choice)]; - const newCount = apChoices!.filter(ap => ap.name === choice)[0].replies!.totalItems; - - if (oldCount !== newCount) { - changed = true; - poll.votes[poll.choices.indexOf(choice)] = newCount; - } - } - - await Polls.update({ noteId: note.id }, { - votes: poll.votes, - }); - - return changed; -} diff --git a/packages/backend/src/services/remote/activitypub/models/tag.ts b/packages/backend/src/services/remote/activitypub/models/tag.ts index 964dabad0435..1feab369c4b6 100644 --- a/packages/backend/src/services/remote/activitypub/models/tag.ts +++ b/packages/backend/src/services/remote/activitypub/models/tag.ts @@ -1,5 +1,6 @@ import { toArray } from '@/prelude/array.js'; -import { IObject, isHashtag, IApHashtag } from '../type.js'; +import { isHashtag } from '../type.js'; +import type { IObject, IApHashtag } from '../type.js'; export function extractApHashtags(tags: IObject | IObject[] | null | undefined) { if (tags == null) return []; From aed72c5db1f4bc6dc9583144c7130d1be9f3b5f9 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 14 Sep 2022 01:18:16 +0900 Subject: [PATCH 074/180] wip --- .../backend/src/server/api/stream/index.ts | 52 +++++++++++-------- packages/backend/src/server/api/streaming.ts | 40 ++++++++++---- packages/backend/src/server/index.ts | 37 +++++++------ 3 files changed, 80 insertions(+), 49 deletions(-) diff --git a/packages/backend/src/server/api/stream/index.ts b/packages/backend/src/server/api/stream/index.ts index 2d23145f14eb..6da8484977fb 100644 --- a/packages/backend/src/server/api/stream/index.ts +++ b/packages/backend/src/server/api/stream/index.ts @@ -1,18 +1,18 @@ -import { EventEmitter } from 'events'; -import * as websocket from 'websocket'; -import readNote from '@/services/note/read.js'; -import { User } from '@/models/entities/user.js'; -import { Channel as ChannelModel } from '@/models/entities/channel.js'; -import { Users, Followings, Mutings, UserProfiles, ChannelFollowings, Blockings } from '@/models/index.js'; -import { AccessToken } from '@/models/entities/access-token.js'; -import { UserProfile } from '@/models/entities/user-profile.js'; -import { publishChannelStream, publishGroupMessagingStream, publishMessagingStream } from '@/services/stream.js'; -import { UserGroup } from '@/models/entities/user-group.js'; -import { Packed } from '@/misc/schema.js'; +import type { User } from '@/models/entities/user.js'; +import type { Channel as ChannelModel } from '@/models/entities/channel.js'; +import type { Followings, Mutings, UserProfiles, ChannelFollowings, Blockings } from '@/models/index.js'; +import type { AccessToken } from '@/models/entities/access-token.js'; +import type { UserProfile } from '@/models/entities/user-profile.js'; +import type { UserGroup } from '@/models/entities/user-group.js'; +import type { Packed } from '@/misc/schema.js'; +import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import type { NoteReadService } from '@/services/NoteReadService.js'; import { readNotification } from '../common/read-notification.js'; import channels from './channels/index.js'; -import Channel from './channel.js'; -import { StreamEventEmitter, StreamMessages } from './types.js'; +import type * as websocket from 'websocket'; +import type { EventEmitter } from 'events'; +import type Channel from './channel.js'; +import type { StreamEventEmitter, StreamMessages } from './types.js'; /** * Main stream connection @@ -32,6 +32,14 @@ export default class Connection { private cachedNotes: Packed<'Note'>[] = []; constructor( + private followingsRepository: typeof Followings, + private mutingsRepository: typeof Mutings, + private blockingsRepository: typeof Blockings, + private channelFollowingsRepository: typeof ChannelFollowings, + private userProfilesRepository: typeof UserProfiles, + private globalEventService: GlobalEventService, + private noteReadService: NoteReadService, + wsConnection: websocket.connection, subscriber: EventEmitter, user: User | null | undefined, @@ -173,7 +181,7 @@ export default class Connection { if (note == null) return; if (this.user && (note.userId !== this.user.id)) { - readNote(this.user.id, [note], { + this.noteReadService.read(this.user.id, [note], { following: this.following, followingChannels: this.followingChannels, }); @@ -299,22 +307,22 @@ export default class Connection { private typingOnChannel(channel: ChannelModel['id']) { if (this.user) { - publishChannelStream(channel, 'typing', this.user.id); + this.globalEventService.publishChannelStream(channel, 'typing', this.user.id); } } private typingOnMessaging(param: { partner?: User['id']; group?: UserGroup['id']; }) { if (this.user) { if (param.partner) { - publishMessagingStream(param.partner, this.user.id, 'typing', this.user.id); + this.globalEventService.publishMessagingStream(param.partner, this.user.id, 'typing', this.user.id); } else if (param.group) { - publishGroupMessagingStream(param.group, 'typing', this.user.id); + this.globalEventService.publishGroupMessagingStream(param.group, 'typing', this.user.id); } } } private async updateFollowing() { - const followings = await Followings.find({ + const followings = await this.followingsRepository.find({ where: { followerId: this.user!.id, }, @@ -325,7 +333,7 @@ export default class Connection { } private async updateMuting() { - const mutings = await Mutings.find({ + const mutings = await this.mutingsRepository.find({ where: { muterId: this.user!.id, }, @@ -336,7 +344,7 @@ export default class Connection { } private async updateBlocking() { // ここでいうBlockingは被Blockingの意 - const blockings = await Blockings.find({ + const blockings = await this.blockingsRepository.find({ where: { blockeeId: this.user!.id, }, @@ -347,7 +355,7 @@ export default class Connection { } private async updateFollowingChannels() { - const followings = await ChannelFollowings.find({ + const followings = await this.channelFollowingsRepository.find({ where: { followerId: this.user!.id, }, @@ -358,7 +366,7 @@ export default class Connection { } private async updateUserProfile() { - this.userProfile = await UserProfiles.findOneBy({ + this.userProfile = await this.userProfilesRepository.findOneBy({ userId: this.user!.id, }); } diff --git a/packages/backend/src/server/api/streaming.ts b/packages/backend/src/server/api/streaming.ts index f8e42d27fe04..a12eba414925 100644 --- a/packages/backend/src/server/api/streaming.ts +++ b/packages/backend/src/server/api/streaming.ts @@ -1,14 +1,25 @@ -import * as http from 'node:http'; +import { EventEmitter } from 'events'; import * as websocket from 'websocket'; - +import type { Blockings, ChannelFollowings, Followings, Mutings, UserProfiles } from '@/models/index.js'; +import { Users } from '@/models/index.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { NoteReadService } from '@/services/NoteReadService.js'; +import { subsdcriber as redisClient } from '../../db/redis.js'; import MainStreamConnection from './stream/index.js'; -import { ParsedUrlQuery } from 'querystring'; import authenticate from './authenticate.js'; -import { EventEmitter } from 'events'; -import { subsdcriber as redisClient } from '../../db/redis.js'; -import { Users } from '@/models/index.js'; +import type { INestApplicationContext } from '@nestjs/common'; +import type { ParsedUrlQuery } from 'querystring'; +import type * as http from 'node:http'; + +export function initializeStreamingServer(app: INestApplicationContext, server: http.Server) { + const followingsRepository = app.get('followingsRepository'); + const mutingsRepository = app.get('mutingsRepository'); + const blockingsRepository = app.get('blockingsRepository'); + const channelFollowingsRepository = app.get('channelFollowingsRepository'); + const userProfilesRepository = app.get('userProfilesRepository'); + const globalEventService = app.get(GlobalEventService); + const noteReadService = app.get(NoteReadService); -export const initializeStreamingServer = (server: http.Server) => { // Init websocket server const ws = new websocket.server({ httpServer: server, @@ -20,7 +31,7 @@ export const initializeStreamingServer = (server: http.Server) => { // TODO: トークンが間違ってるなどしてauthenticateに失敗したら // コネクション切断するなりエラーメッセージ返すなりする // (現状はエラーがキャッチされておらずサーバーのログに流れて邪魔なので) - const [user, app] = await authenticate(q.i as string); + const [user, miapp] = await authenticate(q.i as string); if (user?.isSuspended) { request.reject(400); @@ -38,7 +49,16 @@ export const initializeStreamingServer = (server: http.Server) => { redisClient.on('message', onRedisMessage); - const main = new MainStreamConnection(connection, ev, user, app); + const main = new MainStreamConnection( + followingsRepository, + mutingsRepository, + blockingsRepository, + channelFollowingsRepository, + userProfilesRepository, + globalEventService, + noteReadService, + connection, ev, user, miapp, + ); const intervalId = user ? setInterval(() => { Users.update(user.id, { @@ -64,4 +84,4 @@ export const initializeStreamingServer = (server: http.Server) => { } }); }); -}; +} diff --git a/packages/backend/src/server/index.ts b/packages/backend/src/server/index.ts index 27d4e629676d..4332e9d6d1b9 100644 --- a/packages/backend/src/server/index.ts +++ b/packages/backend/src/server/index.ts @@ -10,15 +10,15 @@ import Router from '@koa/router'; import mount from 'koa-mount'; import koaLogger from 'koa-logger'; import * as slow from 'koa-slow'; - import { IsNull } from 'typeorm'; -import config from '@/config/index.js'; -import Logger from '@/services/logger.js'; +import Logger from '@/logger.js'; import { UserProfiles, Users } from '@/models/index.js'; import { genIdenticon } from '@/misc/gen-identicon.js'; import { createTemp } from '@/misc/create-temp.js'; -import { publishMainStream } from '@/services/stream.js'; import * as Acct from '@/misc/acct.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Config } from '@/config/types.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; import { envOption } from '../env.js'; import activityPub from './activitypub.js'; import nodeinfo from './nodeinfo.js'; @@ -33,19 +33,22 @@ import type { INestApplicationContext } from '@nestjs/common'; export const serverLogger = new Logger('server', 'gray', false); export default (app: INestApplicationContext) => new Promise(resolve => { + const config = app.get(DI_SYMBOLS.config); + const globalEventService = app.get(GlobalEventService); + // Init app - const app = new Koa(); - app.proxy = true; + const koa = new Koa(); + koa.proxy = true; if (!['production', 'test'].includes(process.env.NODE_ENV || '')) { // Logger - app.use(koaLogger(str => { + koa.use(koaLogger(str => { serverLogger.info(str); })); // Delay if (envOption.slow) { - app.use(slow({ + koa.use(slow({ delay: 3000, })); } @@ -54,15 +57,15 @@ export default (app: INestApplicationContext) => new Promise(resolve => { // HSTS // 6months (15552000sec) if (config.url.startsWith('https') && !config.disableHsts) { - app.use(async (ctx, next) => { + koa.use(async (ctx, next) => { ctx.set('strict-transport-security', 'max-age=15552000; preload'); await next(); }); } - app.use(mount('/api', createApiServer(app))); - app.use(mount('/files', fileServer)); - app.use(mount('/proxy', proxyServer)); + koa.use(mount('/api', createApiServer(app))); + koa.use(mount('/files', fileServer)); + koa.use(mount('/proxy', proxyServer)); // Init router const router = new Router(); @@ -111,7 +114,7 @@ export default (app: INestApplicationContext) => new Promise(resolve => { emailVerifyCode: null, }); - publishMainStream(profile.userId, 'meUpdated', await Users.pack(profile.userId, { id: profile.userId }, { + globalEventService.publishMainStream(profile.userId, 'meUpdated', await Users.pack(profile.userId, { id: profile.userId }, { detail: true, includeSecrets: true, })); @@ -121,13 +124,13 @@ export default (app: INestApplicationContext) => new Promise(resolve => { }); // Register router - app.use(router.routes()); + koa.use(router.routes()); - app.use(mount(webServer)); + koa.use(mount(webServer)); - const server = http.createServer(app.callback()); + const server = http.createServer(koa.callback()); - initializeStreamingServer(server); + initializeStreamingServer(app, server); server.on('error', e => { switch ((e as any).code) { From 36d664b562f9abc042619ec0cf8618095de8cafd Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 14 Sep 2022 02:57:33 +0900 Subject: [PATCH 075/180] wip --- packages/backend/src/di-symbols.ts | 1 + .../backend/src/server/api/ApiCallService.ts | 257 ++++++++++++++++++ .../src/server/api/ApiLoggerService.ts | 12 + .../src/server/api/AuthenticateService.ts | 86 ++++++ .../src/server/api/RateLimiterService.ts | 89 ++++++ .../backend/src/server/api/api-handler.ts | 92 ------- .../backend/src/server/api/authenticate.ts | 66 ----- packages/backend/src/server/api/call.ts | 136 --------- packages/backend/src/server/api/index.ts | 16 +- packages/backend/src/server/api/limiter.ts | 77 ------ packages/backend/src/server/api/logger.ts | 3 - .../backend/src/services/AppLockService.ts | 5 +- .../src/services/GlobalEventService.ts | 6 +- 13 files changed, 459 insertions(+), 387 deletions(-) create mode 100644 packages/backend/src/server/api/ApiCallService.ts create mode 100644 packages/backend/src/server/api/ApiLoggerService.ts create mode 100644 packages/backend/src/server/api/AuthenticateService.ts create mode 100644 packages/backend/src/server/api/RateLimiterService.ts delete mode 100644 packages/backend/src/server/api/api-handler.ts delete mode 100644 packages/backend/src/server/api/authenticate.ts delete mode 100644 packages/backend/src/server/api/call.ts delete mode 100644 packages/backend/src/server/api/limiter.ts delete mode 100644 packages/backend/src/server/api/logger.ts diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts index 533e449b4b98..634af59dfc3b 100644 --- a/packages/backend/src/di-symbols.ts +++ b/packages/backend/src/di-symbols.ts @@ -1,4 +1,5 @@ export const DI_SYMBOLS = { config: Symbol('config'), db: Symbol('db'), + redis: Symbol('redis'), }; diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts new file mode 100644 index 000000000000..3ec8c0106bd1 --- /dev/null +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -0,0 +1,257 @@ +import { performance } from 'perf_hooks'; +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { getIpHash } from '@/misc/get-ip-hash.js'; +import type { CacheableLocalUser, User } from '@/models/entities/user.js'; +import type { AccessToken } from '@/models/entities/access-token.js'; +import type Logger from '@/logger.js'; +import type { UserIps } from '@/models/index.js'; +import { MetaService } from '@/services/MetaService.js'; +import { ApiError } from './error.js'; +import { RateLimiterService } from './RateLimiterService.js'; +import { ApiLoggerService } from './ApiLoggerService.js'; +import { AuthenticateService, AuthenticationError } from './AuthenticateService.js'; +import type { OnApplicationShutdown } from '@nestjs/common'; +import type { IEndpointMeta, IEndpoint } from './endpoints.js'; +import type Koa from 'koa'; + +const accessDenied = { + message: 'Access denied.', + code: 'ACCESS_DENIED', + id: '56f35758-7dd5-468b-8439-5d6fb8ec9b8e', +}; + +@Injectable() +export class ApiCallService implements OnApplicationShutdown { + #logger: Logger; + #userIpHistories: Map>; + #userIpHistoriesClearIntervalId: NodeJS.Timer; + + constructor( + @Inject('userIpsRepository') + private userIpsRepository: typeof UserIps, + + private metaService: MetaService, + private authenticateService: AuthenticateService, + private rateLimiterService: RateLimiterService, + private apiLoggerService: ApiLoggerService, + ) { + this.#logger = this.apiLoggerService.logger; + this.#userIpHistories = new Map>(); + + this.#userIpHistoriesClearIntervalId = setInterval(() => { + this.#userIpHistories.clear(); + }, 1000 * 60 * 60); + } + + public handleRequest(endpoint: IEndpoint, exec: any, ctx: Koa.Context) { + return new Promise((res) => { + const body = ctx.is('multipart/form-data') + ? (ctx.request as any).body + : ctx.method === 'GET' + ? ctx.query + : ctx.request.body; + + const reply = (x?: any, y?: ApiError) => { + if (x == null) { + ctx.status = 204; + } else if (typeof x === 'number' && y) { + ctx.status = x; + ctx.body = { + error: { + message: y!.message, + code: y!.code, + id: y!.id, + kind: y!.kind, + ...(y!.info ? { info: y!.info } : {}), + }, + }; + } else { + // 文字列を返す場合は、JSON.stringify通さないとJSONと認識されない + ctx.body = typeof x === 'string' ? JSON.stringify(x) : x; + } + res(); + }; + + // Authentication + this.authenticateService.authenticate(body['i']).then(([user, app]) => { + // API invoking + this.#call(endpoint, exec, user, app, body, ctx).then((res: any) => { + if (ctx.method === 'GET' && endpoint.meta.cacheSec && !body['i'] && !user) { + ctx.set('Cache-Control', `public, max-age=${endpoint.meta.cacheSec}`); + } + reply(res); + }).catch((e: ApiError) => { + reply(e.httpStatusCode ? e.httpStatusCode : e.kind === 'client' ? 400 : 500, e); + }); + + // Log IP + if (user) { + this.metaService.fetch().then(meta => { + if (!meta.enableIpLogging) return; + const ip = ctx.ip; + const ips = this.#userIpHistories.get(user.id); + if (ips == null || !ips.has(ip)) { + if (ips == null) { + this.#userIpHistories.set(user.id, new Set([ip])); + } else { + ips.add(ip); + } + + try { + this.userIpsRepository.createQueryBuilder().insert().values({ + createdAt: new Date(), + userId: user.id, + ip: ip, + }).orIgnore(true).execute(); + } catch { + } + } + }); + } + }).catch(e => { + if (e instanceof AuthenticationError) { + reply(403, new ApiError({ + message: 'Authentication failed. Please ensure your token is correct.', + code: 'AUTHENTICATION_FAILED', + id: 'b0a7f5f8-dc2f-4171-b91f-de88ad238e14', + })); + } else { + reply(500, new ApiError()); + } + }); + }); + } + + async #call( + ep: IEndpoint, + exec: any, + user: CacheableLocalUser | null | undefined, + token: AccessToken | null | undefined, + data: any, + ctx?: Koa.Context, + ) { + const isSecure = user != null && token == null; + const isModerator = user != null && (user.isModerator || user.isAdmin); + + if (ep.meta.secure && !isSecure) { + throw new ApiError(accessDenied); + } + + if (ep.meta.limit) { + // koa will automatically load the `X-Forwarded-For` header if `proxy: true` is configured in the app. + let limitActor: string; + if (user) { + limitActor = user.id; + } else { + limitActor = getIpHash(ctx!.ip); + } + + const limit = Object.assign({}, ep.meta.limit); + + if (!limit.key) { + limit.key = ep.name; + } + + // Rate limit + await this.rateLimiterService.limit(limit as IEndpointMeta['limit'] & { key: NonNullable }, limitActor).catch(e => { + throw new ApiError({ + message: 'Rate limit exceeded. Please try again later.', + code: 'RATE_LIMIT_EXCEEDED', + id: 'd5826d14-3982-4d2e-8011-b9e9f02499ef', + httpStatusCode: 429, + }); + }); + } + + if (ep.meta.requireCredential && user == null) { + throw new ApiError({ + message: 'Credential required.', + code: 'CREDENTIAL_REQUIRED', + id: '1384574d-a912-4b81-8601-c7b1c4085df1', + httpStatusCode: 401, + }); + } + + if (ep.meta.requireCredential && user!.isSuspended) { + throw new ApiError({ + message: 'Your account has been suspended.', + code: 'YOUR_ACCOUNT_SUSPENDED', + id: 'a8c724b3-6e9c-4b46-b1a8-bc3ed6258370', + httpStatusCode: 403, + }); + } + + if (ep.meta.requireAdmin && !user!.isAdmin) { + throw new ApiError(accessDenied, { reason: 'You are not the admin.' }); + } + + if (ep.meta.requireModerator && !isModerator) { + throw new ApiError(accessDenied, { reason: 'You are not a moderator.' }); + } + + if (token && ep.meta.kind && !token.permission.some(p => p === ep.meta.kind)) { + throw new ApiError({ + message: 'Your app does not have the necessary permissions to use this endpoint.', + code: 'PERMISSION_DENIED', + id: '1370e5b7-d4eb-4566-bb1d-7748ee6a1838', + }); + } + + // Cast non JSON input + if ((ep.meta.requireFile || ctx?.method === 'GET') && ep.params.properties) { + for (const k of Object.keys(ep.params.properties)) { + const param = ep.params.properties![k]; + if (['boolean', 'number', 'integer'].includes(param.type ?? '') && typeof data[k] === 'string') { + try { + data[k] = JSON.parse(data[k]); + } catch (e) { + throw new ApiError({ + message: 'Invalid param.', + code: 'INVALID_PARAM', + id: '0b5f1631-7c1a-41a6-b399-cce335f34d85', + }, { + param: k, + reason: `cannot cast to ${param.type}`, + }); + } + } + } + } + + // API invoking + const before = performance.now(); + return await exec(data, user, token, ctx?.file, ctx?.ip, ctx?.headers).catch((e: Error) => { + if (e instanceof ApiError) { + throw e; + } else { + this.#logger.error(`Internal error occurred in ${ep.name}: ${e.message}`, { + ep: ep.name, + ps: data, + e: { + message: e.message, + code: e.name, + stack: e.stack, + }, + }); + throw new ApiError(null, { + e: { + message: e.message, + code: e.name, + stack: e.stack, + }, + }); + } + }).finally(() => { + const after = performance.now(); + const time = after - before; + if (time > 1000) { + this.#logger.warn(`SLOW API CALL DETECTED: ${ep.name} (${time}ms)`); + } + }); + } + + public onApplicationShutdown(signal?: string | undefined) { + clearInterval(this.#userIpHistoriesClearIntervalId); + } +} diff --git a/packages/backend/src/server/api/ApiLoggerService.ts b/packages/backend/src/server/api/ApiLoggerService.ts new file mode 100644 index 000000000000..c8c5fec85cd3 --- /dev/null +++ b/packages/backend/src/server/api/ApiLoggerService.ts @@ -0,0 +1,12 @@ +import { Inject, Injectable } from '@nestjs/common'; +import Logger from '@/logger.js'; + +@Injectable() +export class ApiLoggerService { + public logger: Logger; + + constructor( + ) { + this.logger = new Logger('api'); + } +} diff --git a/packages/backend/src/server/api/AuthenticateService.ts b/packages/backend/src/server/api/AuthenticateService.ts new file mode 100644 index 000000000000..9b5e1dc2a3aa --- /dev/null +++ b/packages/backend/src/server/api/AuthenticateService.ts @@ -0,0 +1,86 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { AccessTokens, Apps , Users } from '@/models/index.js'; +import type { CacheableLocalUser, ILocalUser } from '@/models/entities/user.js'; +import type { AccessToken } from '@/models/entities/access-token.js'; +import { Cache } from '@/misc/cache.js'; +import type { App } from '@/models/entities/app.js'; +import { UserCacheService } from '@/services/UserCacheService.js'; +import isNativeToken from './common/is-native-token.js'; + +export class AuthenticationError extends Error { + constructor(message: string) { + super(message); + this.name = 'AuthenticationError'; + } +} + +@Injectable() +export class AuthenticateService { + #appCache: Cache; + + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('accessTokensRepository') + private accessTokensRepository: typeof AccessTokens, + + @Inject('appsRepository') + private appsRepository: typeof Apps, + + private userCacheService: UserCacheService, + ) { + this.#appCache = new Cache(Infinity); + } + + public async authenticate(token: string | null): Promise<[CacheableLocalUser | null | undefined, AccessToken | null | undefined]> { + if (token == null) { + return [null, null]; + } + + if (isNativeToken(token)) { + const user = await this.userCacheService.localUserByNativeTokenCache.fetch(token, + () => this.usersRepository.findOneBy({ token }) as Promise); + + if (user == null) { + throw new AuthenticationError('user not found'); + } + + return [user, null]; + } else { + const accessToken = await this.accessTokensRepository.findOne({ + where: [{ + hash: token.toLowerCase(), // app + }, { + token: token, // miauth + }], + }); + + if (accessToken == null) { + throw new AuthenticationError('invalid signature'); + } + + this.accessTokensRepository.update(accessToken.id, { + lastUsedAt: new Date(), + }); + + const user = await this.userCacheService.localUserByIdCache.fetch(accessToken.userId, + () => this.usersRepository.findOneBy({ + id: accessToken.userId, + }) as Promise); + + if (accessToken.appId) { + const app = await this.#appCache.fetch(accessToken.appId, + () => this.appsRepository.findOneByOrFail({ id: accessToken.appId! })); + + return [user, { + id: accessToken.id, + permission: app.permission, + } as AccessToken]; + } else { + return [user, accessToken]; + } + } + } +} diff --git a/packages/backend/src/server/api/RateLimiterService.ts b/packages/backend/src/server/api/RateLimiterService.ts new file mode 100644 index 000000000000..9c73955ee555 --- /dev/null +++ b/packages/backend/src/server/api/RateLimiterService.ts @@ -0,0 +1,89 @@ +import { Inject, Injectable } from '@nestjs/common'; +import Limiter from 'ratelimiter'; +import Redis from 'ioredis'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import Logger from '@/logger'; +import type { IEndpointMeta } from './endpoints'; + +const logger = new Logger('limiter'); + +@Injectable() +export class RateLimiterService { + constructor( + @Inject(DI_SYMBOLS.redis) + private redisClient: Redis.Redis, + ) { + } + + public limit(limitation: IEndpointMeta['limit'] & { key: NonNullable }, actor: string) { + return new Promise((ok, reject) => { + if (process.env.NODE_ENV === 'test') ok(); + + // Short-term limit + const min = (): void => { + const minIntervalLimiter = new Limiter({ + id: `${actor}:${limitation.key}:min`, + duration: limitation.minInterval, + max: 1, + db: this.redisClient, + }); + + minIntervalLimiter.get((err, info) => { + if (err) { + return reject('ERR'); + } + + logger.debug(`${actor} ${limitation.key} min remaining: ${info.remaining}`); + + if (info.remaining === 0) { + reject('BRIEF_REQUEST_INTERVAL'); + } else { + if (hasLongTermLimit) { + max(); + } else { + ok(); + } + } + }); + }; + + // Long term limit + const max = (): void => { + const limiter = new Limiter({ + id: `${actor}:${limitation.key}`, + duration: limitation.duration, + max: limitation.max, + db: this.redisClient, + }); + + limiter.get((err, info) => { + if (err) { + return reject('ERR'); + } + + logger.debug(`${actor} ${limitation.key} max remaining: ${info.remaining}`); + + if (info.remaining === 0) { + reject('RATE_LIMIT_EXCEEDED'); + } else { + ok(); + } + }); + }; + + const hasShortTermLimit = typeof limitation.minInterval === 'number'; + + const hasLongTermLimit = + typeof limitation.duration === 'number' && + typeof limitation.max === 'number'; + + if (hasShortTermLimit) { + min(); + } else if (hasLongTermLimit) { + max(); + } else { + ok(); + } + }); + } +} diff --git a/packages/backend/src/server/api/api-handler.ts b/packages/backend/src/server/api/api-handler.ts deleted file mode 100644 index 354ec01681ad..000000000000 --- a/packages/backend/src/server/api/api-handler.ts +++ /dev/null @@ -1,92 +0,0 @@ - -import type { User } from '@/models/entities/user.js'; -import { UserIps } from '@/models/index.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import authenticate, { AuthenticationError } from './authenticate.js'; -import call from './call.js'; -import { ApiError } from './error.js'; -import type { IEndpoint } from './endpoints.js'; -import type Koa from 'koa'; - -const userIpHistories = new Map>(); - -setInterval(() => { - userIpHistories.clear(); -}, 1000 * 60 * 60); - -export default (endpoint: IEndpoint, exec: any, ctx: Koa.Context) => new Promise((res) => { - const body = ctx.is('multipart/form-data') - ? (ctx.request as any).body - : ctx.method === 'GET' - ? ctx.query - : ctx.request.body; - - const reply = (x?: any, y?: ApiError) => { - if (x == null) { - ctx.status = 204; - } else if (typeof x === 'number' && y) { - ctx.status = x; - ctx.body = { - error: { - message: y!.message, - code: y!.code, - id: y!.id, - kind: y!.kind, - ...(y!.info ? { info: y!.info } : {}), - }, - }; - } else { - // 文字列を返す場合は、JSON.stringify通さないとJSONと認識されない - ctx.body = typeof x === 'string' ? JSON.stringify(x) : x; - } - res(); - }; - - // Authentication - authenticate(body['i']).then(([user, app]) => { - // API invoking - call(endpoint, exec, user, app, body, ctx).then((res: any) => { - if (ctx.method === 'GET' && endpoint.meta.cacheSec && !body['i'] && !user) { - ctx.set('Cache-Control', `public, max-age=${endpoint.meta.cacheSec}`); - } - reply(res); - }).catch((e: ApiError) => { - reply(e.httpStatusCode ? e.httpStatusCode : e.kind === 'client' ? 400 : 500, e); - }); - - // Log IP - if (user) { - fetchMeta().then(meta => { - if (!meta.enableIpLogging) return; - const ip = ctx.ip; - const ips = userIpHistories.get(user.id); - if (ips == null || !ips.has(ip)) { - if (ips == null) { - userIpHistories.set(user.id, new Set([ip])); - } else { - ips.add(ip); - } - - try { - UserIps.createQueryBuilder().insert().values({ - createdAt: new Date(), - userId: user.id, - ip: ip, - }).orIgnore(true).execute(); - } catch { - } - } - }); - } - }).catch(e => { - if (e instanceof AuthenticationError) { - reply(403, new ApiError({ - message: 'Authentication failed. Please ensure your token is correct.', - code: 'AUTHENTICATION_FAILED', - id: 'b0a7f5f8-dc2f-4171-b91f-de88ad238e14', - })); - } else { - reply(500, new ApiError()); - } - }); -}); diff --git a/packages/backend/src/server/api/authenticate.ts b/packages/backend/src/server/api/authenticate.ts deleted file mode 100644 index 65ccfcf55147..000000000000 --- a/packages/backend/src/server/api/authenticate.ts +++ /dev/null @@ -1,66 +0,0 @@ -import isNativeToken from './common/is-native-token.js'; -import { CacheableLocalUser, ILocalUser } from '@/models/entities/user.js'; -import { Users, AccessTokens, Apps } from '@/models/index.js'; -import { AccessToken } from '@/models/entities/access-token.js'; -import { Cache } from '@/misc/cache.js'; -import { App } from '@/models/entities/app.js'; -import { localUserByIdCache, localUserByNativeTokenCache } from '@/services/user-cache.js'; - -const appCache = new Cache(Infinity); - -export class AuthenticationError extends Error { - constructor(message: string) { - super(message); - this.name = 'AuthenticationError'; - } -} - -export default async (token: string | null): Promise<[CacheableLocalUser | null | undefined, AccessToken | null | undefined]> => { - if (token == null) { - return [null, null]; - } - - if (isNativeToken(token)) { - const user = await localUserByNativeTokenCache.fetch(token, - () => Users.findOneBy({ token }) as Promise); - - if (user == null) { - throw new AuthenticationError('user not found'); - } - - return [user, null]; - } else { - const accessToken = await AccessTokens.findOne({ - where: [{ - hash: token.toLowerCase(), // app - }, { - token: token, // miauth - }], - }); - - if (accessToken == null) { - throw new AuthenticationError('invalid signature'); - } - - AccessTokens.update(accessToken.id, { - lastUsedAt: new Date(), - }); - - const user = await localUserByIdCache.fetch(accessToken.userId, - () => Users.findOneBy({ - id: accessToken.userId, - }) as Promise); - - if (accessToken.appId) { - const app = await appCache.fetch(accessToken.appId, - () => Apps.findOneByOrFail({ id: accessToken.appId! })); - - return [user, { - id: accessToken.id, - permission: app.permission, - } as AccessToken]; - } else { - return [user, accessToken]; - } - } -}; diff --git a/packages/backend/src/server/api/call.ts b/packages/backend/src/server/api/call.ts deleted file mode 100644 index 46d02d891d15..000000000000 --- a/packages/backend/src/server/api/call.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { performance } from 'perf_hooks'; -import type { CacheableLocalUser } from '@/models/entities/user.js'; -import type { AccessToken } from '@/models/entities/access-token.js'; -import { getIpHash } from '@/misc/get-ip-hash.js'; -import { limiter } from './limiter.js'; -import { ApiError } from './error.js'; -import { apiLogger } from './logger.js'; -import type { IEndpointMeta, IEndpoint } from './endpoints.js'; -import type Koa from 'koa'; - -const accessDenied = { - message: 'Access denied.', - code: 'ACCESS_DENIED', - id: '56f35758-7dd5-468b-8439-5d6fb8ec9b8e', -}; - -export default async (ep: IEndpoint, exec: any, user: CacheableLocalUser | null | undefined, token: AccessToken | null | undefined, data: any, ctx?: Koa.Context) => { - const isSecure = user != null && token == null; - const isModerator = user != null && (user.isModerator || user.isAdmin); - - if (ep.meta.secure && !isSecure) { - throw new ApiError(accessDenied); - } - - if (ep.meta.limit) { - // koa will automatically load the `X-Forwarded-For` header if `proxy: true` is configured in the app. - let limitActor: string; - if (user) { - limitActor = user.id; - } else { - limitActor = getIpHash(ctx!.ip); - } - - const limit = Object.assign({}, ep.meta.limit); - - if (!limit.key) { - limit.key = ep.name; - } - - // Rate limit - await limiter(limit as IEndpointMeta['limit'] & { key: NonNullable }, limitActor).catch(e => { - throw new ApiError({ - message: 'Rate limit exceeded. Please try again later.', - code: 'RATE_LIMIT_EXCEEDED', - id: 'd5826d14-3982-4d2e-8011-b9e9f02499ef', - httpStatusCode: 429, - }); - }); - } - - if (ep.meta.requireCredential && user == null) { - throw new ApiError({ - message: 'Credential required.', - code: 'CREDENTIAL_REQUIRED', - id: '1384574d-a912-4b81-8601-c7b1c4085df1', - httpStatusCode: 401, - }); - } - - if (ep.meta.requireCredential && user!.isSuspended) { - throw new ApiError({ - message: 'Your account has been suspended.', - code: 'YOUR_ACCOUNT_SUSPENDED', - id: 'a8c724b3-6e9c-4b46-b1a8-bc3ed6258370', - httpStatusCode: 403, - }); - } - - if (ep.meta.requireAdmin && !user!.isAdmin) { - throw new ApiError(accessDenied, { reason: 'You are not the admin.' }); - } - - if (ep.meta.requireModerator && !isModerator) { - throw new ApiError(accessDenied, { reason: 'You are not a moderator.' }); - } - - if (token && ep.meta.kind && !token.permission.some(p => p === ep.meta.kind)) { - throw new ApiError({ - message: 'Your app does not have the necessary permissions to use this endpoint.', - code: 'PERMISSION_DENIED', - id: '1370e5b7-d4eb-4566-bb1d-7748ee6a1838', - }); - } - - // Cast non JSON input - if ((ep.meta.requireFile || ctx?.method === 'GET') && ep.params.properties) { - for (const k of Object.keys(ep.params.properties)) { - const param = ep.params.properties![k]; - if (['boolean', 'number', 'integer'].includes(param.type ?? '') && typeof data[k] === 'string') { - try { - data[k] = JSON.parse(data[k]); - } catch (e) { - throw new ApiError({ - message: 'Invalid param.', - code: 'INVALID_PARAM', - id: '0b5f1631-7c1a-41a6-b399-cce335f34d85', - }, { - param: k, - reason: `cannot cast to ${param.type}`, - }); - } - } - } - } - - // API invoking - const before = performance.now(); - return await exec(data, user, token, ctx?.file, ctx?.ip, ctx?.headers).catch((e: Error) => { - if (e instanceof ApiError) { - throw e; - } else { - apiLogger.error(`Internal error occurred in ${ep.name}: ${e.message}`, { - ep: ep.name, - ps: data, - e: { - message: e.message, - code: e.name, - stack: e.stack, - }, - }); - throw new ApiError(null, { - e: { - message: e.message, - code: e.name, - stack: e.stack, - }, - }); - } - }).finally(() => { - const after = performance.now(); - const time = after - before; - if (time > 1000) { - apiLogger.warn(`SLOW API CALL DETECTED: ${ep.name} (${time}ms)`); - } - }); -}; diff --git a/packages/backend/src/server/api/index.ts b/packages/backend/src/server/api/index.ts index 9ddfa7686317..e866d954b343 100644 --- a/packages/backend/src/server/api/index.ts +++ b/packages/backend/src/server/api/index.ts @@ -7,20 +7,20 @@ import Router from '@koa/router'; import multer from '@koa/multer'; import bodyParser from 'koa-bodyparser'; import cors from '@koa/cors'; - import { Instances, AccessTokens, Users } from '@/models/index.js'; -import config from '@/config/index.js'; import endpoints from './endpoints.js'; -import handler from './api-handler.js'; import signup from './private/signup.js'; import signin from './private/signin.js'; import signupPending from './private/signup-pending.js'; import discord from './service/discord.js'; import github from './service/github.js'; import twitter from './service/twitter.js'; +import { ApiCallService } from './ApiCallService.js'; import type { INestApplicationContext } from '@nestjs/common'; export function createApiServer(app: INestApplicationContext) { + const apiCallService = app.get(ApiCallService); + const handlers: Record = {}; for (const endpoint of endpoints) { @@ -62,23 +62,23 @@ export function createApiServer(app: INestApplicationContext) { */ for (const endpoint of endpoints) { if (endpoint.meta.requireFile) { - router.post(`/${endpoint.name}`, upload.single('file'), handler.bind(null, endpoint, handlers[endpoint.name])); + router.post(`/${endpoint.name}`, upload.single('file'), apiCallService.handleRequest.bind(apiCallService, endpoint, handlers[endpoint.name])); } else { // 後方互換性のため if (endpoint.name.includes('-')) { - router.post(`/${endpoint.name.replace(/-/g, '_')}`, handler.bind(null, endpoint, handlers[endpoint.name])); + router.post(`/${endpoint.name.replace(/-/g, '_')}`, apiCallService.handleRequest.bind(apiCallService, endpoint, handlers[endpoint.name])); if (endpoint.meta.allowGet) { - router.get(`/${endpoint.name.replace(/-/g, '_')}`, handler.bind(null, endpoint, handlers[endpoint.name])); + router.get(`/${endpoint.name.replace(/-/g, '_')}`, apiCallService.handleRequest.bind(apiCallService, endpoint, handlers[endpoint.name])); } else { router.get(`/${endpoint.name.replace(/-/g, '_')}`, async ctx => { ctx.status = 405; }); } } - router.post(`/${endpoint.name}`, handler.bind(null, endpoint, handlers[endpoint.name])); + router.post(`/${endpoint.name}`, apiCallService.handleRequest.bind(apiCallService, endpoint, handlers[endpoint.name])); if (endpoint.meta.allowGet) { - router.get(`/${endpoint.name}`, handler.bind(null, endpoint, handlers[endpoint.name])); + router.get(`/${endpoint.name}`, apiCallService.handleRequest.bind(apiCallService, endpoint, handlers[endpoint.name])); } else { router.get(`/${endpoint.name}`, async ctx => { ctx.status = 405; }); } diff --git a/packages/backend/src/server/api/limiter.ts b/packages/backend/src/server/api/limiter.ts deleted file mode 100644 index 9a7751716e17..000000000000 --- a/packages/backend/src/server/api/limiter.ts +++ /dev/null @@ -1,77 +0,0 @@ -import Limiter from 'ratelimiter'; -import { CacheableLocalUser, User } from '@/models/entities/user.js'; -import Logger from '@/services/logger.js'; -import { redisClient } from '../../db/redis.js'; -import { IEndpointMeta } from './endpoints.js'; - -const logger = new Logger('limiter'); - -export const limiter = (limitation: IEndpointMeta['limit'] & { key: NonNullable }, actor: string) => new Promise((ok, reject) => { - if (process.env.NODE_ENV === 'test') ok(); - - const hasShortTermLimit = typeof limitation.minInterval === 'number'; - - const hasLongTermLimit = - typeof limitation.duration === 'number' && - typeof limitation.max === 'number'; - - if (hasShortTermLimit) { - min(); - } else if (hasLongTermLimit) { - max(); - } else { - ok(); - } - - // Short-term limit - function min(): void { - const minIntervalLimiter = new Limiter({ - id: `${actor}:${limitation.key}:min`, - duration: limitation.minInterval, - max: 1, - db: redisClient, - }); - - minIntervalLimiter.get((err, info) => { - if (err) { - return reject('ERR'); - } - - logger.debug(`${actor} ${limitation.key} min remaining: ${info.remaining}`); - - if (info.remaining === 0) { - reject('BRIEF_REQUEST_INTERVAL'); - } else { - if (hasLongTermLimit) { - max(); - } else { - ok(); - } - } - }); - } - - // Long term limit - function max(): void { - const limiter = new Limiter({ - id: `${actor}:${limitation.key}`, - duration: limitation.duration, - max: limitation.max, - db: redisClient, - }); - - limiter.get((err, info) => { - if (err) { - return reject('ERR'); - } - - logger.debug(`${actor} ${limitation.key} max remaining: ${info.remaining}`); - - if (info.remaining === 0) { - reject('RATE_LIMIT_EXCEEDED'); - } else { - ok(); - } - }); - } -}); diff --git a/packages/backend/src/server/api/logger.ts b/packages/backend/src/server/api/logger.ts deleted file mode 100644 index ec22d6c3e28f..000000000000 --- a/packages/backend/src/server/api/logger.ts +++ /dev/null @@ -1,3 +0,0 @@ -import Logger from '@/services/logger.js'; - -export const apiLogger = new Logger('api'); diff --git a/packages/backend/src/services/AppLockService.ts b/packages/backend/src/services/AppLockService.ts index 5bc4dd192354..37516efd56be 100644 --- a/packages/backend/src/services/AppLockService.ts +++ b/packages/backend/src/services/AppLockService.ts @@ -1,7 +1,8 @@ import { promisify } from 'node:util'; import { Inject, Injectable } from '@nestjs/common'; import redisLock from 'redis-lock'; -import type Redis from 'ioredis'; +import Redis from 'ioredis'; +import { DI_SYMBOLS } from '@/di-symbols.js'; /** * Retry delay (ms) for lock acquisition @@ -13,7 +14,7 @@ export class AppLockService { #lock: (key: string, timeout?: number) => Promise<() => void>; constructor( - @Inject('redis') + @Inject(DI_SYMBOLS.redis) private redisClient: Redis.Redis, ) { this.#lock = promisify(redisLock(this.redisClient, retryDelay)); diff --git a/packages/backend/src/services/GlobalEventService.ts b/packages/backend/src/services/GlobalEventService.ts index 340b857e7f30..327eca26a6e0 100644 --- a/packages/backend/src/services/GlobalEventService.ts +++ b/packages/backend/src/services/GlobalEventService.ts @@ -1,4 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; +import Redis from 'ioredis'; import type { User } from '@/models/entities/user.js'; import type { Note } from '@/models/entities/note.js'; import type { UserList } from '@/models/entities/user-list.js'; @@ -23,8 +24,7 @@ import type { } from '@/server/api/stream/types.js'; import type { Packed } from '@/misc/schema.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; -import type Redis from 'ioredis'; +import { Config } from '@/config/types.js'; @Injectable() export class GlobalEventService { @@ -32,7 +32,7 @@ export class GlobalEventService { @Inject(DI_SYMBOLS.config) private config: Config, - @Inject('redis') + @Inject(DI_SYMBOLS.redis) private redisClient: Redis.Redis, ) { } From 4d91bc4d0d5fa566cc81211e49b47c54da4aec86 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 14 Sep 2022 03:07:38 +0900 Subject: [PATCH 076/180] wip --- .../src/server/api/ApiServerService.ts | 148 ++++++++++++++++++ packages/backend/src/server/api/index.ts | 135 ---------------- 2 files changed, 148 insertions(+), 135 deletions(-) create mode 100644 packages/backend/src/server/api/ApiServerService.ts delete mode 100644 packages/backend/src/server/api/index.ts diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts new file mode 100644 index 000000000000..9dbd12072038 --- /dev/null +++ b/packages/backend/src/server/api/ApiServerService.ts @@ -0,0 +1,148 @@ +import { Inject, Injectable } from '@nestjs/common'; +import Koa from 'koa'; +import Router from '@koa/router'; +import multer from '@koa/multer'; +import bodyParser from 'koa-bodyparser'; +import cors from '@koa/cors'; +import { ModuleRef } from '@nestjs/core'; +import { Config } from '@/config/types.js'; +import { Users , Instances, AccessTokens } from '@/models/index.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import endpoints from './endpoints.js'; +import signup from './private/signup.js'; +import signin from './private/signin.js'; +import signupPending from './private/signup-pending.js'; +import discord from './service/discord.js'; +import github from './service/github.js'; +import twitter from './service/twitter.js'; +import { ApiCallService } from './ApiCallService.js'; + +@Injectable() +export class ApiServerService { + constructor( + private moduleRef: ModuleRef, + + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + private apiCallService: ApiCallService, + ) { + } + + public createApiServer() { + const handlers: Record = {}; + + for (const endpoint of endpoints) { + handlers[endpoint.name] = this.moduleRef.get('ep:' + endpoint.name).exec; + } + + // Init app + const apiServer = new Koa(); + + apiServer.use(cors({ + origin: '*', + })); + + // No caching + apiServer.use(async (ctx, next) => { + ctx.set('Cache-Control', 'private, max-age=0, must-revalidate'); + await next(); + }); + + apiServer.use(bodyParser({ + // リクエストが multipart/form-data でない限りはJSONだと見なす + detectJSON: ctx => !ctx.is('multipart/form-data'), + })); + + // Init multer instance + const upload = multer({ + storage: multer.diskStorage({}), + limits: { + fileSize: this.config.maxFileSize ?? 262144000, + files: 1, + }, + }); + + // Init router + const router = new Router(); + + /** + * Register endpoint handlers + */ + for (const endpoint of endpoints) { + if (endpoint.meta.requireFile) { + router.post(`/${endpoint.name}`, upload.single('file'), this.apiCallService.handleRequest.bind(this.apiCallService, endpoint, handlers[endpoint.name])); + } else { + // 後方互換性のため + if (endpoint.name.includes('-')) { + router.post(`/${endpoint.name.replace(/-/g, '_')}`, this.apiCallService.handleRequest.bind(this.apiCallService, endpoint, handlers[endpoint.name])); + + if (endpoint.meta.allowGet) { + router.get(`/${endpoint.name.replace(/-/g, '_')}`, this.apiCallService.handleRequest.bind(this.apiCallService, endpoint, handlers[endpoint.name])); + } else { + router.get(`/${endpoint.name.replace(/-/g, '_')}`, async ctx => { ctx.status = 405; }); + } + } + + router.post(`/${endpoint.name}`, this.apiCallService.handleRequest.bind(this.apiCallService, endpoint, handlers[endpoint.name])); + + if (endpoint.meta.allowGet) { + router.get(`/${endpoint.name}`, this.apiCallService.handleRequest.bind(this.apiCallService, endpoint, handlers[endpoint.name])); + } else { + router.get(`/${endpoint.name}`, async ctx => { ctx.status = 405; }); + } + } + } + + router.post('/signup', signup); + router.post('/signin', signin); + router.post('/signup-pending', signupPending); + + router.use(discord.routes()); + router.use(github.routes()); + router.use(twitter.routes()); + + router.get('/v1/instance/peers', async ctx => { + const instances = await Instances.find({ + select: ['host'], + }); + + ctx.body = instances.map(instance => instance.host); + }); + + router.post('/miauth/:session/check', async ctx => { + const token = await AccessTokens.findOneBy({ + session: ctx.params.session, + }); + + if (token && token.session != null && !token.fetched) { + AccessTokens.update(token.id, { + fetched: true, + }); + + ctx.body = { + ok: true, + token: token.token, + user: await Users.pack(token.userId, null, { detail: true }), + }; + } else { + ctx.body = { + ok: false, + }; + } + }); + + // Return 404 for unknown API + router.all('(.*)', async ctx => { + ctx.status = 404; + }); + + // Register router + apiServer.use(router.routes()); + + return apiServer; + } +} diff --git a/packages/backend/src/server/api/index.ts b/packages/backend/src/server/api/index.ts deleted file mode 100644 index e866d954b343..000000000000 --- a/packages/backend/src/server/api/index.ts +++ /dev/null @@ -1,135 +0,0 @@ -/** - * API Server - */ - -import Koa from 'koa'; -import Router from '@koa/router'; -import multer from '@koa/multer'; -import bodyParser from 'koa-bodyparser'; -import cors from '@koa/cors'; -import { Instances, AccessTokens, Users } from '@/models/index.js'; -import endpoints from './endpoints.js'; -import signup from './private/signup.js'; -import signin from './private/signin.js'; -import signupPending from './private/signup-pending.js'; -import discord from './service/discord.js'; -import github from './service/github.js'; -import twitter from './service/twitter.js'; -import { ApiCallService } from './ApiCallService.js'; -import type { INestApplicationContext } from '@nestjs/common'; - -export function createApiServer(app: INestApplicationContext) { - const apiCallService = app.get(ApiCallService); - - const handlers: Record = {}; - - for (const endpoint of endpoints) { - handlers[endpoint.name] = app.get('ep:' + endpoint.name).exec; - } - - // Init app - const apiServer = new Koa(); - - apiServer.use(cors({ - origin: '*', - })); - - // No caching - apiServer.use(async (ctx, next) => { - ctx.set('Cache-Control', 'private, max-age=0, must-revalidate'); - await next(); - }); - - apiServer.use(bodyParser({ - // リクエストが multipart/form-data でない限りはJSONだと見なす - detectJSON: ctx => !ctx.is('multipart/form-data'), - })); - - // Init multer instance - const upload = multer({ - storage: multer.diskStorage({}), - limits: { - fileSize: config.maxFileSize || 262144000, - files: 1, - }, - }); - - // Init router - const router = new Router(); - - /** - * Register endpoint handlers - */ - for (const endpoint of endpoints) { - if (endpoint.meta.requireFile) { - router.post(`/${endpoint.name}`, upload.single('file'), apiCallService.handleRequest.bind(apiCallService, endpoint, handlers[endpoint.name])); - } else { - // 後方互換性のため - if (endpoint.name.includes('-')) { - router.post(`/${endpoint.name.replace(/-/g, '_')}`, apiCallService.handleRequest.bind(apiCallService, endpoint, handlers[endpoint.name])); - - if (endpoint.meta.allowGet) { - router.get(`/${endpoint.name.replace(/-/g, '_')}`, apiCallService.handleRequest.bind(apiCallService, endpoint, handlers[endpoint.name])); - } else { - router.get(`/${endpoint.name.replace(/-/g, '_')}`, async ctx => { ctx.status = 405; }); - } - } - - router.post(`/${endpoint.name}`, apiCallService.handleRequest.bind(apiCallService, endpoint, handlers[endpoint.name])); - - if (endpoint.meta.allowGet) { - router.get(`/${endpoint.name}`, apiCallService.handleRequest.bind(apiCallService, endpoint, handlers[endpoint.name])); - } else { - router.get(`/${endpoint.name}`, async ctx => { ctx.status = 405; }); - } - } - } - - router.post('/signup', signup); - router.post('/signin', signin); - router.post('/signup-pending', signupPending); - - router.use(discord.routes()); - router.use(github.routes()); - router.use(twitter.routes()); - - router.get('/v1/instance/peers', async ctx => { - const instances = await Instances.find({ - select: ['host'], - }); - - ctx.body = instances.map(instance => instance.host); - }); - - router.post('/miauth/:session/check', async ctx => { - const token = await AccessTokens.findOneBy({ - session: ctx.params.session, - }); - - if (token && token.session != null && !token.fetched) { - AccessTokens.update(token.id, { - fetched: true, - }); - - ctx.body = { - ok: true, - token: token.token, - user: await Users.pack(token.userId, null, { detail: true }), - }; - } else { - ctx.body = { - ok: false, - }; - } - }); - - // Return 404 for unknown API - router.all('(.*)', async ctx => { - ctx.status = 404; - }); - - // Register router - apiServer.use(router.routes()); - - return apiServer; -} From c3ec889080894beac1482b6713e3117007153f97 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 14 Sep 2022 03:14:23 +0900 Subject: [PATCH 077/180] wip --- .../server/api/StreamingApiServerService.ts | 112 ++++++++++++++++++ packages/backend/src/server/api/streaming.ts | 87 -------------- 2 files changed, 112 insertions(+), 87 deletions(-) create mode 100644 packages/backend/src/server/api/StreamingApiServerService.ts delete mode 100644 packages/backend/src/server/api/streaming.ts diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts new file mode 100644 index 000000000000..50d5b16d3bdb --- /dev/null +++ b/packages/backend/src/server/api/StreamingApiServerService.ts @@ -0,0 +1,112 @@ +import { EventEmitter } from 'events'; +import { Inject, Injectable } from '@nestjs/common'; +import Redis from 'ioredis'; +import * as websocket from 'websocket'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { Users } from '@/models/index.js'; +import type { Blockings, ChannelFollowings, Followings, Mutings, UserProfiles } from '@/models/index.js'; +import { Config } from '@/config/types.js'; +import { NoteReadService } from '@/services/NoteReadService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { AuthenticateService } from './AuthenticateService'; +import MainStreamConnection from './stream/index.js'; +import type { ParsedUrlQuery } from 'querystring'; +import type * as http from 'node:http'; + +@Injectable() +export class StreamingApiServerService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject(DI_SYMBOLS.redis) + private redisClient: Redis.Redis, + + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + @Inject('mutingsRepository') + private mutingsRepository: typeof Mutings, + + @Inject('blockingsRepository') + private blockingsRepository: typeof Blockings, + + @Inject('channelFollowingsRepository') + private channelFollowingsRepository: typeof ChannelFollowings, + + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + private globalEventService: GlobalEventService, + private noteReadService: NoteReadService, + private authenticateService: AuthenticateService, + ) { + } + + public attachStreamingApi(server: http.Server) { + // Init websocket server + const ws = new websocket.server({ + httpServer: server, + }); + + ws.on('request', async (request) => { + const q = request.resourceURL.query as ParsedUrlQuery; + + // TODO: トークンが間違ってるなどしてauthenticateに失敗したら + // コネクション切断するなりエラーメッセージ返すなりする + // (現状はエラーがキャッチされておらずサーバーのログに流れて邪魔なので) + const [user, miapp] = await this.authenticateService.authenticate(q.i as string); + + if (user?.isSuspended) { + request.reject(400); + return; + } + + const connection = request.accept(); + + const ev = new EventEmitter(); + + async function onRedisMessage(_: string, data: string) { + const parsed = JSON.parse(data); + ev.emit(parsed.channel, parsed.message); + } + + this.redisClient.on('message', onRedisMessage); + + const main = new MainStreamConnection( + this.followingsRepository, + this.mutingsRepository, + this.blockingsRepository, + this.channelFollowingsRepository, + this.userProfilesRepository, + this.globalEventService, + this.noteReadService, + connection, ev, user, miapp, + ); + + const intervalId = user ? setInterval(() => { + Users.update(user.id, { + lastActiveDate: new Date(), + }); + }, 1000 * 60 * 5) : null; + if (user) { + Users.update(user.id, { + lastActiveDate: new Date(), + }); + } + + connection.once('close', () => { + ev.removeAllListeners(); + main.dispose(); + this.redisClient.off('message', onRedisMessage); + if (intervalId) clearInterval(intervalId); + }); + + connection.on('message', async (data) => { + if (data.type === 'utf8' && data.utf8Data === 'ping') { + connection.send('pong'); + } + }); + }); + } +} diff --git a/packages/backend/src/server/api/streaming.ts b/packages/backend/src/server/api/streaming.ts deleted file mode 100644 index a12eba414925..000000000000 --- a/packages/backend/src/server/api/streaming.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { EventEmitter } from 'events'; -import * as websocket from 'websocket'; -import type { Blockings, ChannelFollowings, Followings, Mutings, UserProfiles } from '@/models/index.js'; -import { Users } from '@/models/index.js'; -import { GlobalEventService } from '@/services/GlobalEventService.js'; -import { NoteReadService } from '@/services/NoteReadService.js'; -import { subsdcriber as redisClient } from '../../db/redis.js'; -import MainStreamConnection from './stream/index.js'; -import authenticate from './authenticate.js'; -import type { INestApplicationContext } from '@nestjs/common'; -import type { ParsedUrlQuery } from 'querystring'; -import type * as http from 'node:http'; - -export function initializeStreamingServer(app: INestApplicationContext, server: http.Server) { - const followingsRepository = app.get('followingsRepository'); - const mutingsRepository = app.get('mutingsRepository'); - const blockingsRepository = app.get('blockingsRepository'); - const channelFollowingsRepository = app.get('channelFollowingsRepository'); - const userProfilesRepository = app.get('userProfilesRepository'); - const globalEventService = app.get(GlobalEventService); - const noteReadService = app.get(NoteReadService); - - // Init websocket server - const ws = new websocket.server({ - httpServer: server, - }); - - ws.on('request', async (request) => { - const q = request.resourceURL.query as ParsedUrlQuery; - - // TODO: トークンが間違ってるなどしてauthenticateに失敗したら - // コネクション切断するなりエラーメッセージ返すなりする - // (現状はエラーがキャッチされておらずサーバーのログに流れて邪魔なので) - const [user, miapp] = await authenticate(q.i as string); - - if (user?.isSuspended) { - request.reject(400); - return; - } - - const connection = request.accept(); - - const ev = new EventEmitter(); - - async function onRedisMessage(_: string, data: string) { - const parsed = JSON.parse(data); - ev.emit(parsed.channel, parsed.message); - } - - redisClient.on('message', onRedisMessage); - - const main = new MainStreamConnection( - followingsRepository, - mutingsRepository, - blockingsRepository, - channelFollowingsRepository, - userProfilesRepository, - globalEventService, - noteReadService, - connection, ev, user, miapp, - ); - - const intervalId = user ? setInterval(() => { - Users.update(user.id, { - lastActiveDate: new Date(), - }); - }, 1000 * 60 * 5) : null; - if (user) { - Users.update(user.id, { - lastActiveDate: new Date(), - }); - } - - connection.once('close', () => { - ev.removeAllListeners(); - main.dispose(); - redisClient.off('message', onRedisMessage); - if (intervalId) clearInterval(intervalId); - }); - - connection.on('message', async (data) => { - if (data.type === 'utf8' && data.utf8Data === 'ping') { - connection.send('pong'); - } - }); - }); -} From eb3209ced0b883e605071e4b0a2a14e4f9c9855c Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 14 Sep 2022 03:50:12 +0900 Subject: [PATCH 078/180] wip --- packages/backend/src/misc/check-word-mute.ts | 4 +- .../server/api/StreamingApiServerService.ts | 3 + .../src/server/api/stream/ChannelsService.ts | 43 +++++++++++++++ .../backend/src/server/api/stream/channel.ts | 2 +- .../api/stream/channels/global-timeline.ts | 48 +++++++++++++--- .../api/stream/channels/home-timeline.ts | 42 +++++++++++--- .../api/stream/channels/hybrid-timeline.ts | 55 +++++++++++++++---- .../src/server/api/stream/channels/index.ts | 33 ----------- .../api/stream/channels/local-timeline.ts | 48 +++++++++++++--- .../src/server/api/stream/channels/main.ts | 40 ++++++++++++-- .../backend/src/server/api/stream/index.ts | 11 ++-- 11 files changed, 246 insertions(+), 83 deletions(-) create mode 100644 packages/backend/src/server/api/stream/ChannelsService.ts delete mode 100644 packages/backend/src/server/api/stream/channels/index.ts diff --git a/packages/backend/src/misc/check-word-mute.ts b/packages/backend/src/misc/check-word-mute.ts index d7662820afc4..e411bbb4e16e 100644 --- a/packages/backend/src/misc/check-word-mute.ts +++ b/packages/backend/src/misc/check-word-mute.ts @@ -1,6 +1,6 @@ import RE2 from 're2'; -import { Note } from '@/models/entities/note.js'; -import { User } from '@/models/entities/user.js'; +import type { Note } from '@/models/entities/note.js'; +import type { User } from '@/models/entities/user.js'; type NoteLike = { userId: Note['userId']; diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts index 50d5b16d3bdb..524261a0071c 100644 --- a/packages/backend/src/server/api/StreamingApiServerService.ts +++ b/packages/backend/src/server/api/StreamingApiServerService.ts @@ -10,6 +10,7 @@ import { NoteReadService } from '@/services/NoteReadService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { AuthenticateService } from './AuthenticateService'; import MainStreamConnection from './stream/index.js'; +import { ChannelsService } from './stream/ChannelsService.js'; import type { ParsedUrlQuery } from 'querystring'; import type * as http from 'node:http'; @@ -40,6 +41,7 @@ export class StreamingApiServerService { private globalEventService: GlobalEventService, private noteReadService: NoteReadService, private authenticateService: AuthenticateService, + private channelsService: ChannelsService, ) { } @@ -79,6 +81,7 @@ export class StreamingApiServerService { this.blockingsRepository, this.channelFollowingsRepository, this.userProfilesRepository, + this.channelsService, this.globalEventService, this.noteReadService, connection, ev, user, miapp, diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts new file mode 100644 index 000000000000..527830350817 --- /dev/null +++ b/packages/backend/src/server/api/stream/ChannelsService.ts @@ -0,0 +1,43 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { HybridTimelineChannelService } from './channels/hybrid-timeline.js'; +import { LocalTimelineChannelService } from './channels/local-timeline.js'; +import { HomeTimelineChannelService } from './channels/home-timeline.js'; +import { GlobalTimelineChannelService } from './channels/global-timeline.js'; +import { MainChannelService } from './channels/main.js'; + +@Injectable() +export class ChannelsService { + constructor( + private mainChannelService: MainChannelService, + private homeTimelineChannelService: HomeTimelineChannelService, + private localTimelineChannelService: LocalTimelineChannelService, + private hybridTimelineChannelService: HybridTimelineChannelService, + private globalTimelineChannelService: GlobalTimelineChannelService, + ) { + } + + public getChannelService(name: string) { + switch (name) { + case 'main': return this.mainChannelService; + case 'homeTimeline': return this.homeTimelineChannelService; + case 'localTimeline': return this.localTimelineChannelService; + case 'hybridTimeline': return this.hybridTimelineChannelService; + case 'globalTimeline': return this.globalTimelineChannelService; + + default: + throw new Error(`no such channel: ${name}`); + } + } +} + + serverStats, + queueStats, + userList, + antenna, + messaging, + messagingIndex, + drive, + hashtag, + channel, + admin, diff --git a/packages/backend/src/server/api/stream/channel.ts b/packages/backend/src/server/api/stream/channel.ts index d2cc5122d59e..5480c12c09e3 100644 --- a/packages/backend/src/server/api/stream/channel.ts +++ b/packages/backend/src/server/api/stream/channel.ts @@ -1,4 +1,4 @@ -import Connection from '.'; +import type Connection from '.'; /** * Stream channel diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index 5b4ae850ec10..d4078ebae273 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -1,23 +1,30 @@ -import Channel from '../channel.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import { Notes } from '@/models/index.js'; +import { Inject, Injectable } from '@nestjs/common'; +import type { Notes } from '@/models/index.js'; import { checkWordMute } from '@/misc/check-word-mute.js'; import { isInstanceMuted } from '@/misc/is-instance-muted.js'; import { isUserRelated } from '@/misc/is-user-related.js'; -import { Packed } from '@/misc/schema.js'; +import type { Packed } from '@/misc/schema.js'; +import { MetaService } from '@/services/MetaService.js'; +import Channel from '../channel.js'; -export default class extends Channel { +class GlobalTimelineChannel extends Channel { public readonly chName = 'globalTimeline'; public static shouldShare = true; public static requireCredential = false; - constructor(id: string, connection: Channel['connection']) { + constructor( + private metaService: MetaService, + private notesRepository: typeof Notes, + + id: string, + connection: Channel['connection'], + ) { super(id, connection); this.onNote = this.onNote.bind(this); } public async init(params: any) { - const meta = await fetchMeta(); + const meta = await this.metaService.fetch(); if (meta.disableGlobalTimeline) { if (this.user == null || (!this.user.isAdmin && !this.user.isModerator)) return; } @@ -32,13 +39,13 @@ export default class extends Channel { // リプライなら再pack if (note.replyId != null) { - note.reply = await Notes.pack(note.replyId, this.user, { + note.reply = await this.notesRepository.pack(note.replyId, this.user, { detail: true, }); } // Renoteなら再pack if (note.renoteId != null) { - note.renote = await Notes.pack(note.renoteId, this.user, { + note.renote = await this.notesRepository.pack(note.renoteId, this.user, { detail: true, }); } @@ -75,3 +82,26 @@ export default class extends Channel { this.subscriber.off('notesStream', this.onNote); } } + +@Injectable() +export class GlobalTimelineChannelService { + public readonly shouldShare = GlobalTimelineChannel.shouldShare; + public readonly requireCredential = GlobalTimelineChannel.requireCredential; + + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + + private metaService: MetaService, + ) { + } + + public create(id: string, connection: Channel['connection']): GlobalTimelineChannel { + return new GlobalTimelineChannel( + this.metaService, + this.notesRepository, + id, + connection, + ); + } +} diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index 075a242ef0bc..56cdbde5b18b 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -1,16 +1,22 @@ -import Channel from '../channel.js'; -import { Notes } from '@/models/index.js'; +import { Inject, Injectable } from '@nestjs/common'; +import type { Notes } from '@/models/index.js'; import { checkWordMute } from '@/misc/check-word-mute.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { isInstanceMuted } from '@/misc/is-instance-muted.js'; -import { Packed } from '@/misc/schema.js'; +import type { Packed } from '@/misc/schema.js'; +import Channel from '../channel.js'; -export default class extends Channel { +class HomeTimelineChannel extends Channel { public readonly chName = 'homeTimeline'; public static shouldShare = true; public static requireCredential = true; - constructor(id: string, connection: Channel['connection']) { + constructor( + private notesRepository: typeof Notes, + + id: string, + connection: Channel['connection'], + ) { super(id, connection); this.onNote = this.onNote.bind(this); } @@ -32,7 +38,7 @@ export default class extends Channel { if (isInstanceMuted(note, new Set(this.userProfile?.mutedInstances ?? []))) return; if (['followers', 'specified'].includes(note.visibility)) { - note = await Notes.pack(note.id, this.user!, { + note = await this.notesRepository.pack(note.id, this.user!, { detail: true, }); @@ -42,13 +48,13 @@ export default class extends Channel { } else { // リプライなら再pack if (note.replyId != null) { - note.reply = await Notes.pack(note.replyId, this.user!, { + note.reply = await this.notesRepository.pack(note.replyId, this.user!, { detail: true, }); } // Renoteなら再pack if (note.renoteId != null) { - note.renote = await Notes.pack(note.renoteId, this.user!, { + note.renote = await this.notesRepository.pack(note.renoteId, this.user!, { detail: true, }); } @@ -83,3 +89,23 @@ export default class extends Channel { this.subscriber.off('notesStream', this.onNote); } } + +@Injectable() +export class HomeTimelineChannelService { + public readonly shouldShare = HomeTimelineChannel.shouldShare; + public readonly requireCredential = HomeTimelineChannel.requireCredential; + + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + } + + public create(id: string, connection: Channel['connection']): HomeTimelineChannel { + return new HomeTimelineChannel( + this.notesRepository, + id, + connection, + ); + } +} diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index f5dedf77ce3d..5cf334546e68 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -1,23 +1,31 @@ -import Channel from '../channel.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import { Notes } from '@/models/index.js'; +import { Inject, Injectable } from '@nestjs/common'; +import type { Notes } from '@/models/index.js'; import { checkWordMute } from '@/misc/check-word-mute.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { isInstanceMuted } from '@/misc/is-instance-muted.js'; -import { Packed } from '@/misc/schema.js'; +import type { Packed } from '@/misc/schema.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { MetaService } from '@/services/MetaService.js'; +import Channel from '../channel.js'; -export default class extends Channel { +class HybridTimelineChannel extends Channel { public readonly chName = 'hybridTimeline'; public static shouldShare = true; public static requireCredential = true; - constructor(id: string, connection: Channel['connection']) { + constructor( + private metaService: MetaService, + private notesRepository: typeof Notes, + + id: string, + connection: Channel['connection'], + ) { super(id, connection); this.onNote = this.onNote.bind(this); } - public async init(params: any) { - const meta = await fetchMeta(); + public async init(params: any): Promise { + const meta = await this.metaService.fetch(); if (meta.disableLocalTimeline && !this.user!.isAdmin && !this.user!.isModerator) return; // Subscribe events @@ -37,7 +45,7 @@ export default class extends Channel { )) return; if (['followers', 'specified'].includes(note.visibility)) { - note = await Notes.pack(note.id, this.user!, { + note = await this.notesRepository.pack(note.id, this.user!, { detail: true, }); @@ -47,13 +55,13 @@ export default class extends Channel { } else { // リプライなら再pack if (note.replyId != null) { - note.reply = await Notes.pack(note.replyId, this.user!, { + note.reply = await this.notesRepository.pack(note.replyId, this.user!, { detail: true, }); } // Renoteなら再pack if (note.renoteId != null) { - note.renote = await Notes.pack(note.renoteId, this.user!, { + note.renote = await this.notesRepository.pack(note.renoteId, this.user!, { detail: true, }); } @@ -86,8 +94,31 @@ export default class extends Channel { this.send('note', note); } - public dispose() { + public dispose(): void { // Unsubscribe events this.subscriber.off('notesStream', this.onNote); } } + +@Injectable() +export class HybridTimelineChannelService { + public readonly shouldShare = HybridTimelineChannel.shouldShare; + public readonly requireCredential = HybridTimelineChannel.requireCredential; + + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + + private metaService: MetaService, + ) { + } + + public create(id: string, connection: Channel['connection']): HybridTimelineChannel { + return new HybridTimelineChannel( + this.metaService, + this.notesRepository, + id, + connection, + ); + } +} diff --git a/packages/backend/src/server/api/stream/channels/index.ts b/packages/backend/src/server/api/stream/channels/index.ts deleted file mode 100644 index d422edde8703..000000000000 --- a/packages/backend/src/server/api/stream/channels/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -import main from './main.js'; -import homeTimeline from './home-timeline.js'; -import localTimeline from './local-timeline.js'; -import hybridTimeline from './hybrid-timeline.js'; -import globalTimeline from './global-timeline.js'; -import serverStats from './server-stats.js'; -import queueStats from './queue-stats.js'; -import userList from './user-list.js'; -import antenna from './antenna.js'; -import messaging from './messaging.js'; -import messagingIndex from './messaging-index.js'; -import drive from './drive.js'; -import hashtag from './hashtag.js'; -import channel from './channel.js'; -import admin from './admin.js'; - -export default { - main, - homeTimeline, - localTimeline, - hybridTimeline, - globalTimeline, - serverStats, - queueStats, - userList, - antenna, - messaging, - messagingIndex, - drive, - hashtag, - channel, - admin, -}; diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index f01f4772383f..42516d5c7504 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -1,22 +1,29 @@ -import Channel from '../channel.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import { Notes } from '@/models/index.js'; +import { Inject, Injectable } from '@nestjs/common'; +import type { Notes } from '@/models/index.js'; import { checkWordMute } from '@/misc/check-word-mute.js'; import { isUserRelated } from '@/misc/is-user-related.js'; -import { Packed } from '@/misc/schema.js'; +import type { Packed } from '@/misc/schema.js'; +import { MetaService } from '@/services/MetaService.js'; +import Channel from '../channel.js'; -export default class extends Channel { +class LocalTimelineChannel extends Channel { public readonly chName = 'localTimeline'; public static shouldShare = true; public static requireCredential = false; - constructor(id: string, connection: Channel['connection']) { + constructor( + private metaService: MetaService, + private notesRepository: typeof Notes, + + id: string, + connection: Channel['connection'], + ) { super(id, connection); this.onNote = this.onNote.bind(this); } public async init(params: any) { - const meta = await fetchMeta(); + const meta = await this.metaService.fetch(); if (meta.disableLocalTimeline) { if (this.user == null || (!this.user.isAdmin && !this.user.isModerator)) return; } @@ -32,13 +39,13 @@ export default class extends Channel { // リプライなら再pack if (note.replyId != null) { - note.reply = await Notes.pack(note.replyId, this.user, { + note.reply = await this.notesRepository.pack(note.replyId, this.user, { detail: true, }); } // Renoteなら再pack if (note.renoteId != null) { - note.renote = await Notes.pack(note.renoteId, this.user, { + note.renote = await this.notesRepository.pack(note.renoteId, this.user, { detail: true, }); } @@ -72,3 +79,26 @@ export default class extends Channel { this.subscriber.off('notesStream', this.onNote); } } + +@Injectable() +export class LocalTimelineChannelService { + public readonly shouldShare = LocalTimelineChannel.shouldShare; + public readonly requireCredential = LocalTimelineChannel.requireCredential; + + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + + private metaService: MetaService, + ) { + } + + public create(id: string, connection: Channel['connection']): LocalTimelineChannel { + return new LocalTimelineChannel( + this.metaService, + this.notesRepository, + id, + connection, + ); + } +} diff --git a/packages/backend/src/server/api/stream/channels/main.ts b/packages/backend/src/server/api/stream/channels/main.ts index 9cfea0bfc4a0..34cb78b17836 100644 --- a/packages/backend/src/server/api/stream/channels/main.ts +++ b/packages/backend/src/server/api/stream/channels/main.ts @@ -1,12 +1,22 @@ -import Channel from '../channel.js'; -import { Notes } from '@/models/index.js'; +import { Inject, Injectable } from '@nestjs/common'; +import type { Notes } from '@/models/index.js'; import { isInstanceMuted, isUserFromMutedInstance } from '@/misc/is-instance-muted.js'; +import Channel from '../channel.js'; -export default class extends Channel { +class MainChannel extends Channel { public readonly chName = 'main'; public static shouldShare = true; public static requireCredential = true; + constructor( + private notesRepository: typeof Notes, + + id: string, + connection: Channel['connection'], + ) { + super(id, connection); + } + public async init(params: any) { // Subscribe main stream channel this.subscriber.on(`mainStream:${this.user!.id}`, async data => { @@ -17,7 +27,7 @@ export default class extends Channel { if (data.body.userId && this.muting.has(data.body.userId)) return; if (data.body.note && data.body.note.isHidden) { - const note = await Notes.pack(data.body.note.id, this.user, { + const note = await this.notesRepository.pack(data.body.note.id, this.user, { detail: true, }); this.connection.cacheNote(note); @@ -30,7 +40,7 @@ export default class extends Channel { if (this.muting.has(data.body.userId)) return; if (data.body.isHidden) { - const note = await Notes.pack(data.body.id, this.user, { + const note = await this.notesRepository.pack(data.body.id, this.user, { detail: true, }); this.connection.cacheNote(note); @@ -44,3 +54,23 @@ export default class extends Channel { }); } } + +@Injectable() +export class MainChannelService { + public readonly shouldShare = MainChannel.shouldShare; + public readonly requireCredential = MainChannel.requireCredential; + + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + } + + public create(id: string, connection: Channel['connection']): MainChannel { + return new MainChannel( + this.notesRepository, + id, + connection, + ); + } +} diff --git a/packages/backend/src/server/api/stream/index.ts b/packages/backend/src/server/api/stream/index.ts index 6da8484977fb..060d09062254 100644 --- a/packages/backend/src/server/api/stream/index.ts +++ b/packages/backend/src/server/api/stream/index.ts @@ -8,7 +8,7 @@ import type { Packed } from '@/misc/schema.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; import type { NoteReadService } from '@/services/NoteReadService.js'; import { readNotification } from '../common/read-notification.js'; -import channels from './channels/index.js'; +import type { ChannelsService } from './ChannelsService.js'; import type * as websocket from 'websocket'; import type { EventEmitter } from 'events'; import type Channel from './channel.js'; @@ -37,6 +37,7 @@ export default class Connection { private blockingsRepository: typeof Blockings, private channelFollowingsRepository: typeof ChannelFollowings, private userProfilesRepository: typeof UserProfiles, + private channelsService: ChannelsService, private globalEventService: GlobalEventService, private noteReadService: NoteReadService, @@ -261,16 +262,18 @@ export default class Connection { * チャンネルに接続 */ public connectChannel(id: string, params: any, channel: string, pong = false) { - if ((channels as any)[channel].requireCredential && this.user == null) { + const channelService = this.channelsService.getChannelService(channel); + + if (channelService.requireCredential && this.user == null) { return; } // 共有可能チャンネルに接続しようとしていて、かつそのチャンネルに既に接続していたら無意味なので無視 - if ((channels as any)[channel].shouldShare && this.channels.some(c => c.chName === channel)) { + if (channelService.shouldShare && this.channels.some(c => c.chName === channel)) { return; } - const ch: Channel = new (channels as any)[channel](id, this); + const ch: Channel = channelService.create(id, this); this.channels.push(ch); ch.init(params); From d507abe70a9fb3a634a134c69ed13db864510674 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 14 Sep 2022 04:25:27 +0900 Subject: [PATCH 079/180] wip --- .../src/server/api/stream/ChannelsService.ts | 41 +++++++++---- .../src/server/api/stream/channels/admin.ts | 20 ++++++- .../src/server/api/stream/channels/antenna.ts | 38 ++++++++++-- .../src/server/api/stream/channels/channel.ts | 51 ++++++++++++---- .../src/server/api/stream/channels/drive.ts | 20 ++++++- .../src/server/api/stream/channels/hashtag.ts | 38 ++++++++++-- .../api/stream/channels/messaging-index.ts | 20 ++++++- .../server/api/stream/channels/messaging.ts | 60 +++++++++++++++---- .../server/api/stream/channels/queue-stats.ts | 20 ++++++- .../api/stream/channels/server-stats.ts | 20 ++++++- .../server/api/stream/channels/user-list.ts | 58 ++++++++++++++---- 11 files changed, 325 insertions(+), 61 deletions(-) diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts index 527830350817..6a31af58cdac 100644 --- a/packages/backend/src/server/api/stream/ChannelsService.ts +++ b/packages/backend/src/server/api/stream/ChannelsService.ts @@ -5,6 +5,16 @@ import { LocalTimelineChannelService } from './channels/local-timeline.js'; import { HomeTimelineChannelService } from './channels/home-timeline.js'; import { GlobalTimelineChannelService } from './channels/global-timeline.js'; import { MainChannelService } from './channels/main.js'; +import { ChannelChannelService } from './channels/channel.js'; +import { AdminChannelService } from './channels/admin.js'; +import { ServerStatsChannelService } from './channels/server-stats.js'; +import { QueueStatsChannelService } from './channels/queue-stats.js'; +import { UserListChannelService } from './channels/user-list.js'; +import { AntennaChannelService } from './channels/antenna.js'; +import { MessagingChannelService } from './channels/messaging.js'; +import { MessagingIndexChannelService } from './channels/messaging-index.js'; +import { DriveChannelService } from './channels/drive.js'; +import { HashtagChannelService } from './channels/hashtag.js'; @Injectable() export class ChannelsService { @@ -14,6 +24,16 @@ export class ChannelsService { private localTimelineChannelService: LocalTimelineChannelService, private hybridTimelineChannelService: HybridTimelineChannelService, private globalTimelineChannelService: GlobalTimelineChannelService, + private userListChannelService: UserListChannelService, + private hashtagChannelService: HashtagChannelService, + private antennaChannelService: AntennaChannelService, + private channelChannelService: ChannelChannelService, + private messagingChannelService: MessagingChannelService, + private messagingIndexChannelService: MessagingIndexChannelService, + private driveChannelService: DriveChannelService, + private serverStatsChannelService: ServerStatsChannelService, + private queueStatsChannelService: QueueStatsChannelService, + private adminChannelService: AdminChannelService, ) { } @@ -24,20 +44,19 @@ export class ChannelsService { case 'localTimeline': return this.localTimelineChannelService; case 'hybridTimeline': return this.hybridTimelineChannelService; case 'globalTimeline': return this.globalTimelineChannelService; + case 'userList': return this.userListChannelService; + case 'hashtag': return this.hashtagChannelService; + case 'antenna': return this.antennaChannelService; + case 'channel': return this.channelChannelService; + case 'messaging': return this.messagingChannelService; + case 'messagingIndex': return this.messagingIndexChannelService; + case 'drive': return this.driveChannelService; + case 'serverStats': return this.serverStatsChannelService; + case 'queueStats': return this.queueStatsChannelService; + case 'admin': return this.adminChannelService; default: throw new Error(`no such channel: ${name}`); } } } - - serverStats, - queueStats, - userList, - antenna, - messaging, - messagingIndex, - drive, - hashtag, - channel, - admin, diff --git a/packages/backend/src/server/api/stream/channels/admin.ts b/packages/backend/src/server/api/stream/channels/admin.ts index 945182ea105e..8c3c0d2adf16 100644 --- a/packages/backend/src/server/api/stream/channels/admin.ts +++ b/packages/backend/src/server/api/stream/channels/admin.ts @@ -1,6 +1,7 @@ +import { Inject, Injectable } from '@nestjs/common'; import Channel from '../channel.js'; -export default class extends Channel { +class AdminChannel extends Channel { public readonly chName = 'admin'; public static shouldShare = true; public static requireCredential = true; @@ -12,3 +13,20 @@ export default class extends Channel { }); } } + +@Injectable() +export class AdminChannelService { + public readonly shouldShare = AdminChannel.shouldShare; + public readonly requireCredential = AdminChannel.requireCredential; + + constructor( + ) { + } + + public create(id: string, connection: Channel['connection']): AdminChannel { + return new AdminChannel( + id, + connection, + ); + } +} diff --git a/packages/backend/src/server/api/stream/channels/antenna.ts b/packages/backend/src/server/api/stream/channels/antenna.ts index d28320d92851..c49786a43e40 100644 --- a/packages/backend/src/server/api/stream/channels/antenna.ts +++ b/packages/backend/src/server/api/stream/channels/antenna.ts @@ -1,15 +1,21 @@ -import Channel from '../channel.js'; -import { Notes } from '@/models/index.js'; +import { Inject, Injectable } from '@nestjs/common'; +import type { Notes } from '@/models/index.js'; import { isUserRelated } from '@/misc/is-user-related.js'; -import { StreamMessages } from '../types.js'; +import Channel from '../channel.js'; +import type { StreamMessages } from '../types.js'; -export default class extends Channel { +class AntennaChannel extends Channel { public readonly chName = 'antenna'; public static shouldShare = false; public static requireCredential = false; private antennaId: string; - constructor(id: string, connection: Channel['connection']) { + constructor( + private notesRepository: typeof Notes, + + id: string, + connection: Channel['connection'], + ) { super(id, connection); this.onEvent = this.onEvent.bind(this); } @@ -23,7 +29,7 @@ export default class extends Channel { private async onEvent(data: StreamMessages['antenna']['payload']) { if (data.type === 'note') { - const note = await Notes.pack(data.body.id, this.user, { detail: true }); + const note = await this.notesRepository.pack(data.body.id, this.user, { detail: true }); // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する if (isUserRelated(note, this.muting)) return; @@ -43,3 +49,23 @@ export default class extends Channel { this.subscriber.off(`antennaStream:${this.antennaId}`, this.onEvent); } } + +@Injectable() +export class AntennaChannelService { + public readonly shouldShare = AntennaChannel.shouldShare; + public readonly requireCredential = AntennaChannel.requireCredential; + + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + } + + public create(id: string, connection: Channel['connection']): AntennaChannel { + return new AntennaChannel( + this.notesRepository, + id, + connection, + ); + } +} diff --git a/packages/backend/src/server/api/stream/channels/channel.ts b/packages/backend/src/server/api/stream/channels/channel.ts index 3cdd89a8b3e0..31bace164a91 100644 --- a/packages/backend/src/server/api/stream/channels/channel.ts +++ b/packages/backend/src/server/api/stream/channels/channel.ts @@ -1,11 +1,12 @@ -import Channel from '../channel.js'; -import { Notes, Users } from '@/models/index.js'; +import { Inject, Injectable } from '@nestjs/common'; +import type { Notes, Users } from '@/models/index.js'; import { isUserRelated } from '@/misc/is-user-related.js'; -import { User } from '@/models/entities/user.js'; -import { StreamMessages } from '../types.js'; -import { Packed } from '@/misc/schema.js'; +import type { User } from '@/models/entities/user.js'; +import type { Packed } from '@/misc/schema.js'; +import Channel from '../channel.js'; +import type { StreamMessages } from '../types.js'; -export default class extends Channel { +class ChannelChannel extends Channel { public readonly chName = 'channel'; public static shouldShare = false; public static requireCredential = false; @@ -13,7 +14,13 @@ export default class extends Channel { private typers: Record = {}; private emitTypersIntervalId: ReturnType; - constructor(id: string, connection: Channel['connection']) { + constructor( + private usersRepository: typeof Users, + private notesRepository: typeof Notes, + + id: string, + connection: Channel['connection'], + ) { super(id, connection); this.onNote = this.onNote.bind(this); this.emitTypers = this.emitTypers.bind(this); @@ -33,13 +40,13 @@ export default class extends Channel { // リプライなら再pack if (note.replyId != null) { - note.reply = await Notes.pack(note.replyId, this.user, { + note.reply = await this.notesRepository.pack(note.replyId, this.user, { detail: true, }); } // Renoteなら再pack if (note.renoteId != null) { - note.renote = await Notes.pack(note.renoteId, this.user, { + note.renote = await this.notesRepository.pack(note.renoteId, this.user, { detail: true, }); } @@ -73,7 +80,7 @@ export default class extends Channel { if (now.getTime() - date.getTime() > 5000) delete this.typers[userId]; } - const users = await Users.packMany(Object.keys(this.typers), null, { detail: false }); + const users = await this.usersRepository.packMany(Object.keys(this.typers), null, { detail: false }); this.send({ type: 'typers', @@ -89,3 +96,27 @@ export default class extends Channel { clearInterval(this.emitTypersIntervalId); } } + +@Injectable() +export class ChannelChannelService { + public readonly shouldShare = ChannelChannel.shouldShare; + public readonly requireCredential = ChannelChannel.requireCredential; + + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + } + + public create(id: string, connection: Channel['connection']): ChannelChannel { + return new ChannelChannel( + this.usersRepository, + this.notesRepository, + id, + connection, + ); + } +} diff --git a/packages/backend/src/server/api/stream/channels/drive.ts b/packages/backend/src/server/api/stream/channels/drive.ts index 140255acd11d..80d83cd6902b 100644 --- a/packages/backend/src/server/api/stream/channels/drive.ts +++ b/packages/backend/src/server/api/stream/channels/drive.ts @@ -1,6 +1,7 @@ +import { Inject, Injectable } from '@nestjs/common'; import Channel from '../channel.js'; -export default class extends Channel { +class DriveChannel extends Channel { public readonly chName = 'drive'; public static shouldShare = true; public static requireCredential = true; @@ -12,3 +13,20 @@ export default class extends Channel { }); } } + +@Injectable() +export class DriveChannelService { + public readonly shouldShare = DriveChannel.shouldShare; + public readonly requireCredential = DriveChannel.requireCredential; + + constructor( + ) { + } + + public create(id: string, connection: Channel['connection']): DriveChannel { + return new DriveChannel( + id, + connection, + ); + } +} diff --git a/packages/backend/src/server/api/stream/channels/hashtag.ts b/packages/backend/src/server/api/stream/channels/hashtag.ts index 741db447e643..e365f4b26e56 100644 --- a/packages/backend/src/server/api/stream/channels/hashtag.ts +++ b/packages/backend/src/server/api/stream/channels/hashtag.ts @@ -1,16 +1,22 @@ -import Channel from '../channel.js'; -import { Notes } from '@/models/index.js'; +import { Inject, Injectable } from '@nestjs/common'; +import type { Notes } from '@/models/index.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { isUserRelated } from '@/misc/is-user-related.js'; -import { Packed } from '@/misc/schema.js'; +import type { Packed } from '@/misc/schema.js'; +import Channel from '../channel.js'; -export default class extends Channel { +class HashtagChannel extends Channel { public readonly chName = 'hashtag'; public static shouldShare = false; public static requireCredential = false; private q: string[][]; - constructor(id: string, connection: Channel['connection']) { + constructor( + private notesRepository: typeof Notes, + + id: string, + connection: Channel['connection'], + ) { super(id, connection); this.onNote = this.onNote.bind(this); } @@ -31,7 +37,7 @@ export default class extends Channel { // Renoteなら再pack if (note.renoteId != null) { - note.renote = await Notes.pack(note.renoteId, this.user, { + note.renote = await this.notesRepository.pack(note.renoteId, this.user, { detail: true, }); } @@ -51,3 +57,23 @@ export default class extends Channel { this.subscriber.off('notesStream', this.onNote); } } + +@Injectable() +export class HashtagChannelService { + public readonly shouldShare = HashtagChannel.shouldShare; + public readonly requireCredential = HashtagChannel.requireCredential; + + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + } + + public create(id: string, connection: Channel['connection']): HashtagChannel { + return new HashtagChannel( + this.notesRepository, + id, + connection, + ); + } +} diff --git a/packages/backend/src/server/api/stream/channels/messaging-index.ts b/packages/backend/src/server/api/stream/channels/messaging-index.ts index b930785d2001..bebc07f4ad66 100644 --- a/packages/backend/src/server/api/stream/channels/messaging-index.ts +++ b/packages/backend/src/server/api/stream/channels/messaging-index.ts @@ -1,6 +1,7 @@ +import { Inject, Injectable } from '@nestjs/common'; import Channel from '../channel.js'; -export default class extends Channel { +class MessagingIndexChannel extends Channel { public readonly chName = 'messagingIndex'; public static shouldShare = true; public static requireCredential = true; @@ -12,3 +13,20 @@ export default class extends Channel { }); } } + +@Injectable() +export class MessagingIndexChannelService { + public readonly shouldShare = MessagingIndexChannel.shouldShare; + public readonly requireCredential = MessagingIndexChannel.requireCredential; + + constructor( + ) { + } + + public create(id: string, connection: Channel['connection']): MessagingIndexChannel { + return new MessagingIndexChannel( + id, + connection, + ); + } +} diff --git a/packages/backend/src/server/api/stream/channels/messaging.ts b/packages/backend/src/server/api/stream/channels/messaging.ts index 877d44c38eb7..8cf490033cb4 100644 --- a/packages/backend/src/server/api/stream/channels/messaging.ts +++ b/packages/backend/src/server/api/stream/channels/messaging.ts @@ -1,11 +1,12 @@ -import { readUserMessagingMessage, readGroupMessagingMessage, deliverReadActivity } from '../../common/read-messaging-message.js'; +import { Inject, Injectable } from '@nestjs/common'; +import type { UserGroupJoinings , Users , MessagingMessages } from '@/models/index.js'; +import type { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; +import type { UserGroup } from '@/models/entities/user-group.js'; import Channel from '../channel.js'; -import { UserGroupJoinings, Users, MessagingMessages } from '@/models/index.js'; -import { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; -import { UserGroup } from '@/models/entities/user-group.js'; -import { StreamMessages } from '../types.js'; +import { readUserMessagingMessage, readGroupMessagingMessage, deliverReadActivity } from '../../common/read-messaging-message.js'; +import type { StreamMessages } from '../types.js'; -export default class extends Channel { +class MessagingChannel extends Channel { public readonly chName = 'messaging'; public static shouldShare = false; public static requireCredential = true; @@ -17,7 +18,14 @@ export default class extends Channel { private typers: Record = {}; private emitTypersIntervalId: ReturnType; - constructor(id: string, connection: Channel['connection']) { + constructor( + private usersRepository: typeof Users, + private userGroupJoiningsRepository: typeof UserGroupJoinings, + private messagingMessagesRepository: typeof MessagingMessages, + + id: string, + connection: Channel['connection'], + ) { super(id, connection); this.onEvent = this.onEvent.bind(this); this.onMessage = this.onMessage.bind(this); @@ -26,12 +34,12 @@ export default class extends Channel { public async init(params: any) { this.otherpartyId = params.otherparty; - this.otherparty = this.otherpartyId ? await Users.findOneByOrFail({ id: this.otherpartyId }) : null; + this.otherparty = this.otherpartyId ? await this.usersRepository.findOneByOrFail({ id: this.otherpartyId }) : null; this.groupId = params.group; // Check joining if (this.groupId) { - const joining = await UserGroupJoinings.findOneBy({ + const joining = await this.userGroupJoiningsRepository.findOneBy({ userId: this.user!.id, userGroupId: this.groupId, }); @@ -71,8 +79,8 @@ export default class extends Channel { readUserMessagingMessage(this.user!.id, this.otherpartyId, [body.id]); // リモートユーザーからのメッセージだったら既読配信 - if (Users.isLocalUser(this.user!) && Users.isRemoteUser(this.otherparty!)) { - MessagingMessages.findOneBy({ id: body.id }).then(message => { + if (this.usersRepository.isLocalUser(this.user!) && this.usersRepository.isRemoteUser(this.otherparty!)) { + this.messagingMessagesRepository.findOneBy({ id: body.id }).then(message => { if (message) deliverReadActivity(this.user as ILocalUser, this.otherparty as IRemoteUser, message); }); } @@ -91,7 +99,7 @@ export default class extends Channel { if (now.getTime() - date.getTime() > 5000) delete this.typers[userId]; } - const users = await Users.packMany(Object.keys(this.typers), null, { detail: false }); + const users = await this.usersRepository.packMany(Object.keys(this.typers), null, { detail: false }); this.send({ type: 'typers', @@ -105,3 +113,31 @@ export default class extends Channel { clearInterval(this.emitTypersIntervalId); } } + +@Injectable() +export class MessagingChannelService { + public readonly shouldShare = MessagingChannel.shouldShare; + public readonly requireCredential = MessagingChannel.requireCredential; + + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('userGroupJoiningsRepository') + private userGroupJoiningsRepository: typeof UserGroupJoinings, + + @Inject('messagingMessagesRepository') + private messagingMessagesRepository: typeof MessagingMessages, + ) { + } + + public create(id: string, connection: Channel['connection']): MessagingChannel { + return new MessagingChannel( + this.usersRepository, + this.userGroupJoiningsRepository, + this.messagingMessagesRepository, + id, + connection, + ); + } +} diff --git a/packages/backend/src/server/api/stream/channels/queue-stats.ts b/packages/backend/src/server/api/stream/channels/queue-stats.ts index b67600474bab..1802c6723b7e 100644 --- a/packages/backend/src/server/api/stream/channels/queue-stats.ts +++ b/packages/backend/src/server/api/stream/channels/queue-stats.ts @@ -1,9 +1,10 @@ import Xev from 'xev'; +import { Inject, Injectable } from '@nestjs/common'; import Channel from '../channel.js'; const ev = new Xev(); -export default class extends Channel { +class QueueStatsChannel extends Channel { public readonly chName = 'queueStats'; public static shouldShare = true; public static requireCredential = false; @@ -40,3 +41,20 @@ export default class extends Channel { ev.removeListener('queueStats', this.onStats); } } + +@Injectable() +export class QueueStatsChannelService { + public readonly shouldShare = QueueStatsChannel.shouldShare; + public readonly requireCredential = QueueStatsChannel.requireCredential; + + constructor( + ) { + } + + public create(id: string, connection: Channel['connection']): QueueStatsChannel { + return new QueueStatsChannel( + id, + connection, + ); + } +} diff --git a/packages/backend/src/server/api/stream/channels/server-stats.ts b/packages/backend/src/server/api/stream/channels/server-stats.ts index db75a6fa38e4..e2b00de25f34 100644 --- a/packages/backend/src/server/api/stream/channels/server-stats.ts +++ b/packages/backend/src/server/api/stream/channels/server-stats.ts @@ -1,9 +1,10 @@ import Xev from 'xev'; +import { Inject, Injectable } from '@nestjs/common'; import Channel from '../channel.js'; const ev = new Xev(); -export default class extends Channel { +class ServerStatsChannel extends Channel { public readonly chName = 'serverStats'; public static shouldShare = true; public static requireCredential = false; @@ -40,3 +41,20 @@ export default class extends Channel { ev.removeListener('serverStats', this.onStats); } } + +@Injectable() +export class ServerStatsChannelService { + public readonly shouldShare = ServerStatsChannel.shouldShare; + public readonly requireCredential = ServerStatsChannel.requireCredential; + + constructor( + ) { + } + + public create(id: string, connection: Channel['connection']): ServerStatsChannel { + return new ServerStatsChannel( + id, + connection, + ); + } +} diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts index 97ad2983c501..7c8f475d99a4 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -1,10 +1,11 @@ -import Channel from '../channel.js'; -import { Notes, UserListJoinings, UserLists } from '@/models/index.js'; -import { User } from '@/models/entities/user.js'; +import { Inject, Injectable } from '@nestjs/common'; +import type { Notes, UserListJoinings, UserLists } from '@/models/index.js'; +import type { User } from '@/models/entities/user.js'; import { isUserRelated } from '@/misc/is-user-related.js'; -import { Packed } from '@/misc/schema.js'; +import type { Packed } from '@/misc/schema.js'; +import Channel from '../channel.js'; -export default class extends Channel { +class UserListChannel extends Channel { public readonly chName = 'userList'; public static shouldShare = false; public static requireCredential = false; @@ -12,7 +13,14 @@ export default class extends Channel { public listUsers: User['id'][] = []; private listUsersClock: NodeJS.Timer; - constructor(id: string, connection: Channel['connection']) { + constructor( + private notesRepository: typeof Notes, + private userListsRepository: typeof UserLists, + private userListJoiningsRepository: typeof UserListJoinings, + + id: string, + connection: Channel['connection'], + ) { super(id, connection); this.updateListUsers = this.updateListUsers.bind(this); this.onNote = this.onNote.bind(this); @@ -22,7 +30,7 @@ export default class extends Channel { this.listId = params.listId as string; // Check existence and owner - const list = await UserLists.findOneBy({ + const list = await this.userListsRepository.findOneBy({ id: this.listId, userId: this.user!.id, }); @@ -38,7 +46,7 @@ export default class extends Channel { } private async updateListUsers() { - const users = await UserListJoinings.find({ + const users = await this.userListJoiningsRepository.find({ where: { userListId: this.listId, }, @@ -52,7 +60,7 @@ export default class extends Channel { if (!this.listUsers.includes(note.userId)) return; if (['followers', 'specified'].includes(note.visibility)) { - note = await Notes.pack(note.id, this.user, { + note = await this.notesRepository.pack(note.id, this.user, { detail: true, }); @@ -62,13 +70,13 @@ export default class extends Channel { } else { // リプライなら再pack if (note.replyId != null) { - note.reply = await Notes.pack(note.replyId, this.user, { + note.reply = await this.notesRepository.pack(note.replyId, this.user, { detail: true, }); } // Renoteなら再pack if (note.renoteId != null) { - note.renote = await Notes.pack(note.renoteId, this.user, { + note.renote = await this.notesRepository.pack(note.renoteId, this.user, { detail: true, }); } @@ -90,3 +98,31 @@ export default class extends Channel { clearInterval(this.listUsersClock); } } + +@Injectable() +export class UserListChannelService { + public readonly shouldShare = UserListChannel.shouldShare; + public readonly requireCredential = UserListChannel.requireCredential; + + constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + + @Inject('userListsRepository') + private userListsRepository: typeof UserLists, + + @Inject('userListJoiningsRepository') + private userListJoiningsRepository: typeof UserListJoinings, + ) { + } + + public create(id: string, connection: Channel['connection']): UserListChannel { + return new UserListChannel( + this.notesRepository, + this.userListsRepository, + this.userListJoiningsRepository, + id, + connection, + ); + } +} From d3a2f4ebc92daa9dbca5b4df5e469418a6deda38 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 14 Sep 2022 05:34:32 +0900 Subject: [PATCH 080/180] wip --- .../src/server/ActivityPubServerService.ts | 581 ++++++++++++++++++ packages/backend/src/server/activitypub.ts | 254 -------- .../src/server/activitypub/featured.ts | 41 -- .../src/server/activitypub/followers.ts | 96 --- .../src/server/activitypub/following.ts | 96 --- .../backend/src/server/activitypub/outbox.ts | 108 ---- 6 files changed, 581 insertions(+), 595 deletions(-) create mode 100644 packages/backend/src/server/ActivityPubServerService.ts delete mode 100644 packages/backend/src/server/activitypub.ts delete mode 100644 packages/backend/src/server/activitypub/featured.ts delete mode 100644 packages/backend/src/server/activitypub/followers.ts delete mode 100644 packages/backend/src/server/activitypub/following.ts delete mode 100644 packages/backend/src/server/activitypub/outbox.ts diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts new file mode 100644 index 000000000000..eb0431e67d89 --- /dev/null +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -0,0 +1,581 @@ +import { Inject, Injectable } from '@nestjs/common'; +import Router from '@koa/router'; +import json from 'koa-json-body'; +import httpSignature from '@peertube/http-signature'; +import { Brackets, In, IsNull, LessThan, Not } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { Followings , Notes } from '@/models/index.js'; +import type { Emojis, NoteReactions , UserProfiles , UserNotePinings , Users } from '@/models/index.js'; +import * as url from '@/prelude/url.js'; +import { Config } from '@/config/types.js'; +import { isSelfHost } from '@/misc/convert-host.js'; +import { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; +import { QueueService } from '@/queue/queue.service.js'; +import type { ILocalUser, User } from '@/models/entities/user.js'; +import { UserKeypairStoreService } from '@/services/UserKeypairStoreService'; +import type { Following } from '@/models/entities/following'; +import { countIf } from '@/prelude/array'; +import type { Note } from '@/models/entities/note'; +import { makePaginationQuery } from './api/common/make-pagination-query'; +import type { FindOptionsWhere } from 'typeorm'; + +const ACTIVITY_JSON = 'application/activity+json; charset=utf-8'; +const LD_JSON = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"; charset=utf-8'; + +@Injectable() +export class ActivityPubServerService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + + @Inject('noteReactionsRepository') + private noteReactionsRepository: typeof NoteReactions, + + @Inject('emojisRepository') + private emojisRepository: typeof Emojis, + + @Inject('userNotePiningsRepository') + private userNotePiningsRepository: typeof UserNotePinings, + + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + private apRendererService: ApRendererService, + private queueService: QueueService, + private userKeypairStoreService: UserKeypairStoreService, + ) { + } + + #setResponseType(ctx: Router.RouterContext) { + const accept = ctx.accepts(ACTIVITY_JSON, LD_JSON); + if (accept === LD_JSON) { + ctx.response.type = LD_JSON; + } else { + ctx.response.type = ACTIVITY_JSON; + } + } + + /** + * Pack Create or Announce Activity + * @param note Note + */ + async #packActivity(note: Note): Promise { + if (note.renoteId && note.text == null && !note.hasPoll && (note.fileIds == null || note.fileIds.length === 0)) { + const renote = await Notes.findOneByOrFail({ id: note.renoteId }); + return this.apRendererService.renderAnnounce(renote.uri ? renote.uri : `${this.config.url}/notes/${renote.id}`, note); + } + + return this.apRendererService.renderCreate(await this.apRendererService.renderNote(note, false), note); + } + + #inbox(ctx: Router.RouterContext) { + let signature; + + try { + signature = httpSignature.parseRequest(ctx.req, { 'headers': [] }); + } catch (e) { + ctx.status = 401; + return; + } + + this.queueService.inbox(ctx.request.body, signature); + + ctx.status = 202; + } + + async #followers(ctx: Router.RouterContext) { + const userId = ctx.params.user; + + const cursor = ctx.request.query.cursor; + if (cursor != null && typeof cursor !== 'string') { + ctx.status = 400; + return; + } + + const page = ctx.request.query.page === 'true'; + + const user = await this.usersRepository.findOneBy({ + id: userId, + host: IsNull(), + }); + + if (user == null) { + ctx.status = 404; + return; + } + + //#region Check ff visibility + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); + + if (profile.ffVisibility === 'private') { + ctx.status = 403; + ctx.set('Cache-Control', 'public, max-age=30'); + return; + } else if (profile.ffVisibility === 'followers') { + ctx.status = 403; + ctx.set('Cache-Control', 'public, max-age=30'); + return; + } + //#endregion + + const limit = 10; + const partOf = `${this.config.url}/users/${userId}/followers`; + + if (page) { + const query = { + followeeId: user.id, + } as FindOptionsWhere; + + // カーソルが指定されている場合 + if (cursor) { + query.id = LessThan(cursor); + } + + // Get followers + const followings = await this.followingsRepository.find({ + where: query, + take: limit + 1, + order: { id: -1 }, + }); + + // 「次のページ」があるかどうか + const inStock = followings.length === limit + 1; + if (inStock) followings.pop(); + + const renderedFollowers = await Promise.all(followings.map(following => this.apRendererService.renderFollowUser(following.followerId))); + const rendered = this.apRendererService.renderOrderedCollectionPage( + `${partOf}?${url.query({ + page: 'true', + cursor, + })}`, + user.followersCount, renderedFollowers, partOf, + undefined, + inStock ? `${partOf}?${url.query({ + page: 'true', + cursor: followings[followings.length - 1].id, + })}` : undefined, + ); + + ctx.body = this.apRendererService.renderActivity(rendered); + this.#setResponseType(ctx); + } else { + // index page + const rendered = this.apRendererService.renderOrderedCollection(partOf, user.followersCount, `${partOf}?page=true`); + ctx.body = this.apRendererService.renderActivity(rendered); + ctx.set('Cache-Control', 'public, max-age=180'); + this.#setResponseType(ctx); + } + } + + async #following(ctx: Router.RouterContext) { + const userId = ctx.params.user; + + const cursor = ctx.request.query.cursor; + if (cursor != null && typeof cursor !== 'string') { + ctx.status = 400; + return; + } + + const page = ctx.request.query.page === 'true'; + + const user = await this.usersRepository.findOneBy({ + id: userId, + host: IsNull(), + }); + + if (user == null) { + ctx.status = 404; + return; + } + + //#region Check ff visibility + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); + + if (profile.ffVisibility === 'private') { + ctx.status = 403; + ctx.set('Cache-Control', 'public, max-age=30'); + return; + } else if (profile.ffVisibility === 'followers') { + ctx.status = 403; + ctx.set('Cache-Control', 'public, max-age=30'); + return; + } + //#endregion + + const limit = 10; + const partOf = `${this.config.url}/users/${userId}/following`; + + if (page) { + const query = { + followerId: user.id, + } as FindOptionsWhere; + + // カーソルが指定されている場合 + if (cursor) { + query.id = LessThan(cursor); + } + + // Get followings + const followings = await Followings.find({ + where: query, + take: limit + 1, + order: { id: -1 }, + }); + + // 「次のページ」があるかどうか + const inStock = followings.length === limit + 1; + if (inStock) followings.pop(); + + const renderedFollowees = await Promise.all(followings.map(following => this.apRendererService.renderFollowUser(following.followeeId))); + const rendered = this.apRendererService.renderOrderedCollectionPage( + `${partOf}?${url.query({ + page: 'true', + cursor, + })}`, + user.followingCount, renderedFollowees, partOf, + undefined, + inStock ? `${partOf}?${url.query({ + page: 'true', + cursor: followings[followings.length - 1].id, + })}` : undefined, + ); + + ctx.body = this.apRendererService.renderActivity(rendered); + this.#setResponseType(ctx); + } else { + // index page + const rendered = this.apRendererService.renderOrderedCollection(partOf, user.followingCount, `${partOf}?page=true`); + ctx.body = this.apRendererService.renderActivity(rendered); + ctx.set('Cache-Control', 'public, max-age=180'); + this.#setResponseType(ctx); + } + } + + async #featured(ctx: Router.RouterContext) { + const userId = ctx.params.user; + + const user = await this.usersRepository.findOneBy({ + id: userId, + host: IsNull(), + }); + + if (user == null) { + ctx.status = 404; + return; + } + + const pinings = await this.userNotePiningsRepository.find({ + where: { userId: user.id }, + order: { id: 'DESC' }, + }); + + const pinnedNotes = await Promise.all(pinings.map(pining => + Notes.findOneByOrFail({ id: pining.noteId }))); + + const renderedNotes = await Promise.all(pinnedNotes.map(note => this.apRendererService.renderNote(note))); + + const rendered = this.apRendererService.renderOrderedCollection( + `${this.config.url}/users/${userId}/collections/featured`, + renderedNotes.length, undefined, undefined, renderedNotes, + ); + + ctx.body = this.apRendererService.renderActivity(rendered); + ctx.set('Cache-Control', 'public, max-age=180'); + this.#setResponseType(ctx); + } + + async #outbox(ctx: Router.RouterContext) { + const userId = ctx.params.user; + + const sinceId = ctx.request.query.since_id; + if (sinceId != null && typeof sinceId !== 'string') { + ctx.status = 400; + return; + } + + const untilId = ctx.request.query.until_id; + if (untilId != null && typeof untilId !== 'string') { + ctx.status = 400; + return; + } + + const page = ctx.request.query.page === 'true'; + + if (countIf(x => x != null, [sinceId, untilId]) > 1) { + ctx.status = 400; + return; + } + + const user = await this.usersRepository.findOneBy({ + id: userId, + host: IsNull(), + }); + + if (user == null) { + ctx.status = 404; + return; + } + + const limit = 20; + const partOf = `${this.config.url}/users/${userId}/outbox`; + + if (page) { + const query = makePaginationQuery(this.notesRepository.createQueryBuilder('note'), sinceId, untilId) + .andWhere('note.userId = :userId', { userId: user.id }) + .andWhere(new Brackets(qb => { qb + .where('note.visibility = \'public\'') + .orWhere('note.visibility = \'home\''); + })) + .andWhere('note.localOnly = FALSE'); + + const notes = await query.take(limit).getMany(); + + if (sinceId) notes.reverse(); + + const activities = await Promise.all(notes.map(note => this.#packActivity(note))); + const rendered = this.apRendererService.renderOrderedCollectionPage( + `${partOf}?${url.query({ + page: 'true', + since_id: sinceId, + until_id: untilId, + })}`, + user.notesCount, activities, partOf, + notes.length ? `${partOf}?${url.query({ + page: 'true', + since_id: notes[0].id, + })}` : undefined, + notes.length ? `${partOf}?${url.query({ + page: 'true', + until_id: notes[notes.length - 1].id, + })}` : undefined, + ); + + ctx.body = this.apRendererService.renderActivity(rendered); + this.#setResponseType(ctx); + } else { + // index page + const rendered = this.apRendererService.renderOrderedCollection(partOf, user.notesCount, + `${partOf}?page=true`, + `${partOf}?page=true&since_id=000000000000000000000000`, + ); + ctx.body = this.apRendererService.renderActivity(rendered); + ctx.set('Cache-Control', 'public, max-age=180'); + this.#setResponseType(ctx); + } + } + + async #userInfo(ctx: Router.RouterContext, user: User | null) { + if (user == null) { + ctx.status = 404; + return; + } + + ctx.body = this.apRendererService.renderActivity(await this.apRendererService.renderPerson(user as ILocalUser)); + ctx.set('Cache-Control', 'public, max-age=180'); + this.#setResponseType(ctx); + } + + public createRouter() { + // Init router + const router = new Router(); + + //#region Routing + function isActivityPubReq(ctx: Router.RouterContext) { + ctx.response.vary('Accept'); + const accepted = ctx.accepts('html', ACTIVITY_JSON, LD_JSON); + return typeof accepted === 'string' && !accepted.match(/html/); + } + + // inbox + router.post('/inbox', json(), ctx => this.#inbox(ctx)); + router.post('/users/:user/inbox', json(), ctx => this.#inbox(ctx)); + + // note + router.get('/notes/:note', async (ctx, next) => { + if (!isActivityPubReq(ctx)) return await next(); + + const note = await this.notesRepository.findOneBy({ + id: ctx.params.note, + visibility: In(['public' as const, 'home' as const]), + localOnly: false, + }); + + if (note == null) { + ctx.status = 404; + return; + } + + // リモートだったらリダイレクト + if (note.userHost != null) { + if (note.uri == null || isSelfHost(note.userHost)) { + ctx.status = 500; + return; + } + ctx.redirect(note.uri); + return; + } + + ctx.body = this.apRendererService.renderActivity(await this.apRendererService.renderNote(note, false)); + ctx.set('Cache-Control', 'public, max-age=180'); + this.#setResponseType(ctx); + }); + + // note activity + router.get('/notes/:note/activity', async ctx => { + const note = await this.notesRepository.findOneBy({ + id: ctx.params.note, + userHost: IsNull(), + visibility: In(['public' as const, 'home' as const]), + localOnly: false, + }); + + if (note == null) { + ctx.status = 404; + return; + } + + ctx.body = this.apRendererService.renderActivity(await this.#packActivity(note)); + ctx.set('Cache-Control', 'public, max-age=180'); + this.#setResponseType(ctx); + }); + + // outbox + router.get('/users/:user/outbox', (ctx) => this.#outbox(ctx)); + + // followers + router.get('/users/:user/followers', (ctx) => this.#followers(ctx)); + + // following + router.get('/users/:user/following', (ctx) => this.#following(ctx)); + + // featured + router.get('/users/:user/collections/featured', (ctx) => this.#featured(ctx)); + + // publickey + router.get('/users/:user/publickey', async ctx => { + const userId = ctx.params.user; + + const user = await this.usersRepository.findOneBy({ + id: userId, + host: IsNull(), + }); + + if (user == null) { + ctx.status = 404; + return; + } + + const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); + + if (this.usersRepository.isLocalUser(user)) { + ctx.body = this.apRendererService.renderActivity(this.apRendererService.renderKey(user, keypair)); + ctx.set('Cache-Control', 'public, max-age=180'); + this.#setResponseType(ctx); + } else { + ctx.status = 400; + } + }); + + router.get('/users/:user', async (ctx, next) => { + if (!isActivityPubReq(ctx)) return await next(); + + const userId = ctx.params.user; + + const user = await this.usersRepository.findOneBy({ + id: userId, + host: IsNull(), + isSuspended: false, + }); + + await this.#userInfo(ctx, user); + }); + + router.get('/@:user', async (ctx, next) => { + if (!isActivityPubReq(ctx)) return await next(); + + const user = await this.usersRepository.findOneBy({ + usernameLower: ctx.params.user.toLowerCase(), + host: IsNull(), + isSuspended: false, + }); + + await this.#userInfo(ctx, user); + }); + //#endregion + + // emoji + router.get('/emojis/:emoji', async ctx => { + const emoji = await this.emojisRepository.findOneBy({ + host: IsNull(), + name: ctx.params.emoji, + }); + + if (emoji == null) { + ctx.status = 404; + return; + } + + ctx.body = this.apRendererService.renderActivity(await this.apRendererService.renderEmoji(emoji)); + ctx.set('Cache-Control', 'public, max-age=180'); + this.#setResponseType(ctx); + }); + + // like + router.get('/likes/:like', async ctx => { + const reaction = await this.noteReactionsRepository.findOneBy({ id: ctx.params.like }); + + if (reaction == null) { + ctx.status = 404; + return; + } + + const note = await this.notesRepository.findOneBy({ id: reaction.noteId }); + + if (note == null) { + ctx.status = 404; + return; + } + + ctx.body = this.apRendererService.renderActivity(await this.apRendererService.renderLike(reaction, note)); + ctx.set('Cache-Control', 'public, max-age=180'); + this.#setResponseType(ctx); + }); + + // follow + router.get('/follows/:follower/:followee', async ctx => { + // This may be used before the follow is completed, so we do not + // check if the following exists. + + const [follower, followee] = await Promise.all([ + this.usersRepository.findOneBy({ + id: ctx.params.follower, + host: IsNull(), + }), + this.usersRepository.findOneBy({ + id: ctx.params.followee, + host: Not(IsNull()), + }), + ]); + + if (follower == null || followee == null) { + ctx.status = 404; + return; + } + + ctx.body = this.apRendererService.renderActivity(this.apRendererService.renderFollow(follower, followee)); + ctx.set('Cache-Control', 'public, max-age=180'); + this.#setResponseType(ctx); + }); + + return router; + } +} diff --git a/packages/backend/src/server/activitypub.ts b/packages/backend/src/server/activitypub.ts deleted file mode 100644 index eed6eed7ee78..000000000000 --- a/packages/backend/src/server/activitypub.ts +++ /dev/null @@ -1,254 +0,0 @@ -import Router from '@koa/router'; -import json from 'koa-json-body'; -import httpSignature from '@peertube/http-signature'; - -import { In, IsNull, Not } from 'typeorm'; -import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; -import renderNote from '@/services/remote/activitypub/renderer/note.js'; -import renderKey from '@/services/remote/activitypub/renderer/key.js'; -import { renderPerson } from '@/services/remote/activitypub/renderer/person.js'; -import renderEmoji from '@/services/remote/activitypub/renderer/emoji.js'; -import { inbox as processInbox } from '@/queue/index.js'; -import { isSelfHost } from '@/misc/convert-host.js'; -import { Notes, Users, Emojis, NoteReactions } from '@/models/index.js'; -import type { ILocalUser, User } from '@/models/entities/user.js'; -import { renderLike } from '@/services/remote/activitypub/renderer/like.js'; -import { getUserKeypair } from '@/misc/keypair-store.js'; -import renderFollow from '@/services/remote/activitypub/renderer/follow.js'; -import Featured from './activitypub/featured.js'; -import Following from './activitypub/following.js'; -import Followers from './activitypub/followers.js'; -import Outbox, { packActivity } from './activitypub/outbox.js'; - -// Init router -const router = new Router(); - -//#region Routing - -function inbox(ctx: Router.RouterContext) { - let signature; - - try { - signature = httpSignature.parseRequest(ctx.req, { 'headers': [] }); - } catch (e) { - ctx.status = 401; - return; - } - - processInbox(ctx.request.body, signature); - - ctx.status = 202; -} - -const ACTIVITY_JSON = 'application/activity+json; charset=utf-8'; -const LD_JSON = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"; charset=utf-8'; - -function isActivityPubReq(ctx: Router.RouterContext) { - ctx.response.vary('Accept'); - const accepted = ctx.accepts('html', ACTIVITY_JSON, LD_JSON); - return typeof accepted === 'string' && !accepted.match(/html/); -} - -export function setResponseType(ctx: Router.RouterContext) { - const accept = ctx.accepts(ACTIVITY_JSON, LD_JSON); - if (accept === LD_JSON) { - ctx.response.type = LD_JSON; - } else { - ctx.response.type = ACTIVITY_JSON; - } -} - -// inbox -router.post('/inbox', json(), inbox); -router.post('/users/:user/inbox', json(), inbox); - -// note -router.get('/notes/:note', async (ctx, next) => { - if (!isActivityPubReq(ctx)) return await next(); - - const note = await Notes.findOneBy({ - id: ctx.params.note, - visibility: In(['public' as const, 'home' as const]), - localOnly: false, - }); - - if (note == null) { - ctx.status = 404; - return; - } - - // リモートだったらリダイレクト - if (note.userHost != null) { - if (note.uri == null || isSelfHost(note.userHost)) { - ctx.status = 500; - return; - } - ctx.redirect(note.uri); - return; - } - - ctx.body = renderActivity(await renderNote(note, false)); - ctx.set('Cache-Control', 'public, max-age=180'); - setResponseType(ctx); -}); - -// note activity -router.get('/notes/:note/activity', async ctx => { - const note = await Notes.findOneBy({ - id: ctx.params.note, - userHost: IsNull(), - visibility: In(['public' as const, 'home' as const]), - localOnly: false, - }); - - if (note == null) { - ctx.status = 404; - return; - } - - ctx.body = renderActivity(await packActivity(note)); - ctx.set('Cache-Control', 'public, max-age=180'); - setResponseType(ctx); -}); - -// outbox -router.get('/users/:user/outbox', Outbox); - -// followers -router.get('/users/:user/followers', Followers); - -// following -router.get('/users/:user/following', Following); - -// featured -router.get('/users/:user/collections/featured', Featured); - -// publickey -router.get('/users/:user/publickey', async ctx => { - const userId = ctx.params.user; - - const user = await Users.findOneBy({ - id: userId, - host: IsNull(), - }); - - if (user == null) { - ctx.status = 404; - return; - } - - const keypair = await getUserKeypair(user.id); - - if (Users.isLocalUser(user)) { - ctx.body = renderActivity(renderKey(user, keypair)); - ctx.set('Cache-Control', 'public, max-age=180'); - setResponseType(ctx); - } else { - ctx.status = 400; - } -}); - -// user -async function userInfo(ctx: Router.RouterContext, user: User | null) { - if (user == null) { - ctx.status = 404; - return; - } - - ctx.body = renderActivity(await renderPerson(user as ILocalUser)); - ctx.set('Cache-Control', 'public, max-age=180'); - setResponseType(ctx); -} - -router.get('/users/:user', async (ctx, next) => { - if (!isActivityPubReq(ctx)) return await next(); - - const userId = ctx.params.user; - - const user = await Users.findOneBy({ - id: userId, - host: IsNull(), - isSuspended: false, - }); - - await userInfo(ctx, user); -}); - -router.get('/@:user', async (ctx, next) => { - if (!isActivityPubReq(ctx)) return await next(); - - const user = await Users.findOneBy({ - usernameLower: ctx.params.user.toLowerCase(), - host: IsNull(), - isSuspended: false, - }); - - await userInfo(ctx, user); -}); -//#endregion - -// emoji -router.get('/emojis/:emoji', async ctx => { - const emoji = await Emojis.findOneBy({ - host: IsNull(), - name: ctx.params.emoji, - }); - - if (emoji == null) { - ctx.status = 404; - return; - } - - ctx.body = renderActivity(await renderEmoji(emoji)); - ctx.set('Cache-Control', 'public, max-age=180'); - setResponseType(ctx); -}); - -// like -router.get('/likes/:like', async ctx => { - const reaction = await NoteReactions.findOneBy({ id: ctx.params.like }); - - if (reaction == null) { - ctx.status = 404; - return; - } - - const note = await Notes.findOneBy({ id: reaction.noteId }); - - if (note == null) { - ctx.status = 404; - return; - } - - ctx.body = renderActivity(await renderLike(reaction, note)); - ctx.set('Cache-Control', 'public, max-age=180'); - setResponseType(ctx); -}); - -// follow -router.get('/follows/:follower/:followee', async ctx => { - // This may be used before the follow is completed, so we do not - // check if the following exists. - - const [follower, followee] = await Promise.all([ - Users.findOneBy({ - id: ctx.params.follower, - host: IsNull(), - }), - Users.findOneBy({ - id: ctx.params.followee, - host: Not(IsNull()), - }), - ]); - - if (follower == null || followee == null) { - ctx.status = 404; - return; - } - - ctx.body = renderActivity(renderFollow(follower, followee)); - ctx.set('Cache-Control', 'public, max-age=180'); - setResponseType(ctx); -}); - -export default router; diff --git a/packages/backend/src/server/activitypub/featured.ts b/packages/backend/src/server/activitypub/featured.ts deleted file mode 100644 index 35916cccea6c..000000000000 --- a/packages/backend/src/server/activitypub/featured.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { IsNull } from 'typeorm'; -import config from '@/config/index.js'; -import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; -import renderOrderedCollection from '@/services/remote/activitypub/renderer/ordered-collection.js'; -import renderNote from '@/services/remote/activitypub/renderer/note.js'; -import { Users, Notes, UserNotePinings } from '@/models/index.js'; -import { setResponseType } from '../activitypub.js'; -import type Router from '@koa/router'; - -export default async (ctx: Router.RouterContext) => { - const userId = ctx.params.user; - - const user = await Users.findOneBy({ - id: userId, - host: IsNull(), - }); - - if (user == null) { - ctx.status = 404; - return; - } - - const pinings = await UserNotePinings.find({ - where: { userId: user.id }, - order: { id: 'DESC' }, - }); - - const pinnedNotes = await Promise.all(pinings.map(pining => - Notes.findOneByOrFail({ id: pining.noteId }))); - - const renderedNotes = await Promise.all(pinnedNotes.map(note => renderNote(note))); - - const rendered = renderOrderedCollection( - `${config.url}/users/${userId}/collections/featured`, - renderedNotes.length, undefined, undefined, renderedNotes, - ); - - ctx.body = renderActivity(rendered); - ctx.set('Cache-Control', 'public, max-age=180'); - setResponseType(ctx); -}; diff --git a/packages/backend/src/server/activitypub/followers.ts b/packages/backend/src/server/activitypub/followers.ts deleted file mode 100644 index 47c078d5c077..000000000000 --- a/packages/backend/src/server/activitypub/followers.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { IsNull, LessThan } from 'typeorm'; -import config from '@/config/index.js'; -import * as url from '@/prelude/url.js'; -import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; -import renderOrderedCollection from '@/services/remote/activitypub/renderer/ordered-collection.js'; -import renderOrderedCollectionPage from '@/services/remote/activitypub/renderer/ordered-collection-page.js'; -import renderFollowUser from '@/services/remote/activitypub/renderer/follow-user.js'; -import { Users, Followings, UserProfiles } from '@/models/index.js'; -import type { Following } from '@/models/entities/following.js'; -import { setResponseType } from '../activitypub.js'; -import type { FindOptionsWhere } from 'typeorm'; -import type Router from '@koa/router'; - -export default async (ctx: Router.RouterContext) => { - const userId = ctx.params.user; - - const cursor = ctx.request.query.cursor; - if (cursor != null && typeof cursor !== 'string') { - ctx.status = 400; - return; - } - - const page = ctx.request.query.page === 'true'; - - const user = await Users.findOneBy({ - id: userId, - host: IsNull(), - }); - - if (user == null) { - ctx.status = 404; - return; - } - - //#region Check ff visibility - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - if (profile.ffVisibility === 'private') { - ctx.status = 403; - ctx.set('Cache-Control', 'public, max-age=30'); - return; - } else if (profile.ffVisibility === 'followers') { - ctx.status = 403; - ctx.set('Cache-Control', 'public, max-age=30'); - return; - } - //#endregion - - const limit = 10; - const partOf = `${config.url}/users/${userId}/followers`; - - if (page) { - const query = { - followeeId: user.id, - } as FindOptionsWhere; - - // カーソルが指定されている場合 - if (cursor) { - query.id = LessThan(cursor); - } - - // Get followers - const followings = await Followings.find({ - where: query, - take: limit + 1, - order: { id: -1 }, - }); - - // 「次のページ」があるかどうか - const inStock = followings.length === limit + 1; - if (inStock) followings.pop(); - - const renderedFollowers = await Promise.all(followings.map(following => renderFollowUser(following.followerId))); - const rendered = renderOrderedCollectionPage( - `${partOf}?${url.query({ - page: 'true', - cursor, - })}`, - user.followersCount, renderedFollowers, partOf, - undefined, - inStock ? `${partOf}?${url.query({ - page: 'true', - cursor: followings[followings.length - 1].id, - })}` : undefined, - ); - - ctx.body = renderActivity(rendered); - setResponseType(ctx); - } else { - // index page - const rendered = renderOrderedCollection(partOf, user.followersCount, `${partOf}?page=true`); - ctx.body = renderActivity(rendered); - ctx.set('Cache-Control', 'public, max-age=180'); - setResponseType(ctx); - } -}; diff --git a/packages/backend/src/server/activitypub/following.ts b/packages/backend/src/server/activitypub/following.ts deleted file mode 100644 index 72d004f7e782..000000000000 --- a/packages/backend/src/server/activitypub/following.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { LessThan, IsNull } from 'typeorm'; -import config from '@/config/index.js'; -import * as url from '@/prelude/url.js'; -import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; -import renderOrderedCollection from '@/services/remote/activitypub/renderer/ordered-collection.js'; -import renderOrderedCollectionPage from '@/services/remote/activitypub/renderer/ordered-collection-page.js'; -import renderFollowUser from '@/services/remote/activitypub/renderer/follow-user.js'; -import { Users, Followings, UserProfiles } from '@/models/index.js'; -import type { Following } from '@/models/entities/following.js'; -import { setResponseType } from '../activitypub.js'; -import type { FindOptionsWhere } from 'typeorm'; -import type Router from '@koa/router'; - -export default async (ctx: Router.RouterContext) => { - const userId = ctx.params.user; - - const cursor = ctx.request.query.cursor; - if (cursor != null && typeof cursor !== 'string') { - ctx.status = 400; - return; - } - - const page = ctx.request.query.page === 'true'; - - const user = await Users.findOneBy({ - id: userId, - host: IsNull(), - }); - - if (user == null) { - ctx.status = 404; - return; - } - - //#region Check ff visibility - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - if (profile.ffVisibility === 'private') { - ctx.status = 403; - ctx.set('Cache-Control', 'public, max-age=30'); - return; - } else if (profile.ffVisibility === 'followers') { - ctx.status = 403; - ctx.set('Cache-Control', 'public, max-age=30'); - return; - } - //#endregion - - const limit = 10; - const partOf = `${config.url}/users/${userId}/following`; - - if (page) { - const query = { - followerId: user.id, - } as FindOptionsWhere; - - // カーソルが指定されている場合 - if (cursor) { - query.id = LessThan(cursor); - } - - // Get followings - const followings = await Followings.find({ - where: query, - take: limit + 1, - order: { id: -1 }, - }); - - // 「次のページ」があるかどうか - const inStock = followings.length === limit + 1; - if (inStock) followings.pop(); - - const renderedFollowees = await Promise.all(followings.map(following => renderFollowUser(following.followeeId))); - const rendered = renderOrderedCollectionPage( - `${partOf}?${url.query({ - page: 'true', - cursor, - })}`, - user.followingCount, renderedFollowees, partOf, - undefined, - inStock ? `${partOf}?${url.query({ - page: 'true', - cursor: followings[followings.length - 1].id, - })}` : undefined, - ); - - ctx.body = renderActivity(rendered); - setResponseType(ctx); - } else { - // index page - const rendered = renderOrderedCollection(partOf, user.followingCount, `${partOf}?page=true`); - ctx.body = renderActivity(rendered); - ctx.set('Cache-Control', 'public, max-age=180'); - setResponseType(ctx); - } -}; diff --git a/packages/backend/src/server/activitypub/outbox.ts b/packages/backend/src/server/activitypub/outbox.ts deleted file mode 100644 index 70eded0b277a..000000000000 --- a/packages/backend/src/server/activitypub/outbox.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { Brackets, IsNull } from 'typeorm'; -import config from '@/config/index.js'; -import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; -import renderOrderedCollection from '@/services/remote/activitypub/renderer/ordered-collection.js'; -import renderOrderedCollectionPage from '@/services/remote/activitypub/renderer/ordered-collection-page.js'; -import renderNote from '@/services/remote/activitypub/renderer/note.js'; -import renderCreate from '@/services/remote/activitypub/renderer/create.js'; -import renderAnnounce from '@/services/remote/activitypub/renderer/announce.js'; -import { countIf } from '@/prelude/array.js'; -import * as url from '@/prelude/url.js'; -import { Users, Notes } from '@/models/index.js'; -import type { Note } from '@/models/entities/note.js'; -import { makePaginationQuery } from '../api/common/make-pagination-query.js'; -import { setResponseType } from '../activitypub.js'; -import type Router from '@koa/router'; - -export default async (ctx: Router.RouterContext) => { - const userId = ctx.params.user; - - const sinceId = ctx.request.query.since_id; - if (sinceId != null && typeof sinceId !== 'string') { - ctx.status = 400; - return; - } - - const untilId = ctx.request.query.until_id; - if (untilId != null && typeof untilId !== 'string') { - ctx.status = 400; - return; - } - - const page = ctx.request.query.page === 'true'; - - if (countIf(x => x != null, [sinceId, untilId]) > 1) { - ctx.status = 400; - return; - } - - const user = await Users.findOneBy({ - id: userId, - host: IsNull(), - }); - - if (user == null) { - ctx.status = 404; - return; - } - - const limit = 20; - const partOf = `${config.url}/users/${userId}/outbox`; - - if (page) { - const query = makePaginationQuery(Notes.createQueryBuilder('note'), sinceId, untilId) - .andWhere('note.userId = :userId', { userId: user.id }) - .andWhere(new Brackets(qb => { qb - .where('note.visibility = \'public\'') - .orWhere('note.visibility = \'home\''); - })) - .andWhere('note.localOnly = FALSE'); - - const notes = await query.take(limit).getMany(); - - if (sinceId) notes.reverse(); - - const activities = await Promise.all(notes.map(note => packActivity(note))); - const rendered = renderOrderedCollectionPage( - `${partOf}?${url.query({ - page: 'true', - since_id: sinceId, - until_id: untilId, - })}`, - user.notesCount, activities, partOf, - notes.length ? `${partOf}?${url.query({ - page: 'true', - since_id: notes[0].id, - })}` : undefined, - notes.length ? `${partOf}?${url.query({ - page: 'true', - until_id: notes[notes.length - 1].id, - })}` : undefined, - ); - - ctx.body = renderActivity(rendered); - setResponseType(ctx); - } else { - // index page - const rendered = renderOrderedCollection(partOf, user.notesCount, - `${partOf}?page=true`, - `${partOf}?page=true&since_id=000000000000000000000000`, - ); - ctx.body = renderActivity(rendered); - ctx.set('Cache-Control', 'public, max-age=180'); - setResponseType(ctx); - } -}; - -/** - * Pack Create or Announce Activity - * @param note Note - */ -export async function packActivity(note: Note): Promise { - if (note.renoteId && note.text == null && !note.hasPoll && (note.fileIds == null || note.fileIds.length === 0)) { - const renote = await Notes.findOneByOrFail({ id: note.renoteId }); - return renderAnnounce(renote.uri ? renote.uri : `${config.url}/notes/${renote.id}`, note); - } - - return renderCreate(await renderNote(note, false), note); -} From f6cb8fc0a342af256a609cf9cf816e3d5e2e90b9 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 14 Sep 2022 05:44:33 +0900 Subject: [PATCH 081/180] wip --- .../src/server/ActivityPubServerService.ts | 10 +- .../src/server/NodeinfoServerService.ts | 128 ++++++++++++++++++ packages/backend/src/server/nodeinfo.ts | 104 -------------- 3 files changed, 133 insertions(+), 109 deletions(-) create mode 100644 packages/backend/src/server/NodeinfoServerService.ts delete mode 100644 packages/backend/src/server/nodeinfo.ts diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index eb0431e67d89..f05c6fa48b2c 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -12,11 +12,11 @@ import { isSelfHost } from '@/misc/convert-host.js'; import { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; import { QueueService } from '@/queue/queue.service.js'; import type { ILocalUser, User } from '@/models/entities/user.js'; -import { UserKeypairStoreService } from '@/services/UserKeypairStoreService'; -import type { Following } from '@/models/entities/following'; -import { countIf } from '@/prelude/array'; -import type { Note } from '@/models/entities/note'; -import { makePaginationQuery } from './api/common/make-pagination-query'; +import { UserKeypairStoreService } from '@/services/UserKeypairStoreService.js'; +import type { Following } from '@/models/entities/following.js'; +import { countIf } from '@/prelude/array.js'; +import type { Note } from '@/models/entities/note.js'; +import { makePaginationQuery } from './api/common/make-pagination-query.js'; import type { FindOptionsWhere } from 'typeorm'; const ACTIVITY_JSON = 'application/activity+json; charset=utf-8'; diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts new file mode 100644 index 000000000000..7f94d96518fa --- /dev/null +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -0,0 +1,128 @@ +import { Inject, Injectable } from '@nestjs/common'; +import Router from '@koa/router'; +import { IsNull, MoreThan } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Notes } from '@/models/index.js'; +import { Users } from '@/models/index.js'; +import { Config } from '@/config/types.js'; +import { MetaService } from '@/services/MetaService.js'; +import { MAX_NOTE_TEXT_LENGTH } from '@/const'; +import { Cache } from '@/misc/cache.js'; + +const nodeinfo2_1path = '/nodeinfo/2.1'; +const nodeinfo2_0path = '/nodeinfo/2.0'; + +@Injectable() +export class NodeinfoServerService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + + private metaService: MetaService, + ) { + } + + public getLinks() { + return [/* (awaiting release) { + rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1', + href: config.url + nodeinfo2_1path + }, */{ + rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0', + href: this.config.url + nodeinfo2_0path, + }]; + } + + public createRouter() { + const router = new Router(); + + const nodeinfo2 = async () => { + const now = Date.now(); + const [ + meta, + total, + activeHalfyear, + activeMonth, + localPosts, + ] = await Promise.all([ + this.metaService.fetch(true), + this.usersRepository.count({ where: { host: IsNull() } }), + this.usersRepository.count({ where: { host: IsNull(), lastActiveDate: MoreThan(new Date(now - 15552000000)) } }), + this.usersRepository.count({ where: { host: IsNull(), lastActiveDate: MoreThan(new Date(now - 2592000000)) } }), + this.notesRepository.count({ where: { userHost: IsNull() } }), + ]); + + const proxyAccount = meta.proxyAccountId ? await Users.pack(meta.proxyAccountId).catch(() => null) : null; + + return { + software: { + name: 'misskey', + version: this.config.version, + repository: meta.repositoryUrl, + }, + protocols: ['activitypub'], + services: { + inbound: [] as string[], + outbound: ['atom1.0', 'rss2.0'], + }, + openRegistrations: !meta.disableRegistration, + usage: { + users: { total, activeHalfyear, activeMonth }, + localPosts, + localComments: 0, + }, + metadata: { + nodeName: meta.name, + nodeDescription: meta.description, + maintainer: { + name: meta.maintainerName, + email: meta.maintainerEmail, + }, + langs: meta.langs, + tosUrl: meta.ToSUrl, + repositoryUrl: meta.repositoryUrl, + feedbackUrl: meta.feedbackUrl, + disableRegistration: meta.disableRegistration, + disableLocalTimeline: meta.disableLocalTimeline, + disableGlobalTimeline: meta.disableGlobalTimeline, + emailRequiredForSignup: meta.emailRequiredForSignup, + enableHcaptcha: meta.enableHcaptcha, + enableRecaptcha: meta.enableRecaptcha, + maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, + enableTwitterIntegration: meta.enableTwitterIntegration, + enableGithubIntegration: meta.enableGithubIntegration, + enableDiscordIntegration: meta.enableDiscordIntegration, + enableEmail: meta.enableEmail, + enableServiceWorker: meta.enableServiceWorker, + proxyAccountName: proxyAccount ? proxyAccount.username : null, + themeColor: meta.themeColor || '#86b300', + }, + }; + }; + + const cache = new Cache>>(1000 * 60 * 10); + + router.get(nodeinfo2_1path, async ctx => { + const base = await cache.fetch(null, () => nodeinfo2()); + + ctx.body = { version: '2.1', ...base }; + ctx.set('Cache-Control', 'public, max-age=600'); + }); + + router.get(nodeinfo2_0path, async ctx => { + const base = await cache.fetch(null, () => nodeinfo2()); + + delete base.software.repository; + + ctx.body = { version: '2.0', ...base }; + ctx.set('Cache-Control', 'public, max-age=600'); + }); + + return router; + } +} diff --git a/packages/backend/src/server/nodeinfo.ts b/packages/backend/src/server/nodeinfo.ts deleted file mode 100644 index f139d203d2bc..000000000000 --- a/packages/backend/src/server/nodeinfo.ts +++ /dev/null @@ -1,104 +0,0 @@ -import Router from '@koa/router'; -import config from '@/config/index.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import { Users, Notes } from '@/models/index.js'; -import { IsNull, MoreThan } from 'typeorm'; -import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; -import { Cache } from '@/misc/cache.js'; - -const router = new Router(); - -const nodeinfo2_1path = '/nodeinfo/2.1'; -const nodeinfo2_0path = '/nodeinfo/2.0'; - -export const links = [/* (awaiting release) { - rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1', - href: config.url + nodeinfo2_1path -}, */{ - rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0', - href: config.url + nodeinfo2_0path, -}]; - -const nodeinfo2 = async () => { - const now = Date.now(); - const [ - meta, - total, - activeHalfyear, - activeMonth, - localPosts, - ] = await Promise.all([ - fetchMeta(true), - Users.count({ where: { host: IsNull() } }), - Users.count({ where: { host: IsNull(), lastActiveDate: MoreThan(new Date(now - 15552000000)) } }), - Users.count({ where: { host: IsNull(), lastActiveDate: MoreThan(new Date(now - 2592000000)) } }), - Notes.count({ where: { userHost: IsNull() } }), - ]); - - const proxyAccount = meta.proxyAccountId ? await Users.pack(meta.proxyAccountId).catch(() => null) : null; - - return { - software: { - name: 'misskey', - version: config.version, - repository: meta.repositoryUrl, - }, - protocols: ['activitypub'], - services: { - inbound: [] as string[], - outbound: ['atom1.0', 'rss2.0'], - }, - openRegistrations: !meta.disableRegistration, - usage: { - users: { total, activeHalfyear, activeMonth }, - localPosts, - localComments: 0, - }, - metadata: { - nodeName: meta.name, - nodeDescription: meta.description, - maintainer: { - name: meta.maintainerName, - email: meta.maintainerEmail, - }, - langs: meta.langs, - tosUrl: meta.ToSUrl, - repositoryUrl: meta.repositoryUrl, - feedbackUrl: meta.feedbackUrl, - disableRegistration: meta.disableRegistration, - disableLocalTimeline: meta.disableLocalTimeline, - disableGlobalTimeline: meta.disableGlobalTimeline, - emailRequiredForSignup: meta.emailRequiredForSignup, - enableHcaptcha: meta.enableHcaptcha, - enableRecaptcha: meta.enableRecaptcha, - maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, - enableTwitterIntegration: meta.enableTwitterIntegration, - enableGithubIntegration: meta.enableGithubIntegration, - enableDiscordIntegration: meta.enableDiscordIntegration, - enableEmail: meta.enableEmail, - enableServiceWorker: meta.enableServiceWorker, - proxyAccountName: proxyAccount ? proxyAccount.username : null, - themeColor: meta.themeColor || '#86b300', - }, - }; -}; - -const cache = new Cache>>(1000 * 60 * 10); - -router.get(nodeinfo2_1path, async ctx => { - const base = await cache.fetch(null, () => nodeinfo2()); - - ctx.body = { version: '2.1', ...base }; - ctx.set('Cache-Control', 'public, max-age=600'); -}); - -router.get(nodeinfo2_0path, async ctx => { - const base = await cache.fetch(null, () => nodeinfo2()); - - delete base.software.repository; - - ctx.body = { version: '2.0', ...base }; - ctx.set('Cache-Control', 'public, max-age=600'); -}); - -export default router; From 1f1255bf44da26da47f3df37a3a7e69221bf1b04 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 14 Sep 2022 06:36:47 +0900 Subject: [PATCH 082/180] wip --- packages/backend/src/server/ServerService.ts | 169 ++++++++++++++++++ .../src/server/WellKnownServerService.ts | 168 +++++++++++++++++ packages/backend/src/server/index.ts | 157 ---------------- packages/backend/src/server/well-known.ts | 151 ---------------- 4 files changed, 337 insertions(+), 308 deletions(-) create mode 100644 packages/backend/src/server/ServerService.ts create mode 100644 packages/backend/src/server/WellKnownServerService.ts delete mode 100644 packages/backend/src/server/index.ts delete mode 100644 packages/backend/src/server/well-known.ts diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts new file mode 100644 index 000000000000..589c74d64be6 --- /dev/null +++ b/packages/backend/src/server/ServerService.ts @@ -0,0 +1,169 @@ +import cluster from 'node:cluster'; +import * as fs from 'node:fs'; +import * as http from 'node:http'; +import { Inject, Injectable } from '@nestjs/common'; +import Koa from 'koa'; +import Router from '@koa/router'; +import mount from 'koa-mount'; +import koaLogger from 'koa-logger'; +import * as slow from 'koa-slow'; +import { IsNull } from 'typeorm'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { Config } from '@/config/types.js'; +import type { UserProfiles , Users } from '@/models/index.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import Logger from '@/logger.js'; +import { envOption } from '@/env.js'; +import * as Acct from '@/misc/acct.js'; +import { genIdenticon } from '@/misc/gen-identicon.js'; +import { createTemp } from '@/misc/create-temp.js'; +import { ActivityPubServerService } from './ActivityPubServerService.js'; +import { NodeinfoServerService } from './NodeinfoServerService.js'; +import { ApiServerService } from './api/ApiServerService.js'; +import { StreamingApiServerService } from './api/StreamingApiServerService.js'; +import { WellKnownServerService } from './WellKnownServerService.js'; + +export const serverLogger = new Logger('server', 'gray', false); + +@Injectable() +export class ServerService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + private apiServerService: ApiServerService, + private streamingApiServerService: StreamingApiServerService, + private activityPubServerService: ActivityPubServerService, + private wellKnownServerService: WellKnownServerService, + private nodeinfoServerService: NodeinfoServerService, + private globalEventService: GlobalEventService, + ) { + } + + public launch() { + // Init app + const koa = new Koa(); + koa.proxy = true; + + if (!['production', 'test'].includes(process.env.NODE_ENV || '')) { + // Logger + koa.use(koaLogger(str => { + serverLogger.info(str); + })); + + // Delay + if (envOption.slow) { + koa.use(slow({ + delay: 3000, + })); + } + } + + // HSTS + // 6months (15552000sec) + if (this.config.url.startsWith('https') && !this.config.disableHsts) { + koa.use(async (ctx, next) => { + ctx.set('strict-transport-security', 'max-age=15552000; preload'); + await next(); + }); + } + + koa.use(mount('/api', this.apiServerService.createApiServer(app))); + koa.use(mount('/files', fileServer)); + koa.use(mount('/proxy', proxyServer)); + + // Init router + const router = new Router(); + + // Routing + router.use(this.activityPubServerService.createRouter().routes()); + router.use(this.nodeinfoServerService.createRouter().routes()); + router.use(this.wellKnownServerService.createRouter().routes()); + + router.get('/avatar/@:acct', async ctx => { + const { username, host } = Acct.parse(ctx.params.acct); + const user = await this.usersRepository.findOne({ + where: { + usernameLower: username.toLowerCase(), + host: (host == null) || (host === this.config.host) ? IsNull() : host, + isSuspended: false, + }, + relations: ['avatar'], + }); + + if (user) { + ctx.redirect(this.usersRepository.getAvatarUrlSync(user)); + } else { + ctx.redirect('/static-assets/user-unknown.png'); + } + }); + + router.get('/identicon/:x', async ctx => { + const [temp, cleanup] = await createTemp(); + await genIdenticon(ctx.params.x, fs.createWriteStream(temp)); + ctx.set('Content-Type', 'image/png'); + ctx.body = fs.createReadStream(temp).on('close', () => cleanup()); + }); + + router.get('/verify-email/:code', async ctx => { + const profile = await this.userProfilesRepository.findOneBy({ + emailVerifyCode: ctx.params.code, + }); + + if (profile != null) { + ctx.body = 'Verify succeeded!'; + ctx.status = 200; + + await this.userProfilesRepository.update({ userId: profile.userId }, { + emailVerified: true, + emailVerifyCode: null, + }); + + this.globalEventService.publishMainStream(profile.userId, 'meUpdated', await this.usersRepository.pack(profile.userId, { id: profile.userId }, { + detail: true, + includeSecrets: true, + })); + } else { + ctx.status = 404; + } + }); + + // Register router + koa.use(router.routes()); + + koa.use(mount(webServer)); + + const server = http.createServer(koa.callback()); + + this.streamingApiServerService.attachStreamingApi(server); + + server.on('error', e => { + switch ((e as any).code) { + case 'EACCES': + serverLogger.error(`You do not have permission to listen on port ${this.config.port}.`); + break; + case 'EADDRINUSE': + serverLogger.error(`Port ${this.config.port} is already in use by another process.`); + break; + default: + serverLogger.error(e); + break; + } + + if (cluster.isWorker) { + process.send!('listenFailed'); + } else { + // disableClustering + process.exit(1); + } + }); + + server.listen(this.config.port); + } +} diff --git a/packages/backend/src/server/WellKnownServerService.ts b/packages/backend/src/server/WellKnownServerService.ts new file mode 100644 index 000000000000..11fa1441dc7f --- /dev/null +++ b/packages/backend/src/server/WellKnownServerService.ts @@ -0,0 +1,168 @@ +import { Inject, Injectable } from '@nestjs/common'; +import Router from '@koa/router'; +import { IsNull, MoreThan } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Users } from '@/models/index.js'; +import { Config } from '@/config/types.js'; +import { escapeAttribute, escapeValue } from '@/prelude/xml'; +import type { User } from '@/models/entities/user'; +import * as Acct from '@/misc/acct.js'; +import { NodeinfoServerService } from './NodeinfoServerService'; +import type { FindOptionsWhere } from 'typeorm'; + +@Injectable() +export class WellKnownServerService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + private nodeinfoServerService: NodeinfoServerService, + ) { + } + + public createRouter() { + const router = new Router(); + + const XRD = (...x: { element: string, value?: string, attributes?: Record }[]) => + `${x.map(({ element, value, attributes }) => + `<${ + Object.entries(typeof attributes === 'object' && attributes || {}).reduce((a, [k, v]) => `${a} ${k}="${escapeAttribute(v)}"`, element) + }${ + typeof value === 'string' ? `>${escapeValue(value)}`).reduce((a, c) => a + c, '')}`; + + const allPath = '/.well-known/(.*)'; + const webFingerPath = '/.well-known/webfinger'; + const jrd = 'application/jrd+json'; + const xrd = 'application/xrd+xml'; + + router.use(allPath, async (ctx, next) => { + ctx.set({ + 'Access-Control-Allow-Headers': 'Accept', + 'Access-Control-Allow-Methods': 'GET, OPTIONS', + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Expose-Headers': 'Vary', + }); + await next(); + }); + + router.options(allPath, async ctx => { + ctx.status = 204; + }); + + router.get('/.well-known/host-meta', async ctx => { + ctx.set('Content-Type', xrd); + ctx.body = XRD({ element: 'Link', attributes: { + rel: 'lrdd', + type: xrd, + template: `${this.config.url}${webFingerPath}?resource={uri}`, + } }); + }); + + router.get('/.well-known/host-meta.json', async ctx => { + ctx.set('Content-Type', jrd); + ctx.body = { + links: [{ + rel: 'lrdd', + type: jrd, + template: `${this.config.url}${webFingerPath}?resource={uri}`, + }], + }; + }); + + router.get('/.well-known/nodeinfo', async ctx => { + ctx.body = { links: this.nodeinfoServerService.getLinks() }; + }); + + /* TODO +router.get('/.well-known/change-password', async ctx => { +}); +*/ + + router.get(webFingerPath, async ctx => { + const fromId = (id: User['id']): FindOptionsWhere => ({ + id, + host: IsNull(), + isSuspended: false, + }); + + const generateQuery = (resource: string): FindOptionsWhere | number => + resource.startsWith(`${this.config.url.toLowerCase()}/users/`) ? + fromId(resource.split('/').pop()!) : + fromAcct(Acct.parse( + resource.startsWith(`${this.config.url.toLowerCase()}/@`) ? resource.split('/').pop()! : + resource.startsWith('acct:') ? resource.slice('acct:'.length) : + resource)); + + const fromAcct = (acct: Acct.Acct): FindOptionsWhere | number => + !acct.host || acct.host === this.config.host.toLowerCase() ? { + usernameLower: acct.username, + host: IsNull(), + isSuspended: false, + } : 422; + + if (typeof ctx.query.resource !== 'string') { + ctx.status = 400; + return; + } + + const query = generateQuery(ctx.query.resource.toLowerCase()); + + if (typeof query === 'number') { + ctx.status = query; + return; + } + + const user = await this.usersRepository.findOneBy(query); + + if (user == null) { + ctx.status = 404; + return; + } + + const subject = `acct:${user.username}@${this.config.host}`; + const self = { + rel: 'self', + type: 'application/activity+json', + href: `${this.config.url}/users/${user.id}`, + }; + const profilePage = { + rel: 'http://webfinger.net/rel/profile-page', + type: 'text/html', + href: `${this.config.url}/@${user.username}`, + }; + const subscribe = { + rel: 'http://ostatus.org/schema/1.0/subscribe', + template: `${this.config.url}/authorize-follow?acct={uri}`, + }; + + if (ctx.accepts(jrd, xrd) === xrd) { + ctx.body = XRD( + { element: 'Subject', value: subject }, + { element: 'Link', attributes: self }, + { element: 'Link', attributes: profilePage }, + { element: 'Link', attributes: subscribe }); + ctx.type = xrd; + } else { + ctx.body = { + subject, + links: [self, profilePage, subscribe], + }; + ctx.type = jrd; + } + + ctx.vary('Accept'); + ctx.set('Cache-Control', 'public, max-age=180'); + }); + + // Return 404 for other .well-known + router.all(allPath, async ctx => { + ctx.status = 404; + }); + + return router; + } +} diff --git a/packages/backend/src/server/index.ts b/packages/backend/src/server/index.ts deleted file mode 100644 index 4332e9d6d1b9..000000000000 --- a/packages/backend/src/server/index.ts +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Core Server - */ - -import cluster from 'node:cluster'; -import * as fs from 'node:fs'; -import * as http from 'node:http'; -import Koa from 'koa'; -import Router from '@koa/router'; -import mount from 'koa-mount'; -import koaLogger from 'koa-logger'; -import * as slow from 'koa-slow'; -import { IsNull } from 'typeorm'; -import Logger from '@/logger.js'; -import { UserProfiles, Users } from '@/models/index.js'; -import { genIdenticon } from '@/misc/gen-identicon.js'; -import { createTemp } from '@/misc/create-temp.js'; -import * as Acct from '@/misc/acct.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; -import { GlobalEventService } from '@/services/GlobalEventService.js'; -import { envOption } from '../env.js'; -import activityPub from './activitypub.js'; -import nodeinfo from './nodeinfo.js'; -import wellKnown from './well-known.js'; -import { createApiServer } from './api/index.js'; -import fileServer from './file/index.js'; -import proxyServer from './proxy/index.js'; -import webServer from './web/index.js'; -import { initializeStreamingServer } from './api/streaming.js'; -import type { INestApplicationContext } from '@nestjs/common'; - -export const serverLogger = new Logger('server', 'gray', false); - -export default (app: INestApplicationContext) => new Promise(resolve => { - const config = app.get(DI_SYMBOLS.config); - const globalEventService = app.get(GlobalEventService); - - // Init app - const koa = new Koa(); - koa.proxy = true; - - if (!['production', 'test'].includes(process.env.NODE_ENV || '')) { - // Logger - koa.use(koaLogger(str => { - serverLogger.info(str); - })); - - // Delay - if (envOption.slow) { - koa.use(slow({ - delay: 3000, - })); - } - } - - // HSTS - // 6months (15552000sec) - if (config.url.startsWith('https') && !config.disableHsts) { - koa.use(async (ctx, next) => { - ctx.set('strict-transport-security', 'max-age=15552000; preload'); - await next(); - }); - } - - koa.use(mount('/api', createApiServer(app))); - koa.use(mount('/files', fileServer)); - koa.use(mount('/proxy', proxyServer)); - - // Init router - const router = new Router(); - - // Routing - router.use(activityPub.routes()); - router.use(nodeinfo.routes()); - router.use(wellKnown.routes()); - - router.get('/avatar/@:acct', async ctx => { - const { username, host } = Acct.parse(ctx.params.acct); - const user = await Users.findOne({ - where: { - usernameLower: username.toLowerCase(), - host: (host == null) || (host === config.host) ? IsNull() : host, - isSuspended: false, - }, - relations: ['avatar'], - }); - - if (user) { - ctx.redirect(Users.getAvatarUrlSync(user)); - } else { - ctx.redirect('/static-assets/user-unknown.png'); - } - }); - - router.get('/identicon/:x', async ctx => { - const [temp, cleanup] = await createTemp(); - await genIdenticon(ctx.params.x, fs.createWriteStream(temp)); - ctx.set('Content-Type', 'image/png'); - ctx.body = fs.createReadStream(temp).on('close', () => cleanup()); - }); - - router.get('/verify-email/:code', async ctx => { - const profile = await UserProfiles.findOneBy({ - emailVerifyCode: ctx.params.code, - }); - - if (profile != null) { - ctx.body = 'Verify succeeded!'; - ctx.status = 200; - - await UserProfiles.update({ userId: profile.userId }, { - emailVerified: true, - emailVerifyCode: null, - }); - - globalEventService.publishMainStream(profile.userId, 'meUpdated', await Users.pack(profile.userId, { id: profile.userId }, { - detail: true, - includeSecrets: true, - })); - } else { - ctx.status = 404; - } - }); - - // Register router - koa.use(router.routes()); - - koa.use(mount(webServer)); - - const server = http.createServer(koa.callback()); - - initializeStreamingServer(app, server); - - server.on('error', e => { - switch ((e as any).code) { - case 'EACCES': - serverLogger.error(`You do not have permission to listen on port ${config.port}.`); - break; - case 'EADDRINUSE': - serverLogger.error(`Port ${config.port} is already in use by another process.`); - break; - default: - serverLogger.error(e); - break; - } - - if (cluster.isWorker) { - process.send!('listenFailed'); - } else { - // disableClustering - process.exit(1); - } - }); - - server.listen(config.port, resolve); -}); diff --git a/packages/backend/src/server/well-known.ts b/packages/backend/src/server/well-known.ts deleted file mode 100644 index 1d094f2edd01..000000000000 --- a/packages/backend/src/server/well-known.ts +++ /dev/null @@ -1,151 +0,0 @@ -import Router from '@koa/router'; - -import config from '@/config/index.js'; -import * as Acct from '@/misc/acct.js'; -import { links } from './nodeinfo.js'; -import { escapeAttribute, escapeValue } from '@/prelude/xml.js'; -import { Users } from '@/models/index.js'; -import { User } from '@/models/entities/user.js'; -import { FindOptionsWhere, IsNull } from 'typeorm'; - -// Init router -const router = new Router(); - -const XRD = (...x: { element: string, value?: string, attributes?: Record }[]) => - `${x.map(({ element, value, attributes }) => - `<${ - Object.entries(typeof attributes === 'object' && attributes || {}).reduce((a, [k, v]) => `${a} ${k}="${escapeAttribute(v)}"`, element) - }${ - typeof value === 'string' ? `>${escapeValue(value)}`).reduce((a, c) => a + c, '')}`; - -const allPath = '/.well-known/(.*)'; -const webFingerPath = '/.well-known/webfinger'; -const jrd = 'application/jrd+json'; -const xrd = 'application/xrd+xml'; - -router.use(allPath, async (ctx, next) => { - ctx.set({ - 'Access-Control-Allow-Headers': 'Accept', - 'Access-Control-Allow-Methods': 'GET, OPTIONS', - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Expose-Headers': 'Vary', - }); - await next(); -}); - -router.options(allPath, async ctx => { - ctx.status = 204; -}); - -router.get('/.well-known/host-meta', async ctx => { - ctx.set('Content-Type', xrd); - ctx.body = XRD({ element: 'Link', attributes: { - rel: 'lrdd', - type: xrd, - template: `${config.url}${webFingerPath}?resource={uri}`, - } }); -}); - -router.get('/.well-known/host-meta.json', async ctx => { - ctx.set('Content-Type', jrd); - ctx.body = { - links: [{ - rel: 'lrdd', - type: jrd, - template: `${config.url}${webFingerPath}?resource={uri}`, - }], - }; -}); - -router.get('/.well-known/nodeinfo', async ctx => { - ctx.body = { links }; -}); - -/* TODO -router.get('/.well-known/change-password', async ctx => { -}); -*/ - -router.get(webFingerPath, async ctx => { - const fromId = (id: User['id']): FindOptionsWhere => ({ - id, - host: IsNull(), - isSuspended: false, - }); - - const generateQuery = (resource: string): FindOptionsWhere | number => - resource.startsWith(`${config.url.toLowerCase()}/users/`) ? - fromId(resource.split('/').pop()!) : - fromAcct(Acct.parse( - resource.startsWith(`${config.url.toLowerCase()}/@`) ? resource.split('/').pop()! : - resource.startsWith('acct:') ? resource.slice('acct:'.length) : - resource)); - - const fromAcct = (acct: Acct.Acct): FindOptionsWhere | number => - !acct.host || acct.host === config.host.toLowerCase() ? { - usernameLower: acct.username, - host: IsNull(), - isSuspended: false, - } : 422; - - if (typeof ctx.query.resource !== 'string') { - ctx.status = 400; - return; - } - - const query = generateQuery(ctx.query.resource.toLowerCase()); - - if (typeof query === 'number') { - ctx.status = query; - return; - } - - const user = await Users.findOneBy(query); - - if (user == null) { - ctx.status = 404; - return; - } - - const subject = `acct:${user.username}@${config.host}`; - const self = { - rel: 'self', - type: 'application/activity+json', - href: `${config.url}/users/${user.id}`, - }; - const profilePage = { - rel: 'http://webfinger.net/rel/profile-page', - type: 'text/html', - href: `${config.url}/@${user.username}`, - }; - const subscribe = { - rel: 'http://ostatus.org/schema/1.0/subscribe', - template: `${config.url}/authorize-follow?acct={uri}`, - }; - - if (ctx.accepts(jrd, xrd) === xrd) { - ctx.body = XRD( - { element: 'Subject', value: subject }, - { element: 'Link', attributes: self }, - { element: 'Link', attributes: profilePage }, - { element: 'Link', attributes: subscribe }); - ctx.type = xrd; - } else { - ctx.body = { - subject, - links: [self, profilePage, subscribe], - }; - ctx.type = jrd; - } - - ctx.vary('Accept'); - ctx.set('Cache-Control', 'public, max-age=180'); -}); - -// Return 404 for other .well-known -router.all(allPath, async ctx => { - ctx.status = 404; -}); - -export default router; From 36a11d577f48cc086308cedc70ceec856db54ad5 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 14 Sep 2022 08:37:40 +0900 Subject: [PATCH 083/180] wip --- packages/backend/src/queue/queue.module.ts | 18 +- packages/backend/src/queue/queue.service.ts | 4 +- .../src/server/web/ClientServerService.ts | 565 ++++++++++++++++++ .../src/server/web/UrlPreviewService.ts | 84 +++ packages/backend/src/server/web/index.ts | 521 ---------------- .../backend/src/server/web/url-preview.ts | 65 -- 6 files changed, 660 insertions(+), 597 deletions(-) create mode 100644 packages/backend/src/server/web/ClientServerService.ts create mode 100644 packages/backend/src/server/web/UrlPreviewService.ts delete mode 100644 packages/backend/src/server/web/index.ts delete mode 100644 packages/backend/src/server/web/url-preview.ts diff --git a/packages/backend/src/queue/queue.module.ts b/packages/backend/src/queue/queue.module.ts index 705f03fc8a3b..2827c15c722e 100644 --- a/packages/backend/src/queue/queue.module.ts +++ b/packages/backend/src/queue/queue.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import { Config } from '@/config/types.js'; +import type { Config } from '@/config/types.js'; import { initialize as initializeQueue } from './initialize.js'; import type Bull from 'bull'; import type { DeliverJobData, InboxJobData, DbJobData, ObjectStorageJobData, EndedPollNotificationJobData, WebhookDeliverJobData } from './types.js'; @@ -17,13 +17,13 @@ export type WebhookDeliverQueue = Bull.Queue; imports: [ ], providers: [ - { provide: 'queue:system', useValue: initializeQueue('system') }, - { provide: 'queue:endedPollNotification', useValue: initializeQueue('endedPollNotification') }, - { provide: 'queue:deliver', useFactory: (config: Config) => initializeQueue('deliver', config.deliverJobPerSec || 128), inject: [DI_SYMBOLS.config] }, - { provide: 'queue:inbox', useFactory: (config: Config) => initializeQueue('inbox', config.inboxJobPerSec || 16), inject: [DI_SYMBOLS.config] }, - { provide: 'queue:db', useValue: initializeQueue('db') }, - { provide: 'queue:objectStorage', useValue: initializeQueue('objectStorage') }, - { provide: 'queue:webhookDeliver', useValue: initializeQueue('webhookDeliver', 64) }, + { provide: 'queue:system', useValue: initializeQueue('system') }, + { provide: 'queue:endedPollNotification', useValue: initializeQueue('endedPollNotification') }, + { provide: 'queue:deliver', useFactory: (config: Config) => initializeQueue('deliver', config.deliverJobPerSec ?? 128), inject: [DI_SYMBOLS.config] }, + { provide: 'queue:inbox', useFactory: (config: Config) => initializeQueue('inbox', config.inboxJobPerSec ?? 16), inject: [DI_SYMBOLS.config] }, + { provide: 'queue:db', useValue: initializeQueue('db') }, + { provide: 'queue:objectStorage', useValue: initializeQueue('objectStorage') }, + { provide: 'queue:webhookDeliver', useValue: initializeQueue('webhookDeliver', 64) }, ], - }) +}) export class QueueModule {} diff --git a/packages/backend/src/queue/queue.service.ts b/packages/backend/src/queue/queue.service.ts index 46afff09eea0..9f642bdd35bf 100644 --- a/packages/backend/src/queue/queue.service.ts +++ b/packages/backend/src/queue/queue.service.ts @@ -3,9 +3,9 @@ import { v4 as uuid } from 'uuid'; import type { IActivity } from '@/services/remote/activitypub/type.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import type { Webhook, webhookEventTypes } from '@/models/entities/webhook.js'; -import type { Config } from '@/config/types.js'; +import { Config } from '@/config/types.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from './queue.module.js'; +import { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from './queue.module.js'; import type { ThinUser } from './types.js'; import type httpSignature from '@peertube/http-signature'; diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts new file mode 100644 index 000000000000..b768c13be202 --- /dev/null +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -0,0 +1,565 @@ +import { dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { PathOrFileDescriptor, readFileSync } from 'node:fs'; +import { Inject, Injectable } from '@nestjs/common'; +import ms from 'ms'; +import Koa from 'koa'; +import Router from '@koa/router'; +import send from 'koa-send'; +import favicon from 'koa-favicon'; +import views from 'koa-views'; +import sharp from 'sharp'; +import { createBullBoard } from '@bull-board/api'; +import { BullAdapter } from '@bull-board/api/bullAdapter.js'; +import { KoaAdapter } from '@bull-board/koa'; +import { In, IsNull } from 'typeorm'; +import { Config } from '@/config/types.js'; +import type { Pages , Channels, Clips, GalleryPosts , Notes, UserProfiles, Users } from '@/models/index.js'; +import { getNoteSummary } from '@/misc/get-note-summary.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import * as Acct from '@/misc/acct.js'; +import { MetaService } from '@/services/MetaService.js'; +import { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from '@/queue/queue.module.js'; +import { UrlPreviewService } from './UrlPreviewService.js'; + +const _filename = fileURLToPath(import.meta.url); +const _dirname = dirname(_filename); + +const staticAssets = `${_dirname}/../../../assets/`; +const clientAssets = `${_dirname}/../../../../client/assets/`; +const assets = `${_dirname}/../../../../../built/_client_dist_/`; +const swAssets = `${_dirname}/../../../../../built/_sw_dist_/`; + +@Injectable() +export class ClientServerService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + + @Inject('galleryPostsRepository') + private galleryPostsRepository: typeof GalleryPosts, + + @Inject('channelsRepository') + private channelsRepository: typeof Channels, + + @Inject('clipsRepository') + private clipsRepository: typeof Clips, + + @Inject('pagesRepository') + private pagesRepository: typeof Pages, + + private metaService: MetaService, + private urlPreviewService: UrlPreviewService, + + @Inject('queue:system') public systemQueue: SystemQueue, + @Inject('queue:endedPollNotification') public endedPollNotificationQueue: EndedPollNotificationQueue, + @Inject('queue:deliver') public deliverQueue: DeliverQueue, + @Inject('queue:inbox') public inboxQueue: InboxQueue, + @Inject('queue:db') public dbQueue: DbQueue, + @Inject('queue:objectStorage') public objectStorageQueue: ObjectStorageQueue, + @Inject('queue:webhookDeliver') public webhookDeliverQueue: WebhookDeliverQueue, + ) { + } + + public createApp() { + const app = new Koa(); + + //#region Bull Dashboard + const bullBoardPath = '/queue'; + + // Authenticate + app.use(async (ctx, next) => { + if (ctx.path === bullBoardPath || ctx.path.startsWith(bullBoardPath + '/')) { + const token = ctx.cookies.get('token'); + if (token == null) { + ctx.status = 401; + return; + } + const user = await this.usersRepository.findOneBy({ token }); + if (user == null || !(user.isAdmin || user.isModerator)) { + ctx.status = 403; + return; + } + } + await next(); + }); + + const serverAdapter = new KoaAdapter(); + + createBullBoard({ + queues: [ + this.systemQueue, + this.endedPollNotificationQueue, + this.deliverQueue, + this.inboxQueue, + this.dbQueue, + this.objectStorageQueue, + this.webhookDeliverQueue, + ].map(q => new BullAdapter(q)), + serverAdapter, + }); + + serverAdapter.setBasePath(bullBoardPath); + app.use(serverAdapter.registerPlugin()); + //#endregion + + // Init renderer + app.use(views(_dirname + '/views', { + extension: 'pug', + options: { + version: this.config.version, + getClientEntry: () => process.env.NODE_ENV === 'production' ? + this.config.clientEntry : + JSON.parse(readFileSync(`${_dirname}/../../../../../built/_client_dist_/manifest.json`, 'utf-8'))['src/init.ts'], + config: this.config, + }, + })); + + // Serve favicon + app.use(favicon(`${_dirname}/../../../assets/favicon.ico`)); + + // Common request handler + app.use(async (ctx, next) => { + // IFrameの中に入れられないようにする + ctx.set('X-Frame-Options', 'DENY'); + await next(); + }); + + // Init router + const router = new Router(); + + //#region static assets + + router.get('/static-assets/(.*)', async ctx => { + await send(ctx as any, ctx.path.replace('/static-assets/', ''), { + root: staticAssets, + maxage: ms('7 days'), + }); + }); + + router.get('/client-assets/(.*)', async ctx => { + await send(ctx as any, ctx.path.replace('/client-assets/', ''), { + root: clientAssets, + maxage: ms('7 days'), + }); + }); + + router.get('/assets/(.*)', async ctx => { + await send(ctx as any, ctx.path.replace('/assets/', ''), { + root: assets, + maxage: ms('7 days'), + }); + }); + + // Apple touch icon + router.get('/apple-touch-icon.png', async ctx => { + await send(ctx as any, '/apple-touch-icon.png', { + root: staticAssets, + }); + }); + + router.get('/twemoji/(.*)', async ctx => { + const path = ctx.path.replace('/twemoji/', ''); + + if (!path.match(/^[0-9a-f-]+\.svg$/)) { + ctx.status = 404; + return; + } + + ctx.set('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\''); + + await send(ctx as any, path, { + root: `${_dirname}/../../../node_modules/@discordapp/twemoji/dist/svg/`, + maxage: ms('30 days'), + }); + }); + + router.get('/twemoji-badge/(.*)', async ctx => { + const path = ctx.path.replace('/twemoji-badge/', ''); + + if (!path.match(/^[0-9a-f-]+\.png$/)) { + ctx.status = 404; + return; + } + + const mask = await sharp( + `${_dirname}/../../../node_modules/@discordapp/twemoji/dist/svg/${path.replace('.png', '')}.svg`, + { density: 1000 }, + ) + .resize(488, 488) + .greyscale() + .normalise() + .linear(1.75, -(128 * 1.75) + 128) // 1.75x contrast + .flatten({ background: '#000' }) + .extend({ + top: 12, + bottom: 12, + left: 12, + right: 12, + background: '#000', + }) + .toColorspace('b-w') + .png() + .toBuffer(); + + const buffer = await sharp({ + create: { width: 512, height: 512, channels: 4, background: { r: 0, g: 0, b: 0, alpha: 0 } }, + }) + .pipelineColorspace('b-w') + .boolean(mask, 'eor') + .resize(96, 96) + .png() + .toBuffer(); + + ctx.set('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\''); + ctx.set('Cache-Control', 'max-age=2592000'); + ctx.set('Content-Type', 'image/png'); + ctx.body = buffer; + }); + + // ServiceWorker + router.get('/sw.js', async ctx => { + await send(ctx as any, '/sw.js', { + root: swAssets, + maxage: ms('10 minutes'), + }); + }); + + // Manifest + router.get('/manifest.json', manifestHandler); + + router.get('/robots.txt', async ctx => { + await send(ctx as any, '/robots.txt', { + root: staticAssets, + }); + }); + + //#endregion + + // Docs + router.get('/api-doc', async ctx => { + await send(ctx as any, '/redoc.html', { + root: staticAssets, + }); + }); + + // URL preview endpoint + router.get('/url', ctx => this.urlPreviewService.handle(ctx)); + + router.get('/api.json', async ctx => { + ctx.body = genOpenapiSpec(); + }); + + const getFeed = async (acct: string) => { + const { username, host } = Acct.parse(acct); + const user = await this.usersRepository.findOneBy({ + usernameLower: username.toLowerCase(), + host: host ?? IsNull(), + isSuspended: false, + }); + + return user && await packFeed(user); + }; + + // Atom + router.get('/@:user.atom', async ctx => { + const feed = await getFeed(ctx.params.user); + + if (feed) { + ctx.set('Content-Type', 'application/atom+xml; charset=utf-8'); + ctx.body = feed.atom1(); + } else { + ctx.status = 404; + } + }); + + // RSS + router.get('/@:user.rss', async ctx => { + const feed = await getFeed(ctx.params.user); + + if (feed) { + ctx.set('Content-Type', 'application/rss+xml; charset=utf-8'); + ctx.body = feed.rss2(); + } else { + ctx.status = 404; + } + }); + + // JSON + router.get('/@:user.json', async ctx => { + const feed = await getFeed(ctx.params.user); + + if (feed) { + ctx.set('Content-Type', 'application/json; charset=utf-8'); + ctx.body = feed.json1(); + } else { + ctx.status = 404; + } + }); + + //#region SSR (for crawlers) + // User + router.get(['/@:user', '/@:user/:sub'], async (ctx, next) => { + const { username, host } = Acct.parse(ctx.params.user); + const user = await this.usersRepository.findOneBy({ + usernameLower: username.toLowerCase(), + host: host ?? IsNull(), + isSuspended: false, + }); + + if (user != null) { + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); + const meta = await this.metaService.fetch(); + const me = profile.fields + ? profile.fields + .filter(filed => filed.value != null && filed.value.match(/^https?:/)) + .map(field => field.value) + : []; + + await ctx.render('user', { + user, profile, me, + avatarUrl: await this.usersRepository.getAvatarUrl(user), + sub: ctx.params.sub, + instanceName: meta.name || 'Misskey', + icon: meta.iconUrl, + themeColor: meta.themeColor, + }); + ctx.set('Cache-Control', 'public, max-age=15'); + } else { + // リモートユーザーなので + // モデレータがAPI経由で参照可能にするために404にはしない + await next(); + } + }); + + router.get('/users/:user', async ctx => { + const user = await this.usersRepository.findOneBy({ + id: ctx.params.user, + host: IsNull(), + isSuspended: false, + }); + + if (user == null) { + ctx.status = 404; + return; + } + + ctx.redirect(`/@${user.username}${ user.host == null ? '' : '@' + user.host}`); + }); + + // Note + router.get('/notes/:note', async (ctx, next) => { + const note = await this.notesRepository.findOneBy({ + id: ctx.params.note, + visibility: In(['public', 'home']), + }); + + if (note) { + const _note = await this.notesRepository.pack(note); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: note.userId }); + const meta = await this.metaService.fetch(); + await ctx.render('note', { + note: _note, + profile, + avatarUrl: await this.usersRepository.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: note.userId })), + // TODO: Let locale changeable by instance setting + summary: getNoteSummary(_note), + instanceName: meta.name || 'Misskey', + icon: meta.iconUrl, + themeColor: meta.themeColor, + }); + + ctx.set('Cache-Control', 'public, max-age=15'); + + return; + } + + await next(); + }); + + // Page + router.get('/@:user/pages/:page', async (ctx, next) => { + const { username, host } = Acct.parse(ctx.params.user); + const user = await this.usersRepository.findOneBy({ + usernameLower: username.toLowerCase(), + host: host ?? IsNull(), + }); + + if (user == null) return; + + const page = await this.pagesRepository.findOneBy({ + name: ctx.params.page, + userId: user.id, + }); + + if (page) { + const _page = await this.pagesRepository.pack(page); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: page.userId }); + const meta = await this.metaService.fetch(); + await ctx.render('page', { + page: _page, + profile, + avatarUrl: await this.usersRepository.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: page.userId })), + instanceName: meta.name || 'Misskey', + icon: meta.iconUrl, + themeColor: meta.themeColor, + }); + + if (['public'].includes(page.visibility)) { + ctx.set('Cache-Control', 'public, max-age=15'); + } else { + ctx.set('Cache-Control', 'private, max-age=0, must-revalidate'); + } + + return; + } + + await next(); + }); + + // Clip + // TODO: 非publicなclipのハンドリング + router.get('/clips/:clip', async (ctx, next) => { + const clip = await this.clipsRepository.findOneBy({ + id: ctx.params.clip, + }); + + if (clip) { + const _clip = await this.clipsRepository.pack(clip); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: clip.userId }); + const meta = await this.metaService.fetch(); + await ctx.render('clip', { + clip: _clip, + profile, + avatarUrl: await this.usersRepository.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: clip.userId })), + instanceName: meta.name || 'Misskey', + icon: meta.iconUrl, + themeColor: meta.themeColor, + }); + + ctx.set('Cache-Control', 'public, max-age=15'); + + return; + } + + await next(); + }); + + // Gallery post + router.get('/gallery/:post', async (ctx, next) => { + const post = await this.galleryPostsRepository.findOneBy({ id: ctx.params.post }); + + if (post) { + const _post = await this.galleryPostsRepository.pack(post); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: post.userId }); + const meta = await this.metaService.fetch(); + await ctx.render('gallery-post', { + post: _post, + profile, + avatarUrl: await this.usersRepository.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: post.userId })), + instanceName: meta.name || 'Misskey', + icon: meta.iconUrl, + themeColor: meta.themeColor, + }); + + ctx.set('Cache-Control', 'public, max-age=15'); + + return; + } + + await next(); + }); + + // Channel + router.get('/channels/:channel', async (ctx, next) => { + const channel = await this.channelsRepository.findOneBy({ + id: ctx.params.channel, + }); + + if (channel) { + const _channel = await this.channelsRepository.pack(channel); + const meta = await this.metaService.fetch(); + await ctx.render('channel', { + channel: _channel, + instanceName: meta.name || 'Misskey', + icon: meta.iconUrl, + themeColor: meta.themeColor, + }); + + ctx.set('Cache-Control', 'public, max-age=15'); + + return; + } + + await next(); + }); + //#endregion + + router.get('/_info_card_', async ctx => { + const meta = await this.metaService.fetch(true); + + ctx.remove('X-Frame-Options'); + + await ctx.render('info-card', { + version: this.config.version, + host: this.config.host, + meta: meta, + originalUsersCount: await this.usersRepository.countBy({ host: IsNull() }), + originalNotesCount: await this.notesRepository.countBy({ userHost: IsNull() }), + }); + }); + + router.get('/bios', async ctx => { + await ctx.render('bios', { + version: this.config.version, + }); + }); + + router.get('/cli', async ctx => { + await ctx.render('cli', { + version: this.config.version, + }); + }); + + const override = (source: string, target: string, depth = 0) => + [, ...target.split('/').filter(x => x), ...source.split('/').filter(x => x).splice(depth)].join('/'); + + router.get('/flush', async ctx => { + await ctx.render('flush'); + }); + + // streamingに非WebSocketリクエストが来た場合にbase htmlをキャシュ付きで返すと、Proxy等でそのパスがキャッシュされておかしくなる + router.get('/streaming', async ctx => { + ctx.status = 503; + ctx.set('Cache-Control', 'private, max-age=0'); + }); + + // Render base html for all requests + router.get('(.*)', async ctx => { + const meta = await this.metaService.fetch(); + await ctx.render('base', { + img: meta.bannerUrl, + title: meta.name || 'Misskey', + instanceName: meta.name || 'Misskey', + desc: meta.description, + icon: meta.iconUrl, + themeColor: meta.themeColor, + }); + ctx.set('Cache-Control', 'public, max-age=15'); + }); + + // Register router + app.use(router.routes()); + + return app; + } +} diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts new file mode 100644 index 000000000000..e646c301370f --- /dev/null +++ b/packages/backend/src/server/web/UrlPreviewService.ts @@ -0,0 +1,84 @@ +import { Inject, Injectable } from '@nestjs/common'; +import summaly from 'summaly'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Users } from '@/models/index.js'; +import { Config } from '@/config/types.js'; +import { MetaService } from '@/services/MetaService.js'; +import { HttpRequestService } from '@/services/HttpRequestService.js'; +import Logger from '@/logger.js'; +import { query } from '@/prelude/url.js'; +import type Koa from 'koa'; + +@Injectable() +export class UrlPreviewService { + #logger: Logger; + + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + private metaService: MetaService, + private httpRequestService: HttpRequestService, + ) { + this.#logger = new Logger('url-preview'); + } + + #wrap(url?: string): string | null { + return url != null + ? url.match(/^https?:\/\//) + ? `${this.config.url}/proxy/preview.webp?${query({ + url, + preview: '1', + })}` + : url + : null; + } + + public async handle(ctx: Koa.Context) { + const url = ctx.query.url; + if (typeof url !== 'string') { + ctx.status = 400; + return; + } + + const lang = ctx.query.lang; + if (Array.isArray(lang)) { + ctx.status = 400; + return; + } + + const meta = await this.metaService.fetch(); + + this.#logger.info(meta.summalyProxy + ? `(Proxy) Getting preview of ${url}@${lang} ...` + : `Getting preview of ${url}@${lang} ...`); + + try { + const summary = meta.summalyProxy ? await this.httpRequestService.getJson(`${meta.summalyProxy}?${query({ + url: url, + lang: lang ?? 'ja-JP', + })}`) : await summaly.default(url, { + followRedirects: false, + lang: lang ?? 'ja-JP', + }); + + this.#logger.succ(`Got preview of ${url}: ${summary.title}`); + + summary.icon = this.#wrap(summary.icon); + summary.thumbnail = this.#wrap(summary.thumbnail); + + // Cache 7days + ctx.set('Cache-Control', 'max-age=604800, immutable'); + + ctx.body = summary; + } catch (err) { + this.#logger.warn(`Failed to get preview of ${url}: ${err}`); + ctx.status = 200; + ctx.set('Cache-Control', 'max-age=86400, immutable'); + ctx.body = '{}'; + } + } +} diff --git a/packages/backend/src/server/web/index.ts b/packages/backend/src/server/web/index.ts deleted file mode 100644 index be95becb6818..000000000000 --- a/packages/backend/src/server/web/index.ts +++ /dev/null @@ -1,521 +0,0 @@ -/** - * Web Client Server - */ - -import { dirname } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { PathOrFileDescriptor, readFileSync } from 'node:fs'; -import ms from 'ms'; -import Koa from 'koa'; -import Router from '@koa/router'; -import send from 'koa-send'; -import favicon from 'koa-favicon'; -import views from 'koa-views'; -import sharp from 'sharp'; -import { createBullBoard } from '@bull-board/api'; -import { BullAdapter } from '@bull-board/api/bullAdapter.js'; -import { KoaAdapter } from '@bull-board/koa'; - -import { In, IsNull } from 'typeorm'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import config from '@/config/index.js'; -import { Users, Notes, UserProfiles, Pages, Channels, Clips, GalleryPosts } from '@/models/index.js'; -import * as Acct from '@/misc/acct.js'; -import { getNoteSummary } from '@/misc/get-note-summary.js'; -import { queues } from '@/queue/queues.js'; -import { genOpenapiSpec } from '../api/openapi/gen-spec.js'; -import { urlPreviewHandler } from './url-preview.js'; -import { manifestHandler } from './manifest.js'; -import packFeed from './feed.js'; - -const _filename = fileURLToPath(import.meta.url); -const _dirname = dirname(_filename); - -const staticAssets = `${_dirname}/../../../assets/`; -const clientAssets = `${_dirname}/../../../../client/assets/`; -const assets = `${_dirname}/../../../../../built/_client_dist_/`; -const swAssets = `${_dirname}/../../../../../built/_sw_dist_/`; - -// Init app -const app = new Koa(); - -//#region Bull Dashboard -const bullBoardPath = '/queue'; - -// Authenticate -app.use(async (ctx, next) => { - if (ctx.path === bullBoardPath || ctx.path.startsWith(bullBoardPath + '/')) { - const token = ctx.cookies.get('token'); - if (token == null) { - ctx.status = 401; - return; - } - const user = await Users.findOneBy({ token }); - if (user == null || !(user.isAdmin || user.isModerator)) { - ctx.status = 403; - return; - } - } - await next(); -}); - -const serverAdapter = new KoaAdapter(); - -createBullBoard({ - queues: queues.map(q => new BullAdapter(q)), - serverAdapter, -}); - -serverAdapter.setBasePath(bullBoardPath); -app.use(serverAdapter.registerPlugin()); -//#endregion - -// Init renderer -app.use(views(_dirname + '/views', { - extension: 'pug', - options: { - version: config.version, - getClientEntry: () => process.env.NODE_ENV === 'production' ? - config.clientEntry : - JSON.parse(readFileSync(`${_dirname}/../../../../../built/_client_dist_/manifest.json`, 'utf-8'))['src/init.ts'], - config, - }, -})); - -// Serve favicon -app.use(favicon(`${_dirname}/../../../assets/favicon.ico`)); - -// Common request handler -app.use(async (ctx, next) => { - // IFrameの中に入れられないようにする - ctx.set('X-Frame-Options', 'DENY'); - await next(); -}); - -// Init router -const router = new Router(); - -//#region static assets - -router.get('/static-assets/(.*)', async ctx => { - await send(ctx as any, ctx.path.replace('/static-assets/', ''), { - root: staticAssets, - maxage: ms('7 days'), - }); -}); - -router.get('/client-assets/(.*)', async ctx => { - await send(ctx as any, ctx.path.replace('/client-assets/', ''), { - root: clientAssets, - maxage: ms('7 days'), - }); -}); - -router.get('/assets/(.*)', async ctx => { - await send(ctx as any, ctx.path.replace('/assets/', ''), { - root: assets, - maxage: ms('7 days'), - }); -}); - -// Apple touch icon -router.get('/apple-touch-icon.png', async ctx => { - await send(ctx as any, '/apple-touch-icon.png', { - root: staticAssets, - }); -}); - -router.get('/twemoji/(.*)', async ctx => { - const path = ctx.path.replace('/twemoji/', ''); - - if (!path.match(/^[0-9a-f-]+\.svg$/)) { - ctx.status = 404; - return; - } - - ctx.set('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\''); - - await send(ctx as any, path, { - root: `${_dirname}/../../../node_modules/@discordapp/twemoji/dist/svg/`, - maxage: ms('30 days'), - }); -}); - -router.get('/twemoji-badge/(.*)', async ctx => { - const path = ctx.path.replace('/twemoji-badge/', ''); - - if (!path.match(/^[0-9a-f-]+\.png$/)) { - ctx.status = 404; - return; - } - - const mask = await sharp( - `${_dirname}/../../../node_modules/@discordapp/twemoji/dist/svg/${path.replace('.png', '')}.svg`, - { density: 1000 }, - ) - .resize(488, 488) - .greyscale() - .normalise() - .linear(1.75, -(128 * 1.75) + 128) // 1.75x contrast - .flatten({ background: '#000' }) - .extend({ - top: 12, - bottom: 12, - left: 12, - right: 12, - background: '#000', - }) - .toColorspace('b-w') - .png() - .toBuffer(); - - const buffer = await sharp({ - create: { width: 512, height: 512, channels: 4, background: { r: 0, g: 0, b: 0, alpha: 0 } }, - }) - .pipelineColorspace('b-w') - .boolean(mask, 'eor') - .resize(96, 96) - .png() - .toBuffer(); - - ctx.set('Content-Security-Policy', 'default-src \'none\'; style-src \'unsafe-inline\''); - ctx.set('Cache-Control', 'max-age=2592000'); - ctx.set('Content-Type', 'image/png'); - ctx.body = buffer; -}); - -// ServiceWorker -router.get(`/sw.js`, async ctx => { - await send(ctx as any, `/sw.js`, { - root: swAssets, - maxage: ms('10 minutes'), - }); -}); - -// Manifest -router.get('/manifest.json', manifestHandler); - -router.get('/robots.txt', async ctx => { - await send(ctx as any, '/robots.txt', { - root: staticAssets, - }); -}); - -//#endregion - -// Docs -router.get('/api-doc', async ctx => { - await send(ctx as any, '/redoc.html', { - root: staticAssets, - }); -}); - -// URL preview endpoint -router.get('/url', urlPreviewHandler); - -router.get('/api.json', async ctx => { - ctx.body = genOpenapiSpec(); -}); - -const getFeed = async (acct: string) => { - const { username, host } = Acct.parse(acct); - const user = await Users.findOneBy({ - usernameLower: username.toLowerCase(), - host: host ?? IsNull(), - isSuspended: false, - }); - - return user && await packFeed(user); -}; - -// Atom -router.get('/@:user.atom', async ctx => { - const feed = await getFeed(ctx.params.user); - - if (feed) { - ctx.set('Content-Type', 'application/atom+xml; charset=utf-8'); - ctx.body = feed.atom1(); - } else { - ctx.status = 404; - } -}); - -// RSS -router.get('/@:user.rss', async ctx => { - const feed = await getFeed(ctx.params.user); - - if (feed) { - ctx.set('Content-Type', 'application/rss+xml; charset=utf-8'); - ctx.body = feed.rss2(); - } else { - ctx.status = 404; - } -}); - -// JSON -router.get('/@:user.json', async ctx => { - const feed = await getFeed(ctx.params.user); - - if (feed) { - ctx.set('Content-Type', 'application/json; charset=utf-8'); - ctx.body = feed.json1(); - } else { - ctx.status = 404; - } -}); - -//#region SSR (for crawlers) -// User -router.get(['/@:user', '/@:user/:sub'], async (ctx, next) => { - const { username, host } = Acct.parse(ctx.params.user); - const user = await Users.findOneBy({ - usernameLower: username.toLowerCase(), - host: host ?? IsNull(), - isSuspended: false, - }); - - if (user != null) { - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - const meta = await fetchMeta(); - const me = profile.fields - ? profile.fields - .filter(filed => filed.value != null && filed.value.match(/^https?:/)) - .map(field => field.value) - : []; - - await ctx.render('user', { - user, profile, me, - avatarUrl: await Users.getAvatarUrl(user), - sub: ctx.params.sub, - instanceName: meta.name || 'Misskey', - icon: meta.iconUrl, - themeColor: meta.themeColor, - }); - ctx.set('Cache-Control', 'public, max-age=15'); - } else { - // リモートユーザーなので - // モデレータがAPI経由で参照可能にするために404にはしない - await next(); - } -}); - -router.get('/users/:user', async ctx => { - const user = await Users.findOneBy({ - id: ctx.params.user, - host: IsNull(), - isSuspended: false, - }); - - if (user == null) { - ctx.status = 404; - return; - } - - ctx.redirect(`/@${user.username}${ user.host == null ? '' : '@' + user.host}`); -}); - -// Note -router.get('/notes/:note', async (ctx, next) => { - const note = await Notes.findOneBy({ - id: ctx.params.note, - visibility: In(['public', 'home']), - }); - - if (note) { - const _note = await Notes.pack(note); - const profile = await UserProfiles.findOneByOrFail({ userId: note.userId }); - const meta = await fetchMeta(); - await ctx.render('note', { - note: _note, - profile, - avatarUrl: await Users.getAvatarUrl(await Users.findOneByOrFail({ id: note.userId })), - // TODO: Let locale changeable by instance setting - summary: getNoteSummary(_note), - instanceName: meta.name || 'Misskey', - icon: meta.iconUrl, - themeColor: meta.themeColor, - }); - - ctx.set('Cache-Control', 'public, max-age=15'); - - return; - } - - await next(); -}); - -// Page -router.get('/@:user/pages/:page', async (ctx, next) => { - const { username, host } = Acct.parse(ctx.params.user); - const user = await Users.findOneBy({ - usernameLower: username.toLowerCase(), - host: host ?? IsNull(), - }); - - if (user == null) return; - - const page = await Pages.findOneBy({ - name: ctx.params.page, - userId: user.id, - }); - - if (page) { - const _page = await Pages.pack(page); - const profile = await UserProfiles.findOneByOrFail({ userId: page.userId }); - const meta = await fetchMeta(); - await ctx.render('page', { - page: _page, - profile, - avatarUrl: await Users.getAvatarUrl(await Users.findOneByOrFail({ id: page.userId })), - instanceName: meta.name || 'Misskey', - icon: meta.iconUrl, - themeColor: meta.themeColor, - }); - - if (['public'].includes(page.visibility)) { - ctx.set('Cache-Control', 'public, max-age=15'); - } else { - ctx.set('Cache-Control', 'private, max-age=0, must-revalidate'); - } - - return; - } - - await next(); -}); - -// Clip -// TODO: 非publicなclipのハンドリング -router.get('/clips/:clip', async (ctx, next) => { - const clip = await Clips.findOneBy({ - id: ctx.params.clip, - }); - - if (clip) { - const _clip = await Clips.pack(clip); - const profile = await UserProfiles.findOneByOrFail({ userId: clip.userId }); - const meta = await fetchMeta(); - await ctx.render('clip', { - clip: _clip, - profile, - avatarUrl: await Users.getAvatarUrl(await Users.findOneByOrFail({ id: clip.userId })), - instanceName: meta.name || 'Misskey', - icon: meta.iconUrl, - themeColor: meta.themeColor, - }); - - ctx.set('Cache-Control', 'public, max-age=15'); - - return; - } - - await next(); -}); - -// Gallery post -router.get('/gallery/:post', async (ctx, next) => { - const post = await GalleryPosts.findOneBy({ id: ctx.params.post }); - - if (post) { - const _post = await GalleryPosts.pack(post); - const profile = await UserProfiles.findOneByOrFail({ userId: post.userId }); - const meta = await fetchMeta(); - await ctx.render('gallery-post', { - post: _post, - profile, - avatarUrl: await Users.getAvatarUrl(await Users.findOneByOrFail({ id: post.userId })), - instanceName: meta.name || 'Misskey', - icon: meta.iconUrl, - themeColor: meta.themeColor, - }); - - ctx.set('Cache-Control', 'public, max-age=15'); - - return; - } - - await next(); -}); - -// Channel -router.get('/channels/:channel', async (ctx, next) => { - const channel = await Channels.findOneBy({ - id: ctx.params.channel, - }); - - if (channel) { - const _channel = await Channels.pack(channel); - const meta = await fetchMeta(); - await ctx.render('channel', { - channel: _channel, - instanceName: meta.name || 'Misskey', - icon: meta.iconUrl, - themeColor: meta.themeColor, - }); - - ctx.set('Cache-Control', 'public, max-age=15'); - - return; - } - - await next(); -}); -//#endregion - -router.get('/_info_card_', async ctx => { - const meta = await fetchMeta(true); - - ctx.remove('X-Frame-Options'); - - await ctx.render('info-card', { - version: config.version, - host: config.host, - meta: meta, - originalUsersCount: await Users.countBy({ host: IsNull() }), - originalNotesCount: await Notes.countBy({ userHost: IsNull() }), - }); -}); - -router.get('/bios', async ctx => { - await ctx.render('bios', { - version: config.version, - }); -}); - -router.get('/cli', async ctx => { - await ctx.render('cli', { - version: config.version, - }); -}); - -const override = (source: string, target: string, depth = 0) => - [, ...target.split('/').filter(x => x), ...source.split('/').filter(x => x).splice(depth)].join('/'); - -router.get('/flush', async ctx => { - await ctx.render('flush'); -}); - -// streamingに非WebSocketリクエストが来た場合にbase htmlをキャシュ付きで返すと、Proxy等でそのパスがキャッシュされておかしくなる -router.get('/streaming', async ctx => { - ctx.status = 503; - ctx.set('Cache-Control', 'private, max-age=0'); -}); - -// Render base html for all requests -router.get('(.*)', async ctx => { - const meta = await fetchMeta(); - await ctx.render('base', { - img: meta.bannerUrl, - title: meta.name || 'Misskey', - instanceName: meta.name || 'Misskey', - desc: meta.description, - icon: meta.iconUrl, - themeColor: meta.themeColor, - }); - ctx.set('Cache-Control', 'public, max-age=15'); -}); - -// Register router -app.use(router.routes()); - -export default app; diff --git a/packages/backend/src/server/web/url-preview.ts b/packages/backend/src/server/web/url-preview.ts deleted file mode 100644 index 1e259649f985..000000000000 --- a/packages/backend/src/server/web/url-preview.ts +++ /dev/null @@ -1,65 +0,0 @@ -import Koa from 'koa'; -import summaly from 'summaly'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import Logger from '@/services/logger.js'; -import config from '@/config/index.js'; -import { query } from '@/prelude/url.js'; -import { getJson } from '@/misc/fetch.js'; - -const logger = new Logger('url-preview'); - -export const urlPreviewHandler = async (ctx: Koa.Context) => { - const url = ctx.query.url; - if (typeof url !== 'string') { - ctx.status = 400; - return; - } - - const lang = ctx.query.lang; - if (Array.isArray(lang)) { - ctx.status = 400; - return; - } - - const meta = await fetchMeta(); - - logger.info(meta.summalyProxy - ? `(Proxy) Getting preview of ${url}@${lang} ...` - : `Getting preview of ${url}@${lang} ...`); - - try { - const summary = meta.summalyProxy ? await getJson(`${meta.summalyProxy}?${query({ - url: url, - lang: lang ?? 'ja-JP', - })}`) : await summaly.default(url, { - followRedirects: false, - lang: lang ?? 'ja-JP', - }); - - logger.succ(`Got preview of ${url}: ${summary.title}`); - - summary.icon = wrap(summary.icon); - summary.thumbnail = wrap(summary.thumbnail); - - // Cache 7days - ctx.set('Cache-Control', 'max-age=604800, immutable'); - - ctx.body = summary; - } catch (err) { - logger.warn(`Failed to get preview of ${url}: ${err}`); - ctx.status = 200; - ctx.set('Cache-Control', 'max-age=86400, immutable'); - ctx.body = '{}'; - } -}; - -function wrap(url?: string): string | null { - return url != null - ? url.match(/^https?:\/\//) - ? `${config.url}/proxy/preview.webp?${query({ - url, - preview: '1', - })}` - : url - : null; -} From 54d4660db9634e70a5ad8070252a8eb9b4759404 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 14 Sep 2022 08:43:21 +0900 Subject: [PATCH 084/180] wip --- .../src/server/web/ClientServerService.ts | 22 ++++- .../backend/src/server/web/FeedService.ts | 80 +++++++++++++++++++ packages/backend/src/server/web/feed.ts | 58 -------------- packages/backend/src/server/web/manifest.ts | 18 ----- 4 files changed, 100 insertions(+), 78 deletions(-) create mode 100644 packages/backend/src/server/web/FeedService.ts delete mode 100644 packages/backend/src/server/web/feed.ts delete mode 100644 packages/backend/src/server/web/manifest.ts diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index b768c13be202..23115fc2886f 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -21,6 +21,8 @@ import * as Acct from '@/misc/acct.js'; import { MetaService } from '@/services/MetaService.js'; import { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from '@/queue/queue.module.js'; import { UrlPreviewService } from './UrlPreviewService.js'; +import { FeedService } from './FeedService.js'; +import manifest from './manifest.json' assert { type: 'json' }; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); @@ -59,6 +61,7 @@ export class ClientServerService { private metaService: MetaService, private urlPreviewService: UrlPreviewService, + private feedService: FeedService, @Inject('queue:system') public systemQueue: SystemQueue, @Inject('queue:endedPollNotification') public endedPollNotificationQueue: EndedPollNotificationQueue, @@ -70,6 +73,21 @@ export class ClientServerService { ) { } + async #manifestHandler(ctx: Koa.Context) { + // TODO + //const res = structuredClone(manifest); + const res = JSON.parse(JSON.stringify(manifest)); + + const instance = await this.metaService.fetch(true); + + res.short_name = instance.name || 'Misskey'; + res.name = instance.name || 'Misskey'; + if (instance.themeColor) res.theme_color = instance.themeColor; + + ctx.set('Cache-Control', 'max-age=300'); + ctx.body = res; + } + public createApp() { const app = new Koa(); @@ -235,7 +253,7 @@ export class ClientServerService { }); // Manifest - router.get('/manifest.json', manifestHandler); + router.get('/manifest.json', ctx => this.#manifestHandler(ctx)); router.get('/robots.txt', async ctx => { await send(ctx as any, '/robots.txt', { @@ -267,7 +285,7 @@ export class ClientServerService { isSuspended: false, }); - return user && await packFeed(user); + return user && await this.feedService.packFeed(user); }; // Atom diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts new file mode 100644 index 000000000000..5f5c01a5c40d --- /dev/null +++ b/packages/backend/src/server/web/FeedService.ts @@ -0,0 +1,80 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { In, IsNull } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { DriveFiles, Notes, UserProfiles , Users } from '@/models/index.js'; +import { Config } from '@/config/types.js'; +import type { User } from '@/models/entities/user'; + +@Injectable() +export class FeedService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + ) { + } + + public async packFeed(user: User) { + const author = { + link: `${this.config.url}/@${user.username}`, + name: user.name ?? user.username, + }; + + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); + + const notes = await this.notesRepository.find({ + where: { + userId: user.id, + renoteId: IsNull(), + visibility: In(['public', 'home']), + }, + order: { createdAt: -1 }, + take: 20, + }); + + const feed = new Feed({ + id: author.link, + title: `${author.name} (@${user.username}@${this.config.host})`, + updated: notes[0].createdAt, + generator: 'Misskey', + description: `${user.notesCount} Notes, ${profile.ffVisibility === 'public' ? user.followingCount : '?'} Following, ${profile.ffVisibility === 'public' ? user.followersCount : '?'} Followers${profile.description ? ` · ${profile.description}` : ''}`, + link: author.link, + image: await this.usersRepository.getAvatarUrl(user), + feedLinks: { + json: `${author.link}.json`, + atom: `${author.link}.atom`, + }, + author, + copyright: user.name || user.username, + }); + + for (const note of notes) { + const files = note.fileIds.length > 0 ? await this.driveFilesRepository.findBy({ + id: In(note.fileIds), + }) : []; + const file = files.find(file => file.type.startsWith('image/')); + + feed.addItem({ + title: `New note by ${author.name}`, + link: `${this.config.url}/notes/${note.id}`, + date: note.createdAt, + description: note.cw || undefined, + content: note.text || undefined, + image: file ? this.driveFilesRepository.getPublicUrl(file) || undefined : undefined, + }); + } + + return feed; + } +} diff --git a/packages/backend/src/server/web/feed.ts b/packages/backend/src/server/web/feed.ts deleted file mode 100644 index 4abe2885cf9d..000000000000 --- a/packages/backend/src/server/web/feed.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Feed } from 'feed'; -import { In, IsNull } from 'typeorm'; -import config from '@/config/index.js'; -import { User } from '@/models/entities/user.js'; -import { Notes, DriveFiles, UserProfiles, Users } from '@/models/index.js'; - -export default async function(user: User) { - const author = { - link: `${config.url}/@${user.username}`, - name: user.name || user.username, - }; - - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - const notes = await Notes.find({ - where: { - userId: user.id, - renoteId: IsNull(), - visibility: In(['public', 'home']), - }, - order: { createdAt: -1 }, - take: 20, - }); - - const feed = new Feed({ - id: author.link, - title: `${author.name} (@${user.username}@${config.host})`, - updated: notes[0].createdAt, - generator: 'Misskey', - description: `${user.notesCount} Notes, ${profile.ffVisibility === 'public' ? user.followingCount : '?'} Following, ${profile.ffVisibility === 'public' ? user.followersCount : '?'} Followers${profile.description ? ` · ${profile.description}` : ''}`, - link: author.link, - image: await Users.getAvatarUrl(user), - feedLinks: { - json: `${author.link}.json`, - atom: `${author.link}.atom`, - }, - author, - copyright: user.name || user.username, - }); - - for (const note of notes) { - const files = note.fileIds.length > 0 ? await DriveFiles.findBy({ - id: In(note.fileIds), - }) : []; - const file = files.find(file => file.type.startsWith('image/')); - - feed.addItem({ - title: `New note by ${author.name}`, - link: `${config.url}/notes/${note.id}`, - date: note.createdAt, - description: note.cw || undefined, - content: note.text || undefined, - image: file ? DriveFiles.getPublicUrl(file) || undefined : undefined, - }); - } - - return feed; -} diff --git a/packages/backend/src/server/web/manifest.ts b/packages/backend/src/server/web/manifest.ts deleted file mode 100644 index ee568b8077fd..000000000000 --- a/packages/backend/src/server/web/manifest.ts +++ /dev/null @@ -1,18 +0,0 @@ -import Koa from 'koa'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import manifest from './manifest.json' assert { type: 'json' }; - -export const manifestHandler = async (ctx: Koa.Context) => { - // TODO - //const res = structuredClone(manifest); - const res = JSON.parse(JSON.stringify(manifest)); - - const instance = await fetchMeta(true); - - res.short_name = instance.name || 'Misskey'; - res.name = instance.name || 'Misskey'; - if (instance.themeColor) res.theme_color = instance.themeColor; - - ctx.set('Cache-Control', 'max-age=300'); - ctx.body = res; -}; From 625a541c1dbcd65e2562256e217ddd4664df4242 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 14 Sep 2022 09:37:59 +0900 Subject: [PATCH 085/180] wip --- packages/backend/src/misc/status-error.ts | 13 ++ .../processors/DeliverProcessorService.ts | 20 +- .../queue/processors/InboxProcessorService.ts | 22 +-- .../WebhookDeliverProcessorService.ts | 8 +- .../backend/src/server/FileServerService.ts | 177 ++++++++++++++++++ .../src/server/MediaProxyServerService.ts | 136 ++++++++++++++ packages/backend/src/server/ServerService.ts | 14 +- .../src/server/{file => }/assets/bad-egg.png | Bin .../{file => }/assets/cache-expired.png | Bin .../src/server/{file => }/assets/dummy.png | Bin .../server/{file => }/assets/not-an-image.png | Bin .../assets/thumbnail-not-available.png | Bin .../server/{file => }/assets/tombstone.png | Bin packages/backend/src/server/file/index.ts | 40 ---- .../src/server/file/send-drive-file.ts | 126 ------------- packages/backend/src/server/proxy/index.ts | 26 --- .../backend/src/server/proxy/proxy-media.ts | 98 ---------- .../backend/src/services/DownloadService.ts | 6 +- .../src/services/HttpRequestService.ts | 17 +- .../remote/activitypub/ApInboxService.ts | 34 ++-- .../activitypub/models/ApNoteService.ts | 29 +-- .../activitypub/models/ApPersonService.ts | 33 ++-- 22 files changed, 415 insertions(+), 384 deletions(-) create mode 100644 packages/backend/src/misc/status-error.ts create mode 100644 packages/backend/src/server/FileServerService.ts create mode 100644 packages/backend/src/server/MediaProxyServerService.ts rename packages/backend/src/server/{file => }/assets/bad-egg.png (100%) rename packages/backend/src/server/{file => }/assets/cache-expired.png (100%) rename packages/backend/src/server/{file => }/assets/dummy.png (100%) rename packages/backend/src/server/{file => }/assets/not-an-image.png (100%) rename packages/backend/src/server/{file => }/assets/thumbnail-not-available.png (100%) rename packages/backend/src/server/{file => }/assets/tombstone.png (100%) delete mode 100644 packages/backend/src/server/file/index.ts delete mode 100644 packages/backend/src/server/file/send-drive-file.ts delete mode 100644 packages/backend/src/server/proxy/index.ts delete mode 100644 packages/backend/src/server/proxy/proxy-media.ts diff --git a/packages/backend/src/misc/status-error.ts b/packages/backend/src/misc/status-error.ts new file mode 100644 index 000000000000..0a33f8acaf9a --- /dev/null +++ b/packages/backend/src/misc/status-error.ts @@ -0,0 +1,13 @@ +export class StatusError extends Error { + public statusCode: number; + public statusMessage?: string; + public isClientError: boolean; + + constructor(message: string, statusCode: number, statusMessage?: string) { + super(message); + this.name = 'StatusError'; + this.statusCode = statusCode; + this.statusMessage = statusMessage; + this.isClientError = typeof this.statusCode === 'number' && this.statusCode >= 400 && this.statusCode < 500; + } +} diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts index 12eff50877fd..99f24fb02ad9 100644 --- a/packages/backend/src/queue/processors/DeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts @@ -2,22 +2,22 @@ import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { DriveFiles , Instances } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import { Config } from '@/config/types.js'; import type Logger from '@/logger.js'; -import type { MetaService } from '@/services/MetaService.js'; +import { MetaService } from '@/services/MetaService.js'; import { toPuny } from '@/misc/convert-host.js'; -import { StatusError } from '@/services/HttpRequestService.js'; -import type { ApRequestService } from '@/services/remote/activitypub/ApRequestService.js'; -import type { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; -import type { FetchInstanceMetadataService } from '@/services/FetchInstanceMetadataService.js'; +import { ApRequestService } from '@/services/remote/activitypub/ApRequestService.js'; +import { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; +import { FetchInstanceMetadataService } from '@/services/FetchInstanceMetadataService.js'; import { Cache } from '@/misc/cache.js'; import type { Instance } from '@/models/entities/instance.js'; -import type InstanceChart from '@/services/chart/charts/instance.js'; -import type ApRequestChart from '@/services/chart/charts/ap-request.js'; -import type FederationChart from '@/services/chart/charts/federation.js'; +import InstanceChart from '@/services/chart/charts/instance.js'; +import ApRequestChart from '@/services/chart/charts/ap-request.js'; +import FederationChart from '@/services/chart/charts/federation.js'; +import { StatusError } from '@/misc/status-error.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DeliverJobData } from '../types.js'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; @Injectable() export class DeliverProcessorService { diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index 54728538e1e1..58fdf0a23ab4 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -5,27 +5,27 @@ import httpSignature from '@peertube/http-signature'; import { DI_SYMBOLS } from '@/di-symbols.js'; import { Instances } from '@/models/index.js'; import type { DriveFiles } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import { Config } from '@/config/types.js'; import type Logger from '@/logger.js'; -import type { MetaService } from '@/services/MetaService.js'; +import { MetaService } from '@/services/MetaService.js'; import { extractDbHost, toPuny } from '@/misc/convert-host.js'; -import { StatusError } from '@/services/HttpRequestService.js'; -import type { ApRequestService } from '@/services/remote/activitypub/ApRequestService.js'; -import type { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; -import type { FetchInstanceMetadataService } from '@/services/FetchInstanceMetadataService.js'; +import { ApRequestService } from '@/services/remote/activitypub/ApRequestService.js'; +import { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; +import { FetchInstanceMetadataService } from '@/services/FetchInstanceMetadataService.js'; import { Cache } from '@/misc/cache.js'; import type { Instance } from '@/models/entities/instance.js'; -import type InstanceChart from '@/services/chart/charts/instance.js'; -import type ApRequestChart from '@/services/chart/charts/ap-request.js'; -import type FederationChart from '@/services/chart/charts/federation.js'; +import InstanceChart from '@/services/chart/charts/instance.js'; +import ApRequestChart from '@/services/chart/charts/ap-request.js'; +import FederationChart from '@/services/chart/charts/federation.js'; import { LdSignature } from '@/services/remote/activitypub/misc/ld-signature.js'; import { getApId } from '@/services/remote/activitypub/type.js'; import type { CacheableRemoteUser } from '@/models/entities/user.js'; import type { UserPublickey } from '@/models/entities/user-publickey.js'; -import type { ApDbResolverService } from '@/services/remote/activitypub/ApDbResolverService.js'; +import { ApDbResolverService } from '@/services/remote/activitypub/ApDbResolverService.js'; +import { StatusError } from '@/misc/status-error.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DeliverJobData, InboxJobData } from '../types.js'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; // ユーザーのinboxにアクティビティが届いた時の処理 @Injectable() diff --git a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts index 57722e9bed19..3ed6ecf12da1 100644 --- a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts @@ -2,13 +2,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Webhooks } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import { Config } from '@/config/types.js'; import type Logger from '@/logger.js'; -import type { HttpRequestService } from '@/services/HttpRequestService.js'; -import { StatusError } from '@/services/HttpRequestService.js'; +import { HttpRequestService } from '@/services/HttpRequestService.js'; +import { StatusError } from '@/misc/status-error.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { WebhookDeliverJobData } from '../types.js'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; @Injectable() export class WebhookDeliverProcessorService { diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts new file mode 100644 index 000000000000..b4e18942d0c3 --- /dev/null +++ b/packages/backend/src/server/FileServerService.ts @@ -0,0 +1,177 @@ +import * as fs from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { dirname } from 'node:path'; +import { Inject, Injectable } from '@nestjs/common'; +import Koa from 'koa'; +import cors from '@koa/cors'; +import Router from '@koa/router'; +import send from 'koa-send'; +import rename from 'rename'; +import { Config } from '@/config/types.js'; +import type { DriveFiles } from '@/models/index.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { createTemp } from '@/misc/create-temp.js'; +import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; +import { StatusError } from '@/misc/status-error.js'; +import Logger from '@/logger.js'; +import { detectType } from '@/misc/get-file-info.js'; +import { DownloadService } from '@/services/DownloadService.js'; +import { ImageProcessingService } from '@/services/ImageProcessingService.js'; +import { VideoProcessingService } from '@/services/VideoProcessingService.js'; +import { InternalStorageService } from '@/services/InternalStorageService.js'; +import { contentDisposition } from '@/misc/content-disposition.js'; + +const serverLogger = new Logger('server', 'gray', false); + +const _filename = fileURLToPath(import.meta.url); +const _dirname = dirname(_filename); + +const assets = `${_dirname}/../../server/file/assets/`; + +const commonReadableHandlerGenerator = (ctx: Koa.Context) => (e: Error): void => { + serverLogger.error(e); + ctx.status = 500; + ctx.set('Cache-Control', 'max-age=300'); +}; + +@Injectable() +export class FileServerService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private downloadService: DownloadService, + private imageProcessingService: ImageProcessingService, + private videoProcessingService: VideoProcessingService, + private internalStorageService: InternalStorageService, + ) { + } + + public createServer() { + const app = new Koa(); + app.use(cors()); + app.use(async (ctx, next) => { + ctx.set('Content-Security-Policy', 'default-src \'none\'; img-src \'self\'; media-src \'self\'; style-src \'unsafe-inline\''); + await next(); + }); + + // Init router + const router = new Router(); + + router.get('/app-default.jpg', ctx => { + const file = fs.createReadStream(`${_dirname}/assets/dummy.png`); + ctx.body = file; + ctx.set('Content-Type', 'image/jpeg'); + ctx.set('Cache-Control', 'max-age=31536000, immutable'); + }); + + router.get('/:key', ctx => this.#sendDriveFile(ctx)); + router.get('/:key/(.*)', ctx => this.#sendDriveFile(ctx)); + + // Register router + app.use(router.routes()); + + return app; + } + + async #sendDriveFile(ctx: Koa.Context) { + const key = ctx.params.key; + + // Fetch drive file + const file = await this.driveFilesRepository.createQueryBuilder('file') + .where('file.accessKey = :accessKey', { accessKey: key }) + .orWhere('file.thumbnailAccessKey = :thumbnailAccessKey', { thumbnailAccessKey: key }) + .orWhere('file.webpublicAccessKey = :webpublicAccessKey', { webpublicAccessKey: key }) + .getOne(); + + if (file == null) { + ctx.status = 404; + ctx.set('Cache-Control', 'max-age=86400'); + await send(ctx as any, '/dummy.png', { root: assets }); + return; + } + + const isThumbnail = file.thumbnailAccessKey === key; + const isWebpublic = file.webpublicAccessKey === key; + + if (!file.storedInternal) { + if (file.isLink && file.uri) { // 期限切れリモートファイル + const [path, cleanup] = await createTemp(); + + try { + await this.downloadService.downloadUrl(file.uri, path); + + const { mime, ext } = await detectType(path); + + const convertFile = async () => { + if (isThumbnail) { + if (['image/jpeg', 'image/webp', 'image/png', 'image/svg+xml'].includes(mime)) { + return await this.imageProcessingService.convertToWebp(path, 498, 280); + } else if (mime.startsWith('video/')) { + return await this.videoProcessingService.generateVideoThumbnail(path); + } + } + + if (isWebpublic) { + if (['image/svg+xml'].includes(mime)) { + return await this.imageProcessingService.convertToPng(path, 2048, 2048); + } + } + + return { + data: fs.readFileSync(path), + ext, + type: mime, + }; + }; + + const image = await convertFile(); + ctx.body = image.data; + ctx.set('Content-Type', FILE_TYPE_BROWSERSAFE.includes(image.type) ? image.type : 'application/octet-stream'); + ctx.set('Cache-Control', 'max-age=31536000, immutable'); + } catch (err) { + serverLogger.error(`${err}`); + + if (err instanceof StatusError && err.isClientError) { + ctx.status = err.statusCode; + ctx.set('Cache-Control', 'max-age=86400'); + } else { + ctx.status = 500; + ctx.set('Cache-Control', 'max-age=300'); + } + } finally { + cleanup(); + } + return; + } + + ctx.status = 204; + ctx.set('Cache-Control', 'max-age=86400'); + return; + } + + if (isThumbnail || isWebpublic) { + const { mime, ext } = await detectType(this.internalStorageService.resolvePath(key)); + const filename = rename(file.name, { + suffix: isThumbnail ? '-thumb' : '-web', + extname: ext ? `.${ext}` : undefined, + }).toString(); + + ctx.body = this.internalStorageService.read(key); + ctx.set('Content-Type', FILE_TYPE_BROWSERSAFE.includes(mime) ? mime : 'application/octet-stream'); + ctx.set('Cache-Control', 'max-age=31536000, immutable'); + ctx.set('Content-Disposition', contentDisposition('inline', filename)); + } else { + const readable = this.internalStorageService.read(file.accessKey!); + readable.on('error', commonReadableHandlerGenerator(ctx)); + ctx.body = readable; + ctx.set('Content-Type', FILE_TYPE_BROWSERSAFE.includes(file.type) ? file.type : 'application/octet-stream'); + ctx.set('Cache-Control', 'max-age=31536000, immutable'); + ctx.set('Content-Disposition', contentDisposition('inline', file.name)); + } + } +} + diff --git a/packages/backend/src/server/MediaProxyServerService.ts b/packages/backend/src/server/MediaProxyServerService.ts new file mode 100644 index 000000000000..d9ff85f801aa --- /dev/null +++ b/packages/backend/src/server/MediaProxyServerService.ts @@ -0,0 +1,136 @@ +import * as fs from 'node:fs'; +import { Inject, Injectable } from '@nestjs/common'; +import Koa from 'koa'; +import cors from '@koa/cors'; +import Router from '@koa/router'; +import sharp from 'sharp'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { Config } from '@/config/types.js'; +import { isMimeImage } from '@/misc/is-mime-image.js'; +import { detectType } from '@/misc/get-file-info.js'; +import { createTemp } from '@/misc/create-temp.js'; +import { DownloadService } from '@/services/DownloadService.js'; +import { ImageProcessingService } from '@/services/ImageProcessingService.js'; +import type { IImage } from '@/services/ImageProcessingService.js'; +import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; +import { StatusError } from '@/misc/status-error.js'; +import Logger from '@/logger.js'; + +const serverLogger = new Logger('server', 'gray', false); + +@Injectable() +export class MediaProxyServerService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + private downloadService: DownloadService, + private imageProcessingService: ImageProcessingService, + ) { + } + + public createServer() { + const app = new Koa(); + app.use(cors()); + app.use(async (ctx, next) => { + ctx.set('Content-Security-Policy', 'default-src \'none\'; img-src \'self\'; media-src \'self\'; style-src \'unsafe-inline\''); + await next(); + }); + + // Init router + const router = new Router(); + + router.get('/:url*', ctx => this.#handler(ctx)); + + // Register router + app.use(router.routes()); + + return app; + } + + async #handler(ctx: Koa.Context) { + const url = 'url' in ctx.query ? ctx.query.url : 'https://' + ctx.params.url; + + if (typeof url !== 'string') { + ctx.status = 400; + return; + } + + // Create temp file + const [path, cleanup] = await createTemp(); + + try { + await this.downloadService.downloadUrl(url, path); + + const { mime, ext } = await detectType(path); + const isConvertibleImage = isMimeImage(mime, 'sharp-convertible-image'); + + let image: IImage; + + if ('static' in ctx.query && isConvertibleImage) { + image = await this.imageProcessingService.convertToWebp(path, 498, 280); + } else if ('preview' in ctx.query && isConvertibleImage) { + image = await this.imageProcessingService.convertToWebp(path, 200, 200); + } else if ('badge' in ctx.query) { + if (!isConvertibleImage) { + // 画像でないなら404でお茶を濁す + throw new StatusError('Unexpected mime', 404); + } + + const mask = sharp(path) + .resize(96, 96, { + fit: 'inside', + withoutEnlargement: false, + }) + .greyscale() + .normalise() + .linear(1.75, -(128 * 1.75) + 128) // 1.75x contrast + .flatten({ background: '#000' }) + .toColorspace('b-w'); + + const stats = await mask.clone().stats(); + + if (stats.entropy < 0.1) { + // エントロピーがあまりない場合は404にする + throw new StatusError('Skip to provide badge', 404); + } + + const data = sharp({ + create: { width: 96, height: 96, channels: 4, background: { r: 0, g: 0, b: 0, alpha: 0 } }, + }) + .pipelineColorspace('b-w') + .boolean(await mask.png().toBuffer(), 'eor'); + + image = { + data: await data.png().toBuffer(), + ext: 'png', + type: 'image/png', + }; + } else if (mime === 'image/svg+xml') { + image = await this.imageProcessingService.convertToWebp(path, 2048, 2048, 1); + } else if (!mime.startsWith('image/') || !FILE_TYPE_BROWSERSAFE.includes(mime)) { + throw new StatusError('Rejected type', 403, 'Rejected type'); + } else { + image = { + data: fs.readFileSync(path), + ext, + type: mime, + }; + } + + ctx.set('Content-Type', image.type); + ctx.set('Cache-Control', 'max-age=31536000, immutable'); + ctx.body = image.data; + } catch (err) { + serverLogger.error(`${err}`); + + if (err instanceof StatusError && (err.statusCode === 302 || err.isClientError)) { + ctx.status = err.statusCode; + } else { + ctx.status = 500; + } + } finally { + cleanup(); + } + } +} diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 589c74d64be6..b2fc83c9bb88 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -22,8 +22,11 @@ import { NodeinfoServerService } from './NodeinfoServerService.js'; import { ApiServerService } from './api/ApiServerService.js'; import { StreamingApiServerService } from './api/StreamingApiServerService.js'; import { WellKnownServerService } from './WellKnownServerService.js'; +import { MediaProxyServerService } from './MediaProxyServerService.js'; +import { FileServerService } from './FileServerService.js'; +import { ClientServerService } from './web/ClientServerService.js'; -export const serverLogger = new Logger('server', 'gray', false); +const serverLogger = new Logger('server', 'gray', false); @Injectable() export class ServerService { @@ -42,6 +45,9 @@ export class ServerService { private activityPubServerService: ActivityPubServerService, private wellKnownServerService: WellKnownServerService, private nodeinfoServerService: NodeinfoServerService, + private fileServerService: FileServerService, + private mediaProxyServerService: MediaProxyServerService, + private clientServerService: ClientServerService, private globalEventService: GlobalEventService, ) { } @@ -75,8 +81,8 @@ export class ServerService { } koa.use(mount('/api', this.apiServerService.createApiServer(app))); - koa.use(mount('/files', fileServer)); - koa.use(mount('/proxy', proxyServer)); + koa.use(mount('/files', this.fileServerService.createServer())); + koa.use(mount('/proxy', this.mediaProxyServerService.createServer())); // Init router const router = new Router(); @@ -137,7 +143,7 @@ export class ServerService { // Register router koa.use(router.routes()); - koa.use(mount(webServer)); + koa.use(mount(this.clientServerService.createApp())); const server = http.createServer(koa.callback()); diff --git a/packages/backend/src/server/file/assets/bad-egg.png b/packages/backend/src/server/assets/bad-egg.png similarity index 100% rename from packages/backend/src/server/file/assets/bad-egg.png rename to packages/backend/src/server/assets/bad-egg.png diff --git a/packages/backend/src/server/file/assets/cache-expired.png b/packages/backend/src/server/assets/cache-expired.png similarity index 100% rename from packages/backend/src/server/file/assets/cache-expired.png rename to packages/backend/src/server/assets/cache-expired.png diff --git a/packages/backend/src/server/file/assets/dummy.png b/packages/backend/src/server/assets/dummy.png similarity index 100% rename from packages/backend/src/server/file/assets/dummy.png rename to packages/backend/src/server/assets/dummy.png diff --git a/packages/backend/src/server/file/assets/not-an-image.png b/packages/backend/src/server/assets/not-an-image.png similarity index 100% rename from packages/backend/src/server/file/assets/not-an-image.png rename to packages/backend/src/server/assets/not-an-image.png diff --git a/packages/backend/src/server/file/assets/thumbnail-not-available.png b/packages/backend/src/server/assets/thumbnail-not-available.png similarity index 100% rename from packages/backend/src/server/file/assets/thumbnail-not-available.png rename to packages/backend/src/server/assets/thumbnail-not-available.png diff --git a/packages/backend/src/server/file/assets/tombstone.png b/packages/backend/src/server/assets/tombstone.png similarity index 100% rename from packages/backend/src/server/file/assets/tombstone.png rename to packages/backend/src/server/assets/tombstone.png diff --git a/packages/backend/src/server/file/index.ts b/packages/backend/src/server/file/index.ts deleted file mode 100644 index 07a493700a31..000000000000 --- a/packages/backend/src/server/file/index.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * File Server - */ - -import * as fs from 'node:fs'; -import { fileURLToPath } from 'node:url'; -import { dirname } from 'node:path'; -import Koa from 'koa'; -import cors from '@koa/cors'; -import Router from '@koa/router'; -import sendDriveFile from './send-drive-file.js'; - -const _filename = fileURLToPath(import.meta.url); -const _dirname = dirname(_filename); - -// Init app -const app = new Koa(); -app.use(cors()); -app.use(async (ctx, next) => { - ctx.set('Content-Security-Policy', `default-src 'none'; img-src 'self'; media-src 'self'; style-src 'unsafe-inline'`); - await next(); -}); - -// Init router -const router = new Router(); - -router.get('/app-default.jpg', ctx => { - const file = fs.createReadStream(`${_dirname}/assets/dummy.png`); - ctx.body = file; - ctx.set('Content-Type', 'image/jpeg'); - ctx.set('Cache-Control', 'max-age=31536000, immutable'); -}); - -router.get('/:key', sendDriveFile); -router.get('/:key/(.*)', sendDriveFile); - -// Register router -app.use(router.routes()); - -export default app; diff --git a/packages/backend/src/server/file/send-drive-file.ts b/packages/backend/src/server/file/send-drive-file.ts deleted file mode 100644 index c34e04314562..000000000000 --- a/packages/backend/src/server/file/send-drive-file.ts +++ /dev/null @@ -1,126 +0,0 @@ -import * as fs from 'node:fs'; -import { fileURLToPath } from 'node:url'; -import { dirname } from 'node:path'; -import Koa from 'koa'; -import send from 'koa-send'; -import rename from 'rename'; -import { serverLogger } from '../index.js'; -import { contentDisposition } from '@/misc/content-disposition.js'; -import { DriveFiles } from '@/models/index.js'; -import { InternalStorage } from '@/services/drive/internal-storage.js'; -import { createTemp } from '@/misc/create-temp.js'; -import { downloadUrl } from '@/misc/download-url.js'; -import { detectType } from '@/misc/get-file-info.js'; -import { convertToWebp, convertToJpeg, convertToPng } from '@/services/drive/image-processor.js'; -import { GenerateVideoThumbnail } from '@/services/drive/generate-video-thumbnail.js'; -import { StatusError } from '@/misc/fetch.js'; -import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; - -const _filename = fileURLToPath(import.meta.url); -const _dirname = dirname(_filename); - -const assets = `${_dirname}/../../server/file/assets/`; - -const commonReadableHandlerGenerator = (ctx: Koa.Context) => (e: Error): void => { - serverLogger.error(e); - ctx.status = 500; - ctx.set('Cache-Control', 'max-age=300'); -}; - -// eslint-disable-next-line import/no-default-export -export default async function(ctx: Koa.Context) { - const key = ctx.params.key; - - // Fetch drive file - const file = await DriveFiles.createQueryBuilder('file') - .where('file.accessKey = :accessKey', { accessKey: key }) - .orWhere('file.thumbnailAccessKey = :thumbnailAccessKey', { thumbnailAccessKey: key }) - .orWhere('file.webpublicAccessKey = :webpublicAccessKey', { webpublicAccessKey: key }) - .getOne(); - - if (file == null) { - ctx.status = 404; - ctx.set('Cache-Control', 'max-age=86400'); - await send(ctx as any, '/dummy.png', { root: assets }); - return; - } - - const isThumbnail = file.thumbnailAccessKey === key; - const isWebpublic = file.webpublicAccessKey === key; - - if (!file.storedInternal) { - if (file.isLink && file.uri) { // 期限切れリモートファイル - const [path, cleanup] = await createTemp(); - - try { - await downloadUrl(file.uri, path); - - const { mime, ext } = await detectType(path); - - const convertFile = async () => { - if (isThumbnail) { - if (['image/jpeg', 'image/webp', 'image/png', 'image/svg+xml'].includes(mime)) { - return await convertToWebp(path, 498, 280); - } else if (mime.startsWith('video/')) { - return await GenerateVideoThumbnail(path); - } - } - - if (isWebpublic) { - if (['image/svg+xml'].includes(mime)) { - return await convertToPng(path, 2048, 2048); - } - } - - return { - data: fs.readFileSync(path), - ext, - type: mime, - }; - }; - - const image = await convertFile(); - ctx.body = image.data; - ctx.set('Content-Type', FILE_TYPE_BROWSERSAFE.includes(image.type) ? image.type : 'application/octet-stream'); - ctx.set('Cache-Control', 'max-age=31536000, immutable'); - } catch (e) { - serverLogger.error(`${e}`); - - if (e instanceof StatusError && e.isClientError) { - ctx.status = e.statusCode; - ctx.set('Cache-Control', 'max-age=86400'); - } else { - ctx.status = 500; - ctx.set('Cache-Control', 'max-age=300'); - } - } finally { - cleanup(); - } - return; - } - - ctx.status = 204; - ctx.set('Cache-Control', 'max-age=86400'); - return; - } - - if (isThumbnail || isWebpublic) { - const { mime, ext } = await detectType(InternalStorage.resolvePath(key)); - const filename = rename(file.name, { - suffix: isThumbnail ? '-thumb' : '-web', - extname: ext ? `.${ext}` : undefined, - }).toString(); - - ctx.body = InternalStorage.read(key); - ctx.set('Content-Type', FILE_TYPE_BROWSERSAFE.includes(mime) ? mime : 'application/octet-stream'); - ctx.set('Cache-Control', 'max-age=31536000, immutable'); - ctx.set('Content-Disposition', contentDisposition('inline', filename)); - } else { - const readable = InternalStorage.read(file.accessKey!); - readable.on('error', commonReadableHandlerGenerator(ctx)); - ctx.body = readable; - ctx.set('Content-Type', FILE_TYPE_BROWSERSAFE.includes(file.type) ? file.type : 'application/octet-stream'); - ctx.set('Cache-Control', 'max-age=31536000, immutable'); - ctx.set('Content-Disposition', contentDisposition('inline', file.name)); - } -} diff --git a/packages/backend/src/server/proxy/index.ts b/packages/backend/src/server/proxy/index.ts deleted file mode 100644 index 506ba10ef11b..000000000000 --- a/packages/backend/src/server/proxy/index.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Media Proxy - */ - -import Koa from 'koa'; -import cors from '@koa/cors'; -import Router from '@koa/router'; -import { proxyMedia } from './proxy-media.js'; - -// Init app -const app = new Koa(); -app.use(cors()); -app.use(async (ctx, next) => { - ctx.set('Content-Security-Policy', `default-src 'none'; img-src 'self'; media-src 'self'; style-src 'unsafe-inline'`); - await next(); -}); - -// Init router -const router = new Router(); - -router.get('/:url*', proxyMedia); - -// Register router -app.use(router.routes()); - -export default app; diff --git a/packages/backend/src/server/proxy/proxy-media.ts b/packages/backend/src/server/proxy/proxy-media.ts deleted file mode 100644 index ca036e8fdfb0..000000000000 --- a/packages/backend/src/server/proxy/proxy-media.ts +++ /dev/null @@ -1,98 +0,0 @@ -import * as fs from 'node:fs'; -import Koa from 'koa'; -import sharp from 'sharp'; -import { IImage, convertToWebp } from '@/services/drive/image-processor.js'; -import { createTemp } from '@/misc/create-temp.js'; -import { downloadUrl } from '@/misc/download-url.js'; -import { detectType } from '@/misc/get-file-info.js'; -import { StatusError } from '@/misc/fetch.js'; -import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; -import { serverLogger } from '../index.js'; -import { isMimeImage } from '@/misc/is-mime-image.js'; - -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export async function proxyMedia(ctx: Koa.Context) { - const url = 'url' in ctx.query ? ctx.query.url : 'https://' + ctx.params.url; - - if (typeof url !== 'string') { - ctx.status = 400; - return; - } - - // Create temp file - const [path, cleanup] = await createTemp(); - - try { - await downloadUrl(url, path); - - const { mime, ext } = await detectType(path); - const isConvertibleImage = isMimeImage(mime, 'sharp-convertible-image'); - - let image: IImage; - - if ('static' in ctx.query && isConvertibleImage) { - image = await convertToWebp(path, 498, 280); - } else if ('preview' in ctx.query && isConvertibleImage) { - image = await convertToWebp(path, 200, 200); - } else if ('badge' in ctx.query) { - if (!isConvertibleImage) { - // 画像でないなら404でお茶を濁す - throw new StatusError('Unexpected mime', 404); - } - - const mask = sharp(path) - .resize(96, 96, { - fit: 'inside', - withoutEnlargement: false, - }) - .greyscale() - .normalise() - .linear(1.75, -(128 * 1.75) + 128) // 1.75x contrast - .flatten({ background: '#000' }) - .toColorspace('b-w'); - - const stats = await mask.clone().stats(); - - if (stats.entropy < 0.1) { - // エントロピーがあまりない場合は404にする - throw new StatusError('Skip to provide badge', 404); - } - - const data = sharp({ - create: { width: 96, height: 96, channels: 4, background: { r: 0, g: 0, b: 0, alpha: 0 } }, - }) - .pipelineColorspace('b-w') - .boolean(await mask.png().toBuffer(), 'eor'); - - image = { - data: await data.png().toBuffer(), - ext: 'png', - type: 'image/png', - }; - } else if (mime === 'image/svg+xml') { - image = await convertToWebp(path, 2048, 2048, 1); - } else if (!mime.startsWith('image/') || !FILE_TYPE_BROWSERSAFE.includes(mime)) { - throw new StatusError('Rejected type', 403, 'Rejected type'); - } else { - image = { - data: fs.readFileSync(path), - ext, - type: mime, - }; - } - - ctx.set('Content-Type', image.type); - ctx.set('Cache-Control', 'max-age=31536000, immutable'); - ctx.body = image.data; - } catch (e) { - serverLogger.error(`${e}`); - - if (e instanceof StatusError && (e.statusCode === 302 || e.isClientError)) { - ctx.status = e.statusCode; - } else { - ctx.status = 500; - } - } finally { - cleanup(); - } -} diff --git a/packages/backend/src/services/DownloadService.ts b/packages/backend/src/services/DownloadService.ts index 9d72e3dcbd7e..da7e38056fba 100644 --- a/packages/backend/src/services/DownloadService.ts +++ b/packages/backend/src/services/DownloadService.ts @@ -7,11 +7,11 @@ import PrivateIp from 'private-ip'; import got, * as Got from 'got'; import chalk from 'chalk'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; +import { Config } from '@/config/types.js'; import Logger from '@/logger.js'; -import { StatusError } from '@/services/HttpRequestService.js'; -import type { HttpRequestService } from '@/services/HttpRequestService.js'; +import { HttpRequestService } from '@/services/HttpRequestService.js'; import { createTemp } from '@/misc/create-temp'; +import { StatusError } from '@/misc/status-error.js'; const pipeline = util.promisify(stream.pipeline); diff --git a/packages/backend/src/services/HttpRequestService.ts b/packages/backend/src/services/HttpRequestService.ts index e26becc95641..616194c79680 100644 --- a/packages/backend/src/services/HttpRequestService.ts +++ b/packages/backend/src/services/HttpRequestService.ts @@ -5,7 +5,8 @@ import fetch from 'node-fetch'; import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent'; import { Inject, Injectable } from '@nestjs/common'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types'; +import { Config } from '@/config/types'; +import { StatusError } from '@/misc/status-error.js'; import type { Response } from 'node-fetch'; import type { URL } from 'node:url'; @@ -151,17 +152,3 @@ export class HttpRequestService { return res; } } - -export class StatusError extends Error { - public statusCode: number; - public statusMessage?: string; - public isClientError: boolean; - - constructor(message: string, statusCode: number, statusMessage?: string) { - super(message); - this.name = 'StatusError'; - this.statusCode = statusCode; - this.statusMessage = statusMessage; - this.isClientError = typeof this.statusCode === 'number' && this.statusCode >= 400 && this.statusCode < 500; - } -} diff --git a/packages/backend/src/services/remote/activitypub/ApInboxService.ts b/packages/backend/src/services/remote/activitypub/ApInboxService.ts index cdda427df1bc..4f2c130745ad 100644 --- a/packages/backend/src/services/remote/activitypub/ApInboxService.ts +++ b/packages/backend/src/services/remote/activitypub/ApInboxService.ts @@ -2,30 +2,30 @@ import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Followings, Notes , Users } from '@/models/index.js'; - -import type { Config } from '@/config/types.js'; +import { Config } from '@/config/types.js'; import type { CacheableRemoteUser } from '@/models/entities/user.js'; -import type { UserFollowingService } from '@/services/UserFollowingService.js'; -import type { ReactionService } from '@/services/ReactionService.js'; -import type { RelayService } from '@/services/RelayService.js'; -import type { NotePiningService } from '@/services/NotePiningService.js'; -import type { UserBlockingService } from '@/services/UserBlockingService.js'; -import { StatusError } from '@/services/HttpRequestService.js'; -import type { NoteDeleteService } from '@/services/NoteDeleteService.js'; -import type { NoteCreateService } from '@/services/NoteCreateService.js'; +import { UserFollowingService } from '@/services/UserFollowingService.js'; +import { ReactionService } from '@/services/ReactionService.js'; +import { RelayService } from '@/services/RelayService.js'; +import { NotePiningService } from '@/services/NotePiningService.js'; +import { UserBlockingService } from '@/services/UserBlockingService.js'; +import { NoteDeleteService } from '@/services/NoteDeleteService.js'; +import { NoteCreateService } from '@/services/NoteCreateService.js'; import { concat, toArray, toSingle, unique } from '@/prelude/array.js'; -import type { AppLockService } from '@/services/AppLockService.js'; +import { AppLockService } from '@/services/AppLockService.js'; import { extractDbHost, isSelfHost } from '@/misc/convert-host.js'; import type Logger from '@/logger.js'; -import type { MetaService } from '@/services/MetaService.js'; -import type { IdService } from '@/services/IdService.js'; +import { MetaService } from '@/services/MetaService.js'; +import { IdService } from '@/services/IdService.js'; +import { StatusError } from '@/misc/status-error.js'; import { updatePerson } from './models/person.js'; import { updateQuestion } from './models/question.js'; import { getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isPost, isRead, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js'; -import type { ApNoteService } from './models/ApNoteService.js'; -import type { ApLoggerService } from './ApLoggerService.js'; -import type { ApDbResolverService } from './ApDbResolverService.js'; -import type { ApResolverService, Resolver } from './ApResolverService.js'; +import { ApNoteService } from './models/ApNoteService.js'; +import { ApLoggerService } from './ApLoggerService.js'; +import { ApDbResolverService } from './ApDbResolverService.js'; +import { ApResolverService } from './ApResolverService.js'; +import type { Resolver } from './ApResolverService.js'; import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IRead, IReject, IRemove, IUndo, IUpdate } from './type.js'; @Injectable() diff --git a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts index b83ef232e950..a584ed1f333e 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts @@ -2,32 +2,33 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Polls , Emojis, Users } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import { Config } from '@/config/types.js'; import type { CacheableRemoteUser } from '@/models/entities/user.js'; import { extractDbHost, toPuny } from '@/misc/convert-host'; import type { Note } from '@/models/entities/note.js'; -import { StatusError } from '@/services/HttpRequestService.js'; import { toArray, toSingle, unique } from '@/prelude/array.js'; import type { Emoji } from '@/models/entities/emoji.js'; -import type { MetaService } from '@/services/MetaService.js'; -import type { AppLockService } from '@/services/AppLockService.js'; +import { MetaService } from '@/services/MetaService.js'; +import { AppLockService } from '@/services/AppLockService.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; -import type { NoteCreateService } from '@/services/NoteCreateService.js'; +import { NoteCreateService } from '@/services/NoteCreateService.js'; import type Logger from '@/logger.js'; -import type { IdService } from '@/services/IdService.js'; -import type { PollService } from '@/services/PollService.js'; +import { IdService } from '@/services/IdService.js'; +import { PollService } from '@/services/PollService.js'; +import { StatusError } from '@/misc/status-error.js'; import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js'; // eslint-disable-next-line @typescript-eslint/consistent-type-imports +import { ApLoggerService } from '../ApLoggerService.js'; +import { ApMfmService } from '../ApMfmService.js'; +import { ApDbResolverService } from '../ApDbResolverService.js'; +import { ApResolverService } from '../ApResolverService.js'; import { ApPersonService } from './ApPersonService.js'; import { extractApHashtags } from './tag.js'; -import type { ApMentionService } from './ApMentionService.js'; -import type { ApQuestionService } from './ApQuestionService.js'; -import type { ApLoggerService } from '../ApLoggerService.js'; -import type { ApMfmService } from '../ApMfmService.js'; -import type { ApDbResolverService } from '../ApDbResolverService.js'; -import type { ApResolverService, Resolver } from '../ApResolverService.js'; +import { ApMentionService } from './ApMentionService.js'; +import { ApQuestionService } from './ApQuestionService.js'; +import { ApImageService } from './ApImageService.js'; +import type { Resolver } from '../ApResolverService.js'; import type { IObject, IPost } from '../type.js'; -import type { ApImageService } from './ApImageService.js'; @Injectable() export class ApNoteService { diff --git a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts index 8b7e987131cf..7cbd91bae568 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts @@ -1,41 +1,42 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; +import { DataSource } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Followings , Instances, UserProfiles, UserPublickeys, Users } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import { Config } from '@/config/types.js'; import type { CacheableUser, IRemoteUser } from '@/models/entities/user.js'; import { User } from '@/models/entities/user.js'; import { toPuny } from '@/misc/convert-host.js'; import { truncate } from '@/misc/truncate.js'; -import type { UserCacheService } from '@/services/UserCacheService.js'; -import { StatusError } from '@/services/HttpRequestService.js'; +import { UserCacheService } from '@/services/UserCacheService.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import type Logger from '@/logger.js'; import type { Note } from '@/models/entities/note.js'; -import type { IdService } from '@/services/IdService.js'; -import type { MfmService } from '@/services/MfmService.js'; +import { IdService } from '@/services/IdService.js'; +import { MfmService } from '@/services/MfmService.js'; import type { Emoji } from '@/models/entities/emoji.js'; import { toArray } from '@/prelude/array.js'; -import type { GlobalEventService } from '@/services/GlobalEventService.js'; -import type { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; -import type { FetchInstanceMetadataService } from '@/services/FetchInstanceMetadataService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; +import { FetchInstanceMetadataService } from '@/services/FetchInstanceMetadataService.js'; import { UserProfile } from '@/models/entities/user-profile.js'; import { UserPublickey } from '@/models/entities/user-publickey.js'; -import type UsersChart from '@/services/chart/charts/users.js'; -import type InstanceChart from '@/services/chart/charts/instance.js'; -import type { HashtagService } from '@/services/HashtagService.js'; +import UsersChart from '@/services/chart/charts/users.js'; +import InstanceChart from '@/services/chart/charts/instance.js'; +import { HashtagService } from '@/services/HashtagService.js'; import { UserNotePining } from '@/models/entities/user-note-pining.js'; +import { StatusError } from '@/misc/status-error.js'; import { getApId, getApType, getOneApHrefNullable, isActor, isCollection, isCollectionOrOrderedCollection, isPropertyValue } from '../type.js'; +import { ApMfmService } from '../ApMfmService.js'; +import { ApResolverService } from '../ApResolverService.js'; +import { ApLoggerService } from '../ApLoggerService.js'; import { extractApHashtags } from './tag.js'; // eslint-disable-next-line @typescript-eslint/consistent-type-imports import { ApNoteService } from './ApNoteService.js'; -import type { ApMfmService } from '../ApMfmService.js'; -import type { Resolver , ApResolverService } from '../ApResolverService.js'; -import type { ApImageService } from './ApImageService.js'; -import type { DataSource } from 'typeorm'; +import { ApImageService } from './ApImageService.js'; +import type { Resolver } from '../ApResolverService.js'; import type { IActor, IObject , IApPropertyValue } from '../type.js'; -import type { ApLoggerService } from '../ApLoggerService.js'; const nameLength = 128; const summaryLength = 2048; From 66a568af5d6ba29ca5212bfbaaf7b47a1e09db6a Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 14 Sep 2022 17:50:41 +0900 Subject: [PATCH 086/180] wip --- packages/backend/src/misc/captcha.ts | 57 ---- .../src/server/api/ApiServerService.ts | 13 +- .../src/server/api/SigninApiService.ts | 276 ++++++++++++++++++ .../backend/src/server/api/SigninService.ts | 61 ++++ .../src/server/api/SignupApiService.ts | 172 +++++++++++ .../backend/src/server/api/common/signin.ts | 44 --- .../backend/src/server/api/common/signup.ts | 114 -------- .../backend/src/server/api/private/signin.ts | 250 ---------------- .../src/server/api/private/signup-pending.ts | 35 --- .../backend/src/server/api/private/signup.ts | 112 ------- .../backend/src/services/CaptchaService.ts | 70 +++++ .../backend/src/services/SignupService.ts | 139 +++++++++ 12 files changed, 725 insertions(+), 618 deletions(-) delete mode 100644 packages/backend/src/misc/captcha.ts create mode 100644 packages/backend/src/server/api/SigninApiService.ts create mode 100644 packages/backend/src/server/api/SigninService.ts create mode 100644 packages/backend/src/server/api/SignupApiService.ts delete mode 100644 packages/backend/src/server/api/common/signin.ts delete mode 100644 packages/backend/src/server/api/common/signup.ts delete mode 100644 packages/backend/src/server/api/private/signin.ts delete mode 100644 packages/backend/src/server/api/private/signup-pending.ts delete mode 100644 packages/backend/src/server/api/private/signup.ts create mode 100644 packages/backend/src/services/CaptchaService.ts create mode 100644 packages/backend/src/services/SignupService.ts diff --git a/packages/backend/src/misc/captcha.ts b/packages/backend/src/misc/captcha.ts deleted file mode 100644 index 9a87a4a3c800..000000000000 --- a/packages/backend/src/misc/captcha.ts +++ /dev/null @@ -1,57 +0,0 @@ -import fetch from 'node-fetch'; -import { URLSearchParams } from 'node:url'; -import { getAgentByUrl } from './fetch.js'; -import config from '@/config/index.js'; - -export async function verifyRecaptcha(secret: string, response: string) { - const result = await getCaptchaResponse('https://www.recaptcha.net/recaptcha/api/siteverify', secret, response).catch(e => { - throw `recaptcha-request-failed: ${e}`; - }); - - if (result.success !== true) { - const errorCodes = result['error-codes'] ? result['error-codes']?.join(', ') : ''; - throw `recaptcha-failed: ${errorCodes}`; - } -} - -export async function verifyHcaptcha(secret: string, response: string) { - const result = await getCaptchaResponse('https://hcaptcha.com/siteverify', secret, response).catch(e => { - throw `hcaptcha-request-failed: ${e}`; - }); - - if (result.success !== true) { - const errorCodes = result['error-codes'] ? result['error-codes']?.join(', ') : ''; - throw `hcaptcha-failed: ${errorCodes}`; - } -} - -type CaptchaResponse = { - success: boolean; - 'error-codes'?: string[]; -}; - -async function getCaptchaResponse(url: string, secret: string, response: string): Promise { - const params = new URLSearchParams({ - secret, - response, - }); - - const res = await fetch(url, { - method: 'POST', - body: params, - headers: { - 'User-Agent': config.userAgent, - }, - // TODO - //timeout: 10 * 1000, - agent: getAgentByUrl, - }).catch(e => { - throw `${e.message || e}`; - }); - - if (!res.ok) { - throw `${res.status}`; - } - - return await res.json() as CaptchaResponse; -} diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts index 9dbd12072038..836918d7088f 100644 --- a/packages/backend/src/server/api/ApiServerService.ts +++ b/packages/backend/src/server/api/ApiServerService.ts @@ -9,13 +9,12 @@ import { Config } from '@/config/types.js'; import { Users , Instances, AccessTokens } from '@/models/index.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import endpoints from './endpoints.js'; -import signup from './private/signup.js'; -import signin from './private/signin.js'; -import signupPending from './private/signup-pending.js'; import discord from './service/discord.js'; import github from './service/github.js'; import twitter from './service/twitter.js'; import { ApiCallService } from './ApiCallService.js'; +import { SignupApiService } from './SignupApiService.js'; +import { SigninApiService } from './SigninApiService.js'; @Injectable() export class ApiServerService { @@ -29,6 +28,8 @@ export class ApiServerService { private usersRepository: typeof Users, private apiCallService: ApiCallService, + private signupApiServiceService: SignupApiService, + private signinApiServiceService: SigninApiService, ) { } @@ -97,9 +98,9 @@ export class ApiServerService { } } - router.post('/signup', signup); - router.post('/signin', signin); - router.post('/signup-pending', signupPending); + router.post('/signup', ctx => this.signupApiServiceService.signup(ctx)); + router.post('/signin', ctx => this.signinApiServiceService.signin(ctx)); + router.post('/signup-pending', ctx => this.signupApiServiceService.signupPending(ctx)); router.use(discord.routes()); router.use(github.routes()); diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts new file mode 100644 index 000000000000..bbe942cc2dcb --- /dev/null +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -0,0 +1,276 @@ +import { randomBytes } from 'node:crypto'; +import { Inject, Injectable } from '@nestjs/common'; +import bcrypt from 'bcryptjs'; +import * as speakeasy from 'speakeasy'; +import { IsNull } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { UserSecurityKeys } from '@/models/index.js'; +import { AttestationChallenges, Users } from '@/models/index.js'; +import { Config } from '@/config/types.js'; +import { getIpHash } from '@/misc/get-ip-hash.js'; +import type { ILocalUser } from '@/models/entities/user.js'; +import { IdService } from '@/services/IdService.js'; +import { RateLimiterService } from '../RateLimiterService.js'; +import { SigninService } from '../SigninService.js'; +import { verifyLogin, hash } from '../2fa.js'; +import type Koa from 'koa'; + +@Injectable() +export class SigninApiService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('userSecurityKeysRepository') + private userSecurityKeysRepository: typeof UserSecurityKeys, + + @Inject('attestationChallengesRepository') + private attestationChallengesRepository: typeof AttestationChallenges, + + private idService: IdService, + private rateLimiterService: RateLimiterService, + private signinService: SigninService, + ) { + } + + public async signin(ctx: Koa.Context) { + ctx.set('Access-Control-Allow-Origin', this.config.url); + ctx.set('Access-Control-Allow-Credentials', 'true'); + + const body = ctx.request.body as any; + const username = body['username']; + const password = body['password']; + const token = body['token']; + + function error(status: number, error: { id: string }) { + ctx.status = status; + ctx.body = { error }; + } + + try { + // not more than 1 attempt per second and not more than 10 attempts per hour + await this.rateLimiterService.limit({ key: 'signin', duration: 60 * 60 * 1000, max: 10, minInterval: 1000 }, getIpHash(ctx.ip)); + } catch (err) { + ctx.status = 429; + ctx.body = { + error: { + message: 'Too many failed attempts to sign in. Try again later.', + code: 'TOO_MANY_AUTHENTICATION_FAILURES', + id: '22d05606-fbcf-421a-a2db-b32610dcfd1b', + }, + }; + return; + } + + if (typeof username !== 'string') { + ctx.status = 400; + return; + } + + if (typeof password !== 'string') { + ctx.status = 400; + return; + } + + if (token != null && typeof token !== 'string') { + ctx.status = 400; + return; + } + + // Fetch user + const user = await Users.findOneBy({ + usernameLower: username.toLowerCase(), + host: IsNull(), + }) as ILocalUser; + + if (user == null) { + error(404, { + id: '6cc579cc-885d-43d8-95c2-b8c7fc963280', + }); + return; + } + + if (user.isSuspended) { + error(403, { + id: 'e03a5f46-d309-4865-9b69-56282d94e1eb', + }); + return; + } + + const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + + // Compare password + const same = await bcrypt.compare(password, profile.password!); + + async function fail(status?: number, failure?: { id: string }) { + // Append signin history + await Signins.insert({ + id: this.idService.genId(), + createdAt: new Date(), + userId: user.id, + ip: ctx.ip, + headers: ctx.headers, + success: false, + }); + + error(status || 500, failure || { id: '4e30e80c-e338-45a0-8c8f-44455efa3b76' }); + } + + if (!profile.twoFactorEnabled) { + if (same) { + this.signinService.signin(ctx, user); + return; + } else { + await fail(403, { + id: '932c904e-9460-45b7-9ce6-7ed33be7eb2c', + }); + return; + } + } + + if (token) { + if (!same) { + await fail(403, { + id: '932c904e-9460-45b7-9ce6-7ed33be7eb2c', + }); + return; + } + + const verified = (speakeasy as any).totp.verify({ + secret: profile.twoFactorSecret, + encoding: 'base32', + token: token, + window: 2, + }); + + if (verified) { + this.signinService.signin(ctx, user); + return; + } else { + await fail(403, { + id: 'cdf1235b-ac71-46d4-a3a6-84ccce48df6f', + }); + return; + } + } else if (body.credentialId) { + if (!same && !profile.usePasswordLessLogin) { + await fail(403, { + id: '932c904e-9460-45b7-9ce6-7ed33be7eb2c', + }); + return; + } + + const clientDataJSON = Buffer.from(body.clientDataJSON, 'hex'); + const clientData = JSON.parse(clientDataJSON.toString('utf-8')); + const challenge = await AttestationChallenges.findOneBy({ + userId: user.id, + id: body.challengeId, + registrationChallenge: false, + challenge: hash(clientData.challenge).toString('hex'), + }); + + if (!challenge) { + await fail(403, { + id: '2715a88a-2125-4013-932f-aa6fe72792da', + }); + return; + } + + await this.attestationChallengesRepository.delete({ + userId: user.id, + id: body.challengeId, + }); + + if (new Date().getTime() - challenge.createdAt.getTime() >= 5 * 60 * 1000) { + await fail(403, { + id: '2715a88a-2125-4013-932f-aa6fe72792da', + }); + return; + } + + const securityKey = await this.userSecurityKeysRepository.findOneBy({ + id: Buffer.from( + body.credentialId + .replace(/-/g, '+') + .replace(/_/g, '/'), + 'base64', + ).toString('hex'), + }); + + if (!securityKey) { + await fail(403, { + id: '66269679-aeaf-4474-862b-eb761197e046', + }); + return; + } + + const isValid = verifyLogin({ + publicKey: Buffer.from(securityKey.publicKey, 'hex'), + authenticatorData: Buffer.from(body.authenticatorData, 'hex'), + clientDataJSON, + clientData, + signature: Buffer.from(body.signature, 'hex'), + challenge: challenge.challenge, + }); + + if (isValid) { + this.signinService.signin(ctx, user); + return; + } else { + await fail(403, { + id: '93b86c4b-72f9-40eb-9815-798928603d1e', + }); + return; + } + } else { + if (!same && !profile.usePasswordLessLogin) { + await fail(403, { + id: '932c904e-9460-45b7-9ce6-7ed33be7eb2c', + }); + return; + } + + const keys = await this.userSecurityKeysRepository.findBy({ + userId: user.id, + }); + + if (keys.length === 0) { + await fail(403, { + id: 'f27fd449-9af4-4841-9249-1f989b9fa4a4', + }); + return; + } + + // 32 byte challenge + const challenge = randomBytes(32).toString('base64') + .replace(/=/g, '') + .replace(/\+/g, '-') + .replace(/\//g, '_'); + + const challengeId = this.idService.genId(); + + await this.attestationChallengesRepository.insert({ + userId: user.id, + id: challengeId, + challenge: hash(Buffer.from(challenge, 'utf-8')).toString('hex'), + createdAt: new Date(), + registrationChallenge: false, + }); + + ctx.body = { + challenge, + challengeId, + securityKeys: keys.map(key => ({ + id: key.id, + })), + }; + ctx.status = 200; + return; + } + // never get here + } +} + diff --git a/packages/backend/src/server/api/SigninService.ts b/packages/backend/src/server/api/SigninService.ts new file mode 100644 index 000000000000..9a1e08b5e339 --- /dev/null +++ b/packages/backend/src/server/api/SigninService.ts @@ -0,0 +1,61 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Signins , Users } from '@/models/index.js'; +import { Config } from '@/config/types.js'; +import { IdService } from '@/services/IdService.js'; +import type { ILocalUser } from '@/models/entities/user.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import type Koa from 'koa'; + +@Injectable() +export class SigninService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('signinsRepository') + private signinsRepository: typeof Signins, + + private idService: IdService, + private globalEventService: GlobalEventService, + ) { + } + + public signin(ctx: Koa.Context, user: ILocalUser, redirect = false) { + if (redirect) { + //#region Cookie + ctx.cookies.set('igi', user.token!, { + path: '/', + // SEE: https://github.com/koajs/koa/issues/974 + // When using a SSL proxy it should be configured to add the "X-Forwarded-Proto: https" header + secure: this.config.url.startsWith('https'), + httpOnly: false, + }); + //#endregion + + ctx.redirect(this.config.url); + } else { + ctx.body = { + id: user.id, + i: user.token, + }; + ctx.status = 200; + } + + (async () => { + // Append signin history + const record = await this.signinsRepository.insert({ + id: this.idService.genId(), + createdAt: new Date(), + userId: user.id, + ip: ctx.ip, + headers: ctx.headers, + success: true, + }).then(x => this.signinsRepository.findOneByOrFail(x.identifiers[0])); + + // Publish signin event + this.globalEventService.publishMainStream(user.id, 'signin', await this.signinsRepository.pack(record)); + })(); + } +} + diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts new file mode 100644 index 000000000000..bd8f0f284182 --- /dev/null +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -0,0 +1,172 @@ +import { Inject, Injectable } from '@nestjs/common'; +import rndstr from 'rndstr'; +import bcrypt from 'bcryptjs'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { RegistrationTickets , UserPendings, UserProfiles } from '@/models/index.js'; +import { Users } from '@/models/index.js'; +import { Config } from '@/config/types.js'; +import { MetaService } from '@/services/MetaService.js'; +import { CaptchaService } from '@/services/CaptchaService.js'; +import { IdService } from '@/services/IdService.js'; +import { SignupService } from '@/services/SignupService.js'; +import { SigninService } from '../SigninService.js'; +import type Koa from 'koa'; + +@Injectable() +export class SignupApiService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + @Inject('userPendingsRepository') + private userPendingsRepository: typeof UserPendings, + + @Inject('registrationTicketsRepository') + private registrationTicketsRepository: typeof RegistrationTickets, + + private idService: IdService, + private metaService: MetaService, + private captchaService: CaptchaService, + private signupService: SignupService, + private signinService: SigninService, + ) { + } + + public async signup(ctx: Koa.Context) { + const body = ctx.request.body; + + const instance = await this.metaService.fetch(true); + + // Verify *Captcha + // ただしテスト時はこの機構は障害となるため無効にする + if (process.env.NODE_ENV !== 'test') { + if (instance.enableHcaptcha && instance.hcaptchaSecretKey) { + await this.captchaService.verifyHcaptcha(instance.hcaptchaSecretKey, body['hcaptcha-response']).catch(e => { + ctx.throw(400, e); + }); + } + + if (instance.enableRecaptcha && instance.recaptchaSecretKey) { + await this.captchaService.verifyRecaptcha(instance.recaptchaSecretKey, body['g-recaptcha-response']).catch(e => { + ctx.throw(400, e); + }); + } + } + + const username = body['username']; + const password = body['password']; + const host: string | null = process.env.NODE_ENV === 'test' ? (body['host'] || null) : null; + const invitationCode = body['invitationCode']; + const emailAddress = body['emailAddress']; + + if (instance.emailRequiredForSignup) { + if (emailAddress == null || typeof emailAddress !== 'string') { + ctx.status = 400; + return; + } + + const available = await validateEmailForAccount(emailAddress); + if (!available) { + ctx.status = 400; + return; + } + } + + if (instance.disableRegistration) { + if (invitationCode == null || typeof invitationCode !== 'string') { + ctx.status = 400; + return; + } + + const ticket = await this.registrationTicketsRepository.findOneBy({ + code: invitationCode, + }); + + if (ticket == null) { + ctx.status = 400; + return; + } + + this.registrationTicketsRepository.delete(ticket.id); + } + + if (instance.emailRequiredForSignup) { + const code = rndstr('a-z0-9', 16); + + // Generate hash of password + const salt = await bcrypt.genSalt(8); + const hash = await bcrypt.hash(password, salt); + + await this.userPendingsRepository.insert({ + id: this.idService.genId(), + createdAt: new Date(), + code, + email: emailAddress, + username: username, + password: hash, + }); + + const link = `${this.config.url}/signup-complete/${code}`; + + sendEmail(emailAddress, 'Signup', + `To complete signup, please click this link:
${link}`, + `To complete signup, please click this link: ${link}`); + + ctx.status = 204; + } else { + try { + const { account, secret } = await this.signupService.signup({ + username, password, host, + }); + + const res = await Users.pack(account, account, { + detail: true, + includeSecrets: true, + }); + + (res as any).token = secret; + + ctx.body = res; + } catch (e) { + ctx.throw(400, e); + } + } + } + + public async signupPending(ctx: Koa.Context) { + const body = ctx.request.body; + + const code = body['code']; + + try { + const pendingUser = await this.userPendingsRepository.findOneByOrFail({ code }); + + const { account, secret } = await this.signupService.signup({ + username: pendingUser.username, + passwordHash: pendingUser.password, + }); + + this.userPendingsRepository.delete({ + id: pendingUser.id, + }); + + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: account.id }); + + await this.userProfilesRepository.update({ userId: profile.userId }, { + email: pendingUser.email, + emailVerified: true, + emailVerifyCode: null, + }); + + this.signinService.signin(ctx, account); + } catch (e) { + ctx.throw(400, e); + } + } +} diff --git a/packages/backend/src/server/api/common/signin.ts b/packages/backend/src/server/api/common/signin.ts deleted file mode 100644 index 592630009627..000000000000 --- a/packages/backend/src/server/api/common/signin.ts +++ /dev/null @@ -1,44 +0,0 @@ -import Koa from 'koa'; - -import config from '@/config/index.js'; -import { ILocalUser } from '@/models/entities/user.js'; -import { Signins } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; -import { publishMainStream } from '@/services/stream.js'; - -export default function(ctx: Koa.Context, user: ILocalUser, redirect = false) { - if (redirect) { - //#region Cookie - ctx.cookies.set('igi', user.token!, { - path: '/', - // SEE: https://github.com/koajs/koa/issues/974 - // When using a SSL proxy it should be configured to add the "X-Forwarded-Proto: https" header - secure: config.url.startsWith('https'), - httpOnly: false, - }); - //#endregion - - ctx.redirect(config.url); - } else { - ctx.body = { - id: user.id, - i: user.token, - }; - ctx.status = 200; - } - - (async () => { - // Append signin history - const record = await Signins.insert({ - id: this.idService.genId(), - createdAt: new Date(), - userId: user.id, - ip: ctx.ip, - headers: ctx.headers, - success: true, - }).then(x => Signins.findOneByOrFail(x.identifiers[0])); - - // Publish signin event - publishMainStream(user.id, 'signin', await Signins.pack(record)); - })(); -} diff --git a/packages/backend/src/server/api/common/signup.ts b/packages/backend/src/server/api/common/signup.ts deleted file mode 100644 index 89fa9ad85382..000000000000 --- a/packages/backend/src/server/api/common/signup.ts +++ /dev/null @@ -1,114 +0,0 @@ -import bcrypt from 'bcryptjs'; -import { generateKeyPair } from 'node:crypto'; -import generateUserToken from './generate-native-user-token.js'; -import { User } from '@/models/entities/user.js'; -import { Users, UsedUsernames } from '@/models/index.js'; -import { UserProfile } from '@/models/entities/user-profile.js'; -import { IsNull } from 'typeorm'; -import type { IdService } from '@/services/IdService.js'; -import { toPunyNullable } from '@/misc/convert-host.js'; -import { UserKeypair } from '@/models/entities/user-keypair.js'; -import { usersChart } from '@/services/chart/index.js'; -import { UsedUsername } from '@/models/entities/used-username.js'; -import { db } from '@/db/postgre.js'; - -export async function signup(opts: { - username: User['username']; - password?: string | null; - passwordHash?: UserProfile['password'] | null; - host?: string | null; -}) { - const { username, password, passwordHash, host } = opts; - let hash = passwordHash; - - // Validate username - if (!Users.validateLocalUsername(username)) { - throw new Error('INVALID_USERNAME'); - } - - if (password != null && passwordHash == null) { - // Validate password - if (!Users.validatePassword(password)) { - throw new Error('INVALID_PASSWORD'); - } - - // Generate hash of password - const salt = await bcrypt.genSalt(8); - hash = await bcrypt.hash(password, salt); - } - - // Generate secret - const secret = generateUserToken(); - - // Check username duplication - if (await Users.findOneBy({ usernameLower: username.toLowerCase(), host: IsNull() })) { - throw new Error('DUPLICATED_USERNAME'); - } - - // Check deleted username duplication - if (await UsedUsernames.findOneBy({ username: username.toLowerCase() })) { - throw new Error('USED_USERNAME'); - } - - const keyPair = await new Promise((res, rej) => - generateKeyPair('rsa', { - modulusLength: 4096, - publicKeyEncoding: { - type: 'spki', - format: 'pem', - }, - privateKeyEncoding: { - type: 'pkcs8', - format: 'pem', - cipher: undefined, - passphrase: undefined, - }, - } as any, (err, publicKey, privateKey) => - err ? rej(err) : res([publicKey, privateKey]) - )); - - let account!: User; - - // Start transaction - await db.transaction(async transactionalEntityManager => { - const exist = await transactionalEntityManager.findOneBy(User, { - usernameLower: username.toLowerCase(), - host: IsNull(), - }); - - if (exist) throw new Error(' the username is already used'); - - account = await transactionalEntityManager.save(new User({ - id: this.idService.genId(), - createdAt: new Date(), - username: username, - usernameLower: username.toLowerCase(), - host: toPunyNullable(host), - token: secret, - isAdmin: (await Users.countBy({ - host: IsNull(), - })) === 0, - })); - - await transactionalEntityManager.save(new UserKeypair({ - publicKey: keyPair[0], - privateKey: keyPair[1], - userId: account.id, - })); - - await transactionalEntityManager.save(new UserProfile({ - userId: account.id, - autoAcceptFollowed: true, - password: hash, - })); - - await transactionalEntityManager.save(new UsedUsername({ - createdAt: new Date(), - username: username.toLowerCase(), - })); - }); - - usersChart.update(account, true); - - return { account, secret }; -} diff --git a/packages/backend/src/server/api/private/signin.ts b/packages/backend/src/server/api/private/signin.ts deleted file mode 100644 index 807f51f4c37d..000000000000 --- a/packages/backend/src/server/api/private/signin.ts +++ /dev/null @@ -1,250 +0,0 @@ -import Koa from 'koa'; -import bcrypt from 'bcryptjs'; -import * as speakeasy from 'speakeasy'; -import signin from '../common/signin.js'; -import config from '@/config/index.js'; -import { Users, Signins, UserProfiles, UserSecurityKeys, AttestationChallenges } from '@/models/index.js'; -import { ILocalUser } from '@/models/entities/user.js'; -import type { IdService } from '@/services/IdService.js'; -import { verifyLogin, hash } from '../2fa.js'; -import { randomBytes } from 'node:crypto'; -import { IsNull } from 'typeorm'; -import { limiter } from '../limiter.js'; -import { getIpHash } from '@/misc/get-ip-hash.js'; - -export default async (ctx: Koa.Context) => { - ctx.set('Access-Control-Allow-Origin', config.url); - ctx.set('Access-Control-Allow-Credentials', 'true'); - - const body = ctx.request.body as any; - const username = body['username']; - const password = body['password']; - const token = body['token']; - - function error(status: number, error: { id: string }) { - ctx.status = status; - ctx.body = { error }; - } - - try { - // not more than 1 attempt per second and not more than 10 attempts per hour - await limiter({ key: 'signin', duration: 60 * 60 * 1000, max: 10, minInterval: 1000 }, getIpHash(ctx.ip)); - } catch (err) { - ctx.status = 429; - ctx.body = { - error: { - message: 'Too many failed attempts to sign in. Try again later.', - code: 'TOO_MANY_AUTHENTICATION_FAILURES', - id: '22d05606-fbcf-421a-a2db-b32610dcfd1b', - }, - }; - return; - } - - if (typeof username !== 'string') { - ctx.status = 400; - return; - } - - if (typeof password !== 'string') { - ctx.status = 400; - return; - } - - if (token != null && typeof token !== 'string') { - ctx.status = 400; - return; - } - - // Fetch user - const user = await Users.findOneBy({ - usernameLower: username.toLowerCase(), - host: IsNull(), - }) as ILocalUser; - - if (user == null) { - error(404, { - id: '6cc579cc-885d-43d8-95c2-b8c7fc963280', - }); - return; - } - - if (user.isSuspended) { - error(403, { - id: 'e03a5f46-d309-4865-9b69-56282d94e1eb', - }); - return; - } - - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - // Compare password - const same = await bcrypt.compare(password, profile.password!); - - async function fail(status?: number, failure?: { id: string }) { - // Append signin history - await Signins.insert({ - id: this.idService.genId(), - createdAt: new Date(), - userId: user.id, - ip: ctx.ip, - headers: ctx.headers, - success: false, - }); - - error(status || 500, failure || { id: '4e30e80c-e338-45a0-8c8f-44455efa3b76' }); - } - - if (!profile.twoFactorEnabled) { - if (same) { - signin(ctx, user); - return; - } else { - await fail(403, { - id: '932c904e-9460-45b7-9ce6-7ed33be7eb2c', - }); - return; - } - } - - if (token) { - if (!same) { - await fail(403, { - id: '932c904e-9460-45b7-9ce6-7ed33be7eb2c', - }); - return; - } - - const verified = (speakeasy as any).totp.verify({ - secret: profile.twoFactorSecret, - encoding: 'base32', - token: token, - window: 2, - }); - - if (verified) { - signin(ctx, user); - return; - } else { - await fail(403, { - id: 'cdf1235b-ac71-46d4-a3a6-84ccce48df6f', - }); - return; - } - } else if (body.credentialId) { - if (!same && !profile.usePasswordLessLogin) { - await fail(403, { - id: '932c904e-9460-45b7-9ce6-7ed33be7eb2c', - }); - return; - } - - const clientDataJSON = Buffer.from(body.clientDataJSON, 'hex'); - const clientData = JSON.parse(clientDataJSON.toString('utf-8')); - const challenge = await AttestationChallenges.findOneBy({ - userId: user.id, - id: body.challengeId, - registrationChallenge: false, - challenge: hash(clientData.challenge).toString('hex'), - }); - - if (!challenge) { - await fail(403, { - id: '2715a88a-2125-4013-932f-aa6fe72792da', - }); - return; - } - - await AttestationChallenges.delete({ - userId: user.id, - id: body.challengeId, - }); - - if (new Date().getTime() - challenge.createdAt.getTime() >= 5 * 60 * 1000) { - await fail(403, { - id: '2715a88a-2125-4013-932f-aa6fe72792da', - }); - return; - } - - const securityKey = await UserSecurityKeys.findOneBy({ - id: Buffer.from( - body.credentialId - .replace(/-/g, '+') - .replace(/_/g, '/'), - 'base64' - ).toString('hex'), - }); - - if (!securityKey) { - await fail(403, { - id: '66269679-aeaf-4474-862b-eb761197e046', - }); - return; - } - - const isValid = verifyLogin({ - publicKey: Buffer.from(securityKey.publicKey, 'hex'), - authenticatorData: Buffer.from(body.authenticatorData, 'hex'), - clientDataJSON, - clientData, - signature: Buffer.from(body.signature, 'hex'), - challenge: challenge.challenge, - }); - - if (isValid) { - signin(ctx, user); - return; - } else { - await fail(403, { - id: '93b86c4b-72f9-40eb-9815-798928603d1e', - }); - return; - } - } else { - if (!same && !profile.usePasswordLessLogin) { - await fail(403, { - id: '932c904e-9460-45b7-9ce6-7ed33be7eb2c', - }); - return; - } - - const keys = await UserSecurityKeys.findBy({ - userId: user.id, - }); - - if (keys.length === 0) { - await fail(403, { - id: 'f27fd449-9af4-4841-9249-1f989b9fa4a4', - }); - return; - } - - // 32 byte challenge - const challenge = randomBytes(32).toString('base64') - .replace(/=/g, '') - .replace(/\+/g, '-') - .replace(/\//g, '_'); - - const challengeId = this.idService.genId(); - - await AttestationChallenges.insert({ - userId: user.id, - id: challengeId, - challenge: hash(Buffer.from(challenge, 'utf-8')).toString('hex'), - createdAt: new Date(), - registrationChallenge: false, - }); - - ctx.body = { - challenge, - challengeId, - securityKeys: keys.map(key => ({ - id: key.id, - })), - }; - ctx.status = 200; - return; - } - // never get here -}; diff --git a/packages/backend/src/server/api/private/signup-pending.ts b/packages/backend/src/server/api/private/signup-pending.ts deleted file mode 100644 index e5e39ba00dde..000000000000 --- a/packages/backend/src/server/api/private/signup-pending.ts +++ /dev/null @@ -1,35 +0,0 @@ -import Koa from 'koa'; -import { Users, UserPendings, UserProfiles } from '@/models/index.js'; -import { signup } from '../common/signup.js'; -import signin from '../common/signin.js'; - -export default async (ctx: Koa.Context) => { - const body = ctx.request.body; - - const code = body['code']; - - try { - const pendingUser = await UserPendings.findOneByOrFail({ code }); - - const { account, secret } = await signup({ - username: pendingUser.username, - passwordHash: pendingUser.password, - }); - - UserPendings.delete({ - id: pendingUser.id, - }); - - const profile = await UserProfiles.findOneByOrFail({ userId: account.id }); - - await UserProfiles.update({ userId: profile.userId }, { - email: pendingUser.email, - emailVerified: true, - emailVerifyCode: null, - }); - - signin(ctx, account); - } catch (e) { - ctx.throw(400, e); - } -}; diff --git a/packages/backend/src/server/api/private/signup.ts b/packages/backend/src/server/api/private/signup.ts deleted file mode 100644 index 21069db32593..000000000000 --- a/packages/backend/src/server/api/private/signup.ts +++ /dev/null @@ -1,112 +0,0 @@ -import Koa from 'koa'; -import rndstr from 'rndstr'; -import bcrypt from 'bcryptjs'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import { verifyHcaptcha, verifyRecaptcha } from '@/misc/captcha.js'; -import { Users, RegistrationTickets, UserPendings } from '@/models/index.js'; -import { signup } from '../common/signup.js'; -import config from '@/config/index.js'; -import { sendEmail } from '@/services/send-email.js'; -import type { IdService } from '@/services/IdService.js'; -import { validateEmailForAccount } from '@/services/validate-email-for-account.js'; - -export default async (ctx: Koa.Context) => { - const body = ctx.request.body; - - const instance = await fetchMeta(true); - - // Verify *Captcha - // ただしテスト時はこの機構は障害となるため無効にする - if (process.env.NODE_ENV !== 'test') { - if (instance.enableHcaptcha && instance.hcaptchaSecretKey) { - await verifyHcaptcha(instance.hcaptchaSecretKey, body['hcaptcha-response']).catch(e => { - ctx.throw(400, e); - }); - } - - if (instance.enableRecaptcha && instance.recaptchaSecretKey) { - await verifyRecaptcha(instance.recaptchaSecretKey, body['g-recaptcha-response']).catch(e => { - ctx.throw(400, e); - }); - } - } - - const username = body['username']; - const password = body['password']; - const host: string | null = process.env.NODE_ENV === 'test' ? (body['host'] || null) : null; - const invitationCode = body['invitationCode']; - const emailAddress = body['emailAddress']; - - if (instance.emailRequiredForSignup) { - if (emailAddress == null || typeof emailAddress !== 'string') { - ctx.status = 400; - return; - } - - const available = await validateEmailForAccount(emailAddress); - if (!available) { - ctx.status = 400; - return; - } - } - - if (instance.disableRegistration) { - if (invitationCode == null || typeof invitationCode !== 'string') { - ctx.status = 400; - return; - } - - const ticket = await RegistrationTickets.findOneBy({ - code: invitationCode, - }); - - if (ticket == null) { - ctx.status = 400; - return; - } - - RegistrationTickets.delete(ticket.id); - } - - if (instance.emailRequiredForSignup) { - const code = rndstr('a-z0-9', 16); - - // Generate hash of password - const salt = await bcrypt.genSalt(8); - const hash = await bcrypt.hash(password, salt); - - await UserPendings.insert({ - id: this.idService.genId(), - createdAt: new Date(), - code, - email: emailAddress, - username: username, - password: hash, - }); - - const link = `${config.url}/signup-complete/${code}`; - - sendEmail(emailAddress, 'Signup', - `To complete signup, please click this link:
${link}`, - `To complete signup, please click this link: ${link}`); - - ctx.status = 204; - } else { - try { - const { account, secret } = await signup({ - username, password, host, - }); - - const res = await Users.pack(account, account, { - detail: true, - includeSecrets: true, - }); - - (res as any).token = secret; - - ctx.body = res; - } catch (e) { - ctx.throw(400, e); - } - } -}; diff --git a/packages/backend/src/services/CaptchaService.ts b/packages/backend/src/services/CaptchaService.ts new file mode 100644 index 000000000000..4809925c1460 --- /dev/null +++ b/packages/backend/src/services/CaptchaService.ts @@ -0,0 +1,70 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Users } from '@/models/index.js'; +import { Config } from '@/config/types.js'; +import { HttpRequestService } from './HttpRequestService.js'; + +type CaptchaResponse = { + success: boolean; + 'error-codes'?: string[]; +}; + +@Injectable() +export class CaptchaService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + private httpRequestService: HttpRequestService, + ) { + } + + async #getCaptchaResponse(url: string, secret: string, response: string): Promise { + const params = new URLSearchParams({ + secret, + response, + }); + + const res = await fetch(url, { + method: 'POST', + body: params, + headers: { + 'User-Agent': this.config.userAgent, + }, + // TODO + //timeout: 10 * 1000, + agent: (url, bypassProxy) => this.httpRequestService.getAgentByUrl(url, bypassProxy), + }).catch(e => { + throw `${e.message || e}`; + }); + + if (!res.ok) { + throw `${res.status}`; + } + + return await res.json() as CaptchaResponse; + } + + public async verifyRecaptcha(secret: string, response: string): Promise { + const result = await this.#getCaptchaResponse('https://www.recaptcha.net/recaptcha/api/siteverify', secret, response).catch(e => { + throw `recaptcha-request-failed: ${e}`; + }); + + if (result.success !== true) { + const errorCodes = result['error-codes'] ? result['error-codes'].join(', ') : ''; + throw `recaptcha-failed: ${errorCodes}`; + } + } + + public async verifyHcaptcha(secret: string, response: string): Promise { + const result = await this.#getCaptchaResponse('https://hcaptcha.com/siteverify', secret, response).catch(e => { + throw `hcaptcha-request-failed: ${e}`; + }); + + if (result.success !== true) { + const errorCodes = result['error-codes'] ? result['error-codes'].join(', ') : ''; + throw `hcaptcha-failed: ${errorCodes}`; + } + } +} + diff --git a/packages/backend/src/services/SignupService.ts b/packages/backend/src/services/SignupService.ts new file mode 100644 index 000000000000..222b130fc618 --- /dev/null +++ b/packages/backend/src/services/SignupService.ts @@ -0,0 +1,139 @@ +import { generateKeyPair } from 'node:crypto'; +import { Inject, Injectable } from '@nestjs/common'; +import bcrypt from 'bcryptjs'; +import { DataSource, IsNull } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { UsedUsernames } from '@/models/index.js'; +import { Users } from '@/models/index.js'; +import { Config } from '@/config/types.js'; +import { User } from '@/models/entities/user.js'; +import { UserProfile } from '@/models/entities/user-profile.js'; +import { IdService } from '@/services/IdService.js'; +import { toPunyNullable } from '@/misc/convert-host'; +import { UserKeypair } from '@/models/entities/user-keypair'; +import { UsedUsername } from '@/models/entities/used-username'; +import UsersChart from './chart/charts/users'; +import generateUserToken from './generate-native-user-token.js'; + +@Injectable() +export class SignupService { + constructor( + @Inject(DI_SYMBOLS.db) + private db: DataSource, + + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('usedUsernamesRepository') + private usedUsernamesRepository: typeof UsedUsernames, + + private idService: IdService, + private usersChart: UsersChart, + ) { + } + + public async signup(opts: { + username: User['username']; + password?: string | null; + passwordHash?: UserProfile['password'] | null; + host?: string | null; + }) { + const { username, password, passwordHash, host } = opts; + let hash = passwordHash; + + // Validate username + if (!Users.validateLocalUsername(username)) { + throw new Error('INVALID_USERNAME'); + } + + if (password != null && passwordHash == null) { + // Validate password + if (!Users.validatePassword(password)) { + throw new Error('INVALID_PASSWORD'); + } + + // Generate hash of password + const salt = await bcrypt.genSalt(8); + hash = await bcrypt.hash(password, salt); + } + + // Generate secret + const secret = generateUserToken(); + + // Check username duplication + if (await Users.findOneBy({ usernameLower: username.toLowerCase(), host: IsNull() })) { + throw new Error('DUPLICATED_USERNAME'); + } + + // Check deleted username duplication + if (await this.usedUsernamesRepository.findOneBy({ username: username.toLowerCase() })) { + throw new Error('USED_USERNAME'); + } + + const keyPair = await new Promise((res, rej) => + generateKeyPair('rsa', { + modulusLength: 4096, + publicKeyEncoding: { + type: 'spki', + format: 'pem', + }, + privateKeyEncoding: { + type: 'pkcs8', + format: 'pem', + cipher: undefined, + passphrase: undefined, + }, + } as any, (err, publicKey, privateKey) => + err ? rej(err) : res([publicKey, privateKey]), + )); + + let account!: User; + + // Start transaction + await this.db.transaction(async transactionalEntityManager => { + const exist = await transactionalEntityManager.findOneBy(User, { + usernameLower: username.toLowerCase(), + host: IsNull(), + }); + + if (exist) throw new Error(' the username is already used'); + + account = await transactionalEntityManager.save(new User({ + id: this.idService.genId(), + createdAt: new Date(), + username: username, + usernameLower: username.toLowerCase(), + host: toPunyNullable(host), + token: secret, + isAdmin: (await Users.countBy({ + host: IsNull(), + })) === 0, + })); + + await transactionalEntityManager.save(new UserKeypair({ + publicKey: keyPair[0], + privateKey: keyPair[1], + userId: account.id, + })); + + await transactionalEntityManager.save(new UserProfile({ + userId: account.id, + autoAcceptFollowed: true, + password: hash, + })); + + await transactionalEntityManager.save(new UsedUsername({ + createdAt: new Date(), + username: username.toLowerCase(), + })); + }); + + this.usersChart.update(account, true); + + return { account, secret }; + } +} + From 9ffb5b6991fa4b0cb56af13c6120bce862ee9d07 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 14 Sep 2022 18:00:55 +0900 Subject: [PATCH 087/180] wip --- packages/backend/src/server/api/2fa.ts | 422 ----------------- .../src/server/api/SigninApiService.ts | 29 +- .../TwoFactorAuthenticationService.ts | 439 ++++++++++++++++++ 3 files changed, 457 insertions(+), 433 deletions(-) delete mode 100644 packages/backend/src/server/api/2fa.ts create mode 100644 packages/backend/src/services/TwoFactorAuthenticationService.ts diff --git a/packages/backend/src/server/api/2fa.ts b/packages/backend/src/server/api/2fa.ts deleted file mode 100644 index 96b9316e4730..000000000000 --- a/packages/backend/src/server/api/2fa.ts +++ /dev/null @@ -1,422 +0,0 @@ -import * as crypto from 'node:crypto'; -import * as jsrsasign from 'jsrsasign'; -import config from '@/config/index.js'; - -const ECC_PRELUDE = Buffer.from([0x04]); -const NULL_BYTE = Buffer.from([0]); -const PEM_PRELUDE = Buffer.from( - '3059301306072a8648ce3d020106082a8648ce3d030107034200', - 'hex', -); - -// Android Safetynet attestations are signed with this cert: -const GSR2 = `-----BEGIN CERTIFICATE----- -MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G -A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp -Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1 -MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG -A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL -v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8 -eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq -tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd -C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa -zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB -mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH -V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n -bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG -3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs -J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO -291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS -ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd -AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 -TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== ------END CERTIFICATE-----\n`; - -function base64URLDecode(source: string) { - return Buffer.from(source.replace(/\-/g, '+').replace(/_/g, '/'), 'base64'); -} - -function getCertSubject(certificate: string) { - const subjectCert = new jsrsasign.X509(); - subjectCert.readCertPEM(certificate); - - const subjectString = subjectCert.getSubjectString(); - const subjectFields = subjectString.slice(1).split('/'); - - const fields = {} as Record; - for (const field of subjectFields) { - const eqIndex = field.indexOf('='); - fields[field.substring(0, eqIndex)] = field.substring(eqIndex + 1); - } - - return fields; -} - -function verifyCertificateChain(certificates: string[]) { - let valid = true; - - for (let i = 0; i < certificates.length; i++) { - const Cert = certificates[i]; - const certificate = new jsrsasign.X509(); - certificate.readCertPEM(Cert); - - const CACert = i + 1 >= certificates.length ? Cert : certificates[i + 1]; - - const certStruct = jsrsasign.ASN1HEX.getTLVbyList(certificate.hex!, 0, [0]); - const algorithm = certificate.getSignatureAlgorithmField(); - const signatureHex = certificate.getSignatureValueHex(); - - // Verify against CA - const Signature = new jsrsasign.KJUR.crypto.Signature({ alg: algorithm }); - Signature.init(CACert); - Signature.updateHex(certStruct); - valid = valid && !!Signature.verify(signatureHex); // true if CA signed the certificate - } - - return valid; -} - -function PEMString(pemBuffer: Buffer, type = 'CERTIFICATE') { - if (pemBuffer.length === 65 && pemBuffer[0] === 0x04) { - pemBuffer = Buffer.concat([PEM_PRELUDE, pemBuffer], 91); - type = 'PUBLIC KEY'; - } - const cert = pemBuffer.toString('base64'); - - const keyParts = []; - const max = Math.ceil(cert.length / 64); - let start = 0; - for (let i = 0; i < max; i++) { - keyParts.push(cert.substring(start, start + 64)); - start += 64; - } - - return ( - `-----BEGIN ${type}-----\n` + - keyParts.join('\n') + - `\n-----END ${type}-----\n` - ); -} - -export function hash(data: Buffer) { - return crypto - .createHash('sha256') - .update(data) - .digest(); -} - -export function verifyLogin({ - publicKey, - authenticatorData, - clientDataJSON, - clientData, - signature, - challenge, -}: { - publicKey: Buffer, - authenticatorData: Buffer, - clientDataJSON: Buffer, - clientData: any, - signature: Buffer, - challenge: string -}) { - if (clientData.type !== 'webauthn.get') { - throw new Error('type is not webauthn.get'); - } - - if (hash(clientData.challenge).toString('hex') !== challenge) { - throw new Error('challenge mismatch'); - } - if (clientData.origin !== config.scheme + '://' + config.host) { - throw new Error('origin mismatch'); - } - - const verificationData = Buffer.concat( - [authenticatorData, hash(clientDataJSON)], - 32 + authenticatorData.length, - ); - - return crypto - .createVerify('SHA256') - .update(verificationData) - .verify(PEMString(publicKey), signature); -} - -export const procedures = { - none: { - verify({ publicKey }: { publicKey: Map }) { - const negTwo = publicKey.get(-2); - - if (!negTwo || negTwo.length !== 32) { - throw new Error('invalid or no -2 key given'); - } - const negThree = publicKey.get(-3); - if (!negThree || negThree.length !== 32) { - throw new Error('invalid or no -3 key given'); - } - - const publicKeyU2F = Buffer.concat( - [ECC_PRELUDE, negTwo, negThree], - 1 + 32 + 32, - ); - - return { - publicKey: publicKeyU2F, - valid: true, - }; - }, - }, - 'android-key': { - verify({ - attStmt, - authenticatorData, - clientDataHash, - publicKey, - rpIdHash, - credentialId, - }: { - attStmt: any, - authenticatorData: Buffer, - clientDataHash: Buffer, - publicKey: Map; - rpIdHash: Buffer, - credentialId: Buffer, - }) { - if (attStmt.alg !== -7) { - throw new Error('alg mismatch'); - } - - const verificationData = Buffer.concat([ - authenticatorData, - clientDataHash, - ]); - - const attCert: Buffer = attStmt.x5c[0]; - - const negTwo = publicKey.get(-2); - - if (!negTwo || negTwo.length !== 32) { - throw new Error('invalid or no -2 key given'); - } - const negThree = publicKey.get(-3); - if (!negThree || negThree.length !== 32) { - throw new Error('invalid or no -3 key given'); - } - - const publicKeyData = Buffer.concat( - [ECC_PRELUDE, negTwo, negThree], - 1 + 32 + 32, - ); - - if (!attCert.equals(publicKeyData)) { - throw new Error('public key mismatch'); - } - - const isValid = crypto - .createVerify('SHA256') - .update(verificationData) - .verify(PEMString(attCert), attStmt.sig); - - // TODO: Check 'attestationChallenge' field in extension of cert matches hash(clientDataJSON) - - return { - valid: isValid, - publicKey: publicKeyData, - }; - }, - }, - // what a stupid attestation - 'android-safetynet': { - verify({ - attStmt, - authenticatorData, - clientDataHash, - publicKey, - rpIdHash, - credentialId, - }: { - attStmt: any, - authenticatorData: Buffer, - clientDataHash: Buffer, - publicKey: Map; - rpIdHash: Buffer, - credentialId: Buffer, - }) { - const verificationData = hash( - Buffer.concat([authenticatorData, clientDataHash]), - ); - - const jwsParts = attStmt.response.toString('utf-8').split('.'); - - const header = JSON.parse(base64URLDecode(jwsParts[0]).toString('utf-8')); - const response = JSON.parse( - base64URLDecode(jwsParts[1]).toString('utf-8'), - ); - const signature = jwsParts[2]; - - if (!verificationData.equals(Buffer.from(response.nonce, 'base64'))) { - throw new Error('invalid nonce'); - } - - const certificateChain = header.x5c - .map((key: any) => PEMString(key)) - .concat([GSR2]); - - if (getCertSubject(certificateChain[0]).CN !== 'attest.android.com') { - throw new Error('invalid common name'); - } - - if (!verifyCertificateChain(certificateChain)) { - throw new Error('Invalid certificate chain!'); - } - - const signatureBase = Buffer.from( - jwsParts[0] + '.' + jwsParts[1], - 'utf-8', - ); - - const valid = crypto - .createVerify('sha256') - .update(signatureBase) - .verify(certificateChain[0], base64URLDecode(signature)); - - const negTwo = publicKey.get(-2); - - if (!negTwo || negTwo.length !== 32) { - throw new Error('invalid or no -2 key given'); - } - const negThree = publicKey.get(-3); - if (!negThree || negThree.length !== 32) { - throw new Error('invalid or no -3 key given'); - } - - const publicKeyData = Buffer.concat( - [ECC_PRELUDE, negTwo, negThree], - 1 + 32 + 32, - ); - return { - valid, - publicKey: publicKeyData, - }; - }, - }, - packed: { - verify({ - attStmt, - authenticatorData, - clientDataHash, - publicKey, - rpIdHash, - credentialId, - }: { - attStmt: any, - authenticatorData: Buffer, - clientDataHash: Buffer, - publicKey: Map; - rpIdHash: Buffer, - credentialId: Buffer, - }) { - const verificationData = Buffer.concat([ - authenticatorData, - clientDataHash, - ]); - - if (attStmt.x5c) { - const attCert = attStmt.x5c[0]; - - const validSignature = crypto - .createVerify('SHA256') - .update(verificationData) - .verify(PEMString(attCert), attStmt.sig); - - const negTwo = publicKey.get(-2); - - if (!negTwo || negTwo.length !== 32) { - throw new Error('invalid or no -2 key given'); - } - const negThree = publicKey.get(-3); - if (!negThree || negThree.length !== 32) { - throw new Error('invalid or no -3 key given'); - } - - const publicKeyData = Buffer.concat( - [ECC_PRELUDE, negTwo, negThree], - 1 + 32 + 32, - ); - - return { - valid: validSignature, - publicKey: publicKeyData, - }; - } else if (attStmt.ecdaaKeyId) { - // https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-ecdaa-algorithm-v2.0-id-20180227.html#ecdaa-verify-operation - throw new Error('ECDAA-Verify is not supported'); - } else { - if (attStmt.alg !== -7) throw new Error('alg mismatch'); - - throw new Error('self attestation is not supported'); - } - }, - }, - - 'fido-u2f': { - verify({ - attStmt, - authenticatorData, - clientDataHash, - publicKey, - rpIdHash, - credentialId, - }: { - attStmt: any, - authenticatorData: Buffer, - clientDataHash: Buffer, - publicKey: Map, - rpIdHash: Buffer, - credentialId: Buffer - }) { - const x5c: Buffer[] = attStmt.x5c; - if (x5c.length !== 1) { - throw new Error('x5c length does not match expectation'); - } - - const attCert = x5c[0]; - - // TODO: make sure attCert is an Elliptic Curve (EC) public key over the P-256 curve - - const negTwo: Buffer = publicKey.get(-2); - - if (!negTwo || negTwo.length !== 32) { - throw new Error('invalid or no -2 key given'); - } - const negThree: Buffer = publicKey.get(-3); - if (!negThree || negThree.length !== 32) { - throw new Error('invalid or no -3 key given'); - } - - const publicKeyU2F = Buffer.concat( - [ECC_PRELUDE, negTwo, negThree], - 1 + 32 + 32, - ); - - const verificationData = Buffer.concat([ - NULL_BYTE, - rpIdHash, - clientDataHash, - credentialId, - publicKeyU2F, - ]); - - const validSignature = crypto - .createVerify('SHA256') - .update(verificationData) - .verify(PEMString(attCert), attStmt.sig); - - return { - valid: validSignature, - publicKey: publicKeyU2F, - }; - }, - }, -}; diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index bbe942cc2dcb..bf4b67f2b7c3 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -4,15 +4,15 @@ import bcrypt from 'bcryptjs'; import * as speakeasy from 'speakeasy'; import { IsNull } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { UserSecurityKeys } from '@/models/index.js'; +import type { UserSecurityKeys , Signins, UserProfiles } from '@/models/index.js'; import { AttestationChallenges, Users } from '@/models/index.js'; import { Config } from '@/config/types.js'; import { getIpHash } from '@/misc/get-ip-hash.js'; import type { ILocalUser } from '@/models/entities/user.js'; import { IdService } from '@/services/IdService.js'; -import { RateLimiterService } from '../RateLimiterService.js'; -import { SigninService } from '../SigninService.js'; -import { verifyLogin, hash } from '../2fa.js'; +import { TwoFactorAuthenticationService } from '@/services/TwoFactorAuthenticationService.js'; +import { RateLimiterService } from './RateLimiterService.js'; +import { SigninService } from './SigninService.js'; import type Koa from 'koa'; @Injectable() @@ -27,12 +27,19 @@ export class SigninApiService { @Inject('userSecurityKeysRepository') private userSecurityKeysRepository: typeof UserSecurityKeys, + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + @Inject('attestationChallengesRepository') private attestationChallengesRepository: typeof AttestationChallenges, + @Inject('signinsRepository') + private signinsRepository: typeof Signins, + private idService: IdService, private rateLimiterService: RateLimiterService, private signinService: SigninService, + private twoFactorAuthenticationService: TwoFactorAuthenticationService, ) { } @@ -100,14 +107,14 @@ export class SigninApiService { return; } - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); // Compare password const same = await bcrypt.compare(password, profile.password!); - async function fail(status?: number, failure?: { id: string }) { + const fail = async (status?: number, failure?: { id: string }) => { // Append signin history - await Signins.insert({ + await this.signinsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), userId: user.id, @@ -117,7 +124,7 @@ export class SigninApiService { }); error(status || 500, failure || { id: '4e30e80c-e338-45a0-8c8f-44455efa3b76' }); - } + }; if (!profile.twoFactorEnabled) { if (same) { @@ -169,7 +176,7 @@ export class SigninApiService { userId: user.id, id: body.challengeId, registrationChallenge: false, - challenge: hash(clientData.challenge).toString('hex'), + challenge: this.twoFactorAuthenticationService.hash(clientData.challenge).toString('hex'), }); if (!challenge) { @@ -207,7 +214,7 @@ export class SigninApiService { return; } - const isValid = verifyLogin({ + const isValid = this.twoFactorAuthenticationService.verifySignin({ publicKey: Buffer.from(securityKey.publicKey, 'hex'), authenticatorData: Buffer.from(body.authenticatorData, 'hex'), clientDataJSON, @@ -255,7 +262,7 @@ export class SigninApiService { await this.attestationChallengesRepository.insert({ userId: user.id, id: challengeId, - challenge: hash(Buffer.from(challenge, 'utf-8')).toString('hex'), + challenge: this.twoFactorAuthenticationService.hash(Buffer.from(challenge, 'utf-8')).toString('hex'), createdAt: new Date(), registrationChallenge: false, }); diff --git a/packages/backend/src/services/TwoFactorAuthenticationService.ts b/packages/backend/src/services/TwoFactorAuthenticationService.ts new file mode 100644 index 000000000000..054814f44fa0 --- /dev/null +++ b/packages/backend/src/services/TwoFactorAuthenticationService.ts @@ -0,0 +1,439 @@ +import * as crypto from 'node:crypto'; +import { Inject, Injectable } from '@nestjs/common'; +import * as jsrsasign from 'jsrsasign'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Users } from '@/models/index.js'; +import { Config } from '@/config/types.js'; + +const ECC_PRELUDE = Buffer.from([0x04]); +const NULL_BYTE = Buffer.from([0]); +const PEM_PRELUDE = Buffer.from( + '3059301306072a8648ce3d020106082a8648ce3d030107034200', + 'hex', +); + +// Android Safetynet attestations are signed with this cert: +const GSR2 = `-----BEGIN CERTIFICATE----- +MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G +A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp +Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1 +MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG +A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL +v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8 +eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq +tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd +C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa +zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB +mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH +V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n +bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG +3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs +J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO +291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS +ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd +AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 +TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== +-----END CERTIFICATE-----\n`; + +function base64URLDecode(source: string) { + return Buffer.from(source.replace(/\-/g, '+').replace(/_/g, '/'), 'base64'); +} + +function getCertSubject(certificate: string) { + const subjectCert = new jsrsasign.X509(); + subjectCert.readCertPEM(certificate); + + const subjectString = subjectCert.getSubjectString(); + const subjectFields = subjectString.slice(1).split('/'); + + const fields = {} as Record; + for (const field of subjectFields) { + const eqIndex = field.indexOf('='); + fields[field.substring(0, eqIndex)] = field.substring(eqIndex + 1); + } + + return fields; +} + +function verifyCertificateChain(certificates: string[]) { + let valid = true; + + for (let i = 0; i < certificates.length; i++) { + const Cert = certificates[i]; + const certificate = new jsrsasign.X509(); + certificate.readCertPEM(Cert); + + const CACert = i + 1 >= certificates.length ? Cert : certificates[i + 1]; + + const certStruct = jsrsasign.ASN1HEX.getTLVbyList(certificate.hex!, 0, [0]); + const algorithm = certificate.getSignatureAlgorithmField(); + const signatureHex = certificate.getSignatureValueHex(); + + // Verify against CA + const Signature = new jsrsasign.KJUR.crypto.Signature({ alg: algorithm }); + Signature.init(CACert); + Signature.updateHex(certStruct); + valid = valid && !!Signature.verify(signatureHex); // true if CA signed the certificate + } + + return valid; +} + +function PEMString(pemBuffer: Buffer, type = 'CERTIFICATE') { + if (pemBuffer.length === 65 && pemBuffer[0] === 0x04) { + pemBuffer = Buffer.concat([PEM_PRELUDE, pemBuffer], 91); + type = 'PUBLIC KEY'; + } + const cert = pemBuffer.toString('base64'); + + const keyParts = []; + const max = Math.ceil(cert.length / 64); + let start = 0; + for (let i = 0; i < max; i++) { + keyParts.push(cert.substring(start, start + 64)); + start += 64; + } + + return ( + `-----BEGIN ${type}-----\n` + + keyParts.join('\n') + + `\n-----END ${type}-----\n` + ); +} + +@Injectable() +export class TwoFactorAuthenticationService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + ) { + } + + public hash(data: Buffer) { + return crypto + .createHash('sha256') + .update(data) + .digest(); + } + + public verifySignin({ + publicKey, + authenticatorData, + clientDataJSON, + clientData, + signature, + challenge, + }: { + publicKey: Buffer, + authenticatorData: Buffer, + clientDataJSON: Buffer, + clientData: any, + signature: Buffer, + challenge: string + }) { + if (clientData.type !== 'webauthn.get') { + throw new Error('type is not webauthn.get'); + } + + if (this.hash(clientData.challenge).toString('hex') !== challenge) { + throw new Error('challenge mismatch'); + } + if (clientData.origin !== this.config.scheme + '://' + this.config.host) { + throw new Error('origin mismatch'); + } + + const verificationData = Buffer.concat( + [authenticatorData, this.hash(clientDataJSON)], + 32 + authenticatorData.length, + ); + + return crypto + .createVerify('SHA256') + .update(verificationData) + .verify(PEMString(publicKey), signature); + } + + public getProcedures() { + return { + none: { + verify({ publicKey }: { publicKey: Map }) { + const negTwo = publicKey.get(-2); + + if (!negTwo || negTwo.length !== 32) { + throw new Error('invalid or no -2 key given'); + } + const negThree = publicKey.get(-3); + if (!negThree || negThree.length !== 32) { + throw new Error('invalid or no -3 key given'); + } + + const publicKeyU2F = Buffer.concat( + [ECC_PRELUDE, negTwo, negThree], + 1 + 32 + 32, + ); + + return { + publicKey: publicKeyU2F, + valid: true, + }; + }, + }, + 'android-key': { + verify({ + attStmt, + authenticatorData, + clientDataHash, + publicKey, + rpIdHash, + credentialId, + }: { + attStmt: any, + authenticatorData: Buffer, + clientDataHash: Buffer, + publicKey: Map; + rpIdHash: Buffer, + credentialId: Buffer, + }) { + if (attStmt.alg !== -7) { + throw new Error('alg mismatch'); + } + + const verificationData = Buffer.concat([ + authenticatorData, + clientDataHash, + ]); + + const attCert: Buffer = attStmt.x5c[0]; + + const negTwo = publicKey.get(-2); + + if (!negTwo || negTwo.length !== 32) { + throw new Error('invalid or no -2 key given'); + } + const negThree = publicKey.get(-3); + if (!negThree || negThree.length !== 32) { + throw new Error('invalid or no -3 key given'); + } + + const publicKeyData = Buffer.concat( + [ECC_PRELUDE, negTwo, negThree], + 1 + 32 + 32, + ); + + if (!attCert.equals(publicKeyData)) { + throw new Error('public key mismatch'); + } + + const isValid = crypto + .createVerify('SHA256') + .update(verificationData) + .verify(PEMString(attCert), attStmt.sig); + + // TODO: Check 'attestationChallenge' field in extension of cert matches hash(clientDataJSON) + + return { + valid: isValid, + publicKey: publicKeyData, + }; + }, + }, + // what a stupid attestation + 'android-safetynet': { + verify: ({ + attStmt, + authenticatorData, + clientDataHash, + publicKey, + rpIdHash, + credentialId, + }: { + attStmt: any, + authenticatorData: Buffer, + clientDataHash: Buffer, + publicKey: Map; + rpIdHash: Buffer, + credentialId: Buffer, + }) => { + const verificationData = this.hash( + Buffer.concat([authenticatorData, clientDataHash]), + ); + + const jwsParts = attStmt.response.toString('utf-8').split('.'); + + const header = JSON.parse(base64URLDecode(jwsParts[0]).toString('utf-8')); + const response = JSON.parse( + base64URLDecode(jwsParts[1]).toString('utf-8'), + ); + const signature = jwsParts[2]; + + if (!verificationData.equals(Buffer.from(response.nonce, 'base64'))) { + throw new Error('invalid nonce'); + } + + const certificateChain = header.x5c + .map((key: any) => PEMString(key)) + .concat([GSR2]); + + if (getCertSubject(certificateChain[0]).CN !== 'attest.android.com') { + throw new Error('invalid common name'); + } + + if (!verifyCertificateChain(certificateChain)) { + throw new Error('Invalid certificate chain!'); + } + + const signatureBase = Buffer.from( + jwsParts[0] + '.' + jwsParts[1], + 'utf-8', + ); + + const valid = crypto + .createVerify('sha256') + .update(signatureBase) + .verify(certificateChain[0], base64URLDecode(signature)); + + const negTwo = publicKey.get(-2); + + if (!negTwo || negTwo.length !== 32) { + throw new Error('invalid or no -2 key given'); + } + const negThree = publicKey.get(-3); + if (!negThree || negThree.length !== 32) { + throw new Error('invalid or no -3 key given'); + } + + const publicKeyData = Buffer.concat( + [ECC_PRELUDE, negTwo, negThree], + 1 + 32 + 32, + ); + return { + valid, + publicKey: publicKeyData, + }; + }, + }, + packed: { + verify({ + attStmt, + authenticatorData, + clientDataHash, + publicKey, + rpIdHash, + credentialId, + }: { + attStmt: any, + authenticatorData: Buffer, + clientDataHash: Buffer, + publicKey: Map; + rpIdHash: Buffer, + credentialId: Buffer, + }) { + const verificationData = Buffer.concat([ + authenticatorData, + clientDataHash, + ]); + + if (attStmt.x5c) { + const attCert = attStmt.x5c[0]; + + const validSignature = crypto + .createVerify('SHA256') + .update(verificationData) + .verify(PEMString(attCert), attStmt.sig); + + const negTwo = publicKey.get(-2); + + if (!negTwo || negTwo.length !== 32) { + throw new Error('invalid or no -2 key given'); + } + const negThree = publicKey.get(-3); + if (!negThree || negThree.length !== 32) { + throw new Error('invalid or no -3 key given'); + } + + const publicKeyData = Buffer.concat( + [ECC_PRELUDE, negTwo, negThree], + 1 + 32 + 32, + ); + + return { + valid: validSignature, + publicKey: publicKeyData, + }; + } else if (attStmt.ecdaaKeyId) { + // https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-ecdaa-algorithm-v2.0-id-20180227.html#ecdaa-verify-operation + throw new Error('ECDAA-Verify is not supported'); + } else { + if (attStmt.alg !== -7) throw new Error('alg mismatch'); + + throw new Error('self attestation is not supported'); + } + }, + }, + + 'fido-u2f': { + verify({ + attStmt, + authenticatorData, + clientDataHash, + publicKey, + rpIdHash, + credentialId, + }: { + attStmt: any, + authenticatorData: Buffer, + clientDataHash: Buffer, + publicKey: Map, + rpIdHash: Buffer, + credentialId: Buffer + }) { + const x5c: Buffer[] = attStmt.x5c; + if (x5c.length !== 1) { + throw new Error('x5c length does not match expectation'); + } + + const attCert = x5c[0]; + + // TODO: make sure attCert is an Elliptic Curve (EC) public key over the P-256 curve + + const negTwo: Buffer = publicKey.get(-2); + + if (!negTwo || negTwo.length !== 32) { + throw new Error('invalid or no -2 key given'); + } + const negThree: Buffer = publicKey.get(-3); + if (!negThree || negThree.length !== 32) { + throw new Error('invalid or no -3 key given'); + } + + const publicKeyU2F = Buffer.concat( + [ECC_PRELUDE, negTwo, negThree], + 1 + 32 + 32, + ); + + const verificationData = Buffer.concat([ + NULL_BYTE, + rpIdHash, + clientDataHash, + credentialId, + publicKeyU2F, + ]); + + const validSignature = crypto + .createVerify('SHA256') + .update(verificationData) + .verify(PEMString(attCert), attStmt.sig); + + return { + valid: validSignature, + publicKey: publicKeyU2F, + }; + }, + }, + }; + } +} From 9e04003437416f5a05eb2ece1012a2f000fcdee7 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 14 Sep 2022 18:02:17 +0900 Subject: [PATCH 088/180] wip --- .../backend/src/{server/api/common => misc}/is-native-token.ts | 0 packages/backend/src/server/api/AuthenticateService.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename packages/backend/src/{server/api/common => misc}/is-native-token.ts (100%) diff --git a/packages/backend/src/server/api/common/is-native-token.ts b/packages/backend/src/misc/is-native-token.ts similarity index 100% rename from packages/backend/src/server/api/common/is-native-token.ts rename to packages/backend/src/misc/is-native-token.ts diff --git a/packages/backend/src/server/api/AuthenticateService.ts b/packages/backend/src/server/api/AuthenticateService.ts index 9b5e1dc2a3aa..bbedfc2b2206 100644 --- a/packages/backend/src/server/api/AuthenticateService.ts +++ b/packages/backend/src/server/api/AuthenticateService.ts @@ -6,7 +6,7 @@ import type { AccessToken } from '@/models/entities/access-token.js'; import { Cache } from '@/misc/cache.js'; import type { App } from '@/models/entities/app.js'; import { UserCacheService } from '@/services/UserCacheService.js'; -import isNativeToken from './common/is-native-token.js'; +import isNativeToken from '@/misc/is-native-token.js'; export class AuthenticationError extends Error { constructor(message: string) { From 079a729824f2813317f78bb90ed923d1a384d4ad Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 14 Sep 2022 18:03:48 +0900 Subject: [PATCH 089/180] wip --- .../common => misc}/generate-native-user-token.ts | 0 .../src/server/api/common/generate-block-query.ts | 9 +++++---- .../server/api/common/generate-channel-query.ts | 5 +++-- .../api/common/generate-muted-note-query.ts | 4 ++-- .../common/generate-muted-note-thread-query.ts | 7 ++++--- .../api/common/generate-muted-user-query.ts | 5 +++-- .../server/api/common/generate-replies-query.ts | 15 ++++++++------- .../api/common/generate-visibility-query.ts | 15 ++++++++------- .../server/api/endpoints/i/regenerate-token.ts | 2 +- .../src/services/CreateSystemUserService.ts | 7 +++---- 10 files changed, 37 insertions(+), 32 deletions(-) rename packages/backend/src/{server/api/common => misc}/generate-native-user-token.ts (100%) diff --git a/packages/backend/src/server/api/common/generate-native-user-token.ts b/packages/backend/src/misc/generate-native-user-token.ts similarity index 100% rename from packages/backend/src/server/api/common/generate-native-user-token.ts rename to packages/backend/src/misc/generate-native-user-token.ts diff --git a/packages/backend/src/server/api/common/generate-block-query.ts b/packages/backend/src/server/api/common/generate-block-query.ts index 60db1e731b5b..d0dfe101120a 100644 --- a/packages/backend/src/server/api/common/generate-block-query.ts +++ b/packages/backend/src/server/api/common/generate-block-query.ts @@ -1,6 +1,7 @@ -import { User } from '@/models/entities/user.js'; +import { Brackets } from 'typeorm'; +import type { User } from '@/models/entities/user.js'; import { Blockings } from '@/models/index.js'; -import { Brackets, SelectQueryBuilder } from 'typeorm'; +import type { SelectQueryBuilder } from 'typeorm'; // ここでいうBlockedは被Blockedの意 export function generateBlockedUserQuery(q: SelectQueryBuilder, me: { id: User['id'] }) { @@ -14,11 +15,11 @@ export function generateBlockedUserQuery(q: SelectQueryBuilder, me: { id: U q .andWhere(`note.userId NOT IN (${ blockingQuery.getQuery() })`) .andWhere(new Brackets(qb => { qb - .where(`note.replyUserId IS NULL`) + .where('note.replyUserId IS NULL') .orWhere(`note.replyUserId NOT IN (${ blockingQuery.getQuery() })`); })) .andWhere(new Brackets(qb => { qb - .where(`note.renoteUserId IS NULL`) + .where('note.renoteUserId IS NULL') .orWhere(`note.renoteUserId NOT IN (${ blockingQuery.getQuery() })`); })); diff --git a/packages/backend/src/server/api/common/generate-channel-query.ts b/packages/backend/src/server/api/common/generate-channel-query.ts index 333bb73b86cd..5374249533db 100644 --- a/packages/backend/src/server/api/common/generate-channel-query.ts +++ b/packages/backend/src/server/api/common/generate-channel-query.ts @@ -1,6 +1,7 @@ -import { User } from '@/models/entities/user.js'; +import { Brackets } from 'typeorm'; +import type { User } from '@/models/entities/user.js'; import { ChannelFollowings } from '@/models/index.js'; -import { Brackets, SelectQueryBuilder } from 'typeorm'; +import type { SelectQueryBuilder } from 'typeorm'; export function generateChannelQuery(q: SelectQueryBuilder, me?: { id: User['id'] } | null) { if (me == null) { diff --git a/packages/backend/src/server/api/common/generate-muted-note-query.ts b/packages/backend/src/server/api/common/generate-muted-note-query.ts index f544e334d3bf..a4cc6c6c0d27 100644 --- a/packages/backend/src/server/api/common/generate-muted-note-query.ts +++ b/packages/backend/src/server/api/common/generate-muted-note-query.ts @@ -1,6 +1,6 @@ -import { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/user.js'; import { MutedNotes } from '@/models/index.js'; -import { SelectQueryBuilder } from 'typeorm'; +import type { SelectQueryBuilder } from 'typeorm'; export function generateMutedNoteQuery(q: SelectQueryBuilder, me: { id: User['id'] }) { const mutedQuery = MutedNotes.createQueryBuilder('muted') diff --git a/packages/backend/src/server/api/common/generate-muted-note-thread-query.ts b/packages/backend/src/server/api/common/generate-muted-note-thread-query.ts index 7263ea2e6097..5411b90907e6 100644 --- a/packages/backend/src/server/api/common/generate-muted-note-thread-query.ts +++ b/packages/backend/src/server/api/common/generate-muted-note-thread-query.ts @@ -1,6 +1,7 @@ -import { User } from '@/models/entities/user.js'; +import { Brackets } from 'typeorm'; +import type { User } from '@/models/entities/user.js'; import { NoteThreadMutings } from '@/models/index.js'; -import { Brackets, SelectQueryBuilder } from 'typeorm'; +import type { SelectQueryBuilder } from 'typeorm'; export function generateMutedNoteThreadQuery(q: SelectQueryBuilder, me: { id: User['id'] }) { const mutedQuery = NoteThreadMutings.createQueryBuilder('threadMuted') @@ -9,7 +10,7 @@ export function generateMutedNoteThreadQuery(q: SelectQueryBuilder, me: { i q.andWhere(`note.id NOT IN (${ mutedQuery.getQuery() })`); q.andWhere(new Brackets(qb => { qb - .where(`note.threadId IS NULL`) + .where('note.threadId IS NULL') .orWhere(`note.threadId NOT IN (${ mutedQuery.getQuery() })`); })); diff --git a/packages/backend/src/server/api/common/generate-muted-user-query.ts b/packages/backend/src/server/api/common/generate-muted-user-query.ts index 470ece1a627c..0571096f411a 100644 --- a/packages/backend/src/server/api/common/generate-muted-user-query.ts +++ b/packages/backend/src/server/api/common/generate-muted-user-query.ts @@ -1,6 +1,7 @@ -import { SelectQueryBuilder, Brackets } from 'typeorm'; -import { User } from '@/models/entities/user.js'; +import { Brackets } from 'typeorm'; +import type { User } from '@/models/entities/user.js'; import { Mutings, UserProfiles } from '@/models/index.js'; +import type { SelectQueryBuilder } from 'typeorm'; export function generateMutedUserQuery(q: SelectQueryBuilder, me: { id: User['id'] }, exclude?: User) { const mutingQuery = Mutings.createQueryBuilder('muting') diff --git a/packages/backend/src/server/api/common/generate-replies-query.ts b/packages/backend/src/server/api/common/generate-replies-query.ts index 301782eab982..46651d88e1af 100644 --- a/packages/backend/src/server/api/common/generate-replies-query.ts +++ b/packages/backend/src/server/api/common/generate-replies-query.ts @@ -1,25 +1,26 @@ -import { User } from '@/models/entities/user.js'; -import { Brackets, SelectQueryBuilder } from 'typeorm'; +import { Brackets } from 'typeorm'; +import type { User } from '@/models/entities/user.js'; +import type { SelectQueryBuilder } from 'typeorm'; export function generateRepliesQuery(q: SelectQueryBuilder, me?: Pick | null) { if (me == null) { q.andWhere(new Brackets(qb => { qb - .where(`note.replyId IS NULL`) // 返信ではない + .where('note.replyId IS NULL') // 返信ではない .orWhere(new Brackets(qb => { qb // 返信だけど投稿者自身への返信 - .where(`note.replyId IS NOT NULL`) + .where('note.replyId IS NOT NULL') .andWhere('note.replyUserId = note.userId'); })); })); } else if (!me.showTimelineReplies) { q.andWhere(new Brackets(qb => { qb - .where(`note.replyId IS NULL`) // 返信ではない + .where('note.replyId IS NULL') // 返信ではない .orWhere('note.replyUserId = :meId', { meId: me.id }) // 返信だけど自分のノートへの返信 .orWhere(new Brackets(qb => { qb // 返信だけど自分の行った返信 - .where(`note.replyId IS NOT NULL`) + .where('note.replyId IS NOT NULL') .andWhere('note.userId = :meId', { meId: me.id }); })) .orWhere(new Brackets(qb => { qb // 返信だけど投稿者自身への返信 - .where(`note.replyId IS NOT NULL`) + .where('note.replyId IS NOT NULL') .andWhere('note.replyUserId = note.userId'); })); })); diff --git a/packages/backend/src/server/api/common/generate-visibility-query.ts b/packages/backend/src/server/api/common/generate-visibility-query.ts index b50b6812f487..bcecbd790ab3 100644 --- a/packages/backend/src/server/api/common/generate-visibility-query.ts +++ b/packages/backend/src/server/api/common/generate-visibility-query.ts @@ -1,13 +1,14 @@ -import { User } from '@/models/entities/user.js'; +import { Brackets } from 'typeorm'; +import type { User } from '@/models/entities/user.js'; import { Followings } from '@/models/index.js'; -import { Brackets, SelectQueryBuilder } from 'typeorm'; +import type { SelectQueryBuilder } from 'typeorm'; export function generateVisibilityQuery(q: SelectQueryBuilder, me?: { id: User['id'] } | null) { // This code must always be synchronized with the checks in Notes.isVisibleForMe. if (me == null) { q.andWhere(new Brackets(qb => { qb - .where(`note.visibility = 'public'`) - .orWhere(`note.visibility = 'home'`); + .where('note.visibility = \'public\'') + .orWhere('note.visibility = \'home\''); })); } else { const followingQuery = Followings.createQueryBuilder('following') @@ -17,8 +18,8 @@ export function generateVisibilityQuery(q: SelectQueryBuilder, me?: { id: U q.andWhere(new Brackets(qb => { qb // 公開投稿である .where(new Brackets(qb => { qb - .where(`note.visibility = 'public'`) - .orWhere(`note.visibility = 'home'`); + .where('note.visibility = \'public\'') + .orWhere('note.visibility = \'home\''); })) // または 自分自身 .orWhere('note.userId = :meId') @@ -27,7 +28,7 @@ export function generateVisibilityQuery(q: SelectQueryBuilder, me?: { id: U .orWhere(':meId = ANY(note.mentions)') .orWhere(new Brackets(qb => { qb // または フォロワー宛ての投稿であり、 - .where(`note.visibility = 'followers'`) + .where('note.visibility = \'followers\'') .andWhere(new Brackets(qb => { qb // 自分がフォロワーである .where(`note.userId IN (${ followingQuery.getQuery() })`) diff --git a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts index 483a21add35c..da6248dafc5f 100644 --- a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts +++ b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts @@ -4,7 +4,7 @@ import { publishInternalEvent, publishMainStream, publishUserEvent } from '@/ser import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; import { UserProfiles } from '@/models/index.js'; -import generateUserToken from '../../common/generate-native-user-token.js'; +import generateUserToken from '@/misc/generate-native-user-token.js'; export const meta = { requireCredential: true, diff --git a/packages/backend/src/services/CreateSystemUserService.ts b/packages/backend/src/services/CreateSystemUserService.ts index f0fe5e1e3db1..52694e691b92 100644 --- a/packages/backend/src/services/CreateSystemUserService.ts +++ b/packages/backend/src/services/CreateSystemUserService.ts @@ -2,16 +2,15 @@ import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; import { v4 as uuid } from 'uuid'; -import { IsNull } from 'typeorm'; +import { IsNull , DataSource } from 'typeorm'; import { genRsaKeyPair } from '@/misc/gen-key-pair.js'; import { User } from '@/models/entities/user.js'; import { UserProfile } from '@/models/entities/user-profile.js'; -import type { IdService } from '@/services/IdService.js'; +import { IdService } from '@/services/IdService.js'; import { UserKeypair } from '@/models/entities/user-keypair.js'; import { UsedUsername } from '@/models/entities/used-username.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import generateNativeUserToken from '../server/api/common/generate-native-user-token.js'; -import type { DataSource } from 'typeorm'; +import generateNativeUserToken from '@/misc/generate-native-user-token.js'; @Injectable() export class CreateSystemUserService { From 5cc87c2e56ddf3e7383f586b8a30350c1da72f28 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 14 Sep 2022 23:31:39 +0900 Subject: [PATCH 090/180] wip --- .../src/server/ActivityPubServerService.ts | 5 +- .../server/api/common/generate-block-query.ts | 43 --- .../api/common/generate-channel-query.ts | 25 -- .../api/common/generate-muted-note-query.ts | 13 - .../generate-muted-note-thread-query.ts | 18 -- .../api/common/generate-muted-user-query.ts | 58 ---- .../api/common/generate-replies-query.ts | 28 -- .../api/common/generate-visibility-query.ts | 43 --- .../api/common/make-pagination-query.ts | 28 -- .../api/endpoints/admin/abuse-user-reports.ts | 5 +- .../src/server/api/endpoints/admin/ad/list.ts | 5 +- .../api/endpoints/admin/announcements/list.ts | 5 +- .../server/api/endpoints/admin/drive/files.ts | 6 +- .../api/endpoints/admin/emoji/list-remote.ts | 5 +- .../server/api/endpoints/admin/emoji/list.ts | 5 +- .../endpoints/admin/show-moderation-logs.ts | 6 +- .../src/server/api/endpoints/announcements.ts | 5 +- .../server/api/endpoints/antennas/notes.ts | 14 +- .../src/server/api/endpoints/blocking/list.ts | 12 +- .../server/api/endpoints/channels/followed.ts | 12 +- .../server/api/endpoints/channels/owned.ts | 12 +- .../server/api/endpoints/channels/timeline.ts | 5 +- .../src/server/api/endpoints/clips/notes.ts | 14 +- .../src/server/api/endpoints/drive/files.ts | 5 +- .../src/server/api/endpoints/drive/folders.ts | 5 +- .../src/server/api/endpoints/drive/stream.ts | 5 +- .../api/endpoints/federation/followers.ts | 12 +- .../api/endpoints/federation/following.ts | 12 +- .../server/api/endpoints/federation/users.ts | 6 +- .../src/server/api/endpoints/gallery/posts.ts | 8 +- .../src/server/api/endpoints/i/favorites.ts | 5 +- .../server/api/endpoints/i/gallery/likes.ts | 5 +- .../server/api/endpoints/i/gallery/posts.ts | 5 +- .../server/api/endpoints/i/notifications.ts | 6 +- .../src/server/api/endpoints/i/page-likes.ts | 5 +- .../src/server/api/endpoints/i/pages.ts | 5 +- .../server/api/endpoints/i/signin-history.ts | 5 +- .../api/endpoints/i/user-group-invites.ts | 5 +- .../api/endpoints/messaging/messages.ts | 8 +- .../src/server/api/endpoints/mute/list.ts | 6 +- .../backend/src/server/api/endpoints/notes.ts | 6 +- .../server/api/endpoints/notes/children.ts | 14 +- .../server/api/endpoints/notes/featured.ts | 8 +- .../api/endpoints/notes/global-timeline.ts | 17 +- .../api/endpoints/notes/hybrid-timeline.ts | 23 +- .../api/endpoints/notes/local-timeline.ts | 23 +- .../server/api/endpoints/notes/mentions.ts | 17 +- .../src/server/api/endpoints/notes/renotes.ts | 14 +- .../src/server/api/endpoints/notes/replies.ts | 14 +- .../api/endpoints/notes/search-by-tag.ts | 15 +- .../src/server/api/endpoints/notes/search.ts | 15 +- .../server/api/endpoints/notes/timeline.ts | 23 +- .../api/endpoints/notes/user-list-timeline.ts | 8 +- .../backend/src/server/api/endpoints/users.ts | 9 +- .../src/server/api/endpoints/users/clips.ts | 5 +- .../server/api/endpoints/users/followers.ts | 9 +- .../server/api/endpoints/users/following.ts | 9 +- .../api/endpoints/users/gallery/posts.ts | 5 +- .../src/server/api/endpoints/users/notes.ts | 15 +- .../src/server/api/endpoints/users/pages.ts | 5 +- .../server/api/endpoints/users/reactions.ts | 9 +- .../api/endpoints/users/recommendation.ts | 14 +- packages/backend/src/services/QueryService.ts | 263 ++++++++++++++++++ 63 files changed, 524 insertions(+), 496 deletions(-) delete mode 100644 packages/backend/src/server/api/common/generate-block-query.ts delete mode 100644 packages/backend/src/server/api/common/generate-channel-query.ts delete mode 100644 packages/backend/src/server/api/common/generate-muted-note-query.ts delete mode 100644 packages/backend/src/server/api/common/generate-muted-note-thread-query.ts delete mode 100644 packages/backend/src/server/api/common/generate-muted-user-query.ts delete mode 100644 packages/backend/src/server/api/common/generate-replies-query.ts delete mode 100644 packages/backend/src/server/api/common/generate-visibility-query.ts delete mode 100644 packages/backend/src/server/api/common/make-pagination-query.ts create mode 100644 packages/backend/src/services/QueryService.ts diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index f05c6fa48b2c..389cae6a68ce 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -16,7 +16,7 @@ import { UserKeypairStoreService } from '@/services/UserKeypairStoreService.js'; import type { Following } from '@/models/entities/following.js'; import { countIf } from '@/prelude/array.js'; import type { Note } from '@/models/entities/note.js'; -import { makePaginationQuery } from './api/common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService'; import type { FindOptionsWhere } from 'typeorm'; const ACTIVITY_JSON = 'application/activity+json; charset=utf-8'; @@ -52,6 +52,7 @@ export class ActivityPubServerService { private apRendererService: ApRendererService, private queueService: QueueService, private userKeypairStoreService: UserKeypairStoreService, + private queryService: QueryService, ) { } @@ -329,7 +330,7 @@ export class ActivityPubServerService { const partOf = `${this.config.url}/users/${userId}/outbox`; if (page) { - const query = makePaginationQuery(this.notesRepository.createQueryBuilder('note'), sinceId, untilId) + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), sinceId, untilId) .andWhere('note.userId = :userId', { userId: user.id }) .andWhere(new Brackets(qb => { qb .where('note.visibility = \'public\'') diff --git a/packages/backend/src/server/api/common/generate-block-query.ts b/packages/backend/src/server/api/common/generate-block-query.ts deleted file mode 100644 index d0dfe101120a..000000000000 --- a/packages/backend/src/server/api/common/generate-block-query.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Brackets } from 'typeorm'; -import type { User } from '@/models/entities/user.js'; -import { Blockings } from '@/models/index.js'; -import type { SelectQueryBuilder } from 'typeorm'; - -// ここでいうBlockedは被Blockedの意 -export function generateBlockedUserQuery(q: SelectQueryBuilder, me: { id: User['id'] }) { - const blockingQuery = Blockings.createQueryBuilder('blocking') - .select('blocking.blockerId') - .where('blocking.blockeeId = :blockeeId', { blockeeId: me.id }); - - // 投稿の作者にブロックされていない かつ - // 投稿の返信先の作者にブロックされていない かつ - // 投稿の引用元の作者にブロックされていない - q - .andWhere(`note.userId NOT IN (${ blockingQuery.getQuery() })`) - .andWhere(new Brackets(qb => { qb - .where('note.replyUserId IS NULL') - .orWhere(`note.replyUserId NOT IN (${ blockingQuery.getQuery() })`); - })) - .andWhere(new Brackets(qb => { qb - .where('note.renoteUserId IS NULL') - .orWhere(`note.renoteUserId NOT IN (${ blockingQuery.getQuery() })`); - })); - - q.setParameters(blockingQuery.getParameters()); -} - -export function generateBlockQueryForUsers(q: SelectQueryBuilder, me: { id: User['id'] }) { - const blockingQuery = Blockings.createQueryBuilder('blocking') - .select('blocking.blockeeId') - .where('blocking.blockerId = :blockerId', { blockerId: me.id }); - - const blockedQuery = Blockings.createQueryBuilder('blocking') - .select('blocking.blockerId') - .where('blocking.blockeeId = :blockeeId', { blockeeId: me.id }); - - q.andWhere(`user.id NOT IN (${ blockingQuery.getQuery() })`); - q.setParameters(blockingQuery.getParameters()); - - q.andWhere(`user.id NOT IN (${ blockedQuery.getQuery() })`); - q.setParameters(blockedQuery.getParameters()); -} diff --git a/packages/backend/src/server/api/common/generate-channel-query.ts b/packages/backend/src/server/api/common/generate-channel-query.ts deleted file mode 100644 index 5374249533db..000000000000 --- a/packages/backend/src/server/api/common/generate-channel-query.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Brackets } from 'typeorm'; -import type { User } from '@/models/entities/user.js'; -import { ChannelFollowings } from '@/models/index.js'; -import type { SelectQueryBuilder } from 'typeorm'; - -export function generateChannelQuery(q: SelectQueryBuilder, me?: { id: User['id'] } | null) { - if (me == null) { - q.andWhere('note.channelId IS NULL'); - } else { - q.leftJoinAndSelect('note.channel', 'channel'); - - const channelFollowingQuery = ChannelFollowings.createQueryBuilder('channelFollowing') - .select('channelFollowing.followeeId') - .where('channelFollowing.followerId = :followerId', { followerId: me.id }); - - q.andWhere(new Brackets(qb => { qb - // チャンネルのノートではない - .where('note.channelId IS NULL') - // または自分がフォローしているチャンネルのノート - .orWhere(`note.channelId IN (${ channelFollowingQuery.getQuery() })`); - })); - - q.setParameters(channelFollowingQuery.getParameters()); - } -} diff --git a/packages/backend/src/server/api/common/generate-muted-note-query.ts b/packages/backend/src/server/api/common/generate-muted-note-query.ts deleted file mode 100644 index a4cc6c6c0d27..000000000000 --- a/packages/backend/src/server/api/common/generate-muted-note-query.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { User } from '@/models/entities/user.js'; -import { MutedNotes } from '@/models/index.js'; -import type { SelectQueryBuilder } from 'typeorm'; - -export function generateMutedNoteQuery(q: SelectQueryBuilder, me: { id: User['id'] }) { - const mutedQuery = MutedNotes.createQueryBuilder('muted') - .select('muted.noteId') - .where('muted.userId = :userId', { userId: me.id }); - - q.andWhere(`note.id NOT IN (${ mutedQuery.getQuery() })`); - - q.setParameters(mutedQuery.getParameters()); -} diff --git a/packages/backend/src/server/api/common/generate-muted-note-thread-query.ts b/packages/backend/src/server/api/common/generate-muted-note-thread-query.ts deleted file mode 100644 index 5411b90907e6..000000000000 --- a/packages/backend/src/server/api/common/generate-muted-note-thread-query.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Brackets } from 'typeorm'; -import type { User } from '@/models/entities/user.js'; -import { NoteThreadMutings } from '@/models/index.js'; -import type { SelectQueryBuilder } from 'typeorm'; - -export function generateMutedNoteThreadQuery(q: SelectQueryBuilder, me: { id: User['id'] }) { - const mutedQuery = NoteThreadMutings.createQueryBuilder('threadMuted') - .select('threadMuted.threadId') - .where('threadMuted.userId = :userId', { userId: me.id }); - - q.andWhere(`note.id NOT IN (${ mutedQuery.getQuery() })`); - q.andWhere(new Brackets(qb => { qb - .where('note.threadId IS NULL') - .orWhere(`note.threadId NOT IN (${ mutedQuery.getQuery() })`); - })); - - q.setParameters(mutedQuery.getParameters()); -} diff --git a/packages/backend/src/server/api/common/generate-muted-user-query.ts b/packages/backend/src/server/api/common/generate-muted-user-query.ts deleted file mode 100644 index 0571096f411a..000000000000 --- a/packages/backend/src/server/api/common/generate-muted-user-query.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Brackets } from 'typeorm'; -import type { User } from '@/models/entities/user.js'; -import { Mutings, UserProfiles } from '@/models/index.js'; -import type { SelectQueryBuilder } from 'typeorm'; - -export function generateMutedUserQuery(q: SelectQueryBuilder, me: { id: User['id'] }, exclude?: User) { - const mutingQuery = Mutings.createQueryBuilder('muting') - .select('muting.muteeId') - .where('muting.muterId = :muterId', { muterId: me.id }); - - if (exclude) { - mutingQuery.andWhere('muting.muteeId != :excludeId', { excludeId: exclude.id }); - } - - const mutingInstanceQuery = UserProfiles.createQueryBuilder('user_profile') - .select('user_profile.mutedInstances') - .where('user_profile.userId = :muterId', { muterId: me.id }); - - // 投稿の作者をミュートしていない かつ - // 投稿の返信先の作者をミュートしていない かつ - // 投稿の引用元の作者をミュートしていない - q - .andWhere(`note.userId NOT IN (${ mutingQuery.getQuery() })`) - .andWhere(new Brackets(qb => { qb - .where('note.replyUserId IS NULL') - .orWhere(`note.replyUserId NOT IN (${ mutingQuery.getQuery() })`); - })) - .andWhere(new Brackets(qb => { qb - .where('note.renoteUserId IS NULL') - .orWhere(`note.renoteUserId NOT IN (${ mutingQuery.getQuery() })`); - })) - // mute instances - .andWhere(new Brackets(qb => { qb - .andWhere('note.userHost IS NULL') - .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.userHost)`); - })) - .andWhere(new Brackets(qb => { qb - .where('note.replyUserHost IS NULL') - .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.replyUserHost)`); - })) - .andWhere(new Brackets(qb => { qb - .where('note.renoteUserHost IS NULL') - .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.renoteUserHost)`); - })); - - q.setParameters(mutingQuery.getParameters()); - q.setParameters(mutingInstanceQuery.getParameters()); -} - -export function generateMutedUserQueryForUsers(q: SelectQueryBuilder, me: { id: User['id'] }) { - const mutingQuery = Mutings.createQueryBuilder('muting') - .select('muting.muteeId') - .where('muting.muterId = :muterId', { muterId: me.id }); - - q.andWhere(`user.id NOT IN (${ mutingQuery.getQuery() })`); - - q.setParameters(mutingQuery.getParameters()); -} diff --git a/packages/backend/src/server/api/common/generate-replies-query.ts b/packages/backend/src/server/api/common/generate-replies-query.ts deleted file mode 100644 index 46651d88e1af..000000000000 --- a/packages/backend/src/server/api/common/generate-replies-query.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Brackets } from 'typeorm'; -import type { User } from '@/models/entities/user.js'; -import type { SelectQueryBuilder } from 'typeorm'; - -export function generateRepliesQuery(q: SelectQueryBuilder, me?: Pick | null) { - if (me == null) { - q.andWhere(new Brackets(qb => { qb - .where('note.replyId IS NULL') // 返信ではない - .orWhere(new Brackets(qb => { qb // 返信だけど投稿者自身への返信 - .where('note.replyId IS NOT NULL') - .andWhere('note.replyUserId = note.userId'); - })); - })); - } else if (!me.showTimelineReplies) { - q.andWhere(new Brackets(qb => { qb - .where('note.replyId IS NULL') // 返信ではない - .orWhere('note.replyUserId = :meId', { meId: me.id }) // 返信だけど自分のノートへの返信 - .orWhere(new Brackets(qb => { qb // 返信だけど自分の行った返信 - .where('note.replyId IS NOT NULL') - .andWhere('note.userId = :meId', { meId: me.id }); - })) - .orWhere(new Brackets(qb => { qb // 返信だけど投稿者自身への返信 - .where('note.replyId IS NOT NULL') - .andWhere('note.replyUserId = note.userId'); - })); - })); - } -} diff --git a/packages/backend/src/server/api/common/generate-visibility-query.ts b/packages/backend/src/server/api/common/generate-visibility-query.ts deleted file mode 100644 index bcecbd790ab3..000000000000 --- a/packages/backend/src/server/api/common/generate-visibility-query.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Brackets } from 'typeorm'; -import type { User } from '@/models/entities/user.js'; -import { Followings } from '@/models/index.js'; -import type { SelectQueryBuilder } from 'typeorm'; - -export function generateVisibilityQuery(q: SelectQueryBuilder, me?: { id: User['id'] } | null) { - // This code must always be synchronized with the checks in Notes.isVisibleForMe. - if (me == null) { - q.andWhere(new Brackets(qb => { qb - .where('note.visibility = \'public\'') - .orWhere('note.visibility = \'home\''); - })); - } else { - const followingQuery = Followings.createQueryBuilder('following') - .select('following.followeeId') - .where('following.followerId = :meId'); - - q.andWhere(new Brackets(qb => { qb - // 公開投稿である - .where(new Brackets(qb => { qb - .where('note.visibility = \'public\'') - .orWhere('note.visibility = \'home\''); - })) - // または 自分自身 - .orWhere('note.userId = :meId') - // または 自分宛て - .orWhere(':meId = ANY(note.visibleUserIds)') - .orWhere(':meId = ANY(note.mentions)') - .orWhere(new Brackets(qb => { qb - // または フォロワー宛ての投稿であり、 - .where('note.visibility = \'followers\'') - .andWhere(new Brackets(qb => { qb - // 自分がフォロワーである - .where(`note.userId IN (${ followingQuery.getQuery() })`) - // または 自分の投稿へのリプライ - .orWhere('note.replyUserId = :meId'); - })); - })); - })); - - q.setParameters({ meId: me.id }); - } -} diff --git a/packages/backend/src/server/api/common/make-pagination-query.ts b/packages/backend/src/server/api/common/make-pagination-query.ts deleted file mode 100644 index 51c11e5dff36..000000000000 --- a/packages/backend/src/server/api/common/make-pagination-query.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { SelectQueryBuilder } from 'typeorm'; - -export function makePaginationQuery(q: SelectQueryBuilder, sinceId?: string, untilId?: string, sinceDate?: number, untilDate?: number) { - if (sinceId && untilId) { - q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: sinceId }); - q.andWhere(`${q.alias}.id < :untilId`, { untilId: untilId }); - q.orderBy(`${q.alias}.id`, 'DESC'); - } else if (sinceId) { - q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: sinceId }); - q.orderBy(`${q.alias}.id`, 'ASC'); - } else if (untilId) { - q.andWhere(`${q.alias}.id < :untilId`, { untilId: untilId }); - q.orderBy(`${q.alias}.id`, 'DESC'); - } else if (sinceDate && untilDate) { - q.andWhere(`${q.alias}.createdAt > :sinceDate`, { sinceDate: new Date(sinceDate) }); - q.andWhere(`${q.alias}.createdAt < :untilDate`, { untilDate: new Date(untilDate) }); - q.orderBy(`${q.alias}.createdAt`, 'DESC'); - } else if (sinceDate) { - q.andWhere(`${q.alias}.createdAt > :sinceDate`, { sinceDate: new Date(sinceDate) }); - q.orderBy(`${q.alias}.createdAt`, 'ASC'); - } else if (untilDate) { - q.andWhere(`${q.alias}.createdAt < :untilDate`, { untilDate: new Date(untilDate) }); - q.orderBy(`${q.alias}.createdAt`, 'DESC'); - } else { - q.orderBy(`${q.alias}.id`, 'DESC'); - } - return q; -} diff --git a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts index 38e0160efb47..0fe0454ea522 100644 --- a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts +++ b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { AbuseUserReports } from '@/models/index.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['admin'], @@ -89,9 +89,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(AbuseUserReports.createQueryBuilder('report'), ps.sinceId, ps.untilId); + const query = this.queryService.makePaginationQuery(AbuseUserReports.createQueryBuilder('report'), ps.sinceId, ps.untilId); switch (ps.state) { case 'resolved': query.andWhere('report.resolved = TRUE'); break; diff --git a/packages/backend/src/server/api/endpoints/admin/ad/list.ts b/packages/backend/src/server/api/endpoints/admin/ad/list.ts index 10813b7a1410..7078bddfd443 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/list.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Ads } from '@/models/index.js'; -import { makePaginationQuery } from '../../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['admin'], @@ -24,9 +24,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Ads.createQueryBuilder('ad'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(Ads.createQueryBuilder('ad'), ps.sinceId, ps.untilId) .andWhere('ad.expiresAt > :now', { now: new Date() }); const ads = await query.take(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts index 92543926f9ac..64099c42c3c8 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Announcements, AnnouncementReads } from '@/models/index.js'; import type { Announcement } from '@/models/entities/announcement.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { makePaginationQuery } from '../../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['admin'], @@ -68,9 +68,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Announcements.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); + const query = this.queryService.makePaginationQuery(Announcements.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); const announcements = await query.take(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/admin/drive/files.ts b/packages/backend/src/server/api/endpoints/admin/drive/files.ts index 6fd0209c4073..3158149c461d 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/files.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DriveFiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { makePaginationQuery } from '../../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['admin'], @@ -48,9 +48,11 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId); + const query = this.queryService.makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId); if (ps.userId) { query.andWhere('file.userId = :userId', { userId: ps.userId }); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts index d9771896abb4..b73fafeb5876 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; import { toPuny } from '@/misc/convert-host.js'; -import { makePaginationQuery } from '../../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['admin'], @@ -73,9 +73,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const q = makePaginationQuery(Emojis.createQueryBuilder('emoji'), ps.sinceId, ps.untilId); + const q = this.queryService.makePaginationQuery(Emojis.createQueryBuilder('emoji'), ps.sinceId, ps.untilId); if (ps.host == null) { q.andWhere('emoji.host IS NOT NULL'); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts index 5758b709d93e..685bad1bda94 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Emojis } from '@/models/index.js'; import type { Emoji } from '@/models/entities/emoji.js'; -import { makePaginationQuery } from '../../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['admin'], @@ -67,9 +67,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const q = makePaginationQuery(Emojis.createQueryBuilder('emoji'), ps.sinceId, ps.untilId) + const q = this.queryService.makePaginationQuery(Emojis.createQueryBuilder('emoji'), ps.sinceId, ps.untilId) .andWhere('emoji.host IS NULL'); let emojis: Emoji[]; diff --git a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts index c9fa9e535af2..de3e2197f69e 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ModerationLogs } from '@/models/index.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['admin'], @@ -63,10 +63,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(ModerationLogs.createQueryBuilder('report'), ps.sinceId, ps.untilId); + const query = this.queryService.makePaginationQuery(ModerationLogs.createQueryBuilder('report'), ps.sinceId, ps.untilId); const reports = await query.take(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/announcements.ts b/packages/backend/src/server/api/endpoints/announcements.ts index bf26baeeb372..680bbbceb733 100644 --- a/packages/backend/src/server/api/endpoints/announcements.ts +++ b/packages/backend/src/server/api/endpoints/announcements.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Announcements, AnnouncementReads } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { makePaginationQuery } from '../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['meta'], @@ -67,9 +67,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Announcements.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); + const query = this.queryService.makePaginationQuery(Announcements.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); const announcements = await query.take(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index 49a125fe7190..f514d20589c8 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -2,11 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import readNote from '@/services/note/read.js'; import { Antennas, Notes, AntennaNotes } from '@/models/index.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; -import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; +import { QueryService } from '@/services/QueryService.js'; import { ApiError } from '../../error.js'; -import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; export const meta = { tags: ['antennas', 'account', 'notes'], @@ -51,6 +48,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { const antenna = await Antennas.findOneBy({ @@ -62,7 +60,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchAntenna); } - const query = makePaginationQuery(Notes.createQueryBuilder('note'), + const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .innerJoin(AntennaNotes.metadata.targetName, 'antennaNote', 'antennaNote.noteId = note.id') .innerJoinAndSelect('note.user', 'user') @@ -78,9 +76,9 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') .andWhere('antennaNote.antennaId = :antennaId', { antennaId: antenna.id }); - generateVisibilityQuery(query, me); - generateMutedUserQuery(query, me); - generateBlockedUserQuery(query, me); + this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateMutedUserQuery(query, me); + this.queryService.generateBlockedUserQuery(query, me); const notes = await query .take(ps.limit) diff --git a/packages/backend/src/server/api/endpoints/blocking/list.ts b/packages/backend/src/server/api/endpoints/blocking/list.ts index 996aafe63a42..ef141b40f18c 100644 --- a/packages/backend/src/server/api/endpoints/blocking/list.ts +++ b/packages/backend/src/server/api/endpoints/blocking/list.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Blockings } from '@/models/index.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['account'], @@ -40,14 +40,16 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Blockings.createQueryBuilder('blocking'), ps.sinceId, ps.untilId) - .andWhere('blocking.blockerId = :meId', { meId: me.id }); + const query = this.queryService.makePaginationQuery(Blockings.createQueryBuilder('blocking'), ps.sinceId, ps.untilId) + .andWhere('blocking.blockerId = :meId', { meId: me.id }); const blockings = await query - .take(ps.limit) - .getMany(); + .take(ps.limit) + .getMany(); return await Blockings.packMany(blockings, me); }); diff --git a/packages/backend/src/server/api/endpoints/channels/followed.ts b/packages/backend/src/server/api/endpoints/channels/followed.ts index 8b06409e63f4..53e09736ffcb 100644 --- a/packages/backend/src/server/api/endpoints/channels/followed.ts +++ b/packages/backend/src/server/api/endpoints/channels/followed.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Channels, ChannelFollowings } from '@/models/index.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['channels', 'account'], @@ -40,14 +40,16 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(ChannelFollowings.createQueryBuilder(), ps.sinceId, ps.untilId) - .andWhere({ followerId: me.id }); + const query = this.queryService.makePaginationQuery(ChannelFollowings.createQueryBuilder(), ps.sinceId, ps.untilId) + .andWhere({ followerId: me.id }); const followings = await query - .take(ps.limit) - .getMany(); + .take(ps.limit) + .getMany(); return await Promise.all(followings.map(x => Channels.pack(x.followeeId, me))); }); diff --git a/packages/backend/src/server/api/endpoints/channels/owned.ts b/packages/backend/src/server/api/endpoints/channels/owned.ts index f33b1bd42bc3..0198a8d0781c 100644 --- a/packages/backend/src/server/api/endpoints/channels/owned.ts +++ b/packages/backend/src/server/api/endpoints/channels/owned.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Channels } from '@/models/index.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['channels', 'account'], @@ -40,14 +40,16 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Channels.createQueryBuilder(), ps.sinceId, ps.untilId) - .andWhere({ userId: me.id }); + const query = this.queryService.makePaginationQuery(Channels.createQueryBuilder(), ps.sinceId, ps.untilId) + .andWhere({ userId: me.id }); const channels = await query - .take(ps.limit) - .getMany(); + .take(ps.limit) + .getMany(); return await Promise.all(channels.map(x => Channels.pack(x, me))); }); diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index d5edb9e7791a..50d2eb463b60 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -2,8 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Notes, Channels } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; +import { QueryService } from '@/services/QueryService.js'; import { ApiError } from '../../error.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; export const meta = { tags: ['notes', 'channels'], @@ -46,6 +46,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { const channel = await Channels.findOneBy({ @@ -57,7 +58,7 @@ export default class extends Endpoint { } //#region Construct query - const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.channelId = :channelId', { channelId: channel.id }) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts index 300d8a0b5f25..260afb37ed1e 100644 --- a/packages/backend/src/server/api/endpoints/clips/notes.ts +++ b/packages/backend/src/server/api/endpoints/clips/notes.ts @@ -1,11 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ClipNotes, Clips, Notes } from '@/models/index.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; -import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; +import { QueryService } from '@/services/QueryService.js'; import { ApiError } from '../../error.js'; -import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; export const meta = { tags: ['account', 'notes', 'clips'], @@ -48,6 +45,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { const clip = await Clips.findOneBy({ @@ -62,7 +60,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchClip); } - const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .innerJoin(ClipNotes.metadata.targetName, 'clipNote', 'clipNote.noteId = note.id') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') @@ -78,9 +76,9 @@ export default class extends Endpoint { .andWhere('clipNote.clipId = :clipId', { clipId: clip.id }); if (me) { - generateVisibilityQuery(query, me); - generateMutedUserQuery(query, me); - generateBlockedUserQuery(query, me); + this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateMutedUserQuery(query, me); + this.queryService.generateBlockedUserQuery(query, me); } const notes = await query diff --git a/packages/backend/src/server/api/endpoints/drive/files.ts b/packages/backend/src/server/api/endpoints/drive/files.ts index 093363085806..57ce52202acc 100644 --- a/packages/backend/src/server/api/endpoints/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/drive/files.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFiles } from '@/models/index.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['drive'], @@ -37,9 +37,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId) .andWhere('file.userId = :userId', { userId: me.id }); if (ps.folderId) { diff --git a/packages/backend/src/server/api/endpoints/drive/folders.ts b/packages/backend/src/server/api/endpoints/drive/folders.ts index b3c8950592b9..1ef7deec1fb1 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFolders } from '@/models/index.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['drive'], @@ -36,9 +36,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(DriveFolders.createQueryBuilder('folder'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(DriveFolders.createQueryBuilder('folder'), ps.sinceId, ps.untilId) .andWhere('folder.userId = :userId', { userId: me.id }); if (ps.folderId) { diff --git a/packages/backend/src/server/api/endpoints/drive/stream.ts b/packages/backend/src/server/api/endpoints/drive/stream.ts index c41b707b22e2..4176d7d84e17 100644 --- a/packages/backend/src/server/api/endpoints/drive/stream.ts +++ b/packages/backend/src/server/api/endpoints/drive/stream.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFiles } from '@/models/index.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['drive'], @@ -36,9 +36,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId) .andWhere('file.userId = :userId', { userId: me.id }); if (ps.type) { diff --git a/packages/backend/src/server/api/endpoints/federation/followers.ts b/packages/backend/src/server/api/endpoints/federation/followers.ts index 591e29089d4c..66a3a331967c 100644 --- a/packages/backend/src/server/api/endpoints/federation/followers.ts +++ b/packages/backend/src/server/api/endpoints/federation/followers.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Followings } from '@/models/index.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['federation'], @@ -39,14 +39,16 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) - .andWhere('following.followeeHost = :host', { host: ps.host }); + const query = this.queryService.makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) + .andWhere('following.followeeHost = :host', { host: ps.host }); const followings = await query - .take(ps.limit) - .getMany(); + .take(ps.limit) + .getMany(); return await Followings.packMany(followings, me, { populateFollowee: true }); }); diff --git a/packages/backend/src/server/api/endpoints/federation/following.ts b/packages/backend/src/server/api/endpoints/federation/following.ts index f7ffa38a62a9..52664d666012 100644 --- a/packages/backend/src/server/api/endpoints/federation/following.ts +++ b/packages/backend/src/server/api/endpoints/federation/following.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Followings } from '@/models/index.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['federation'], @@ -39,14 +39,16 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) - .andWhere('following.followerHost = :host', { host: ps.host }); + const query = this.queryService.makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) + .andWhere('following.followerHost = :host', { host: ps.host }); const followings = await query - .take(ps.limit) - .getMany(); + .take(ps.limit) + .getMany(); return await Followings.packMany(followings, me, { populateFollowee: true }); }); diff --git a/packages/backend/src/server/api/endpoints/federation/users.ts b/packages/backend/src/server/api/endpoints/federation/users.ts index 2e315c91f09d..aad7ed89392e 100644 --- a/packages/backend/src/server/api/endpoints/federation/users.ts +++ b/packages/backend/src/server/api/endpoints/federation/users.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['federation'], @@ -36,9 +36,11 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(this.usersRepository.createQueryBuilder('user'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.usersRepository.createQueryBuilder('user'), ps.sinceId, ps.untilId) .andWhere('user.host = :host', { host: ps.host }); const users = await query diff --git a/packages/backend/src/server/api/endpoints/gallery/posts.ts b/packages/backend/src/server/api/endpoints/gallery/posts.ts index 6c7a78de3641..90331412956c 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GalleryPosts } from '@/models/index.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['gallery'], @@ -36,10 +36,12 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) - .innerJoinAndSelect('post.user', 'user'); + const query = this.queryService.makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) + .innerJoinAndSelect('post.user', 'user'); const posts = await query.take(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/i/favorites.ts b/packages/backend/src/server/api/endpoints/i/favorites.ts index a44086516106..411eb1f84422 100644 --- a/packages/backend/src/server/api/endpoints/i/favorites.ts +++ b/packages/backend/src/server/api/endpoints/i/favorites.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteFavorites } from '@/models/index.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['account', 'notes', 'favorites'], @@ -35,9 +35,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(NoteFavorites.createQueryBuilder('favorite'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(NoteFavorites.createQueryBuilder('favorite'), ps.sinceId, ps.untilId) .andWhere('favorite.userId = :meId', { meId: me.id }) .leftJoinAndSelect('favorite.note', 'note'); diff --git a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts index e602d56c5cf1..22924c7a4f5a 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GalleryLikes } from '@/models/index.js'; -import { makePaginationQuery } from '../../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['account', 'gallery'], @@ -46,9 +46,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(GalleryLikes.createQueryBuilder('like'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(GalleryLikes.createQueryBuilder('like'), ps.sinceId, ps.untilId) .andWhere('like.userId = :meId', { meId: me.id }) .leftJoinAndSelect('like.post', 'post'); diff --git a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts index f74268a54b8d..b21e604f7597 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GalleryPosts } from '@/models/index.js'; -import { makePaginationQuery } from '../../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['account', 'gallery'], @@ -35,9 +35,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) .andWhere('post.userId = :meId', { meId: me.id }); const posts = await query diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index b4328e79dc1b..3a38cbaa5d5f 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -5,8 +5,8 @@ import { Notifications, Followings, Mutings, UserProfiles } from '@/models/index import { notificationTypes } from '@/types.js'; import read from '@/services/note/read.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { QueryService } from '@/services/QueryService.js'; import { readNotification } from '../../common/read-notification.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; export const meta = { tags: ['account', 'notifications'], @@ -56,6 +56,8 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { // includeTypes が空の場合はクエリしない @@ -82,7 +84,7 @@ export default class extends Endpoint { .select('users.id') .where('users.isSuspended = TRUE'); - const query = makePaginationQuery(Notifications.createQueryBuilder('notification'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(Notifications.createQueryBuilder('notification'), ps.sinceId, ps.untilId) .andWhere('notification.notifieeId = :meId', { meId: me.id }) .leftJoinAndSelect('notification.notifier', 'notifier') .leftJoinAndSelect('notification.note', 'note') diff --git a/packages/backend/src/server/api/endpoints/i/page-likes.ts b/packages/backend/src/server/api/endpoints/i/page-likes.ts index e2f1469c969c..7c1d4b0a235c 100644 --- a/packages/backend/src/server/api/endpoints/i/page-likes.ts +++ b/packages/backend/src/server/api/endpoints/i/page-likes.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { PageLikes } from '@/models/index.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['account', 'pages'], @@ -45,9 +45,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(PageLikes.createQueryBuilder('like'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(PageLikes.createQueryBuilder('like'), ps.sinceId, ps.untilId) .andWhere('like.userId = :meId', { meId: me.id }) .leftJoinAndSelect('like.page', 'page'); diff --git a/packages/backend/src/server/api/endpoints/i/pages.ts b/packages/backend/src/server/api/endpoints/i/pages.ts index 9dd008eea447..8d65d32e5c0c 100644 --- a/packages/backend/src/server/api/endpoints/i/pages.ts +++ b/packages/backend/src/server/api/endpoints/i/pages.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Pages } from '@/models/index.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['account', 'pages'], @@ -35,9 +35,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Pages.createQueryBuilder('page'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(Pages.createQueryBuilder('page'), ps.sinceId, ps.untilId) .andWhere('page.userId = :meId', { meId: me.id }); const pages = await query diff --git a/packages/backend/src/server/api/endpoints/i/signin-history.ts b/packages/backend/src/server/api/endpoints/i/signin-history.ts index 5b34969f1cb5..8deb32c100b0 100644 --- a/packages/backend/src/server/api/endpoints/i/signin-history.ts +++ b/packages/backend/src/server/api/endpoints/i/signin-history.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Signins } from '@/models/index.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { requireCredential: true, @@ -23,9 +23,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Signins.createQueryBuilder('signin'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(Signins.createQueryBuilder('signin'), ps.sinceId, ps.untilId) .andWhere('signin.userId = :meId', { meId: me.id }); const history = await query.take(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/i/user-group-invites.ts b/packages/backend/src/server/api/endpoints/i/user-group-invites.ts index 4502e63e21f3..ea5c731cf217 100644 --- a/packages/backend/src/server/api/endpoints/i/user-group-invites.ts +++ b/packages/backend/src/server/api/endpoints/i/user-group-invites.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserGroupInvitations } from '@/models/index.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['account', 'groups'], @@ -46,9 +46,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(UserGroupInvitations.createQueryBuilder('invitation'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(UserGroupInvitations.createQueryBuilder('invitation'), ps.sinceId, ps.untilId) .andWhere('invitation.userId = :meId', { meId: me.id }) .leftJoinAndSelect('invitation.userGroup', 'user_group'); diff --git a/packages/backend/src/server/api/endpoints/messaging/messages.ts b/packages/backend/src/server/api/endpoints/messaging/messages.ts index fcef633ce23a..c1cc05990bbd 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages.ts @@ -3,9 +3,9 @@ import { Brackets } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; import { MessagingMessages, UserGroups, UserGroupJoinings } from '@/models/index.js'; +import { QueryService } from '@/services/QueryService.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { readUserMessagingMessage, readGroupMessagingMessage, deliverReadActivity } from '../../common/read-messaging-message.js'; export const meta = { @@ -76,6 +76,8 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { if (ps.userId != null) { @@ -85,7 +87,7 @@ export default class extends Endpoint { throw e; }); - const query = makePaginationQuery(MessagingMessages.createQueryBuilder('message'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(MessagingMessages.createQueryBuilder('message'), ps.sinceId, ps.untilId) .andWhere(new Brackets(qb => { qb .where(new Brackets(qb => { qb .where('message.userId = :meId') @@ -132,7 +134,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.groupAccessDenied); } - const query = makePaginationQuery(MessagingMessages.createQueryBuilder('message'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(MessagingMessages.createQueryBuilder('message'), ps.sinceId, ps.untilId) .andWhere('message.groupId = :groupId', { groupId: recipientGroup.id }); const messages = await query.take(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/mute/list.ts b/packages/backend/src/server/api/endpoints/mute/list.ts index 8e7a21f30d2a..c7c6849af967 100644 --- a/packages/backend/src/server/api/endpoints/mute/list.ts +++ b/packages/backend/src/server/api/endpoints/mute/list.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Mutings } from '@/models/index.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['account'], @@ -40,9 +40,11 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Mutings.createQueryBuilder('muting'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(Mutings.createQueryBuilder('muting'), ps.sinceId, ps.untilId) .andWhere('muting.muterId = :meId', { meId: me.id }); const mutings = await query diff --git a/packages/backend/src/server/api/endpoints/notes.ts b/packages/backend/src/server/api/endpoints/notes.ts index a71b4c3e1d2b..8b1390c93dc6 100644 --- a/packages/backend/src/server/api/endpoints/notes.ts +++ b/packages/backend/src/server/api/endpoints/notes.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { makePaginationQuery } from '../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['notes'], @@ -38,9 +38,11 @@ export default class extends Endpoint { constructor( @Inject('notesRepository') private notesRepository: typeof Notes, + + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere('note.visibility = \'public\'') .andWhere('note.localOnly = FALSE') .innerJoinAndSelect('note.user', 'user') diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index a8b4a0794bc6..b00f093bc349 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -2,10 +2,7 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; -import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; -import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['notes'], @@ -38,9 +35,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(new Brackets(qb => { qb .where('note.replyId = :noteId', { noteId: ps.noteId }) .orWhere(new Brackets(qb => { qb @@ -64,10 +62,10 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateVisibilityQuery(query, me); + this.queryService.generateVisibilityQuery(query, me); if (me) { - generateMutedUserQuery(query, me); - generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQuery(query, me); + this.queryService.generateBlockedUserQuery(query, me); } const notes = await query.take(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index f767266d997f..81ab8e626c37 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -1,8 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; -import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['notes'], @@ -33,6 +32,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { const max = 30; @@ -56,8 +56,8 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - if (me) generateMutedUserQuery(query, me); - if (me) generateBlockedUserQuery(query, me); + if (me) this.queryService.generateMutedUserQuery(query, me); + if (me) this.queryService.generateBlockedUserQuery(query, me); let notes = await query .orderBy('note.score', 'DESC') diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index d4a8a4eefff9..8e4435dc3a08 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -3,12 +3,8 @@ import { fetchMeta } from '@/misc/fetch-meta.js'; import { Notes } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { QueryService } from '@/services/QueryService.js'; import { ApiError } from '../../error.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; -import { generateRepliesQuery } from '../../common/generate-replies-query.js'; -import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js'; -import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; export const meta = { tags: ['notes'], @@ -53,6 +49,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { const m = await fetchMeta(); @@ -63,7 +60,7 @@ export default class extends Endpoint { } //#region Construct query - const query = makePaginationQuery(Notes.createQueryBuilder('note'), + const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.visibility = \'public\'') .andWhere('note.channelId IS NULL') @@ -79,11 +76,11 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateRepliesQuery(query, me); + this.queryService.generateRepliesQuery(query, me); if (me) { - generateMutedUserQuery(query, me); - generateMutedNoteQuery(query, me); - generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQuery(query, me); + this.queryService.generateMutedNoteQuery(query, me); + this.queryService.generateBlockedUserQuery(query, me); } if (ps.withFiles) { diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index deff4135be5c..49b2a0d8aaf3 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -4,14 +4,8 @@ import { fetchMeta } from '@/misc/fetch-meta.js'; import { Followings, Notes } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { QueryService } from '@/services/QueryService.js'; import { ApiError } from '../../error.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; -import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; -import { generateRepliesQuery } from '../../common/generate-replies-query.js'; -import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js'; -import { generateChannelQuery } from '../../common/generate-channel-query.js'; -import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; export const meta = { tags: ['notes'], @@ -61,6 +55,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { const m = await fetchMeta(); @@ -73,7 +68,7 @@ export default class extends Endpoint { .select('following.followeeId') .where('following.followerId = :followerId', { followerId: me.id }); - const query = makePaginationQuery(Notes.createQueryBuilder('note'), + const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere(new Brackets(qb => { qb.where(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: me.id }) @@ -92,12 +87,12 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') .setParameters(followingQuery.getParameters()); - generateChannelQuery(query, me); - generateRepliesQuery(query, me); - generateVisibilityQuery(query, me); - generateMutedUserQuery(query, me); - generateMutedNoteQuery(query, me); - generateBlockedUserQuery(query, me); + this.queryService.generateChannelQuery(query, me); + this.queryService.generateRepliesQuery(query, me); + this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateMutedUserQuery(query, me); + this.queryService.generateMutedNoteQuery(query, me); + this.queryService.generateBlockedUserQuery(query, me); if (ps.includeMyRenotes === false) { query.andWhere(new Brackets(qb => { diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 0b945e1599a5..8bd7c3459d52 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -5,14 +5,8 @@ import type { Users } from '@/models/index.js'; import { Notes } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { QueryService } from '@/services/QueryService.js'; import { ApiError } from '../../error.js'; -import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; -import { generateRepliesQuery } from '../../common/generate-replies-query.js'; -import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js'; -import { generateChannelQuery } from '../../common/generate-channel-query.js'; -import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; export const meta = { tags: ['notes'], @@ -61,6 +55,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { const m = await fetchMeta(); @@ -71,7 +66,7 @@ export default class extends Endpoint { } //#region Construct query - const query = makePaginationQuery(Notes.createQueryBuilder('note'), + const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)') .innerJoinAndSelect('note.user', 'user') @@ -86,12 +81,12 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateChannelQuery(query, me); - generateRepliesQuery(query, me); - generateVisibilityQuery(query, me); - if (me) generateMutedUserQuery(query, me); - if (me) generateMutedNoteQuery(query, me); - if (me) generateBlockedUserQuery(query, me); + this.queryService.generateChannelQuery(query, me); + this.queryService.generateRepliesQuery(query, me); + this.queryService.generateVisibilityQuery(query, me); + if (me) this.queryService.generateMutedUserQuery(query, me); + if (me) this.queryService.generateMutedNoteQuery(query, me); + if (me) this.queryService.generateBlockedUserQuery(query, me); if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index e100e7df72e6..8e7e7abb8417 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -3,11 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import read from '@/services/note/read.js'; import { Notes, Followings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; -import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; -import { generateMutedNoteThreadQuery } from '../../common/generate-muted-note-thread-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['notes'], @@ -41,13 +37,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { const followingQuery = Followings.createQueryBuilder('following') .select('following.followeeId') .where('following.followerId = :followerId', { followerId: me.id }); - const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(new Brackets(qb => { qb .where(`'{"${me.id}"}' <@ note.mentions`) .orWhere(`'{"${me.id}"}' <@ note.visibleUserIds`); @@ -64,10 +61,10 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateVisibilityQuery(query, me); - generateMutedUserQuery(query, me); - generateMutedNoteThreadQuery(query, me); - generateBlockedUserQuery(query, me); + this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateMutedUserQuery(query, me); + this.queryService.generateMutedNoteThreadQuery(query, me); + this.queryService.generateBlockedUserQuery(query, me); if (ps.visibility) { query.andWhere('note.visibility = :visibility', { visibility: ps.visibility }); diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index 4e787a9d58a8..0c81118b01f3 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -1,12 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { QueryService } from '@/services/QueryService.js'; import { getNote } from '../../common/getters.js'; import { ApiError } from '../../error.js'; -import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; -import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; export const meta = { tags: ['notes'], @@ -47,6 +44,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { @@ -54,7 +52,7 @@ export default class extends Endpoint { throw e; }); - const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere('note.renoteId = :renoteId', { renoteId: note.id }) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') @@ -68,9 +66,9 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateVisibilityQuery(query, me); - if (me) generateMutedUserQuery(query, me); - if (me) generateBlockedUserQuery(query, me); + this.queryService.generateVisibilityQuery(query, me); + if (me) this.queryService.generateMutedUserQuery(query, me); + if (me) this.queryService.generateBlockedUserQuery(query, me); const renotes = await query.take(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index 38ff5852886e..8ebca2c88fdf 100644 --- a/packages/backend/src/server/api/endpoints/notes/replies.ts +++ b/packages/backend/src/server/api/endpoints/notes/replies.ts @@ -1,10 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; -import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; -import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['notes'], @@ -37,9 +34,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere('note.replyId = :replyId', { replyId: ps.noteId }) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') @@ -53,9 +51,9 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateVisibilityQuery(query, me); - if (me) generateMutedUserQuery(query, me); - if (me) generateBlockedUserQuery(query, me); + this.queryService.generateVisibilityQuery(query, me); + if (me) this.queryService.generateMutedUserQuery(query, me); + if (me) this.queryService.generateBlockedUserQuery(query, me); const timeline = await query.take(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts index 78657ca65545..47d0ef585faf 100644 --- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts +++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts @@ -4,10 +4,7 @@ import { Notes } from '@/models/index.js'; import { safeForSql } from '@/misc/safe-for-sql.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; -import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; -import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['notes', 'hashtags'], @@ -75,9 +72,11 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') .leftJoinAndSelect('user.banner', 'banner') @@ -90,9 +89,9 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateVisibilityQuery(query, me); - if (me) generateMutedUserQuery(query, me); - if (me) generateBlockedUserQuery(query, me); + this.queryService.generateVisibilityQuery(query, me); + if (me) this.queryService.generateMutedUserQuery(query, me); + if (me) this.queryService.generateBlockedUserQuery(query, me); try { if (ps.tag) { diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index 5f53594c0f88..83bcd7b38344 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -3,11 +3,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Notes } from '@/models/index.js'; import config from '@/config/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { QueryService } from '@/services/QueryService.js'; import es from '../../../../db/elasticsearch.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; -import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; -import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; export const meta = { tags: ['notes'], @@ -56,10 +53,12 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { if (es == null) { - const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId); + const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId); if (ps.userId) { query.andWhere('note.userId = :userId', { userId: ps.userId }); @@ -81,9 +80,9 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateVisibilityQuery(query, me); - if (me) generateMutedUserQuery(query, me); - if (me) generateBlockedUserQuery(query, me); + this.queryService.generateVisibilityQuery(query, me); + if (me) this.queryService.generateMutedUserQuery(query, me); + if (me) this.queryService.generateBlockedUserQuery(query, me); const notes = await query.take(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 9732c9302afd..bc3088738d56 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -3,13 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Notes, Followings } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; -import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; -import { generateRepliesQuery } from '../../common/generate-replies-query.js'; -import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js'; -import { generateChannelQuery } from '../../common/generate-channel-query.js'; -import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['notes'], @@ -51,6 +45,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { const hasFollowing = (await Followings.count({ @@ -65,7 +60,7 @@ export default class extends Endpoint { .select('following.followeeId') .where('following.followerId = :followerId', { followerId: me.id }); - const query = makePaginationQuery(Notes.createQueryBuilder('note'), + const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere(new Brackets(qb => { qb .where('note.userId = :meId', { meId: me.id }); @@ -84,12 +79,12 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') .setParameters(followingQuery.getParameters()); - generateChannelQuery(query, me); - generateRepliesQuery(query, me); - generateVisibilityQuery(query, me); - generateMutedUserQuery(query, me); - generateMutedNoteQuery(query, me); - generateBlockedUserQuery(query, me); + this.queryService.generateChannelQuery(query, me); + this.queryService.generateRepliesQuery(query, me); + this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateMutedUserQuery(query, me); + this.queryService.generateMutedNoteQuery(query, me); + this.queryService.generateBlockedUserQuery(query, me); if (ps.includeMyRenotes === false) { query.andWhere(new Brackets(qb => { diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 524635bc2e22..b76b3c9a9cc4 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -3,9 +3,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { UserLists, UserListJoinings, Notes } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { QueryService } from '@/services/QueryService.js'; import { ApiError } from '../../error.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; export const meta = { tags: ['notes', 'lists'], @@ -56,6 +55,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { const list = await UserLists.findOneBy({ @@ -68,7 +68,7 @@ export default class extends Endpoint { } //#region Construct query - const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .innerJoin(UserListJoinings.metadata.targetName, 'userListJoining', 'userListJoining.userId = note.userId') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') @@ -83,7 +83,7 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner') .andWhere('userListJoining.userListId = :userListId', { userListId: list.id }); - generateVisibilityQuery(query, me); + this.queryService.generateVisibilityQuery(query, me); if (ps.includeMyRenotes === false) { query.andWhere(new Brackets(qb => { diff --git a/packages/backend/src/server/api/endpoints/users.ts b/packages/backend/src/server/api/endpoints/users.ts index fb40916370fb..eb407555a11c 100644 --- a/packages/backend/src/server/api/endpoints/users.ts +++ b/packages/backend/src/server/api/endpoints/users.ts @@ -1,8 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { generateMutedUserQueryForUsers } from '../common/generate-muted-user-query.js'; -import { generateBlockQueryForUsers } from '../common/generate-block-query.js'; +import { QueryService } from '@/services/QueryService'; export const meta = { tags: ['users'], @@ -47,6 +46,8 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { const query = this.usersRepository.createQueryBuilder('user'); @@ -78,8 +79,8 @@ export default class extends Endpoint { default: query.orderBy('user.id', 'ASC'); break; } - if (me) generateMutedUserQueryForUsers(query, me); - if (me) generateBlockQueryForUsers(query, me); + if (me) this.queryService.generateMutedUserQueryForUsers(query, me); + if (me) this.queryService.generateBlockQueryForUsers(query, me); query.take(ps.limit); query.skip(ps.offset); diff --git a/packages/backend/src/server/api/endpoints/users/clips.ts b/packages/backend/src/server/api/endpoints/users/clips.ts index 2d5bba6c45ba..a3e71e96cc94 100644 --- a/packages/backend/src/server/api/endpoints/users/clips.ts +++ b/packages/backend/src/server/api/endpoints/users/clips.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Clips } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['users', 'clips'], @@ -34,9 +34,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Clips.createQueryBuilder('clip'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(Clips.createQueryBuilder('clip'), ps.sinceId, ps.untilId) .andWhere('clip.userId = :userId', { userId: ps.userId }) .andWhere('clip.isPublic = true'); diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index fe6649514641..e4a3f3d9fee9 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -1,10 +1,11 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { Users, Followings, UserProfiles } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { Followings, UserProfiles } from '@/models/index.js'; import { toPunyNullable } from '@/misc/convert-host.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { QueryService } from '@/services/QueryService.js'; import { ApiError } from '../../error.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; export const meta = { tags: ['users'], @@ -75,6 +76,8 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy(ps.userId != null @@ -105,7 +108,7 @@ export default class extends Endpoint { } } - const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) .andWhere('following.followeeId = :userId', { userId: user.id }) .innerJoinAndSelect('following.follower', 'follower'); diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index fb8ff20d6478..73ff2a927d9b 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -1,10 +1,11 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { Users, Followings, UserProfiles } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { Followings, UserProfiles } from '@/models/index.js'; import { toPunyNullable } from '@/misc/convert-host.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { QueryService } from '@/services/QueryService.js'; import { ApiError } from '../../error.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; export const meta = { tags: ['users'], @@ -75,6 +76,8 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy(ps.userId != null @@ -105,7 +108,7 @@ export default class extends Endpoint { } } - const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) .andWhere('following.followerId = :userId', { userId: user.id }) .innerJoinAndSelect('following.followee', 'followee'); diff --git a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts index 4e5a9225308a..824398b1bb59 100644 --- a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GalleryPosts } from '@/models/index.js'; -import { makePaginationQuery } from '../../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['users', 'gallery'], @@ -34,9 +34,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) .andWhere('post.userId = :userId', { userId: ps.userId }); const posts = await query diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 14e8a4712624..4c28664ca4dd 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -2,12 +2,9 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { QueryService } from '@/services/QueryService.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; -import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; -import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; export const meta = { tags: ['users', 'notes'], @@ -62,6 +59,8 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { // Lookup user @@ -71,7 +70,7 @@ export default class extends Endpoint { }); //#region Construct query - const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.userId = :userId', { userId: user.id }) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') @@ -85,10 +84,10 @@ export default class extends Endpoint { .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - generateVisibilityQuery(query, me); + this.queryService.generateVisibilityQuery(query, me); if (me) { - generateMutedUserQuery(query, me, user); - generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQuery(query, me, user); + this.queryService.generateBlockedUserQuery(query, me); } if (ps.withFiles) { diff --git a/packages/backend/src/server/api/endpoints/users/pages.ts b/packages/backend/src/server/api/endpoints/users/pages.ts index 6179289d923a..bfbe57a5aa95 100644 --- a/packages/backend/src/server/api/endpoints/users/pages.ts +++ b/packages/backend/src/server/api/endpoints/users/pages.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Pages } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['users', 'pages'], @@ -34,9 +34,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery(Pages.createQueryBuilder('page'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(Pages.createQueryBuilder('page'), ps.sinceId, ps.untilId) .andWhere('page.userId = :userId', { userId: ps.userId }) .andWhere('page.visibility = \'public\''); diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts index 58cddba551c8..2e6ad8c8fdf7 100644 --- a/packages/backend/src/server/api/endpoints/users/reactions.ts +++ b/packages/backend/src/server/api/endpoints/users/reactions.ts @@ -1,8 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { NoteReactions, UserProfiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; +import { QueryService } from '@/services/QueryService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -53,6 +52,8 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { const profile = await UserProfiles.findOneByOrFail({ userId: ps.userId }); @@ -61,12 +62,12 @@ export default class extends Endpoint { throw new ApiError(meta.errors.reactionsNotPublic); } - const query = makePaginationQuery(NoteReactions.createQueryBuilder('reaction'), + const query = this.queryService.makePaginationQuery(NoteReactions.createQueryBuilder('reaction'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('reaction.userId = :userId', { userId: ps.userId }) .leftJoinAndSelect('reaction.note', 'note'); - generateVisibilityQuery(query, me); + this.queryService.generateVisibilityQuery(query, me); const reactions = await query .take(ps.limit) diff --git a/packages/backend/src/server/api/endpoints/users/recommendation.ts b/packages/backend/src/server/api/endpoints/users/recommendation.ts index 7f571ca8178e..fb23764e2333 100644 --- a/packages/backend/src/server/api/endpoints/users/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/users/recommendation.ts @@ -1,9 +1,9 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import { Users, Followings } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { Followings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { generateMutedUserQueryForUsers } from '../../common/generate-muted-user-query.js'; -import { generateBlockedUserQuery, generateBlockQueryForUsers } from '../../common/generate-block-query.js'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['users'], @@ -43,6 +43,8 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { const query = this.usersRepository.createQueryBuilder('user') @@ -53,9 +55,9 @@ export default class extends Endpoint { .andWhere('user.id != :meId', { meId: me.id }) .orderBy('user.followersCount', 'DESC'); - generateMutedUserQueryForUsers(query, me); - generateBlockQueryForUsers(query, me); - generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserQueryForUsers(query, me); + this.queryService.generateBlockQueryForUsers(query, me); + this.queryService.generateBlockedUserQuery(query, me); const followingQuery = Followings.createQueryBuilder('following') .select('following.followeeId') diff --git a/packages/backend/src/services/QueryService.ts b/packages/backend/src/services/QueryService.ts new file mode 100644 index 000000000000..b5f1b06cfa81 --- /dev/null +++ b/packages/backend/src/services/QueryService.ts @@ -0,0 +1,263 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Brackets } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { NoteThreadMutings , Blockings , ChannelFollowings , MutedNotes , Followings , Mutings , UserProfiles } from '@/models/index.js'; +import type { User } from '@/models/entities/user.js'; +import type { SelectQueryBuilder } from 'typeorm'; + +@Injectable() +export class QueryService { + constructor( + @Inject('usersRepository') + private userProfilesRepository: typeof UserProfiles, + + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + @Inject('channelFollowingsRepository') + private channelFollowingsRepository: typeof ChannelFollowings, + + @Inject('mutedNotesRepository') + private mutedNotesRepository: typeof MutedNotes, + + @Inject('blockingsRepository') + private blockingsRepository: typeof Blockings, + + @Inject('noteThreadMutingsRepository') + private noteThreadMutingsRepository: typeof NoteThreadMutings, + + @Inject('mutingsRepository') + private mutingsRepository: typeof Mutings, + ) { + } + + public makePaginationQuery(q: SelectQueryBuilder, sinceId?: string, untilId?: string, sinceDate?: number, untilDate?: number): SelectQueryBuilder { + if (sinceId && untilId) { + q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: sinceId }); + q.andWhere(`${q.alias}.id < :untilId`, { untilId: untilId }); + q.orderBy(`${q.alias}.id`, 'DESC'); + } else if (sinceId) { + q.andWhere(`${q.alias}.id > :sinceId`, { sinceId: sinceId }); + q.orderBy(`${q.alias}.id`, 'ASC'); + } else if (untilId) { + q.andWhere(`${q.alias}.id < :untilId`, { untilId: untilId }); + q.orderBy(`${q.alias}.id`, 'DESC'); + } else if (sinceDate && untilDate) { + q.andWhere(`${q.alias}.createdAt > :sinceDate`, { sinceDate: new Date(sinceDate) }); + q.andWhere(`${q.alias}.createdAt < :untilDate`, { untilDate: new Date(untilDate) }); + q.orderBy(`${q.alias}.createdAt`, 'DESC'); + } else if (sinceDate) { + q.andWhere(`${q.alias}.createdAt > :sinceDate`, { sinceDate: new Date(sinceDate) }); + q.orderBy(`${q.alias}.createdAt`, 'ASC'); + } else if (untilDate) { + q.andWhere(`${q.alias}.createdAt < :untilDate`, { untilDate: new Date(untilDate) }); + q.orderBy(`${q.alias}.createdAt`, 'DESC'); + } else { + q.orderBy(`${q.alias}.id`, 'DESC'); + } + return q; + } + + // ここでいうBlockedは被Blockedの意 + public generateBlockedUserQuery(q: SelectQueryBuilder, me: { id: User['id'] }): void { + const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking') + .select('blocking.blockerId') + .where('blocking.blockeeId = :blockeeId', { blockeeId: me.id }); + + // 投稿の作者にブロックされていない かつ + // 投稿の返信先の作者にブロックされていない かつ + // 投稿の引用元の作者にブロックされていない + q + .andWhere(`note.userId NOT IN (${ blockingQuery.getQuery() })`) + .andWhere(new Brackets(qb => { qb + .where('note.replyUserId IS NULL') + .orWhere(`note.replyUserId NOT IN (${ blockingQuery.getQuery() })`); + })) + .andWhere(new Brackets(qb => { qb + .where('note.renoteUserId IS NULL') + .orWhere(`note.renoteUserId NOT IN (${ blockingQuery.getQuery() })`); + })); + + q.setParameters(blockingQuery.getParameters()); + } + + public generateBlockQueryForUsers(q: SelectQueryBuilder, me: { id: User['id'] }): void { + const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking') + .select('blocking.blockeeId') + .where('blocking.blockerId = :blockerId', { blockerId: me.id }); + + const blockedQuery = this.blockingsRepository.createQueryBuilder('blocking') + .select('blocking.blockerId') + .where('blocking.blockeeId = :blockeeId', { blockeeId: me.id }); + + q.andWhere(`user.id NOT IN (${ blockingQuery.getQuery() })`); + q.setParameters(blockingQuery.getParameters()); + + q.andWhere(`user.id NOT IN (${ blockedQuery.getQuery() })`); + q.setParameters(blockedQuery.getParameters()); + } + + public generateChannelQuery(q: SelectQueryBuilder, me?: { id: User['id'] } | null): void { + if (me == null) { + q.andWhere('note.channelId IS NULL'); + } else { + q.leftJoinAndSelect('note.channel', 'channel'); + + const channelFollowingQuery = this.channelFollowingsRepository.createQueryBuilder('channelFollowing') + .select('channelFollowing.followeeId') + .where('channelFollowing.followerId = :followerId', { followerId: me.id }); + + q.andWhere(new Brackets(qb => { qb + // チャンネルのノートではない + .where('note.channelId IS NULL') + // または自分がフォローしているチャンネルのノート + .orWhere(`note.channelId IN (${ channelFollowingQuery.getQuery() })`); + })); + + q.setParameters(channelFollowingQuery.getParameters()); + } + } + + public generateMutedNoteQuery(q: SelectQueryBuilder, me: { id: User['id'] }): void { + const mutedQuery = this.mutedNotesRepository.createQueryBuilder('muted') + .select('muted.noteId') + .where('muted.userId = :userId', { userId: me.id }); + + q.andWhere(`note.id NOT IN (${ mutedQuery.getQuery() })`); + + q.setParameters(mutedQuery.getParameters()); + } + + public generateMutedNoteThreadQuery(q: SelectQueryBuilder, me: { id: User['id'] }): void { + const mutedQuery = this.noteThreadMutingsRepository.createQueryBuilder('threadMuted') + .select('threadMuted.threadId') + .where('threadMuted.userId = :userId', { userId: me.id }); + + q.andWhere(`note.id NOT IN (${ mutedQuery.getQuery() })`); + q.andWhere(new Brackets(qb => { qb + .where('note.threadId IS NULL') + .orWhere(`note.threadId NOT IN (${ mutedQuery.getQuery() })`); + })); + + q.setParameters(mutedQuery.getParameters()); + } + + public generateMutedUserQuery(q: SelectQueryBuilder, me: { id: User['id'] }, exclude?: User): void { + const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') + .select('muting.muteeId') + .where('muting.muterId = :muterId', { muterId: me.id }); + + if (exclude) { + mutingQuery.andWhere('muting.muteeId != :excludeId', { excludeId: exclude.id }); + } + + const mutingInstanceQuery = this.userProfilesRepository.createQueryBuilder('user_profile') + .select('user_profile.mutedInstances') + .where('user_profile.userId = :muterId', { muterId: me.id }); + + // 投稿の作者をミュートしていない かつ + // 投稿の返信先の作者をミュートしていない かつ + // 投稿の引用元の作者をミュートしていない + q + .andWhere(`note.userId NOT IN (${ mutingQuery.getQuery() })`) + .andWhere(new Brackets(qb => { qb + .where('note.replyUserId IS NULL') + .orWhere(`note.replyUserId NOT IN (${ mutingQuery.getQuery() })`); + })) + .andWhere(new Brackets(qb => { qb + .where('note.renoteUserId IS NULL') + .orWhere(`note.renoteUserId NOT IN (${ mutingQuery.getQuery() })`); + })) + // mute instances + .andWhere(new Brackets(qb => { qb + .andWhere('note.userHost IS NULL') + .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.userHost)`); + })) + .andWhere(new Brackets(qb => { qb + .where('note.replyUserHost IS NULL') + .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.replyUserHost)`); + })) + .andWhere(new Brackets(qb => { qb + .where('note.renoteUserHost IS NULL') + .orWhere(`NOT ((${ mutingInstanceQuery.getQuery() })::jsonb ? note.renoteUserHost)`); + })); + + q.setParameters(mutingQuery.getParameters()); + q.setParameters(mutingInstanceQuery.getParameters()); + } + + public generateMutedUserQueryForUsers(q: SelectQueryBuilder, me: { id: User['id'] }): void { + const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') + .select('muting.muteeId') + .where('muting.muterId = :muterId', { muterId: me.id }); + + q.andWhere(`user.id NOT IN (${ mutingQuery.getQuery() })`); + + q.setParameters(mutingQuery.getParameters()); + } + + public generateRepliesQuery(q: SelectQueryBuilder, me?: Pick | null): void { + if (me == null) { + q.andWhere(new Brackets(qb => { qb + .where('note.replyId IS NULL') // 返信ではない + .orWhere(new Brackets(qb => { qb // 返信だけど投稿者自身への返信 + .where('note.replyId IS NOT NULL') + .andWhere('note.replyUserId = note.userId'); + })); + })); + } else if (!me.showTimelineReplies) { + q.andWhere(new Brackets(qb => { qb + .where('note.replyId IS NULL') // 返信ではない + .orWhere('note.replyUserId = :meId', { meId: me.id }) // 返信だけど自分のノートへの返信 + .orWhere(new Brackets(qb => { qb // 返信だけど自分の行った返信 + .where('note.replyId IS NOT NULL') + .andWhere('note.userId = :meId', { meId: me.id }); + })) + .orWhere(new Brackets(qb => { qb // 返信だけど投稿者自身への返信 + .where('note.replyId IS NOT NULL') + .andWhere('note.replyUserId = note.userId'); + })); + })); + } + } + + public generateVisibilityQuery(q: SelectQueryBuilder, me?: { id: User['id'] } | null): void { + // This code must always be synchronized with the checks in Notes.isVisibleForMe. + if (me == null) { + q.andWhere(new Brackets(qb => { qb + .where('note.visibility = \'public\'') + .orWhere('note.visibility = \'home\''); + })); + } else { + const followingQuery = this.followingsRepository.createQueryBuilder('following') + .select('following.followeeId') + .where('following.followerId = :meId'); + + q.andWhere(new Brackets(qb => { qb + // 公開投稿である + .where(new Brackets(qb => { qb + .where('note.visibility = \'public\'') + .orWhere('note.visibility = \'home\''); + })) + // または 自分自身 + .orWhere('note.userId = :meId') + // または 自分宛て + .orWhere(':meId = ANY(note.visibleUserIds)') + .orWhere(':meId = ANY(note.mentions)') + .orWhere(new Brackets(qb => { qb + // または フォロワー宛ての投稿であり、 + .where('note.visibility = \'followers\'') + .andWhere(new Brackets(qb => { qb + // 自分がフォロワーである + .where(`note.userId IN (${ followingQuery.getQuery() })`) + // または 自分の投稿へのリプライ + .orWhere('note.replyUserId = :meId'); + })); + })); + })); + + q.setParameters({ meId: me.id }); + } + } +} + From 55e0d5397863752ba8753433e5ba931c2821cc8c Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 00:42:56 +0900 Subject: [PATCH 091/180] wip --- .../src/server/ActivityPubServerService.ts | 2 +- .../src/server/WellKnownServerService.ts | 2 +- .../server/api/StreamingApiServerService.ts | 2 +- .../api/endpoints/admin/accounts/create.ts | 8 ++-- .../api/endpoints/admin/accounts/delete.ts | 20 ++++++---- .../server/api/endpoints/admin/ad/create.ts | 4 +- .../server/api/endpoints/admin/ad/delete.ts | 2 +- .../server/api/endpoints/admin/ad/update.ts | 11 ++---- .../endpoints/admin/announcements/create.ts | 11 ++++-- .../endpoints/admin/announcements/delete.ts | 13 +++---- .../api/endpoints/admin/announcements/list.ts | 12 ++++-- .../endpoints/admin/announcements/update.ts | 13 +++---- .../api/endpoints/admin/delete-account.ts | 2 +- .../admin/delete-all-files-of-a-user.ts | 4 +- .../admin/drive-capacity-override.ts | 4 +- .../admin/drive/clean-remote-files.ts | 10 ++--- .../api/endpoints/admin/drive/cleanup.ts | 15 ++++---- .../server/api/endpoints/admin/drive/files.ts | 13 +++---- .../api/endpoints/admin/drive/show-file.ts | 11 ++---- .../endpoints/admin/emoji/add-aliases-bulk.ts | 20 ++++++---- .../server/api/endpoints/admin/emoji/add.ts | 37 ++++++++++++------- .../server/api/endpoints/admin/emoji/copy.ts | 37 +++++++++++-------- .../api/endpoints/admin/emoji/delete-bulk.ts | 29 ++++++++------- .../api/endpoints/admin/emoji/delete.ts | 27 ++++++++------ .../api/endpoints/admin/emoji/import-zip.ts | 6 +-- .../api/endpoints/admin/emoji/list-remote.ts | 9 +++-- .../server/api/endpoints/admin/emoji/list.ts | 13 ++++--- .../admin/emoji/remove-aliases-bulk.ts | 20 ++++++---- .../endpoints/admin/emoji/set-aliases-bulk.ts | 17 ++++++--- .../admin/emoji/set-category-bulk.ts | 18 ++++++--- .../api/endpoints/admin/emoji/update.ts | 18 ++++++--- .../admin/federation/delete-all-files.ts | 4 +- .../refresh-remote-instance-metadata.ts | 4 +- .../admin/federation/remove-all-following.ts | 4 +- .../admin/federation/update-instance.ts | 4 +- .../api/endpoints/admin/get-index-stats.ts | 4 +- .../api/endpoints/admin/get-table-stats.ts | 4 +- .../api/endpoints/admin/get-user-ips.ts | 4 +- .../src/server/api/endpoints/admin/meta.ts | 4 +- .../api/endpoints/admin/moderators/add.ts | 2 +- .../api/endpoints/admin/moderators/remove.ts | 2 +- .../server/api/endpoints/admin/queue/clear.ts | 4 +- .../api/endpoints/admin/reset-password.ts | 2 +- .../admin/resolve-abuse-user-report.ts | 4 +- .../server/api/endpoints/admin/server-info.ts | 4 +- .../server/api/endpoints/admin/show-user.ts | 4 +- .../server/api/endpoints/admin/show-users.ts | 4 +- .../api/endpoints/admin/silence-user.ts | 4 +- .../api/endpoints/admin/suspend-user.ts | 4 +- .../api/endpoints/admin/unsilence-user.ts | 4 +- .../api/endpoints/admin/unsuspend-user.ts | 4 +- .../server/api/endpoints/admin/update-meta.ts | 4 +- .../api/endpoints/admin/update-user-note.ts | 4 +- .../src/server/api/endpoints/admin/vacuum.ts | 4 +- .../src/server/api/endpoints/antennas/list.ts | 4 +- .../src/server/api/endpoints/antennas/show.ts | 4 +- .../src/server/api/endpoints/ap/show.ts | 4 +- .../src/server/api/endpoints/app/show.ts | 4 +- .../api/endpoints/auth/session/userkey.ts | 2 +- .../server/api/endpoints/blocking/create.ts | 2 +- .../server/api/endpoints/blocking/delete.ts | 2 +- .../src/server/api/endpoints/blocking/list.ts | 4 +- .../server/api/endpoints/channels/featured.ts | 4 +- .../server/api/endpoints/channels/followed.ts | 4 +- .../server/api/endpoints/channels/owned.ts | 4 +- .../src/server/api/endpoints/channels/show.ts | 4 +- .../server/api/endpoints/channels/update.ts | 4 +- .../src/server/api/endpoints/clips/list.ts | 4 +- .../src/server/api/endpoints/clips/show.ts | 4 +- .../src/server/api/endpoints/endpoints.ts | 4 +- .../api/endpoints/federation/followers.ts | 4 +- .../api/endpoints/federation/following.ts | 4 +- .../api/endpoints/federation/instances.ts | 4 +- .../api/endpoints/federation/show-instance.ts | 4 +- .../server/api/endpoints/federation/users.ts | 2 +- .../server/api/endpoints/following/create.ts | 2 +- .../server/api/endpoints/following/delete.ts | 2 +- .../api/endpoints/following/invalidate.ts | 2 +- .../endpoints/following/requests/cancel.ts | 2 +- .../server/api/endpoints/gallery/featured.ts | 4 +- .../server/api/endpoints/gallery/popular.ts | 4 +- .../src/server/api/endpoints/gallery/posts.ts | 4 +- .../api/endpoints/gallery/posts/show.ts | 4 +- .../api/endpoints/get-online-users-count.ts | 4 +- .../src/server/api/endpoints/hashtags/list.ts | 4 +- .../server/api/endpoints/hashtags/trend.ts | 4 +- .../server/api/endpoints/hashtags/users.ts | 4 +- .../backend/src/server/api/endpoints/i.ts | 4 +- .../server/api/endpoints/i/2fa/key-done.ts | 2 +- .../server/api/endpoints/i/2fa/remove-key.ts | 2 +- .../server/api/endpoints/i/delete-account.ts | 2 +- .../server/api/endpoints/i/notifications.ts | 2 +- .../backend/src/server/api/endpoints/i/pin.ts | 2 +- .../api/endpoints/i/read-announcement.ts | 2 +- .../api/endpoints/i/regenerate-token.ts | 2 +- .../src/server/api/endpoints/i/unpin.ts | 2 +- .../server/api/endpoints/i/update-email.ts | 2 +- .../src/server/api/endpoints/i/update.ts | 4 +- .../server/api/endpoints/i/webhooks/list.ts | 4 +- .../api/endpoints/messaging/messages.ts | 2 +- .../backend/src/server/api/endpoints/meta.ts | 4 +- .../src/server/api/endpoints/mute/list.ts | 4 +- .../backend/src/server/api/endpoints/notes.ts | 2 +- .../src/server/api/endpoints/notes/clips.ts | 4 +- .../src/server/api/endpoints/notes/create.ts | 2 +- .../src/server/api/endpoints/notes/delete.ts | 2 +- .../server/api/endpoints/notes/polls/vote.ts | 2 +- .../api/endpoints/notes/search-by-tag.ts | 4 +- .../src/server/api/endpoints/notes/search.ts | 4 +- .../server/api/endpoints/notes/unrenote.ts | 2 +- .../api/endpoints/notifications/create.ts | 4 +- .../src/server/api/endpoints/page-push.ts | 2 +- .../server/api/endpoints/pages/featured.ts | 4 +- .../src/server/api/endpoints/pages/show.ts | 2 +- .../backend/src/server/api/endpoints/ping.ts | 4 +- .../src/server/api/endpoints/pinned-users.ts | 4 +- .../api/endpoints/request-reset-password.ts | 2 +- .../src/server/api/endpoints/server-info.ts | 4 +- .../backend/src/server/api/endpoints/stats.ts | 4 +- .../backend/src/server/api/endpoints/test.ts | 4 +- .../api/endpoints/username/available.ts | 2 +- .../backend/src/server/api/endpoints/users.ts | 6 +-- .../server/api/endpoints/users/followers.ts | 4 +- .../server/api/endpoints/users/following.ts | 4 +- .../users/get-frequently-replied-users.ts | 4 +- .../api/endpoints/users/groups/invite.ts | 4 +- .../api/endpoints/users/groups/joined.ts | 4 +- .../api/endpoints/users/groups/leave.ts | 4 +- .../api/endpoints/users/groups/owned.ts | 4 +- .../server/api/endpoints/users/groups/pull.ts | 4 +- .../server/api/endpoints/users/groups/show.ts | 4 +- .../api/endpoints/users/groups/transfer.ts | 4 +- .../api/endpoints/users/groups/update.ts | 4 +- .../server/api/endpoints/users/lists/list.ts | 4 +- .../server/api/endpoints/users/lists/pull.ts | 4 +- .../server/api/endpoints/users/lists/push.ts | 4 +- .../server/api/endpoints/users/lists/show.ts | 4 +- .../src/server/api/endpoints/users/notes.ts | 4 +- .../server/api/endpoints/users/reactions.ts | 4 +- .../api/endpoints/users/recommendation.ts | 4 +- .../server/api/endpoints/users/relation.ts | 4 +- .../api/endpoints/users/report-abuse.ts | 4 +- .../users/search-by-username-and-host.ts | 4 +- .../src/server/api/endpoints/users/search.ts | 4 +- .../src/server/api/endpoints/users/show.ts | 4 +- .../src/server/api/endpoints/users/stats.ts | 4 +- .../services/FetchInstanceMetadataService.ts | 2 +- packages/backend/src/services/S3Service.ts | 2 +- .../src/services/chart/charts/active-users.ts | 4 +- .../src/services/chart/charts/ap-request.ts | 4 +- .../src/services/chart/charts/drive.ts | 5 +-- .../src/services/chart/charts/federation.ts | 4 +- .../src/services/chart/charts/hashtag.ts | 4 +- .../src/services/chart/charts/instance.ts | 4 +- .../src/services/chart/charts/notes.ts | 5 +-- .../services/chart/charts/per-user-drive.ts | 4 +- .../chart/charts/per-user-following.ts | 5 +-- .../services/chart/charts/per-user-notes.ts | 4 +- .../chart/charts/per-user-reactions.ts | 4 +- .../src/services/chart/charts/test-grouped.ts | 4 +- .../chart/charts/test-intersection.ts | 4 +- .../src/services/chart/charts/test-unique.ts | 4 +- .../backend/src/services/chart/charts/test.ts | 4 +- .../src/services/chart/charts/users.ts | 5 +-- .../remote/activitypub/ApMfmService.ts | 2 +- 165 files changed, 468 insertions(+), 418 deletions(-) diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 389cae6a68ce..dc2d1d8291cb 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -16,7 +16,7 @@ import { UserKeypairStoreService } from '@/services/UserKeypairStoreService.js'; import type { Following } from '@/models/entities/following.js'; import { countIf } from '@/prelude/array.js'; import type { Note } from '@/models/entities/note.js'; -import { QueryService } from '@/services/QueryService'; +import { QueryService } from '@/services/QueryService.js'; import type { FindOptionsWhere } from 'typeorm'; const ACTIVITY_JSON = 'application/activity+json; charset=utf-8'; diff --git a/packages/backend/src/server/WellKnownServerService.ts b/packages/backend/src/server/WellKnownServerService.ts index 11fa1441dc7f..b309916d4f7f 100644 --- a/packages/backend/src/server/WellKnownServerService.ts +++ b/packages/backend/src/server/WellKnownServerService.ts @@ -7,7 +7,7 @@ import { Config } from '@/config/types.js'; import { escapeAttribute, escapeValue } from '@/prelude/xml'; import type { User } from '@/models/entities/user'; import * as Acct from '@/misc/acct.js'; -import { NodeinfoServerService } from './NodeinfoServerService'; +import { NodeinfoServerService } from './NodeinfoServerService.js'; import type { FindOptionsWhere } from 'typeorm'; @Injectable() diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts index 524261a0071c..753dd1a9e397 100644 --- a/packages/backend/src/server/api/StreamingApiServerService.ts +++ b/packages/backend/src/server/api/StreamingApiServerService.ts @@ -8,7 +8,7 @@ import type { Blockings, ChannelFollowings, Followings, Mutings, UserProfiles } import { Config } from '@/config/types.js'; import { NoteReadService } from '@/services/NoteReadService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; -import { AuthenticateService } from './AuthenticateService'; +import { AuthenticateService } from './AuthenticateService.js'; import MainStreamConnection from './stream/index.js'; import { ChannelsService } from './stream/ChannelsService.js'; import type { ParsedUrlQuery } from 'querystring'; diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts index af5399519d31..4a281200bcc1 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Users } from '@/models/index.js'; -import { signup } from '../../../common/signup.js'; +import { SignupService } from '@/services/SignupService.js'; export const meta = { tags: ['admin'], @@ -34,7 +34,9 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, + + private signupService: SignupService, ) { super(meta, paramDef, async (ps, _me) => { const me = _me ? await this.usersRepository.findOneByOrFail({ id: _me.id }) : null; @@ -43,7 +45,7 @@ export default class extends Endpoint { })) === 0; if (!noUsers && !me?.isAdmin) throw new Error('access denied'); - const { account, secret } = await signup({ + const { account, secret } = await this.signupService.signup({ username: ps.username, password: ps.password, }); diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts index c51016deb922..1c618af6e30c 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; -import { doPostSuspend } from '@/services/suspend-user.js'; -import { publishUserEvent } from '@/services/stream.js'; -import { createDeleteAccountJob } from '@/queue/index.js'; +import { QueueService } from '@/queue/queue.service.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { UserSuspendService } from '@/services/UserSuspendService.js'; export const meta = { tags: ['admin'], @@ -25,7 +25,11 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, + + private queueService: QueueService, + private globalEventService: GlobalEventService, + private userSuspendService: UserSuspendService, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); @@ -44,13 +48,13 @@ export default class extends Endpoint { if (this.usersRepository.isLocalUser(user)) { // 物理削除する前にDelete activityを送信する - await doPostSuspend(user).catch(e => {}); + await this.userSuspendService.doPostSuspend(user).catch(err => {}); - createDeleteAccountJob(user, { + this.queueService.createDeleteAccountJob(user, { soft: false, }); } else { - createDeleteAccountJob(user, { + this.queueService.createDeleteAccountJob(user, { soft: true, // リモートユーザーの削除は、完全にDBから物理削除してしまうと再度連合してきてアカウントが復活する可能性があるため、soft指定する }); } @@ -61,7 +65,7 @@ export default class extends Endpoint { if (this.usersRepository.isLocalUser(user)) { // Terminate streaming - publishUserEvent(user.id, 'terminate', {}); + this.globalEventService.publishUserEvent(user.id, 'terminate', {}); } }); } diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts index a1b415e057c7..1c8d6b2b8454 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Ads } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import { IdService } from '@/services/IdService.js'; export const meta = { tags: ['admin'], @@ -29,7 +29,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('adsRepository') - private adsRepository: typeof Ads, + private adsRepository: typeof Ads, private idService: IdService, ) { diff --git a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts index a03076fec2b9..76e1b45cb612 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts @@ -31,7 +31,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('adsRepository') - private adsRepository: typeof Ads, + private adsRepository: typeof Ads, ) { super(meta, paramDef, async (ps, me) => { const ad = await this.adsRepository.findOneBy({ id: ps.id }); diff --git a/packages/backend/src/server/api/endpoints/admin/ad/update.ts b/packages/backend/src/server/api/endpoints/admin/ad/update.ts index 5889b7c9c048..7d15a1e3a0f6 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/update.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Ads } from '@/models/index.js'; +import type { Ads } from '@/models/index.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -38,17 +38,14 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + private adsRepository: typeof Ads, ) { super(meta, paramDef, async (ps, me) => { - const ad = await Ads.findOneBy({ id: ps.id }); + const ad = await this.adsRepository.findOneBy({ id: ps.id }); if (ad == null) throw new ApiError(meta.errors.noSuchAd); - await Ads.update(ad.id, { + await this.adsRepository.update(ad.id, { url: ps.url, place: ps.place, priority: ps.priority, diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts index 21d3bc4b5666..ad17c6c19d14 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Announcements } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import type { Announcements } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; export const meta = { tags: ['admin'], @@ -59,17 +59,20 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('announcementsRepository') + private announcementsRepository: typeof Announcements, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { - const announcement = await Announcements.insert({ + const announcement = await this.announcementsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), updatedAt: null, title: ps.title, text: ps.text, imageUrl: ps.imageUrl, - }).then(x => Announcements.findOneByOrFail(x.identifiers[0])); + }).then(x => this.announcementsRepository.findOneByOrFail(x.identifiers[0])); return Object.assign({}, announcement, { createdAt: announcement.createdAt.toISOString(), updatedAt: null }); }); diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts b/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts index 511f2094ba5c..2e045a693192 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Announcements } from '@/models/index.js'; +import type { Announcements } from '@/models/index.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -30,18 +30,15 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('announcementsRepository') + private announcementsRepository: typeof Announcements, ) { super(meta, paramDef, async (ps, me) => { - const announcement = await Announcements.findOneBy({ id: ps.id }); + const announcement = await this.announcementsRepository.findOneBy({ id: ps.id }); if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement); - await Announcements.delete(announcement.id); + await this.announcementsRepository.delete(announcement.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts index 64099c42c3c8..ae33b3b98b85 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Announcements, AnnouncementReads } from '@/models/index.js'; +import type { Announcements , AnnouncementReads } from '@/models/index.js'; import type { Announcement } from '@/models/entities/announcement.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; @@ -68,17 +68,23 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('announcementsRepository') + private announcementsRepository: typeof Announcements, + + @Inject('announcementReadsRepository') + private announcementReadsRepository: typeof AnnouncementReads, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(Announcements.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); + const query = this.queryService.makePaginationQuery(this.announcementsRepository.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); const announcements = await query.take(ps.limit).getMany(); const reads = new Map(); for (const announcement of announcements) { - reads.set(announcement, await AnnouncementReads.countBy({ + reads.set(announcement, await this.announcementReadsRepository.countBy({ announcementId: announcement.id, })); } diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts index dd23c97429bf..e66d4db37caf 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Announcements } from '@/models/index.js'; +import type { Announcements } from '@/models/index.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -33,18 +33,15 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('announcementsRepository') + private announcementsRepository: typeof Announcements, ) { super(meta, paramDef, async (ps, me) => { - const announcement = await Announcements.findOneBy({ id: ps.id }); + const announcement = await this.announcementsRepository.findOneBy({ id: ps.id }); if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement); - await Announcements.update(announcement.id, { + await this.announcementsRepository.update(announcement.id, { updatedAt: new Date(), title: ps.title, text: ps.text, diff --git a/packages/backend/src/server/api/endpoints/admin/delete-account.ts b/packages/backend/src/server/api/endpoints/admin/delete-account.ts index 7e465e11c73c..14364d6ce90b 100644 --- a/packages/backend/src/server/api/endpoints/admin/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/admin/delete-account.ts @@ -26,7 +26,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps) => { const user = await this.usersRepository.findOneByOrFail({ id: ps.userId }); diff --git a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts index 8ef9fe500b2d..7bdeeef5df50 100644 --- a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts @@ -23,10 +23,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const files = await DriveFiles.findBy({ diff --git a/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts b/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts index f05264830b99..a49f50e6d083 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts @@ -24,10 +24,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); diff --git a/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts b/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts index 185765a97108..3b5a31e4df5c 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { createCleanRemoteFilesJob } from '@/queue/index.js'; +import { QueueService } from '@/queue/queue.service.js'; export const meta = { tags: ['admin'], @@ -19,14 +19,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + private queueService: QueueService, ) { super(meta, paramDef, async (ps, me) => { - createCleanRemoteFilesJob(); + this.queueService.createCleanRemoteFilesJob(); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts b/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts index 113dd1444b65..f273f1dfb09b 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts @@ -1,8 +1,8 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { deleteFile } from '@/services/drive/delete-file.js'; -import { DriveFiles } from '@/models/index.js'; +import type { DriveFiles } from '@/models/index.js'; +import { DriveService } from '@/services/DriveService.js'; export const meta = { tags: ['admin'], @@ -21,19 +21,18 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private driveService: DriveService, ) { super(meta, paramDef, async (ps, me) => { - const files = await DriveFiles.findBy({ + const files = await this.driveFilesRepository.findBy({ userId: IsNull(), }); for (const file of files) { - deleteFile(file); + this.driveService.deleteFile(file); } }); } diff --git a/packages/backend/src/server/api/endpoints/admin/drive/files.ts b/packages/backend/src/server/api/endpoints/admin/drive/files.ts index 3158149c461d..c1c8ccf2bfc3 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/files.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DriveFiles } from '@/models/index.js'; +import type { DriveFiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; @@ -43,16 +43,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId); + const query = this.queryService.makePaginationQuery(this.driveFilesRepository.createQueryBuilder('file'), ps.sinceId, ps.untilId); if (ps.userId) { query.andWhere('file.userId = :userId', { userId: ps.userId }); @@ -78,7 +75,7 @@ export default class extends Endpoint { const files = await query.take(ps.limit).getMany(); - return await DriveFiles.packMany(files, { detail: true, withUser: true, self: true }); + return await this.driveFilesRepository.packMany(files, { detail: true, withUser: true, self: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts index 8ba3137dd826..608d35424dcb 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DriveFiles } from '@/models/index.js'; +import type { DriveFiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; @@ -173,14 +173,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, ) { super(meta, paramDef, async (ps, me) => { - const file = ps.fileId ? await DriveFiles.findOneBy({ id: ps.fileId }) : await DriveFiles.findOne({ + const file = ps.fileId ? await this.driveFilesRepository.findOneBy({ id: ps.fileId }) : await this.driveFilesRepository.findOne({ where: [{ url: ps.url, }, { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts index fb13059cc261..74ee4f40d6f5 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts @@ -1,9 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; -import { In } from 'typeorm'; +import { DataSource, In } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Emojis } from '@/models/index.js'; -import { db } from '@/db/postgre.js'; -import { ApiError } from '../../../error.js'; +import type { Emojis } from '@/models/index.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -25,24 +24,31 @@ export const paramDef = { required: ['ids', 'aliases'], } as const; +// TODO: ロジックをサービスに切り出す + // eslint-disable-next-line import/no-default-export @Injectable() export default class extends Endpoint { constructor( + @Inject(DI_SYMBOLS.db) + private db: DataSource, + + @Inject('emojisRepository') + private emojisRepository: typeof Emojis, ) { super(meta, paramDef, async (ps, me) => { - const emojis = await Emojis.findBy({ + const emojis = await this.emojisRepository.findBy({ id: In(ps.ids), }); for (const emoji of emojis) { - await Emojis.update(emoji.id, { + await this.emojisRepository.update(emoji.id, { updatedAt: new Date(), aliases: [...new Set(emoji.aliases.concat(ps.aliases))], }); } - await db.queryResultCache!.remove(['meta_emojis']); + await this.db.queryResultCache!.remove(['meta_emojis']); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index a5f6cdba38d7..e688081e5516 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -1,11 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; import rndstr from 'rndstr'; +import { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Emojis, DriveFiles } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; -import { insertModerationLog } from '@/services/insert-moderation-log.js'; -import { publishBroadcastStream } from '@/services/stream.js'; -import { db } from '@/db/postgre.js'; +import type { DriveFiles } from '@/models/index.js'; +import { Emojis } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { ModerationLogService } from '@/services/ModerationLogService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -31,26 +33,33 @@ export const paramDef = { required: ['fileId'], } as const; +// TODO: ロジックをサービスに切り出す + // eslint-disable-next-line import/no-default-export @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject(DI_SYMBOLS.db) + private db: DataSource, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('emojisRepository') + private emojisRepository: typeof Emojis, private idService: IdService, + private globalEventService: GlobalEventService, + private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { - const file = await DriveFiles.findOneBy({ id: ps.fileId }); + const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (file == null) throw new ApiError(meta.errors.noSuchFile); const name = file.name.split('.')[0].match(/^[a-z0-9_]+$/) ? file.name.split('.')[0] : `_${rndstr('a-z0-9', 8)}_`; - const emoji = await Emojis.insert({ + const emoji = await this.emojisRepository.insert({ id: this.idService.genId(), updatedAt: new Date(), name: name, @@ -62,13 +71,13 @@ export default class extends Endpoint { type: file.webpublicType ?? file.type, }).then(x => Emojis.findOneByOrFail(x.identifiers[0])); - await db.queryResultCache!.remove(['meta_emojis']); + await this.db.queryResultCache!.remove(['meta_emojis']); - publishBroadcastStream('emojiAdded', { + this.globalEventService.publishBroadcastStream('emojiAdded', { emoji: await Emojis.pack(emoji.id), }); - insertModerationLog(me, 'addEmoji', { + this.moderationLogService.insertModerationLog(me, 'addEmoji', { emojiId: emoji.id, }); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index 5ec82c26b8a9..2f1a0f014179 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -1,11 +1,12 @@ import { Inject, Injectable } from '@nestjs/common'; +import { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Emojis } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import type { Emojis } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; -import { uploadFromUrl } from '@/services/drive/upload-from-url.js'; -import { publishBroadcastStream } from '@/services/stream.js'; -import { db } from '@/db/postgre.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DriveService } from '@/services/DriveService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -43,20 +44,24 @@ export const paramDef = { required: ['emojiId'], } as const; +// TODO: ロジックをサービスに切り出す + // eslint-disable-next-line import/no-default-export @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject(DI_SYMBOLS.db) + private db: DataSource, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('emojisRepository') + private emojisRepository: typeof Emojis, private idService: IdService, + private globalEventService: GlobalEventService, + private driveService: DriveService, ) { super(meta, paramDef, async (ps, me) => { - const emoji = await Emojis.findOneBy({ id: ps.emojiId }); + const emoji = await this.emojisRepository.findOneBy({ id: ps.emojiId }); if (emoji == null) { throw new ApiError(meta.errors.noSuchEmoji); @@ -66,12 +71,12 @@ export default class extends Endpoint { try { // Create file - driveFile = await uploadFromUrl({ url: emoji.originalUrl, user: null, force: true }); + driveFile = await this.driveService.uploadFromUrl({ url: emoji.originalUrl, user: null, force: true }); } catch (e) { throw new ApiError(); } - const copied = await Emojis.insert({ + const copied = await this.emojisRepository.insert({ id: this.idService.genId(), updatedAt: new Date(), name: emoji.name, @@ -80,12 +85,12 @@ export default class extends Endpoint { originalUrl: driveFile.url, publicUrl: driveFile.webpublicUrl ?? driveFile.url, type: driveFile.webpublicType ?? driveFile.type, - }).then(x => Emojis.findOneByOrFail(x.identifiers[0])); + }).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0])); - await db.queryResultCache!.remove(['meta_emojis']); + await this.db.queryResultCache!.remove(['meta_emojis']); - publishBroadcastStream('emojiAdded', { - emoji: await Emojis.pack(copied.id), + this.globalEventService.publishBroadcastStream('emojiAdded', { + emoji: await this.emojisRepository.pack(copied.id), }); return { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts index 3182ed33bdd7..caad95e700e9 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts @@ -1,10 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; -import { In } from 'typeorm'; +import { DataSource, In } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Emojis } from '@/models/index.js'; -import { insertModerationLog } from '@/services/insert-moderation-log.js'; -import { db } from '@/db/postgre.js'; -import { ApiError } from '../../../error.js'; +import type { Emojis } from '@/models/index.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { ModerationLogService } from '@/services/ModerationLogService.js'; export const meta = { tags: ['admin'], @@ -23,27 +22,31 @@ export const paramDef = { required: ['ids'], } as const; +// TODO: ロジックをサービスに切り出す + // eslint-disable-next-line import/no-default-export @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject(DI_SYMBOLS.db) + private db: DataSource, + + @Inject('emojisRepository') + private emojisRepository: typeof Emojis, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { - const emojis = await Emojis.findBy({ + const emojis = await this.emojisRepository.findBy({ id: In(ps.ids), }); for (const emoji of emojis) { - await Emojis.delete(emoji.id); + await this.emojisRepository.delete(emoji.id); - await db.queryResultCache!.remove(['meta_emojis']); + await this.db.queryResultCache!.remove(['meta_emojis']); - insertModerationLog(me, 'deleteEmoji', { + this.moderationLogService.insertModerationLog(me, 'deleteEmoji', { emoji: emoji, }); } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts index eb2d35718395..e1795c7d21a5 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; +import { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Emojis } from '@/models/index.js'; -import { insertModerationLog } from '@/services/insert-moderation-log.js'; -import { db } from '@/db/postgre.js'; +import type { Emojis } from '@/models/index.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { ModerationLogService } from '@/services/ModerationLogService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -28,26 +29,30 @@ export const paramDef = { required: ['id'], } as const; +// TODO: ロジックをサービスに切り出す + // eslint-disable-next-line import/no-default-export @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject(DI_SYMBOLS.db) + private db: DataSource, + + @Inject('emojisRepository') + private emojisRepository: typeof Emojis, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { - const emoji = await Emojis.findOneBy({ id: ps.id }); + const emoji = await this.emojisRepository.findOneBy({ id: ps.id }); if (emoji == null) throw new ApiError(meta.errors.noSuchEmoji); - await Emojis.delete(emoji.id); + await this.emojisRepository.delete(emoji.id); - await db.queryResultCache!.remove(['meta_emojis']); + await this.db.queryResultCache!.remove(['meta_emojis']); - insertModerationLog(me, 'deleteEmoji', { + this.moderationLogService.insertModerationLog(me, 'deleteEmoji', { emoji: emoji, }); }); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts index 56846fe53759..333af15d353a 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts @@ -1,7 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; -import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { createImportCustomEmojisJob } from '@/queue/index.js'; +import { QueueService } from '@/queue/queue.service.js'; export const meta = { secure: true, @@ -21,9 +20,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queueService: QueueService, ) { super(meta, paramDef, async (ps, me) => { - createImportCustomEmojisJob(me, ps.fileId); + this.queueService.createImportCustomEmojisJob(me, ps.fileId); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts index b73fafeb5876..313c794beb7a 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Emojis } from '@/models/index.js'; +import type { Emojis } from '@/models/index.js'; import { toPuny } from '@/misc/convert-host.js'; import { QueryService } from '@/services/QueryService.js'; @@ -73,10 +73,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('emojisRepository') + private emojisRepository: typeof Emojis, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const q = this.queryService.makePaginationQuery(Emojis.createQueryBuilder('emoji'), ps.sinceId, ps.untilId); + const q = this.queryService.makePaginationQuery(this.emojisRepository.createQueryBuilder('emoji'), ps.sinceId, ps.untilId); if (ps.host == null) { q.andWhere('emoji.host IS NOT NULL'); @@ -93,7 +96,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return Emojis.packMany(emojis); + return this.emojisRepository.packMany(emojis); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts index 685bad1bda94..5b3af9e86450 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Emojis } from '@/models/index.js'; +import type { Emojis } from '@/models/index.js'; import type { Emoji } from '@/models/entities/emoji.js'; import { QueryService } from '@/services/QueryService.js'; @@ -67,10 +67,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('emojisRepository') + private emojisRepository: typeof Emojis, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const q = this.queryService.makePaginationQuery(Emojis.createQueryBuilder('emoji'), ps.sinceId, ps.untilId) + const q = this.queryService.makePaginationQuery(this.emojisRepository.createQueryBuilder('emoji'), ps.sinceId, ps.untilId) .andWhere('emoji.host IS NULL'); let emojis: Emoji[]; @@ -83,15 +86,15 @@ export default class extends Endpoint { emojis = emojis.filter(emoji => emoji.name.includes(ps.query!) || - emoji.aliases.some(a => a.includes(ps.query!)) || - emoji.category?.includes(ps.query!)); + emoji.aliases.some(a => a.includes(ps.query!)) || + emoji.category?.includes(ps.query!)); emojis.splice(ps.limit + 1); } else { emojis = await q.take(ps.limit).getMany(); } - return Emojis.packMany(emojis); + return this.emojisRepository.packMany(emojis); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts index d4ea51b29c83..335198b82302 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts @@ -1,9 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; -import { In } from 'typeorm'; +import { DataSource, In } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Emojis } from '@/models/index.js'; -import { db } from '@/db/postgre.js'; -import { ApiError } from '../../../error.js'; +import type { Emojis } from '@/models/index.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -25,24 +24,31 @@ export const paramDef = { required: ['ids', 'aliases'], } as const; +// TODO: ロジックをサービスに切り出す + // eslint-disable-next-line import/no-default-export @Injectable() export default class extends Endpoint { constructor( + @Inject(DI_SYMBOLS.db) + private db: DataSource, + + @Inject('emojisRepository') + private emojisRepository: typeof Emojis, ) { super(meta, paramDef, async (ps, me) => { - const emojis = await Emojis.findBy({ + const emojis = await this.emojisRepository.findBy({ id: In(ps.ids), }); for (const emoji of emojis) { - await Emojis.update(emoji.id, { + await this.emojisRepository.update(emoji.id, { updatedAt: new Date(), aliases: emoji.aliases.filter(x => !ps.aliases.includes(x)), }); } - await db.queryResultCache!.remove(['meta_emojis']); + await this.db.queryResultCache!.remove(['meta_emojis']); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts index a258ab734abb..708a60b867d2 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts @@ -1,9 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; -import { In } from 'typeorm'; +import { DataSource, In } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Emojis } from '@/models/index.js'; -import { db } from '@/db/postgre.js'; -import { ApiError } from '../../../error.js'; +import type { Emojis } from '@/models/index.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -25,21 +24,27 @@ export const paramDef = { required: ['ids', 'aliases'], } as const; +// TODO: ロジックをサービスに切り出す + // eslint-disable-next-line import/no-default-export @Injectable() export default class extends Endpoint { constructor( + @Inject(DI_SYMBOLS.db) + private db: DataSource, + @Inject('emojisRepository') + private emojisRepository: typeof Emojis, ) { super(meta, paramDef, async (ps, me) => { - await Emojis.update({ + await this.emojisRepository.update({ id: In(ps.ids), }, { updatedAt: new Date(), aliases: ps.aliases, }); - await db.queryResultCache!.remove(['meta_emojis']); + await this.db.queryResultCache!.remove(['meta_emojis']); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts index 4d6e6fe84466..d5aa6049f317 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts @@ -1,9 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; -import { In } from 'typeorm'; +import { DataSource, In } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Emojis } from '@/models/index.js'; -import { db } from '@/db/postgre.js'; -import { ApiError } from '../../../error.js'; +import type { Emojis } from '@/models/index.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -27,20 +26,27 @@ export const paramDef = { required: ['ids'], } as const; +// TODO: ロジックをサービスに切り出す + // eslint-disable-next-line import/no-default-export @Injectable() export default class extends Endpoint { constructor( + @Inject(DI_SYMBOLS.db) + private db: DataSource, + + @Inject('emojisRepository') + private emojisRepository: typeof Emojis, ) { super(meta, paramDef, async (ps, me) => { - await Emojis.update({ + await this.emojisRepository.update({ id: In(ps.ids), }, { updatedAt: new Date(), category: ps.category, }); - await db.queryResultCache!.remove(['meta_emojis']); + await this.db.queryResultCache!.remove(['meta_emojis']); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index 7fbc9c2e4f00..ce77966ca23c 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; +import { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Emojis } from '@/models/index.js'; -import { db } from '@/db/postgre.js'; +import type { Emojis } from '@/models/index.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -36,24 +37,31 @@ export const paramDef = { required: ['id', 'name', 'aliases'], } as const; +// TODO: ロジックをサービスに切り出す + // eslint-disable-next-line import/no-default-export @Injectable() export default class extends Endpoint { constructor( + @Inject(DI_SYMBOLS.db) + private db: DataSource, + + @Inject('emojisRepository') + private emojisRepository: typeof Emojis, ) { super(meta, paramDef, async (ps, me) => { - const emoji = await Emojis.findOneBy({ id: ps.id }); + const emoji = await this.emojisRepository.findOneBy({ id: ps.id }); if (emoji == null) throw new ApiError(meta.errors.noSuchEmoji); - await Emojis.update(emoji.id, { + await this.emojisRepository.update(emoji.id, { updatedAt: new Date(), name: ps.name, category: ps.category, aliases: ps.aliases, }); - await db.queryResultCache!.remove(['meta_emojis']); + await this.db.queryResultCache!.remove(['meta_emojis']); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts index 16b775f1399d..68a902cf299a 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts @@ -23,10 +23,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const files = await DriveFiles.findBy({ diff --git a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts index 5bf49af92695..71fd49717c0a 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts @@ -24,10 +24,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const instance = await Instances.findOneBy({ host: toPuny(ps.host) }); diff --git a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts index 53492b7f64d2..e9253a7c00ed 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts @@ -23,10 +23,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const followings = await Followings.findBy({ diff --git a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts index 21d59c3d6440..ead725d62d7b 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts @@ -24,10 +24,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const instance = await Instances.findOneBy({ host: toPuny(ps.host) }); diff --git a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts index ef1515763220..891b22f31c4a 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts @@ -20,10 +20,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async () => { const stats = await db.query('SELECT * FROM pg_indexes;').then(recs => { diff --git a/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts index 57f46e540a74..0a6009923a56 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts @@ -31,10 +31,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async () => { const sizes = await diff --git a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts index 0a1d974b0dcf..fd7a24f3e160 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts @@ -22,10 +22,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const ips = await UserIps.find({ diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 026a201a35d6..ca95f588b339 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -345,10 +345,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const instance = await fetchMeta(true); diff --git a/packages/backend/src/server/api/endpoints/admin/moderators/add.ts b/packages/backend/src/server/api/endpoints/admin/moderators/add.ts index 61016f20feb1..025a3093bbed 100644 --- a/packages/backend/src/server/api/endpoints/admin/moderators/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/moderators/add.ts @@ -23,7 +23,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); diff --git a/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts b/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts index 79be9b1e0dac..44df3705f5a6 100644 --- a/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts +++ b/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts @@ -23,7 +23,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); diff --git a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts index 7fbb07611dab..cf56a23c3489 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts @@ -21,10 +21,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { destroy(); diff --git a/packages/backend/src/server/api/endpoints/admin/reset-password.ts b/packages/backend/src/server/api/endpoints/admin/reset-password.ts index 1e5326d6c425..328ee15cf961 100644 --- a/packages/backend/src/server/api/endpoints/admin/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/admin/reset-password.ts @@ -38,7 +38,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); diff --git a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts index 2f6fcc785d50..40217e7278c1 100644 --- a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts +++ b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts @@ -4,7 +4,7 @@ import type { Users } from '@/models/index.js'; import { AbuseUserReports } from '@/models/index.js'; import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; import { renderFlag } from '@/services/remote/activitypub/renderer/flag.js'; -import type { InstanceActorService } from '@/services/InstanceActorService'; +import type { InstanceActorService } from '@/services/InstanceActorService.js'; import type { QueueService } from '@/queue/queue.service'; export const meta = { @@ -28,7 +28,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, private queueService: QueueService, diff --git a/packages/backend/src/server/api/endpoints/admin/server-info.ts b/packages/backend/src/server/api/endpoints/admin/server-info.ts index fee612fe494e..d5bc57d90f7e 100644 --- a/packages/backend/src/server/api/endpoints/admin/server-info.ts +++ b/packages/backend/src/server/api/endpoints/admin/server-info.ts @@ -99,10 +99,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async () => { const memStats = await si.mem(); diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index 83ef06f6139c..683a6ffdb643 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -27,10 +27,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const [user, profile] = await Promise.all([ diff --git a/packages/backend/src/server/api/endpoints/admin/show-users.ts b/packages/backend/src/server/api/endpoints/admin/show-users.ts index d60e25608c1f..4ef1572409ea 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-users.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts @@ -43,10 +43,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const query = this.usersRepository.createQueryBuilder('user'); diff --git a/packages/backend/src/server/api/endpoints/admin/silence-user.ts b/packages/backend/src/server/api/endpoints/admin/silence-user.ts index 200903d901f2..9c6da20883f3 100644 --- a/packages/backend/src/server/api/endpoints/admin/silence-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/silence-user.ts @@ -24,10 +24,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); diff --git a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts index 3b5f4e9b6b92..76830b9d285e 100644 --- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts @@ -27,10 +27,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); diff --git a/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts b/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts index 25c62ae849c3..3638e22f4f44 100644 --- a/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts @@ -24,10 +24,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); diff --git a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts index d57dd9154097..50b689047529 100644 --- a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts @@ -24,10 +24,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index adb8a0c4d914..77d99ed66e62 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -112,10 +112,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const set = {} as Partial; diff --git a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts index 8dac56a11095..e89f1593e960 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts @@ -23,10 +23,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); diff --git a/packages/backend/src/server/api/endpoints/admin/vacuum.ts b/packages/backend/src/server/api/endpoints/admin/vacuum.ts index 243f57859430..a3aa30fba4a9 100644 --- a/packages/backend/src/server/api/endpoints/admin/vacuum.ts +++ b/packages/backend/src/server/api/endpoints/admin/vacuum.ts @@ -24,10 +24,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const params: string[] = []; diff --git a/packages/backend/src/server/api/endpoints/antennas/list.ts b/packages/backend/src/server/api/endpoints/antennas/list.ts index 6041273a5c7a..544d067c0aff 100644 --- a/packages/backend/src/server/api/endpoints/antennas/list.ts +++ b/packages/backend/src/server/api/endpoints/antennas/list.ts @@ -31,10 +31,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const antennas = await Antennas.findBy({ diff --git a/packages/backend/src/server/api/endpoints/antennas/show.ts b/packages/backend/src/server/api/endpoints/antennas/show.ts index adc11e475328..9bc7dca8c871 100644 --- a/packages/backend/src/server/api/endpoints/antennas/show.ts +++ b/packages/backend/src/server/api/endpoints/antennas/show.ts @@ -38,10 +38,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { // Fetch the antenna diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index 3f3d1142882a..67f2a1e1b0fe 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -84,10 +84,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const object = await fetchAny(ps.uri, me); diff --git a/packages/backend/src/server/api/endpoints/app/show.ts b/packages/backend/src/server/api/endpoints/app/show.ts index 43dcc1f9f729..4f8c48e79185 100644 --- a/packages/backend/src/server/api/endpoints/app/show.ts +++ b/packages/backend/src/server/api/endpoints/app/show.ts @@ -34,10 +34,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user, token) => { const isSecure = user != null && token == null; diff --git a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts index b614d97cedc7..49e6d2d1caec 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts @@ -61,7 +61,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps, me) => { // Lookup app diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts index c4bb7f81cd45..6f97f0763c70 100644 --- a/packages/backend/src/server/api/endpoints/blocking/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking/create.ts @@ -59,7 +59,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps, me) => { const blocker = await this.usersRepository.findOneByOrFail({ id: me.id }); diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts index 0ad52a32f87c..ad74205c726b 100644 --- a/packages/backend/src/server/api/endpoints/blocking/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts @@ -59,7 +59,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps, me) => { const blocker = await this.usersRepository.findOneByOrFail({ id: me.id }); diff --git a/packages/backend/src/server/api/endpoints/blocking/list.ts b/packages/backend/src/server/api/endpoints/blocking/list.ts index ef141b40f18c..8ba56cba6b22 100644 --- a/packages/backend/src/server/api/endpoints/blocking/list.ts +++ b/packages/backend/src/server/api/endpoints/blocking/list.ts @@ -36,10 +36,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, private queryService: QueryService, ) { diff --git a/packages/backend/src/server/api/endpoints/channels/featured.ts b/packages/backend/src/server/api/endpoints/channels/featured.ts index ada374f18fe1..483c8a47bd35 100644 --- a/packages/backend/src/server/api/endpoints/channels/featured.ts +++ b/packages/backend/src/server/api/endpoints/channels/featured.ts @@ -29,10 +29,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const query = Channels.createQueryBuilder('channel') diff --git a/packages/backend/src/server/api/endpoints/channels/followed.ts b/packages/backend/src/server/api/endpoints/channels/followed.ts index 53e09736ffcb..6799994d3ae7 100644 --- a/packages/backend/src/server/api/endpoints/channels/followed.ts +++ b/packages/backend/src/server/api/endpoints/channels/followed.ts @@ -36,10 +36,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, private queryService: QueryService, ) { diff --git a/packages/backend/src/server/api/endpoints/channels/owned.ts b/packages/backend/src/server/api/endpoints/channels/owned.ts index 0198a8d0781c..33e2efcae95f 100644 --- a/packages/backend/src/server/api/endpoints/channels/owned.ts +++ b/packages/backend/src/server/api/endpoints/channels/owned.ts @@ -36,10 +36,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, private queryService: QueryService, ) { diff --git a/packages/backend/src/server/api/endpoints/channels/show.ts b/packages/backend/src/server/api/endpoints/channels/show.ts index e31cc45c2d80..c5a3dacec807 100644 --- a/packages/backend/src/server/api/endpoints/channels/show.ts +++ b/packages/backend/src/server/api/endpoints/channels/show.ts @@ -36,10 +36,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const channel = await Channels.findOneBy({ diff --git a/packages/backend/src/server/api/endpoints/channels/update.ts b/packages/backend/src/server/api/endpoints/channels/update.ts index 12c843302d40..4e0396c416a3 100644 --- a/packages/backend/src/server/api/endpoints/channels/update.ts +++ b/packages/backend/src/server/api/endpoints/channels/update.ts @@ -53,10 +53,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const channel = await Channels.findOneBy({ diff --git a/packages/backend/src/server/api/endpoints/clips/list.ts b/packages/backend/src/server/api/endpoints/clips/list.ts index 2b1d4ece6512..c0155d3ff163 100644 --- a/packages/backend/src/server/api/endpoints/clips/list.ts +++ b/packages/backend/src/server/api/endpoints/clips/list.ts @@ -31,10 +31,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const clips = await Clips.findBy({ diff --git a/packages/backend/src/server/api/endpoints/clips/show.ts b/packages/backend/src/server/api/endpoints/clips/show.ts index a9ae8d055f3b..7dbe68e091fd 100644 --- a/packages/backend/src/server/api/endpoints/clips/show.ts +++ b/packages/backend/src/server/api/endpoints/clips/show.ts @@ -38,10 +38,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { // Fetch the clip diff --git a/packages/backend/src/server/api/endpoints/endpoints.ts b/packages/backend/src/server/api/endpoints/endpoints.ts index b42bf88bf239..7dbfb7262a70 100644 --- a/packages/backend/src/server/api/endpoints/endpoints.ts +++ b/packages/backend/src/server/api/endpoints/endpoints.ts @@ -34,10 +34,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async () => { return endpoints.map(x => x.name); diff --git a/packages/backend/src/server/api/endpoints/federation/followers.ts b/packages/backend/src/server/api/endpoints/federation/followers.ts index 66a3a331967c..9986342343dc 100644 --- a/packages/backend/src/server/api/endpoints/federation/followers.ts +++ b/packages/backend/src/server/api/endpoints/federation/followers.ts @@ -35,10 +35,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, private queryService: QueryService, ) { diff --git a/packages/backend/src/server/api/endpoints/federation/following.ts b/packages/backend/src/server/api/endpoints/federation/following.ts index 52664d666012..c08624c2e262 100644 --- a/packages/backend/src/server/api/endpoints/federation/following.ts +++ b/packages/backend/src/server/api/endpoints/federation/following.ts @@ -35,10 +35,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, private queryService: QueryService, ) { diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts index e7646d0c289d..283246eafef9 100644 --- a/packages/backend/src/server/api/endpoints/federation/instances.ts +++ b/packages/backend/src/server/api/endpoints/federation/instances.ts @@ -42,10 +42,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const query = Instances.createQueryBuilder('instance'); diff --git a/packages/backend/src/server/api/endpoints/federation/show-instance.ts b/packages/backend/src/server/api/endpoints/federation/show-instance.ts index 792c7920dcf5..9a876461a587 100644 --- a/packages/backend/src/server/api/endpoints/federation/show-instance.ts +++ b/packages/backend/src/server/api/endpoints/federation/show-instance.ts @@ -31,10 +31,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const instance = await Instances diff --git a/packages/backend/src/server/api/endpoints/federation/users.ts b/packages/backend/src/server/api/endpoints/federation/users.ts index aad7ed89392e..6c9317907dcb 100644 --- a/packages/backend/src/server/api/endpoints/federation/users.ts +++ b/packages/backend/src/server/api/endpoints/federation/users.ts @@ -35,7 +35,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, private queryService: QueryService, ) { diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts index 5eb6a5541768..e0146a0cedd9 100644 --- a/packages/backend/src/server/api/endpoints/following/create.ts +++ b/packages/backend/src/server/api/endpoints/following/create.ts @@ -72,7 +72,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps, me) => { const follower = me; diff --git a/packages/backend/src/server/api/endpoints/following/delete.ts b/packages/backend/src/server/api/endpoints/following/delete.ts index d2ba53633b18..a99ea1d6d8c3 100644 --- a/packages/backend/src/server/api/endpoints/following/delete.ts +++ b/packages/backend/src/server/api/endpoints/following/delete.ts @@ -59,7 +59,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps, me) => { const follower = me; diff --git a/packages/backend/src/server/api/endpoints/following/invalidate.ts b/packages/backend/src/server/api/endpoints/following/invalidate.ts index f9386a18170d..36379df80146 100644 --- a/packages/backend/src/server/api/endpoints/following/invalidate.ts +++ b/packages/backend/src/server/api/endpoints/following/invalidate.ts @@ -59,7 +59,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps, me) => { const followee = me; diff --git a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts index 6eea1f38f8d3..1080f4ce395a 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts @@ -47,7 +47,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps, me) => { // Fetch followee diff --git a/packages/backend/src/server/api/endpoints/gallery/featured.ts b/packages/backend/src/server/api/endpoints/gallery/featured.ts index 509093b565d3..07265ac2ff83 100644 --- a/packages/backend/src/server/api/endpoints/gallery/featured.ts +++ b/packages/backend/src/server/api/endpoints/gallery/featured.ts @@ -29,10 +29,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const query = GalleryPosts.createQueryBuilder('post') diff --git a/packages/backend/src/server/api/endpoints/gallery/popular.ts b/packages/backend/src/server/api/endpoints/gallery/popular.ts index 80bb5a52a92a..69c3500a3fda 100644 --- a/packages/backend/src/server/api/endpoints/gallery/popular.ts +++ b/packages/backend/src/server/api/endpoints/gallery/popular.ts @@ -29,10 +29,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const query = GalleryPosts.createQueryBuilder('post') diff --git a/packages/backend/src/server/api/endpoints/gallery/posts.ts b/packages/backend/src/server/api/endpoints/gallery/posts.ts index 90331412956c..d6d6e103516c 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts.ts @@ -32,10 +32,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, private queryService: QueryService, ) { diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts b/packages/backend/src/server/api/endpoints/gallery/posts/show.ts index 540576603b1c..f16a2aaa0e43 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/show.ts @@ -36,10 +36,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const post = await GalleryPosts.findOneBy({ diff --git a/packages/backend/src/server/api/endpoints/get-online-users-count.ts b/packages/backend/src/server/api/endpoints/get-online-users-count.ts index 81fdab30481e..36336ca6e910 100644 --- a/packages/backend/src/server/api/endpoints/get-online-users-count.ts +++ b/packages/backend/src/server/api/endpoints/get-online-users-count.ts @@ -21,10 +21,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async () => { const count = await this.usersRepository.countBy({ diff --git a/packages/backend/src/server/api/endpoints/hashtags/list.ts b/packages/backend/src/server/api/endpoints/hashtags/list.ts index c7c6137491bc..eb496f33da61 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/list.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/list.ts @@ -35,10 +35,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const query = Hashtags.createQueryBuilder('tag'); diff --git a/packages/backend/src/server/api/endpoints/hashtags/trend.ts b/packages/backend/src/server/api/endpoints/hashtags/trend.ts index 0c132ca1d0e1..73f2d8ff510b 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/trend.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/trend.ts @@ -65,10 +65,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async () => { const instance = await fetchMeta(true); diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts index 9879cf524c4b..274c944837b1 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/users.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts @@ -36,10 +36,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const query = this.usersRepository.createQueryBuilder('user') diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index da8cd4790129..65c2f3b7458d 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -25,10 +25,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user, token) => { const isSecure = token == null; diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts index ff281c8c85e3..acc5a1d7824c 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts @@ -40,7 +40,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts index 01af620568b8..5decc8d3a11c 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts @@ -25,7 +25,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps, me) => { const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); diff --git a/packages/backend/src/server/api/endpoints/i/delete-account.ts b/packages/backend/src/server/api/endpoints/i/delete-account.ts index 0d0467a9dd69..b13a996d99fb 100644 --- a/packages/backend/src/server/api/endpoints/i/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts @@ -24,7 +24,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps, me) => { const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index 3a38cbaa5d5f..37b7ecd07732 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -55,7 +55,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, private queryService: QueryService, ) { diff --git a/packages/backend/src/server/api/endpoints/i/pin.ts b/packages/backend/src/server/api/endpoints/i/pin.ts index 5a6d783533f6..8b56a61a0708 100644 --- a/packages/backend/src/server/api/endpoints/i/pin.ts +++ b/packages/backend/src/server/api/endpoints/i/pin.ts @@ -51,7 +51,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps, me) => { await addPinned(me, ps.noteId).catch(e => { diff --git a/packages/backend/src/server/api/endpoints/i/read-announcement.ts b/packages/backend/src/server/api/endpoints/i/read-announcement.ts index 6452d78012f2..de38ae4ffa70 100644 --- a/packages/backend/src/server/api/endpoints/i/read-announcement.ts +++ b/packages/backend/src/server/api/endpoints/i/read-announcement.ts @@ -35,7 +35,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, private idService: IdService, ) { diff --git a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts index da6248dafc5f..6c350db7f7b2 100644 --- a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts +++ b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts @@ -25,7 +25,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps, me) => { const freshUser = await this.usersRepository.findOneByOrFail({ id: me.id }); diff --git a/packages/backend/src/server/api/endpoints/i/unpin.ts b/packages/backend/src/server/api/endpoints/i/unpin.ts index ccf2ced0a176..54e1238e05bf 100644 --- a/packages/backend/src/server/api/endpoints/i/unpin.ts +++ b/packages/backend/src/server/api/endpoints/i/unpin.ts @@ -39,7 +39,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps, me) => { await removePinned(me, ps.noteId).catch(e => { diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts index 9a8ad3d9218d..c6f730a76f34 100644 --- a/packages/backend/src/server/api/endpoints/i/update-email.ts +++ b/packages/backend/src/server/api/endpoints/i/update-email.ts @@ -50,7 +50,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps, me) => { const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index adf4eca125fa..4fb29162da09 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -127,10 +127,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, _user, token) => { const user = await this.usersRepository.findOneByOrFail({ id: _user.id }); diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/list.ts b/packages/backend/src/server/api/endpoints/i/webhooks/list.ts index 2735def6865e..bfbc52d48f59 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/list.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/list.ts @@ -21,10 +21,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const webhooks = await Webhooks.findBy({ diff --git a/packages/backend/src/server/api/endpoints/messaging/messages.ts b/packages/backend/src/server/api/endpoints/messaging/messages.ts index c1cc05990bbd..af02431e59e7 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages.ts @@ -75,7 +75,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, private queryService: QueryService, ) { diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index c672a64e1482..8e156c6cd0d2 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -309,10 +309,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const instance = await fetchMeta(true); diff --git a/packages/backend/src/server/api/endpoints/mute/list.ts b/packages/backend/src/server/api/endpoints/mute/list.ts index c7c6849af967..3b90d637f629 100644 --- a/packages/backend/src/server/api/endpoints/mute/list.ts +++ b/packages/backend/src/server/api/endpoints/mute/list.ts @@ -36,10 +36,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, private queryService: QueryService, ) { diff --git a/packages/backend/src/server/api/endpoints/notes.ts b/packages/backend/src/server/api/endpoints/notes.ts index 8b1390c93dc6..db47fd89aac1 100644 --- a/packages/backend/src/server/api/endpoints/notes.ts +++ b/packages/backend/src/server/api/endpoints/notes.ts @@ -37,7 +37,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, private queryService: QueryService, ) { diff --git a/packages/backend/src/server/api/endpoints/notes/clips.ts b/packages/backend/src/server/api/endpoints/notes/clips.ts index 9a66d3a56dbe..48034dd21de4 100644 --- a/packages/backend/src/server/api/endpoints/notes/clips.ts +++ b/packages/backend/src/server/api/endpoints/notes/clips.ts @@ -42,10 +42,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 1ede64617525..c924aaf8ab92 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -167,7 +167,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps, me) => { let visibleUsers: User[] = []; diff --git a/packages/backend/src/server/api/endpoints/notes/delete.ts b/packages/backend/src/server/api/endpoints/notes/delete.ts index 67c10eec3598..89f5a9f2b2e1 100644 --- a/packages/backend/src/server/api/endpoints/notes/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/delete.ts @@ -47,7 +47,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { 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 3e5d6429c5f5..6bfc11b552e5 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -74,7 +74,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, private idService: IdService, ) { diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts index 47d0ef585faf..b9f0bd598d41 100644 --- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts +++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts @@ -68,10 +68,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, private queryService: QueryService, ) { diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index 83bcd7b38344..a4af13304035 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -49,10 +49,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, private queryService: QueryService, ) { diff --git a/packages/backend/src/server/api/endpoints/notes/unrenote.ts b/packages/backend/src/server/api/endpoints/notes/unrenote.ts index dae2fd142423..79943fde6d6f 100644 --- a/packages/backend/src/server/api/endpoints/notes/unrenote.ts +++ b/packages/backend/src/server/api/endpoints/notes/unrenote.ts @@ -42,7 +42,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { diff --git a/packages/backend/src/server/api/endpoints/notifications/create.ts b/packages/backend/src/server/api/endpoints/notifications/create.ts index 25fd4349e626..811b637bec6b 100644 --- a/packages/backend/src/server/api/endpoints/notifications/create.ts +++ b/packages/backend/src/server/api/endpoints/notifications/create.ts @@ -28,10 +28,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, user, token) => { createNotification(user.id, 'app', { diff --git a/packages/backend/src/server/api/endpoints/page-push.ts b/packages/backend/src/server/api/endpoints/page-push.ts index 1ed97ae7b12d..067974169121 100644 --- a/packages/backend/src/server/api/endpoints/page-push.ts +++ b/packages/backend/src/server/api/endpoints/page-push.ts @@ -33,7 +33,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps, me) => { const page = await Pages.findOneBy({ id: ps.pageId }); diff --git a/packages/backend/src/server/api/endpoints/pages/featured.ts b/packages/backend/src/server/api/endpoints/pages/featured.ts index 209ca7d520b4..c34b7ddff4f3 100644 --- a/packages/backend/src/server/api/endpoints/pages/featured.ts +++ b/packages/backend/src/server/api/endpoints/pages/featured.ts @@ -29,10 +29,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const query = Pages.createQueryBuilder('page') diff --git a/packages/backend/src/server/api/endpoints/pages/show.ts b/packages/backend/src/server/api/endpoints/pages/show.ts index 91da8933c808..d8bf733f2a31 100644 --- a/packages/backend/src/server/api/endpoints/pages/show.ts +++ b/packages/backend/src/server/api/endpoints/pages/show.ts @@ -50,7 +50,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps, me) => { let page: Page | null = null; diff --git a/packages/backend/src/server/api/endpoints/ping.ts b/packages/backend/src/server/api/endpoints/ping.ts index 1dfd8dce4640..8c221c561bdb 100644 --- a/packages/backend/src/server/api/endpoints/ping.ts +++ b/packages/backend/src/server/api/endpoints/ping.ts @@ -29,10 +29,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async () => { return { diff --git a/packages/backend/src/server/api/endpoints/pinned-users.ts b/packages/backend/src/server/api/endpoints/pinned-users.ts index e74fc6c18340..1b57f3921f50 100644 --- a/packages/backend/src/server/api/endpoints/pinned-users.ts +++ b/packages/backend/src/server/api/endpoints/pinned-users.ts @@ -33,10 +33,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const meta = await fetchMeta(); diff --git a/packages/backend/src/server/api/endpoints/request-reset-password.ts b/packages/backend/src/server/api/endpoints/request-reset-password.ts index 4d62e910fc76..9c04d541717a 100644 --- a/packages/backend/src/server/api/endpoints/request-reset-password.ts +++ b/packages/backend/src/server/api/endpoints/request-reset-password.ts @@ -42,7 +42,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, private idService: IdService, ) { diff --git a/packages/backend/src/server/api/endpoints/server-info.ts b/packages/backend/src/server/api/endpoints/server-info.ts index 114f3ac7642e..292cc37e7e51 100644 --- a/packages/backend/src/server/api/endpoints/server-info.ts +++ b/packages/backend/src/server/api/endpoints/server-info.ts @@ -20,10 +20,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async () => { const memStats = await si.mem(); diff --git a/packages/backend/src/server/api/endpoints/stats.ts b/packages/backend/src/server/api/endpoints/stats.ts index d09d9399c489..64e59f730712 100644 --- a/packages/backend/src/server/api/endpoints/stats.ts +++ b/packages/backend/src/server/api/endpoints/stats.ts @@ -56,10 +56,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async () => { const [ diff --git a/packages/backend/src/server/api/endpoints/test.ts b/packages/backend/src/server/api/endpoints/test.ts index bd47e647dcb4..094ef90ab6d9 100644 --- a/packages/backend/src/server/api/endpoints/test.ts +++ b/packages/backend/src/server/api/endpoints/test.ts @@ -26,10 +26,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { return ps; diff --git a/packages/backend/src/server/api/endpoints/username/available.ts b/packages/backend/src/server/api/endpoints/username/available.ts index fcee55d27a8a..127d553d7746 100644 --- a/packages/backend/src/server/api/endpoints/username/available.ts +++ b/packages/backend/src/server/api/endpoints/username/available.ts @@ -33,7 +33,7 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps, me) => { // Get exist diff --git a/packages/backend/src/server/api/endpoints/users.ts b/packages/backend/src/server/api/endpoints/users.ts index eb407555a11c..a1ad632814c3 100644 --- a/packages/backend/src/server/api/endpoints/users.ts +++ b/packages/backend/src/server/api/endpoints/users.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { QueryService } from '@/services/QueryService'; +import { QueryService } from '@/services/QueryService.js'; export const meta = { tags: ['users'], @@ -42,10 +42,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, private queryService: QueryService, ) { diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index e4a3f3d9fee9..31accbbc5bdf 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -72,10 +72,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, private queryService: QueryService, ) { diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index 73ff2a927d9b..7a476b7d09a3 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -72,10 +72,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, private queryService: QueryService, ) { diff --git a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts index d49cdf1031d3..2d4c6322a97b 100644 --- a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts +++ b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts @@ -56,10 +56,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { // Lookup user diff --git a/packages/backend/src/server/api/endpoints/users/groups/invite.ts b/packages/backend/src/server/api/endpoints/users/groups/invite.ts index bb78e570a405..663a1fbb04f9 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invite.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invite.ts @@ -57,10 +57,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, private idService: IdService, ) { diff --git a/packages/backend/src/server/api/endpoints/users/groups/joined.ts b/packages/backend/src/server/api/endpoints/users/groups/joined.ts index 9bf60c42dd3b..37011c221c96 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/joined.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/joined.ts @@ -34,10 +34,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const ownedGroups = await UserGroups.findBy({ diff --git a/packages/backend/src/server/api/endpoints/users/groups/leave.ts b/packages/backend/src/server/api/endpoints/users/groups/leave.ts index 1c5be433639d..945e4c132fcf 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/leave.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/leave.ts @@ -40,10 +40,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { // Fetch the group diff --git a/packages/backend/src/server/api/endpoints/users/groups/owned.ts b/packages/backend/src/server/api/endpoints/users/groups/owned.ts index e8cee2943d35..00ad9eb025a9 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/owned.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/owned.ts @@ -33,10 +33,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const userGroups = await UserGroups.findBy({ diff --git a/packages/backend/src/server/api/endpoints/users/groups/pull.ts b/packages/backend/src/server/api/endpoints/users/groups/pull.ts index 07c5afbcd364..48e16c7d8613 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/pull.ts @@ -48,10 +48,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { // Fetch the group diff --git a/packages/backend/src/server/api/endpoints/users/groups/show.ts b/packages/backend/src/server/api/endpoints/users/groups/show.ts index 2da2502d633c..525e520219f5 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/show.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/show.ts @@ -40,10 +40,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { // Fetch the group diff --git a/packages/backend/src/server/api/endpoints/users/groups/transfer.ts b/packages/backend/src/server/api/endpoints/users/groups/transfer.ts index f8755500bba5..5bc72a438777 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/transfer.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/transfer.ts @@ -54,10 +54,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { // Fetch the group diff --git a/packages/backend/src/server/api/endpoints/users/groups/update.ts b/packages/backend/src/server/api/endpoints/users/groups/update.ts index 8d3b3d347bc6..0795570d2b6a 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/update.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/update.ts @@ -41,10 +41,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { // Fetch the group diff --git a/packages/backend/src/server/api/endpoints/users/lists/list.ts b/packages/backend/src/server/api/endpoints/users/lists/list.ts index 3bed98e615ba..8977a3033d3b 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/list.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/list.ts @@ -33,10 +33,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const userLists = await UserLists.findBy({ diff --git a/packages/backend/src/server/api/endpoints/users/lists/pull.ts b/packages/backend/src/server/api/endpoints/users/lists/pull.ts index c6d6bd5c89ea..c89bbbff30dc 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/pull.ts @@ -43,10 +43,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { // Fetch the list diff --git a/packages/backend/src/server/api/endpoints/users/lists/push.ts b/packages/backend/src/server/api/endpoints/users/lists/push.ts index dd7d813e2f52..d486fc4c5a2c 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/push.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/push.ts @@ -55,10 +55,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { // Fetch the list diff --git a/packages/backend/src/server/api/endpoints/users/lists/show.ts b/packages/backend/src/server/api/endpoints/users/lists/show.ts index fc13a50ada68..0a573a809fab 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/show.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/show.ts @@ -40,10 +40,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { // Fetch the list diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 4c28664ca4dd..25ad75b4a151 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -55,10 +55,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, private queryService: QueryService, ) { diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts index 2e6ad8c8fdf7..bf76413fefef 100644 --- a/packages/backend/src/server/api/endpoints/users/reactions.ts +++ b/packages/backend/src/server/api/endpoints/users/reactions.ts @@ -48,10 +48,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, private queryService: QueryService, ) { diff --git a/packages/backend/src/server/api/endpoints/users/recommendation.ts b/packages/backend/src/server/api/endpoints/users/recommendation.ts index fb23764e2333..0c8f868acceb 100644 --- a/packages/backend/src/server/api/endpoints/users/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/users/recommendation.ts @@ -39,10 +39,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, private queryService: QueryService, ) { diff --git a/packages/backend/src/server/api/endpoints/users/relation.ts b/packages/backend/src/server/api/endpoints/users/relation.ts index 75d7481bcda5..b506a3a56ae6 100644 --- a/packages/backend/src/server/api/endpoints/users/relation.ts +++ b/packages/backend/src/server/api/endpoints/users/relation.ts @@ -117,10 +117,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const ids = Array.isArray(ps.userId) ? ps.userId : [ps.userId]; diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts index 13a811110d06..11399ff4153e 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -52,10 +52,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, private idService: IdService, ) { diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index 71be7ecae6da..b045ba0daccb 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -45,10 +45,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日 diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index b76283b77a79..0e3028d9f2f2 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -40,10 +40,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日 diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts index 59a9476c1ef3..50835c14dd77 100644 --- a/packages/backend/src/server/api/endpoints/users/show.ts +++ b/packages/backend/src/server/api/endpoints/users/show.ts @@ -84,10 +84,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { let user; diff --git a/packages/backend/src/server/api/endpoints/users/stats.ts b/packages/backend/src/server/api/endpoints/users/stats.ts index ebf01dba9b27..348b1e44ef64 100644 --- a/packages/backend/src/server/api/endpoints/users/stats.ts +++ b/packages/backend/src/server/api/endpoints/users/stats.ts @@ -122,10 +122,10 @@ export const paramDef = { export default class extends Endpoint { constructor( @Inject('usersRepository') - private usersRepository: typeof Users, + private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); diff --git a/packages/backend/src/services/FetchInstanceMetadataService.ts b/packages/backend/src/services/FetchInstanceMetadataService.ts index 8ffab0150241..57485fe95f71 100644 --- a/packages/backend/src/services/FetchInstanceMetadataService.ts +++ b/packages/backend/src/services/FetchInstanceMetadataService.ts @@ -7,7 +7,7 @@ import type { Instance } from '@/models/entities/instance.js'; import type { Instances } from '@/models/index.js'; import type { AppLockService } from '@/services/AppLockService.js'; import Logger from '@/logger.js'; -import type { HttpRequestService } from './HttpRequestService'; +import type { HttpRequestService } from './HttpRequestService.js'; import type { DOMWindow } from 'jsdom'; const logger = new Logger('metadata', 'cyan'); diff --git a/packages/backend/src/services/S3Service.ts b/packages/backend/src/services/S3Service.ts index c7e49a1b7a69..8a3d1a218e2d 100644 --- a/packages/backend/src/services/S3Service.ts +++ b/packages/backend/src/services/S3Service.ts @@ -4,7 +4,7 @@ import S3 from 'aws-sdk/clients/s3.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Config } from '@/config/types.js'; import type { Meta } from '@/models/entities/meta'; -import type { HttpRequestService } from '../HttpRequestService'; +import type { HttpRequestService } from '../HttpRequestService.js'; @Injectable() export class S3Service { diff --git a/packages/backend/src/services/chart/charts/active-users.ts b/packages/backend/src/services/chart/charts/active-users.ts index cc0013210be4..a007d45c2356 100644 --- a/packages/backend/src/services/chart/charts/active-users.ts +++ b/packages/backend/src/services/chart/charts/active-users.ts @@ -1,10 +1,10 @@ import { Injectable, Inject } from '@nestjs/common'; -import type { AppLockService } from '@/services/AppLockService.js'; +import { DataSource } from 'typeorm'; +import { AppLockService } from '@/services/AppLockService.js'; import type { User } from '@/models/entities/user.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/active-users.js'; -import type { DataSource } from 'typeorm'; import type { KVs } from '../core.js'; const week = 1000 * 60 * 60 * 24 * 7; diff --git a/packages/backend/src/services/chart/charts/ap-request.ts b/packages/backend/src/services/chart/charts/ap-request.ts index e10e46f0e75f..0ebc159c7823 100644 --- a/packages/backend/src/services/chart/charts/ap-request.ts +++ b/packages/backend/src/services/chart/charts/ap-request.ts @@ -1,10 +1,10 @@ import { Injectable, Inject } from '@nestjs/common'; -import type { AppLockService } from '@/services/AppLockService.js'; +import { DataSource } from 'typeorm'; +import { AppLockService } from '@/services/AppLockService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/ap-request.js'; import type { KVs } from '../core.js'; -import type { DataSource } from 'typeorm'; /** * Chart about ActivityPub requests diff --git a/packages/backend/src/services/chart/charts/drive.ts b/packages/backend/src/services/chart/charts/drive.ts index 3aaaae1ee312..83c7e3afe4e6 100644 --- a/packages/backend/src/services/chart/charts/drive.ts +++ b/packages/backend/src/services/chart/charts/drive.ts @@ -1,13 +1,12 @@ import { Injectable, Inject } from '@nestjs/common'; -import { Not, IsNull } from 'typeorm'; +import { Not, IsNull , DataSource } from 'typeorm'; import { DriveFiles } from '@/models/index.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; -import type { AppLockService } from '@/services/AppLockService.js'; +import { AppLockService } from '@/services/AppLockService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/drive.js'; import type { KVs } from '../core.js'; -import type { DataSource } from 'typeorm'; /** * ドライブに関するチャート diff --git a/packages/backend/src/services/chart/charts/federation.ts b/packages/backend/src/services/chart/charts/federation.ts index aa6710bb003b..3d5cd1da189b 100644 --- a/packages/backend/src/services/chart/charts/federation.ts +++ b/packages/backend/src/services/chart/charts/federation.ts @@ -1,12 +1,12 @@ import { Injectable, Inject } from '@nestjs/common'; +import { DataSource } from 'typeorm'; import { Followings, Instances } from '@/models/index.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; -import type { AppLockService } from '@/services/AppLockService.js'; +import { AppLockService } from '@/services/AppLockService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/federation.js'; import type { KVs } from '../core.js'; -import type { DataSource } from 'typeorm'; /** * フェデレーションに関するチャート diff --git a/packages/backend/src/services/chart/charts/hashtag.ts b/packages/backend/src/services/chart/charts/hashtag.ts index cf5d7ea0610b..3581bb347085 100644 --- a/packages/backend/src/services/chart/charts/hashtag.ts +++ b/packages/backend/src/services/chart/charts/hashtag.ts @@ -1,12 +1,12 @@ import { Injectable, Inject } from '@nestjs/common'; +import { DataSource } from 'typeorm'; import type { User } from '@/models/entities/user.js'; import { Users } from '@/models/index.js'; -import type { AppLockService } from '@/services/AppLockService.js'; +import { AppLockService } from '@/services/AppLockService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/hashtag.js'; import type { KVs } from '../core.js'; -import type { DataSource } from 'typeorm'; /** * ハッシュタグに関するチャート diff --git a/packages/backend/src/services/chart/charts/instance.ts b/packages/backend/src/services/chart/charts/instance.ts index 254bb04448b1..3b3029e947fb 100644 --- a/packages/backend/src/services/chart/charts/instance.ts +++ b/packages/backend/src/services/chart/charts/instance.ts @@ -1,14 +1,14 @@ import { Injectable, Inject } from '@nestjs/common'; +import { DataSource } from 'typeorm'; import { DriveFiles, Followings, Users, Notes } from '@/models/index.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import type { Note } from '@/models/entities/note.js'; import { toPuny } from '@/misc/convert-host.js'; -import type { AppLockService } from '@/services/AppLockService.js'; +import { AppLockService } from '@/services/AppLockService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/instance.js'; import type { KVs } from '../core.js'; -import type { DataSource } from 'typeorm'; /** * インスタンスごとのチャート diff --git a/packages/backend/src/services/chart/charts/notes.ts b/packages/backend/src/services/chart/charts/notes.ts index 43f18e9cd303..ca0c736bbe59 100644 --- a/packages/backend/src/services/chart/charts/notes.ts +++ b/packages/backend/src/services/chart/charts/notes.ts @@ -1,13 +1,12 @@ import { Injectable, Inject } from '@nestjs/common'; -import { Not, IsNull } from 'typeorm'; +import { Not, IsNull , DataSource } from 'typeorm'; import { Notes } from '@/models/index.js'; import type { Note } from '@/models/entities/note.js'; -import type { AppLockService } from '@/services/AppLockService.js'; +import { AppLockService } from '@/services/AppLockService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/notes.js'; import type { KVs } from '../core.js'; -import type { DataSource } from 'typeorm'; /** * ノートに関するチャート diff --git a/packages/backend/src/services/chart/charts/per-user-drive.ts b/packages/backend/src/services/chart/charts/per-user-drive.ts index 2c8325dcf3f9..fec15cfcba0e 100644 --- a/packages/backend/src/services/chart/charts/per-user-drive.ts +++ b/packages/backend/src/services/chart/charts/per-user-drive.ts @@ -1,12 +1,12 @@ import { Injectable, Inject } from '@nestjs/common'; +import { DataSource } from 'typeorm'; import { DriveFiles } from '@/models/index.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; -import type { AppLockService } from '@/services/AppLockService.js'; +import { AppLockService } from '@/services/AppLockService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/per-user-drive.js'; import type { KVs } from '../core.js'; -import type { DataSource } from 'typeorm'; /** * ユーザーごとのドライブに関するチャート diff --git a/packages/backend/src/services/chart/charts/per-user-following.ts b/packages/backend/src/services/chart/charts/per-user-following.ts index c6767b074fd5..e4a9d9d1ac53 100644 --- a/packages/backend/src/services/chart/charts/per-user-following.ts +++ b/packages/backend/src/services/chart/charts/per-user-following.ts @@ -1,13 +1,12 @@ import { Injectable, Inject } from '@nestjs/common'; -import { Not, IsNull } from 'typeorm'; +import { Not, IsNull , DataSource } from 'typeorm'; import { Followings, Users } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; -import type { AppLockService } from '@/services/AppLockService.js'; +import { AppLockService } from '@/services/AppLockService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/per-user-following.js'; import type { KVs } from '../core.js'; -import type { DataSource } from 'typeorm'; /** * ユーザーごとのフォローに関するチャート diff --git a/packages/backend/src/services/chart/charts/per-user-notes.ts b/packages/backend/src/services/chart/charts/per-user-notes.ts index 1caf5fe1d814..23dd5fbbee16 100644 --- a/packages/backend/src/services/chart/charts/per-user-notes.ts +++ b/packages/backend/src/services/chart/charts/per-user-notes.ts @@ -1,13 +1,13 @@ import { Injectable, Inject } from '@nestjs/common'; +import { DataSource } from 'typeorm'; import type { User } from '@/models/entities/user.js'; import { Notes } from '@/models/index.js'; import type { Note } from '@/models/entities/note.js'; -import type { AppLockService } from '@/services/AppLockService.js'; +import { AppLockService } from '@/services/AppLockService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/per-user-notes.js'; import type { KVs } from '../core.js'; -import type { DataSource } from 'typeorm'; /** * ユーザーごとのノートに関するチャート diff --git a/packages/backend/src/services/chart/charts/per-user-reactions.ts b/packages/backend/src/services/chart/charts/per-user-reactions.ts index e865bec639cb..5b1fb623d590 100644 --- a/packages/backend/src/services/chart/charts/per-user-reactions.ts +++ b/packages/backend/src/services/chart/charts/per-user-reactions.ts @@ -1,13 +1,13 @@ import { Injectable, Inject } from '@nestjs/common'; +import { DataSource } from 'typeorm'; import type { User } from '@/models/entities/user.js'; import type { Note } from '@/models/entities/note.js'; import { Users } from '@/models/index.js'; -import type { AppLockService } from '@/services/AppLockService.js'; +import { AppLockService } from '@/services/AppLockService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/per-user-reactions.js'; import type { KVs } from '../core.js'; -import type { DataSource } from 'typeorm'; /** * ユーザーごとのリアクションに関するチャート diff --git a/packages/backend/src/services/chart/charts/test-grouped.ts b/packages/backend/src/services/chart/charts/test-grouped.ts index 3851fac7239e..befa514c7715 100644 --- a/packages/backend/src/services/chart/charts/test-grouped.ts +++ b/packages/backend/src/services/chart/charts/test-grouped.ts @@ -1,9 +1,9 @@ import { Injectable, Inject } from '@nestjs/common'; -import type { AppLockService } from '@/services/AppLockService.js'; +import { DataSource , DataSource } from 'typeorm'; +import { AppLockService } from '@/services/AppLockService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/test-grouped.js'; -import type { DataSource , DataSource } from 'typeorm'; import type { KVs } from '../core.js'; /** diff --git a/packages/backend/src/services/chart/charts/test-intersection.ts b/packages/backend/src/services/chart/charts/test-intersection.ts index 94511eba9a09..5ac8339ac165 100644 --- a/packages/backend/src/services/chart/charts/test-intersection.ts +++ b/packages/backend/src/services/chart/charts/test-intersection.ts @@ -1,9 +1,9 @@ import { Injectable, Inject } from '@nestjs/common'; -import type { AppLockService } from '@/services/AppLockService.js'; +import { DataSource , DataSource } from 'typeorm'; +import { AppLockService } from '@/services/AppLockService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/test-intersection.js'; -import type { DataSource , DataSource } from 'typeorm'; import type { KVs } from '../core.js'; /** diff --git a/packages/backend/src/services/chart/charts/test-unique.ts b/packages/backend/src/services/chart/charts/test-unique.ts index 47906fb6b038..f304b844f109 100644 --- a/packages/backend/src/services/chart/charts/test-unique.ts +++ b/packages/backend/src/services/chart/charts/test-unique.ts @@ -1,9 +1,9 @@ import { Injectable, Inject } from '@nestjs/common'; -import type { AppLockService } from '@/services/AppLockService.js'; +import { DataSource , DataSource } from 'typeorm'; +import { AppLockService } from '@/services/AppLockService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/test-unique.js'; -import type { DataSource , DataSource } from 'typeorm'; import type { KVs } from '../core.js'; /** diff --git a/packages/backend/src/services/chart/charts/test.ts b/packages/backend/src/services/chart/charts/test.ts index 10323649e307..2d800271f57d 100644 --- a/packages/backend/src/services/chart/charts/test.ts +++ b/packages/backend/src/services/chart/charts/test.ts @@ -1,9 +1,9 @@ import { Injectable, Inject } from '@nestjs/common'; -import type { AppLockService } from '@/services/AppLockService.js'; +import { DataSource , DataSource } from 'typeorm'; +import { AppLockService } from '@/services/AppLockService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/test.js'; -import type { DataSource , DataSource } from 'typeorm'; import type { KVs } from '../core.js'; /** diff --git a/packages/backend/src/services/chart/charts/users.ts b/packages/backend/src/services/chart/charts/users.ts index 5db35451c5c5..10830b69baf3 100644 --- a/packages/backend/src/services/chart/charts/users.ts +++ b/packages/backend/src/services/chart/charts/users.ts @@ -1,13 +1,12 @@ import { Injectable, Inject } from '@nestjs/common'; -import { Not, IsNull } from 'typeorm'; +import { Not, IsNull , DataSource } from 'typeorm'; import { Users } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; -import type { AppLockService } from '@/services/AppLockService.js'; +import { AppLockService } from '@/services/AppLockService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/users.js'; import type { KVs } from '../core.js'; -import type { DataSource } from 'typeorm'; /** * ユーザー数に関するチャート diff --git a/packages/backend/src/services/remote/activitypub/ApMfmService.ts b/packages/backend/src/services/remote/activitypub/ApMfmService.ts index 219a6dbad422..d8c39391d283 100644 --- a/packages/backend/src/services/remote/activitypub/ApMfmService.ts +++ b/packages/backend/src/services/remote/activitypub/ApMfmService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Config } from '@/config/types.js'; -import type { MfmService } from '@/services/MfmService'; +import type { MfmService } from '@/services/MfmService.js'; import type { IObject } from './type'; @Injectable() From 25838f897dd1133d61ac341c04a5ba78d9fee888 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 00:50:39 +0900 Subject: [PATCH 092/180] wip --- .../admin/federation/delete-all-files.ts | 15 +++++++-------- .../refresh-remote-instance-metadata.ts | 15 +++++++-------- .../admin/federation/remove-all-following.ts | 12 +++++++----- .../endpoints/admin/federation/update-instance.ts | 13 +++++-------- .../server/api/endpoints/admin/moderators/add.ts | 6 ++++-- .../api/endpoints/admin/moderators/remove.ts | 6 ++++-- 6 files changed, 34 insertions(+), 33 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts index 68a902cf299a..4939144ce437 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { deleteFile } from '@/services/drive/delete-file.js'; -import { DriveFiles } from '@/models/index.js'; +import type { DriveFiles } from '@/models/index.js'; +import { DriveService } from '@/services/DriveService'; export const meta = { tags: ['admin'], @@ -22,19 +22,18 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private driveService: DriveService, ) { super(meta, paramDef, async (ps, me) => { - const files = await DriveFiles.findBy({ + const files = await this.driveFilesRepository.findBy({ userHost: ps.host, }); for (const file of files) { - deleteFile(file); + this.driveService.deleteFile(file); } }); } diff --git a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts index 71fd49717c0a..6b62d1bf67a0 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Instances } from '@/models/index.js'; +import type { Instances } from '@/models/index.js'; import { toPuny } from '@/misc/convert-host.js'; -import { fetchInstanceMetadata } from '@/services/fetch-instance-metadata.js'; +import { FetchInstanceMetadataService } from '@/services/FetchInstanceMetadataService.js'; export const meta = { tags: ['admin'], @@ -23,20 +23,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('instancesRepository') + private instancesRepository: typeof Instances, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private fetchInstanceMetadataService: FetchInstanceMetadataService, ) { super(meta, paramDef, async (ps, me) => { - const instance = await Instances.findOneBy({ host: toPuny(ps.host) }); + const instance = await this.instancesRepository.findOneBy({ host: toPuny(ps.host) }); if (instance == null) { throw new Error('instance not found'); } - fetchInstanceMetadata(instance, true); + this.fetchInstanceMetadataService.fetchInstanceMetadata(instance, true); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts index e9253a7c00ed..6a6c930803ec 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import deleteFollowing from '@/services/following/delete.js'; -import { Followings, Users } from '@/models/index.js'; +import type { Followings, Users } from '@/models/index.js'; +import { UserFollowingService } from '@/services/UserFollowingService.js'; export const meta = { tags: ['admin'], @@ -26,10 +26,12 @@ export default class extends Endpoint { private usersRepository: typeof Users, @Inject('notesRepository') - private notesRepository: typeof Notes, + private followingsRepository: typeof Followings, + + private userFollowingService: UserFollowingService, ) { super(meta, paramDef, async (ps, me) => { - const followings = await Followings.findBy({ + const followings = await this.followingsRepository.findBy({ followerHost: ps.host, }); @@ -39,7 +41,7 @@ export default class extends Endpoint { ]))); for (const pair of pairs) { - deleteFollowing(pair[0], pair[1]); + this.userFollowingService.unfollow(pair[0], pair[1]); } }); } diff --git a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts index ead725d62d7b..e7aea70ff69f 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Instances } from '@/models/index.js'; +import type { Instances } from '@/models/index.js'; import { toPuny } from '@/misc/convert-host.js'; export const meta = { @@ -23,20 +23,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('instancesRepository') + private instancesRepository: typeof Instances, ) { super(meta, paramDef, async (ps, me) => { - const instance = await Instances.findOneBy({ host: toPuny(ps.host) }); + const instance = await this.instancesRepository.findOneBy({ host: toPuny(ps.host) }); if (instance == null) { throw new Error('instance not found'); } - Instances.update({ host: toPuny(ps.host) }, { + this.instancesRepository.update({ host: toPuny(ps.host) }, { isSuspended: ps.isSuspended, }); }); diff --git a/packages/backend/src/server/api/endpoints/admin/moderators/add.ts b/packages/backend/src/server/api/endpoints/admin/moderators/add.ts index 025a3093bbed..be688fbb73ff 100644 --- a/packages/backend/src/server/api/endpoints/admin/moderators/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/moderators/add.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; -import { publishInternalEvent } from '@/services/stream.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; export const meta = { tags: ['admin'], @@ -24,6 +24,8 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); @@ -40,7 +42,7 @@ export default class extends Endpoint { isModerator: true, }); - publishInternalEvent('userChangeModeratorState', { id: user.id, isModerator: true }); + this.globalEventService.publishInternalEvent('userChangeModeratorState', { id: user.id, isModerator: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts b/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts index 44df3705f5a6..4c5eb114e9a1 100644 --- a/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts +++ b/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; -import { publishInternalEvent } from '@/services/stream.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; export const meta = { tags: ['admin'], @@ -24,6 +24,8 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); @@ -36,7 +38,7 @@ export default class extends Endpoint { isModerator: false, }); - publishInternalEvent('userChangeModeratorState', { id: user.id, isModerator: false }); + this.globalEventService.publishInternalEvent('userChangeModeratorState', { id: user.id, isModerator: false }); }); } } From 88b6d86a69003bc4c286d051cb2827aa97fdc39d Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 00:53:03 +0900 Subject: [PATCH 093/180] wip --- .../src/server/api/common/GetterService.ts | 71 +++++++++++++++++++ .../backend/src/server/api/common/getters.ts | 56 --------------- 2 files changed, 71 insertions(+), 56 deletions(-) create mode 100644 packages/backend/src/server/api/common/GetterService.ts delete mode 100644 packages/backend/src/server/api/common/getters.ts diff --git a/packages/backend/src/server/api/common/GetterService.ts b/packages/backend/src/server/api/common/GetterService.ts new file mode 100644 index 000000000000..09cf2af011f2 --- /dev/null +++ b/packages/backend/src/server/api/common/GetterService.ts @@ -0,0 +1,71 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Notes , Users } from '@/models/index.js'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; +import type { User } from '@/models/entities/user.js'; +import type { Note } from '@/models/entities/note.js'; + +@Injectable() +export class GetterService { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + ) { + } + + /** + * Get note for API processing + */ + public async getNote(noteId: Note['id']) { + const note = await this.notesRepository.findOneBy({ id: noteId }); + + if (note == null) { + throw new IdentifiableError('9725d0ce-ba28-4dde-95a7-2cbb2c15de24', 'No such note.'); + } + + return note; + } + + /** + * Get user for API processing + */ + public async getUser(userId: User['id']) { + const user = await this.usersRepository.findOneBy({ id: userId }); + + if (user == null) { + throw new IdentifiableError('15348ddd-432d-49c2-8a5a-8069753becff', 'No such user.'); + } + + return user; + } + + /** + * Get remote user for API processing + */ + public async getRemoteUser(userId: User['id']) { + const user = await this.getUser(userId); + + if (!this.usersRepository.isRemoteUser(user)) { + throw new Error('user is not a remote user'); + } + + return user; + } + + /** + * Get local user for API processing + */ + public async getLocalUser(userId: User['id']) { + const user = await this.getUser(userId); + + if (!this.usersRepository.isLocalUser(user)) { + throw new Error('user is not a local user'); + } + + return user; + } +} + diff --git a/packages/backend/src/server/api/common/getters.ts b/packages/backend/src/server/api/common/getters.ts deleted file mode 100644 index 783ea9ef757b..000000000000 --- a/packages/backend/src/server/api/common/getters.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { IdentifiableError } from '@/misc/identifiable-error.js'; -import { User } from '@/models/entities/user.js'; -import { Note } from '@/models/entities/note.js'; -import { Notes, Users } from '@/models/index.js'; - -/** - * Get note for API processing - */ -export async function getNote(noteId: Note['id']) { - const note = await Notes.findOneBy({ id: noteId }); - - if (note == null) { - throw new IdentifiableError('9725d0ce-ba28-4dde-95a7-2cbb2c15de24', 'No such note.'); - } - - return note; -} - -/** - * Get user for API processing - */ -export async function getUser(userId: User['id']) { - const user = await Users.findOneBy({ id: userId }); - - if (user == null) { - throw new IdentifiableError('15348ddd-432d-49c2-8a5a-8069753becff', 'No such user.'); - } - - return user; -} - -/** - * Get remote user for API processing - */ -export async function getRemoteUser(userId: User['id']) { - const user = await getUser(userId); - - if (!Users.isRemoteUser(user)) { - throw new Error('user is not a remote user'); - } - - return user; -} - -/** - * Get local user for API processing - */ -export async function getLocalUser(userId: User['id']) { - const user = await getUser(userId); - - if (!Users.isLocalUser(user)) { - throw new Error('user is not a local user'); - } - - return user; -} From 908e558e983e836c42bc8760651f421e092b6e3e Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 02:21:58 +0900 Subject: [PATCH 094/180] wip --- .../api/endpoints/admin/abuse-user-reports.ts | 9 ++- .../api/endpoints/admin/delete-account.ts | 6 +- .../admin/delete-all-files-of-a-user.ts | 15 ++-- .../admin/drive-capacity-override.ts | 11 ++- .../api/endpoints/admin/get-index-stats.ts | 12 ++- .../api/endpoints/admin/get-table-stats.ts | 13 ++-- .../api/endpoints/admin/get-user-ips.ts | 11 +-- .../src/server/api/endpoints/admin/invite.ts | 9 ++- .../src/server/api/endpoints/admin/meta.ts | 18 ++--- .../api/endpoints/admin/promo/create.ts | 14 ++-- .../server/api/endpoints/admin/queue/clear.ts | 15 ++-- .../endpoints/admin/queue/deliver-delayed.ts | 7 +- .../endpoints/admin/queue/inbox-delayed.ts | 7 +- .../server/api/endpoints/admin/queue/stats.ts | 17 ++-- .../server/api/endpoints/admin/relays/add.ts | 5 +- .../server/api/endpoints/admin/relays/list.ts | 5 +- .../api/endpoints/admin/relays/remove.ts | 5 +- .../api/endpoints/admin/reset-password.ts | 8 +- .../admin/resolve-abuse-user-report.ts | 23 +++--- .../server/api/endpoints/admin/send-email.ts | 6 +- .../server/api/endpoints/admin/server-info.ts | 18 +++-- .../endpoints/admin/show-moderation-logs.ts | 9 ++- .../server/api/endpoints/admin/show-user.ts | 13 ++-- .../server/api/endpoints/admin/show-users.ts | 5 +- .../api/endpoints/admin/silence-user.ts | 14 ++-- .../api/endpoints/admin/suspend-user.ts | 78 ++++++++++--------- .../api/endpoints/admin/unsilence-user.ts | 14 ++-- .../api/endpoints/admin/unsuspend-user.ts | 14 ++-- .../server/api/endpoints/admin/update-meta.ts | 18 ++--- .../api/endpoints/admin/update-user-note.ts | 8 +- .../src/server/api/endpoints/admin/vacuum.ts | 48 ------------ 31 files changed, 218 insertions(+), 237 deletions(-) delete mode 100644 packages/backend/src/server/api/endpoints/admin/vacuum.ts diff --git a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts index 0fe0454ea522..effb8a9e45d3 100644 --- a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts +++ b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { AbuseUserReports } from '@/models/index.js'; +import type { AbuseUserReports } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; export const meta = { @@ -89,10 +89,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('abuseUserReportsRepository') + private abuseUserReportsRepository: typeof AbuseUserReports, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(AbuseUserReports.createQueryBuilder('report'), ps.sinceId, ps.untilId); + const query = this.queryService.makePaginationQuery(this.abuseUserReportsRepository.createQueryBuilder('report'), ps.sinceId, ps.untilId); switch (ps.state) { case 'resolved': query.andWhere('report.resolved = TRUE'); break; @@ -111,7 +114,7 @@ export default class extends Endpoint { const reports = await query.take(ps.limit).getMany(); - return await AbuseUserReports.packMany(reports); + return await this.abuseUserReportsRepository.packMany(reports); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/delete-account.ts b/packages/backend/src/server/api/endpoints/admin/delete-account.ts index 14364d6ce90b..3d6777d44dd7 100644 --- a/packages/backend/src/server/api/endpoints/admin/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/admin/delete-account.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users } from '@/models/index.js'; -import { deleteAccount } from '@/services/delete-account.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DeleteAccountService } from '@/services/DeleteAccountService'; export const meta = { tags: ['admin'], @@ -27,6 +27,8 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + private deleteAccountService: DeleteAccountService, ) { super(meta, paramDef, async (ps) => { const user = await this.usersRepository.findOneByOrFail({ id: ps.userId }); @@ -34,7 +36,7 @@ export default class extends Endpoint { return; } - await deleteAccount(user); + await this.deleteAccountService.deleteAccount(user); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts index 7bdeeef5df50..16cfcde75349 100644 --- a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { deleteFile } from '@/services/drive/delete-file.js'; -import { DriveFiles } from '@/models/index.js'; +import type { DriveFiles } from '@/models/index.js'; +import { DriveService } from '@/services/DriveService'; export const meta = { tags: ['admin'], @@ -22,19 +22,18 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private driveService: DriveService, ) { super(meta, paramDef, async (ps, me) => { - const files = await DriveFiles.findBy({ + const files = await this.driveFilesRepository.findBy({ userId: ps.userId, }); for (const file of files) { - deleteFile(file); + this.driveService.deleteFile(file); } }); } diff --git a/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts b/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts index a49f50e6d083..fcb28b141dca 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Users } from '@/models/index.js'; -import { User } from '@/models/entities/user.js'; -import { insertModerationLog } from '@/services/insert-moderation-log.js'; +import type { Users } from '@/models/index.js'; +import { ModerationLogService } from '@/services/ModerationLogService.js'; + export const meta = { tags: ['admin'], @@ -26,8 +26,7 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); @@ -51,7 +50,7 @@ export default class extends Endpoint { driveCapacityOverrideMb: ps.overrideMb, }); - insertModerationLog(me, 'change-drive-capacity-override', { + this.moderationLogService.insertModerationLog(me, 'change-drive-capacity-override', { targetId: user.id, }); }); diff --git a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts index 891b22f31c4a..09f54375aae4 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; +import { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { db } from '@/db/postgre.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -19,14 +20,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject(DI_SYMBOLS.db) + private db: DataSource, ) { super(meta, paramDef, async () => { - const stats = await db.query('SELECT * FROM pg_indexes;').then(recs => { + const stats = await this.db.query('SELECT * FROM pg_indexes;').then(recs => { const res = [] as { tablename: string; indexname: string; }[]; for (const rec of recs) { res.push(rec); diff --git a/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts index 0a6009923a56..9259eb95ba55 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; -import { db } from '@/db/postgre.js'; +import { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -30,15 +31,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject(DI_SYMBOLS.db) + private db: DataSource, ) { super(meta, paramDef, async () => { - const sizes = await - db.query(` + const sizes = await this.db.query(` SELECT relname AS "table", reltuples as "count", pg_total_relation_size(C.oid) AS "size" FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) WHERE nspname NOT IN ('pg_catalog', 'information_schema') diff --git a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts index fd7a24f3e160..a68b98be356d 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserIps } from '@/models/index.js'; +import type { UserIps } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -21,14 +21,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('userIpsRepository') + private userIpsRepository: typeof UserIps, ) { super(meta, paramDef, async (ps, me) => { - const ips = await UserIps.find({ + const ips = await this.userIpsRepository.find({ where: { userId: ps.userId }, order: { createdAt: 'DESC' }, take: 30, diff --git a/packages/backend/src/server/api/endpoints/admin/invite.ts b/packages/backend/src/server/api/endpoints/admin/invite.ts index 665ecd10a869..b651a29d72cf 100644 --- a/packages/backend/src/server/api/endpoints/admin/invite.ts +++ b/packages/backend/src/server/api/endpoints/admin/invite.ts @@ -1,8 +1,8 @@ import rndstr from 'rndstr'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { RegistrationTickets } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import type { RegistrationTickets } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; export const meta = { tags: ['admin'], @@ -35,6 +35,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('registrationTicketsRepository') + private registrationTicketsRepository: typeof RegistrationTickets, + private idService: IdService, ) { super(meta, paramDef, async () => { @@ -43,7 +46,7 @@ export default class extends Endpoint { chars: '2-9A-HJ-NP-Z', // [0-9A-Z] w/o [01IO] (32 patterns) }); - await RegistrationTickets.insert({ + await this.registrationTicketsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), code, diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index ca95f588b339..22a484f0911b 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; -import config from '@/config/index.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { MetaService } from '@/services/MetaService.js'; +import { Config } from '@/config/types.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; export const meta = { tags: ['meta'], @@ -344,21 +345,20 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject(DI_SYMBOLS.config) + private config: Config, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private metaService: MetaService, ) { super(meta, paramDef, async (ps, me) => { - const instance = await fetchMeta(true); + const instance = await this.metaService.fetch(true); return { maintainerName: instance.maintainerName, maintainerEmail: instance.maintainerEmail, - version: config.version, + version: this.config.version, name: instance.name, - uri: config.url, + uri: this.config.url, description: instance.description, langs: instance.langs, tosUrl: instance.ToSUrl, diff --git a/packages/backend/src/server/api/endpoints/admin/promo/create.ts b/packages/backend/src/server/api/endpoints/admin/promo/create.ts index 7c369d0169e0..9d63727f9e37 100644 --- a/packages/backend/src/server/api/endpoints/admin/promo/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/promo/create.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { PromoNotes } from '@/models/index.js'; +import type { PromoNotes } from '@/models/index.js'; +import { GetterService } from '@/server/api/common/GetterService.js'; import { ApiError } from '../../../error.js'; -import { getNote } from '../../../common/getters.js'; export const meta = { tags: ['admin'], @@ -38,20 +38,24 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('promoNotesRepository') + private promoNotesRepository: typeof PromoNotes, + + private getterService: GetterService, ) { super(meta, paramDef, async (ps, me) => { - const note = await getNote(ps.noteId).catch(e => { + const note = await this.getterService.getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; }); - const exist = await PromoNotes.findOneBy({ noteId: note.id }); + const exist = await this.promoNotesRepository.findOneBy({ noteId: note.id }); if (exist != null) { throw new ApiError(meta.errors.alreadyPromoted); } - await PromoNotes.insert({ + await this.promoNotesRepository.insert({ noteId: note.id, expiresAt: new Date(ps.expiresAt), userId: note.userId, diff --git a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts index cf56a23c3489..ceaa4c273704 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { destroy } from '@/queue/index.js'; -import { insertModerationLog } from '@/services/insert-moderation-log.js'; +import { ModerationLogService } from '@/services/ModerationLogService.js'; +import { QueueService } from '@/queue/queue.service'; export const meta = { tags: ['admin'], @@ -20,16 +20,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + private moderationLogService: ModerationLogService, + private queueService: QueueService, ) { super(meta, paramDef, async (ps, me) => { - destroy(); + this.queueService.destroy(); - insertModerationLog(me, 'clearQueue'); + this.moderationLogService.insertModerationLog(me, 'clearQueue'); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts index 5a1e48021d22..e327e40d5b14 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts @@ -1,7 +1,7 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; -import { deliverQueue } from '@/queue/queues.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DeliverQueue } from '@/queue/queue.module.js'; export const meta = { tags: ['admin'], @@ -43,16 +43,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('queue:deliver') public deliverQueue: DeliverQueue, ) { super(meta, paramDef, async (ps, me) => { - const jobs = await deliverQueue.getJobs(['delayed']); + const jobs = await this.deliverQueue.getJobs(['delayed']); const res = [] as [string, number][]; for (const job of jobs) { const host = new URL(job.data.to).host; if (res.find(x => x[0] === host)) { - res.find(x => x[0] === host)![1]++; + res.find(x => x[0] === host)![1]++; } else { res.push([host, 1]); } diff --git a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts index 062642a71b2f..a1010b5063a9 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts @@ -1,7 +1,7 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { inboxQueue } from '@/queue/queues.js'; +import { InboxQueue } from '@/queue/queue.module.js'; export const meta = { tags: ['admin'], @@ -43,16 +43,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('queue:inbox') public inboxQueue: InboxQueue, ) { super(meta, paramDef, async (ps, me) => { - const jobs = await inboxQueue.getJobs(['delayed']); + const jobs = await this.inboxQueue.getJobs(['delayed']); const res = [] as [string, number][]; for (const job of jobs) { const host = new URL(job.data.signature.keyId).host; if (res.find(x => x[0] === host)) { - res.find(x => x[0] === host)![1]++; + res.find(x => x[0] === host)![1]++; } else { res.push([host, 1]); } diff --git a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts index ff0482c46eb7..1b2bc99f9dc4 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; -import { deliverQueue, inboxQueue, dbQueue, objectStorageQueue } from '@/queue/queues.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from '@/queue/queue.module.js'; export const meta = { tags: ['admin'], @@ -42,12 +42,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('queue:system') public systemQueue: SystemQueue, + @Inject('queue:endedPollNotification') public endedPollNotificationQueue: EndedPollNotificationQueue, + @Inject('queue:deliver') public deliverQueue: DeliverQueue, + @Inject('queue:inbox') public inboxQueue: InboxQueue, + @Inject('queue:db') public dbQueue: DbQueue, + @Inject('queue:objectStorage') public objectStorageQueue: ObjectStorageQueue, + @Inject('queue:webhookDeliver') public webhookDeliverQueue: WebhookDeliverQueue, ) { super(meta, paramDef, async (ps, me) => { - const deliverJobCounts = await deliverQueue.getJobCounts(); - const inboxJobCounts = await inboxQueue.getJobCounts(); - const dbJobCounts = await dbQueue.getJobCounts(); - const objectStorageJobCounts = await objectStorageQueue.getJobCounts(); + const deliverJobCounts = await this.deliverQueue.getJobCounts(); + const inboxJobCounts = await this.inboxQueue.getJobCounts(); + const dbJobCounts = await this.dbQueue.getJobCounts(); + const objectStorageJobCounts = await this.objectStorageQueue.getJobCounts(); return { deliver: deliverJobCounts, diff --git a/packages/backend/src/server/api/endpoints/admin/relays/add.ts b/packages/backend/src/server/api/endpoints/admin/relays/add.ts index 1750a8d0f766..e1f88aaa3fe5 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/add.ts @@ -1,7 +1,7 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { addRelay } from '@/services/relay.js'; +import { RelayService } from '@/services/RelayService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -58,6 +58,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private relayService: RelayService, ) { super(meta, paramDef, async (ps, me) => { try { @@ -66,7 +67,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.invalidUrl); } - return await addRelay(ps.inbox); + return await this.relayService.addRelay(ps.inbox); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/relays/list.ts b/packages/backend/src/server/api/endpoints/admin/relays/list.ts index 5ac2f05d20c6..23ce41bcf346 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/list.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { listRelay } from '@/services/relay.js'; +import { RelayService } from '@/services/RelayService.js'; export const meta = { tags: ['admin'], @@ -50,9 +50,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private relayService: RelayService, ) { super(meta, paramDef, async (ps, me) => { - return await listRelay(); + return await this.relayService.listRelay(); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/relays/remove.ts b/packages/backend/src/server/api/endpoints/admin/relays/remove.ts index 808a7afcab84..5cdc14438f36 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/remove.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/remove.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { removeRelay } from '@/services/relay.js'; +import { RelayService } from '@/services/RelayService.js'; export const meta = { tags: ['admin'], @@ -21,9 +21,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private relayService: RelayService, ) { super(meta, paramDef, async (ps, me) => { - return await removeRelay(ps.inbox); + return await this.relayService.removeRelay(ps.inbox); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/reset-password.ts b/packages/backend/src/server/api/endpoints/admin/reset-password.ts index 328ee15cf961..45fcf477d0a6 100644 --- a/packages/backend/src/server/api/endpoints/admin/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/admin/reset-password.ts @@ -2,8 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; import rndstr from 'rndstr'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users } from '@/models/index.js'; -import { UserProfiles } from '@/models/index.js'; +import type { Users , UserProfiles } from '@/models/index.js'; export const meta = { tags: ['admin'], @@ -39,6 +38,9 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, ) { super(meta, paramDef, async (ps) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); @@ -56,7 +58,7 @@ export default class extends Endpoint { // Generate hash of password const hash = bcrypt.hashSync(passwd); - await UserProfiles.update({ + await this.userProfilesRepository.update({ userId: user.id, }, { password: hash, diff --git a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts index 40217e7278c1..1b291d922b02 100644 --- a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts +++ b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts @@ -1,11 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users } from '@/models/index.js'; -import { AbuseUserReports } from '@/models/index.js'; -import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; -import { renderFlag } from '@/services/remote/activitypub/renderer/flag.js'; -import type { InstanceActorService } from '@/services/InstanceActorService.js'; -import type { QueueService } from '@/queue/queue.service'; +import type { Users , AbuseUserReports } from '@/models/index.js'; +import { InstanceActorService } from '@/services/InstanceActorService.js'; +import { QueueService } from '@/queue/queue.service.js'; +import { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; export const meta = { tags: ['admin'], @@ -23,6 +21,8 @@ export const paramDef = { required: ['reportId'], } as const; +// TODO: ロジックをサービスに切り出す + // eslint-disable-next-line import/no-default-export @Injectable() export default class extends Endpoint { @@ -30,12 +30,15 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - private queueService: QueueService, + @Inject('abuseUserReportsRepository') + private abuseUserReportsRepository: typeof AbuseUserReports, + private queueService: QueueService, private instanceActorService: InstanceActorService, + private apRendererService: ApRendererService, ) { super(meta, paramDef, async (ps, me) => { - const report = await AbuseUserReports.findOneBy({ id: ps.reportId }); + const report = await this.abuseUserReportsRepository.findOneBy({ id: ps.reportId }); if (report == null) { throw new Error('report not found'); @@ -45,10 +48,10 @@ export default class extends Endpoint { const actor = await this.instanceActorService.getInstanceActor(); const targetUser = await this.usersRepository.findOneByOrFail({ id: report.targetUserId }); - this.queueService.deliver(actor, renderActivity(renderFlag(actor, [targetUser.uri!], report.comment)), targetUser.inbox); + this.queueService.deliver(actor, this.apRendererService.renderActivity(this.apRendererService.renderFlag(actor, [targetUser.uri!], report.comment)), targetUser.inbox); } - await AbuseUserReports.update(report.id, { + await this.abuseUserReportsRepository.update(report.id, { resolved: true, assigneeId: me.id, forwarded: ps.forward && report.targetUserHost != null, diff --git a/packages/backend/src/server/api/endpoints/admin/send-email.ts b/packages/backend/src/server/api/endpoints/admin/send-email.ts index 51465dbf1017..7603a5a3c6b8 100644 --- a/packages/backend/src/server/api/endpoints/admin/send-email.ts +++ b/packages/backend/src/server/api/endpoints/admin/send-email.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { sendEmail } from '@/services/send-email.js'; +import { EmailService } from '@/services/EmailService.js'; export const meta = { tags: ['admin'], @@ -23,10 +23,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - + private emailService: EmailService, ) { super(meta, paramDef, async (ps, me) => { - await sendEmail(ps.to, ps.subject, ps.text, ps.text); + await this.emailService.sendEmail(ps.to, ps.subject, ps.text, ps.text); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/server-info.ts b/packages/backend/src/server/api/endpoints/admin/server-info.ts index d5bc57d90f7e..3d1287b26a5c 100644 --- a/packages/backend/src/server/api/endpoints/admin/server-info.ts +++ b/packages/backend/src/server/api/endpoints/admin/server-info.ts @@ -1,9 +1,10 @@ import * as os from 'node:os'; import si from 'systeminformation'; import { Inject, Injectable } from '@nestjs/common'; +import { DataSource } from 'typeorm'; +import Redis from 'ioredis'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { db } from '@/db/postgre.js'; -import { redisClient } from '../../../../db/redis.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -98,18 +99,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject(DI_SYMBOLS.db) + private db: DataSource, + + @Inject(DI_SYMBOLS.redis) + private redisClient: Redis.Redis, - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { super(meta, paramDef, async () => { const memStats = await si.mem(); const fsStats = await si.fsSize(); const netInterface = await si.networkInterfaceDefault(); - const redisServerInfo = await redisClient.info('Server'); + const redisServerInfo = await this.redisClient.info('Server'); const m = redisServerInfo.match(new RegExp('^redis_version:(.*)', 'm')); const redis_version = m?.[1]; @@ -117,7 +119,7 @@ export default class extends Endpoint { machine: os.hostname(), os: os.platform(), node: process.version, - psql: await db.query('SHOW server_version').then(x => x[0].server_version), + psql: await this.db.query('SHOW server_version').then(x => x[0].server_version), redis: redis_version, cpu: { model: os.cpus()[0].model, diff --git a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts index de3e2197f69e..b9e500785dbe 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ModerationLogs } from '@/models/index.js'; +import type { ModerationLogs } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; export const meta = { @@ -63,14 +63,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('moderationLogsRepository') + private moderationLogsRepository: typeof ModerationLogs, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(ModerationLogs.createQueryBuilder('report'), ps.sinceId, ps.untilId); + const query = this.queryService.makePaginationQuery(this.moderationLogsRepository.createQueryBuilder('report'), ps.sinceId, ps.untilId); const reports = await query.take(ps.limit).getMany(); - return await ModerationLogs.packMany(reports); + return await this.moderationLogsRepository.packMany(reports); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index 683a6ffdb643..bb09320b0b95 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Signins, UserProfiles, Users } from '@/models/index.js'; +import type { Users , Signins, UserProfiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -29,13 +29,16 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + @Inject('signinsRepository') + private signinsRepository: typeof Signins, ) { super(meta, paramDef, async (ps, me) => { const [user, profile] = await Promise.all([ this.usersRepository.findOneBy({ id: ps.userId }), - UserProfiles.findOneBy({ userId: ps.userId }), + this.userProfilesRepository.findOneBy({ userId: ps.userId }), ]); if (user == null || profile == null) { @@ -60,7 +63,7 @@ export default class extends Endpoint { maskedKeys.forEach(key => profile.integrations[integration][key] = ''); }); - const signins = await Signins.findBy({ userId: user.id }); + const signins = await this.signinsRepository.findBy({ userId: user.id }); return { email: profile.email, diff --git a/packages/backend/src/server/api/endpoints/admin/show-users.ts b/packages/backend/src/server/api/endpoints/admin/show-users.ts index 4ef1572409ea..dd8b21eb343b 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-users.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -44,9 +44,6 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const query = this.usersRepository.createQueryBuilder('user'); diff --git a/packages/backend/src/server/api/endpoints/admin/silence-user.ts b/packages/backend/src/server/api/endpoints/admin/silence-user.ts index 9c6da20883f3..506cddf22364 100644 --- a/packages/backend/src/server/api/endpoints/admin/silence-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/silence-user.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Users } from '@/models/index.js'; -import { insertModerationLog } from '@/services/insert-moderation-log.js'; -import { publishInternalEvent } from '@/services/stream.js'; +import { ModerationLogService } from '@/services/ModerationLogService.js'; +import type { Users } from '@/models/index.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; export const meta = { tags: ['admin'], @@ -26,8 +26,8 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private moderationLogService: ModerationLogService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); @@ -44,9 +44,9 @@ export default class extends Endpoint { isSilenced: true, }); - publishInternalEvent('userChangeSilencedState', { id: user.id, isSilenced: true }); + this.globalEventService.publishInternalEvent('userChangeSilencedState', { id: user.id, isSilenced: true }); - insertModerationLog(me, 'silence', { + this.moderationLogService.insertModerationLog(me, 'silence', { targetId: user.id, }); }); diff --git a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts index 76830b9d285e..7aadf35eb735 100644 --- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts @@ -1,11 +1,11 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import deleteFollowing from '@/services/following/delete.js'; -import { Users, Followings, Notifications } from '@/models/index.js'; +import type { Users , Followings , Notifications } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; -import { insertModerationLog } from '@/services/insert-moderation-log.js'; -import { doPostSuspend } from '@/services/suspend-user.js'; -import { publishUserEvent } from '@/services/stream.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { ModerationLogService } from '@/services/ModerationLogService.js'; +import { UserSuspendService } from '@/services/UserSuspendService.js'; +import { UserFollowingService } from '@/services/UserFollowingService.js'; export const meta = { tags: ['admin'], @@ -29,8 +29,16 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + @Inject('notificationsRepository') + private notificationsRepository: typeof Notifications, + + private userFollowingService: UserFollowingService, + private userSuspendService: UserSuspendService, + private moderationLogService: ModerationLogService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); @@ -51,47 +59,47 @@ export default class extends Endpoint { isSuspended: true, }); - insertModerationLog(me, 'suspend', { + this.moderationLogService.insertModerationLog(me, 'suspend', { targetId: user.id, }); // Terminate streaming if (this.usersRepository.isLocalUser(user)) { - publishUserEvent(user.id, 'terminate', {}); + this.globalEventService.publishUserEvent(user.id, 'terminate', {}); } (async () => { - await doPostSuspend(user).catch(e => {}); - await unFollowAll(user).catch(e => {}); - await readAllNotify(user).catch(e => {}); + await this.userSuspendService.doPostSuspend(user).catch(e => {}); + await this.#unFollowAll(user).catch(e => {}); + await this.#readAllNotify(user).catch(e => {}); })(); }); } -} -async function unFollowAll(follower: User) { - const followings = await Followings.findBy({ - followerId: follower.id, - }); - - for (const following of followings) { - const followee = await this.usersRepository.findOneBy({ - id: following.followeeId, + async #unFollowAll(follower: User) { + const followings = await this.followingsRepository.findBy({ + followerId: follower.id, }); - - if (followee == null) { - throw `Cant find followee ${following.followeeId}`; + + for (const following of followings) { + const followee = await this.usersRepository.findOneBy({ + id: following.followeeId, + }); + + if (followee == null) { + throw `Cant find followee ${following.followeeId}`; + } + + await this.userFollowingService.unfollow(follower, followee, true); } - - await deleteFollowing(follower, followee, true); } -} - -async function readAllNotify(notifier: User) { - await Notifications.update({ - notifierId: notifier.id, - isRead: false, - }, { - isRead: true, - }); + + async #readAllNotify(notifier: User) { + await this.notificationsRepository.update({ + notifierId: notifier.id, + isRead: false, + }, { + isRead: true, + }); + } } diff --git a/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts b/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts index 3638e22f4f44..7ce4cae8a445 100644 --- a/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Users } from '@/models/index.js'; -import { insertModerationLog } from '@/services/insert-moderation-log.js'; -import { publishInternalEvent } from '@/services/stream.js'; +import type { Users } from '@/models/index.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { ModerationLogService } from '@/services/ModerationLogService.js'; export const meta = { tags: ['admin'], @@ -26,8 +26,8 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private moderationLogService: ModerationLogService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); @@ -40,9 +40,9 @@ export default class extends Endpoint { isSilenced: false, }); - publishInternalEvent('userChangeSilencedState', { id: user.id, isSilenced: false }); + this.globalEventService.publishInternalEvent('userChangeSilencedState', { id: user.id, isSilenced: false }); - insertModerationLog(me, 'unsilence', { + this.moderationLogService.insertModerationLog(me, 'unsilence', { targetId: user.id, }); }); diff --git a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts index 50b689047529..c526cb70f556 100644 --- a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Users } from '@/models/index.js'; -import { insertModerationLog } from '@/services/insert-moderation-log.js'; -import { doPostUnsuspend } from '@/services/unsuspend-user.js'; +import type { Users } from '@/models/index.js'; +import { ModerationLogService } from '@/services/ModerationLogService.js'; +import { UserSuspendService } from '@/services/UserSuspendService.js'; export const meta = { tags: ['admin'], @@ -26,8 +26,8 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private userSuspendService: UserSuspendService, + private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); @@ -40,11 +40,11 @@ export default class extends Endpoint { isSuspended: false, }); - insertModerationLog(me, 'unsuspend', { + this.moderationLogService.insertModerationLog(me, 'unsuspend', { targetId: user.id, }); - doPostUnsuspend(user); + this.userSuspendService.doPostUnsuspend(user); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 77d99ed66e62..d8cf64d870cc 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -1,9 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; +import { DataSource } from 'typeorm'; import { Meta } from '@/models/entities/meta.js'; -import { insertModerationLog } from '@/services/insert-moderation-log.js'; +import { ModerationLogService } from '@/services/ModerationLogService.js'; import { DB_MAX_NOTE_TEXT_LENGTH } from '@/misc/hard-limits.js'; -import { db } from '@/db/postgre.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -111,11 +112,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject(DI_SYMBOLS.db) + private db: DataSource, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { const set = {} as Partial; @@ -137,7 +137,7 @@ export default class extends Endpoint { } if (Array.isArray(ps.pinnedUsers)) { - set.pinnedUsers = ps.pinnedthis.usersRepository.filter(Boolean); + set.pinnedUsers = ps.pinnedUsers.filter(Boolean); } if (Array.isArray(ps.hiddenTags)) { @@ -436,7 +436,7 @@ export default class extends Endpoint { set.enableActiveEmailValidation = ps.enableActiveEmailValidation; } - await db.transaction(async transactionalEntityManager => { + await this.db.transaction(async transactionalEntityManager => { const metas = await transactionalEntityManager.find(Meta, { order: { id: 'DESC', @@ -452,7 +452,7 @@ export default class extends Endpoint { } }); - insertModerationLog(me, 'updateMeta'); + this.moderationLogService.insertModerationLog(me, 'updateMeta'); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts index e89f1593e960..a986eb1a9565 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserProfiles, Users } from '@/models/index.js'; +import type { UserProfiles, Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -25,8 +25,8 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); @@ -35,7 +35,7 @@ export default class extends Endpoint { throw new Error('user not found'); } - await UserProfiles.update({ userId: user.id }, { + await this.userProfilesRepository.update({ userId: user.id }, { moderationNote: ps.text, }); }); diff --git a/packages/backend/src/server/api/endpoints/admin/vacuum.ts b/packages/backend/src/server/api/endpoints/admin/vacuum.ts deleted file mode 100644 index a3aa30fba4a9..000000000000 --- a/packages/backend/src/server/api/endpoints/admin/vacuum.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import { insertModerationLog } from '@/services/insert-moderation-log.js'; -import { db } from '@/db/postgre.js'; - -export const meta = { - tags: ['admin'], - - requireCredential: true, - requireModerator: true, -} as const; - -export const paramDef = { - type: 'object', - properties: { - full: { type: 'boolean' }, - analyze: { type: 'boolean' }, - }, - required: ['full', 'analyze'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint { - constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, - ) { - super(meta, paramDef, async (ps, me) => { - const params: string[] = []; - - if (ps.full) { - params.push('FULL'); - } - - if (ps.analyze) { - params.push('ANALYZE'); - } - - db.query('VACUUM ' + params.join(' ')); - - insertModerationLog(me, 'vacuum', ps); - }); - } -} From b18490a7799e43e8898ca57cf0b17134a77f3f42 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 02:37:44 +0900 Subject: [PATCH 095/180] wip --- .../server/api/endpoints/antennas/create.ts | 28 +++-- .../server/api/endpoints/antennas/delete.ts | 14 ++- .../src/server/api/endpoints/antennas/list.ts | 9 +- .../server/api/endpoints/antennas/notes.ts | 20 +++- .../src/server/api/endpoints/antennas/show.ts | 13 +- .../server/api/endpoints/antennas/update.ts | 26 ++-- .../src/server/api/endpoints/ap/get.ts | 5 +- .../src/server/api/endpoints/ap/show.ts | 113 +++++++++--------- .../src/server/api/endpoints/app/create.ts | 13 +- .../src/server/api/endpoints/app/show.ts | 13 +- .../src/server/api/endpoints/auth/accept.ts | 23 ++-- .../api/endpoints/auth/session/generate.ts | 24 ++-- .../server/api/endpoints/auth/session/show.ts | 8 +- .../api/endpoints/auth/session/userkey.ts | 20 +++- .../api/endpoints/charts/active-users.ts | 2 +- .../server/api/endpoints/charts/ap-request.ts | 2 +- .../src/server/api/endpoints/charts/drive.ts | 2 +- .../server/api/endpoints/charts/federation.ts | 2 +- .../server/api/endpoints/charts/hashtag.ts | 2 +- .../server/api/endpoints/charts/instance.ts | 2 +- .../src/server/api/endpoints/charts/notes.ts | 2 +- .../server/api/endpoints/charts/user/drive.ts | 2 +- .../api/endpoints/charts/user/following.ts | 2 +- .../server/api/endpoints/charts/user/notes.ts | 2 +- .../api/endpoints/charts/user/reactions.ts | 2 +- .../src/server/api/endpoints/charts/users.ts | 2 +- 26 files changed, 206 insertions(+), 147 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index d7b2b753fcd6..dcad185e0b89 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { IdService } from '@/services/IdService.js'; -import { Antennas, UserLists, UserGroupJoinings } from '@/models/index.js'; -import { publishInternalEvent } from '@/services/stream.js'; +import { IdService } from '@/services/IdService.js'; +import type { UserLists, UserGroupJoinings , Antennas } from '@/models/index.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -65,14 +65,24 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('antennasRepository') + private antennasRepository: typeof Antennas, + + @Inject('userListsRepository') + private userListsRepository: typeof UserLists, + + @Inject('userGroupJoiningsRepository') + private userGroupJoiningsRepository: typeof UserGroupJoinings, + private idService: IdService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { let userList; let userGroupJoining; if (ps.src === 'list' && ps.userListId) { - userList = await UserLists.findOneBy({ + userList = await this.userListsRepository.findOneBy({ id: ps.userListId, userId: me.id, }); @@ -81,7 +91,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchUserList); } } else if (ps.src === 'group' && ps.userGroupId) { - userGroupJoining = await UserGroupJoinings.findOneBy({ + userGroupJoining = await this.userGroupJoiningsRepository.findOneBy({ userGroupId: ps.userGroupId, userId: me.id, }); @@ -91,7 +101,7 @@ export default class extends Endpoint { } } - const antenna = await Antennas.insert({ + const antenna = await this.antennasRepository.insert({ id: this.idService.genId(), createdAt: new Date(), userId: me.id, @@ -106,11 +116,11 @@ export default class extends Endpoint { withReplies: ps.withReplies, withFile: ps.withFile, notify: ps.notify, - }).then(x => Antennas.findOneByOrFail(x.identifiers[0])); + }).then(x => this.antennasRepository.findOneByOrFail(x.identifiers[0])); - publishInternalEvent('antennaCreated', antenna); + this.globalEventService.publishInternalEvent('antennaCreated', antenna); - return await Antennas.pack(antenna); + return await this.antennasRepository.pack(antenna); }); } } diff --git a/packages/backend/src/server/api/endpoints/antennas/delete.ts b/packages/backend/src/server/api/endpoints/antennas/delete.ts index bb5f1b4b39e3..5599c858a1d4 100644 --- a/packages/backend/src/server/api/endpoints/antennas/delete.ts +++ b/packages/backend/src/server/api/endpoints/antennas/delete.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Antennas } from '@/models/index.js'; -import { publishInternalEvent } from '@/services/stream.js'; +import type { Antennas } from '@/models/index.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -32,9 +32,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('antennasRepository') + private antennasRepository: typeof Antennas, + + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { - const antenna = await Antennas.findOneBy({ + const antenna = await this.antennasRepository.findOneBy({ id: ps.antennaId, userId: me.id, }); @@ -43,9 +47,9 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchAntenna); } - await Antennas.delete(antenna.id); + await this.antennasRepository.delete(antenna.id); - publishInternalEvent('antennaDeleted', antenna); + this.globalEventService.publishInternalEvent('antennaDeleted', antenna); }); } } diff --git a/packages/backend/src/server/api/endpoints/antennas/list.ts b/packages/backend/src/server/api/endpoints/antennas/list.ts index 544d067c0aff..eb6d964cc517 100644 --- a/packages/backend/src/server/api/endpoints/antennas/list.ts +++ b/packages/backend/src/server/api/endpoints/antennas/list.ts @@ -30,14 +30,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('antennasRepository') + private antennasRepository: typeof Antennas, ) { super(meta, paramDef, async (ps, me) => { - const antennas = await Antennas.findBy({ + const antennas = await this.antennasRepository.findBy({ userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index f514d20589c8..2bde5fa464ed 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import readNote from '@/services/note/read.js'; -import { Antennas, Notes, AntennaNotes } from '@/models/index.js'; +import type { Notes , AntennaNotes } from '@/models/index.js'; +import { Antennas } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { NoteReadService } from '@/services/NoteReadService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -48,7 +49,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + + @Inject('antennaNotesRepository') + private antennaNotesRepository: typeof AntennaNotes, + private queryService: QueryService, + private noteReadService: NoteReadService, ) { super(meta, paramDef, async (ps, me) => { const antenna = await Antennas.findOneBy({ @@ -60,9 +68,9 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchAntenna); } - const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .innerJoin(AntennaNotes.metadata.targetName, 'antennaNote', 'antennaNote.noteId = note.id') + .innerJoin(this.antennaNotesRepository.metadata.targetName, 'antennaNote', 'antennaNote.noteId = note.id') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') .leftJoinAndSelect('user.banner', 'banner') @@ -85,10 +93,10 @@ export default class extends Endpoint { .getMany(); if (notes.length > 0) { - readNote(me.id, notes); + this.noteReadService.read(me.id, notes); } - return await Notes.packMany(notes, me); + return await this.notesRepository.packMany(notes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/antennas/show.ts b/packages/backend/src/server/api/endpoints/antennas/show.ts index 9bc7dca8c871..c358a4e78fbe 100644 --- a/packages/backend/src/server/api/endpoints/antennas/show.ts +++ b/packages/backend/src/server/api/endpoints/antennas/show.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Antennas } from '@/models/index.js'; +import type { Antennas } from '@/models/index.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -37,15 +37,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('antennasRepository') + private antennasRepository: typeof Antennas, ) { super(meta, paramDef, async (ps, me) => { // Fetch the antenna - const antenna = await Antennas.findOneBy({ + const antenna = await this.antennasRepository.findOneBy({ id: ps.antennaId, userId: me.id, }); @@ -54,7 +51,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchAntenna); } - return await Antennas.pack(antenna); + return await this.antennasRepository.pack(antenna); }); } } diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts index 39c65c7f2c74..2e470cfb2071 100644 --- a/packages/backend/src/server/api/endpoints/antennas/update.ts +++ b/packages/backend/src/server/api/endpoints/antennas/update.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Antennas, UserLists, UserGroupJoinings } from '@/models/index.js'; -import { publishInternalEvent } from '@/services/stream.js'; +import type { Antennas , UserLists, UserGroupJoinings } from '@/models/index.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -71,10 +71,20 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('antennasRepository') + private antennasRepository: typeof Antennas, + + @Inject('userListsRepository') + private userListsRepository: typeof UserLists, + + @Inject('userGroupJoiningsRepository') + private userGroupJoiningsRepository: typeof UserGroupJoinings, + + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { // Fetch the antenna - const antenna = await Antennas.findOneBy({ + const antenna = await this.antennasRepository.findOneBy({ id: ps.antennaId, userId: me.id, }); @@ -87,7 +97,7 @@ export default class extends Endpoint { let userGroupJoining; if (ps.src === 'list' && ps.userListId) { - userList = await UserLists.findOneBy({ + userList = await this.userListsRepository.findOneBy({ id: ps.userListId, userId: me.id, }); @@ -96,7 +106,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchUserList); } } else if (ps.src === 'group' && ps.userGroupId) { - userGroupJoining = await UserGroupJoinings.findOneBy({ + userGroupJoining = await this.userGroupJoiningsRepository.findOneBy({ userGroupId: ps.userGroupId, userId: me.id, }); @@ -106,7 +116,7 @@ export default class extends Endpoint { } } - await Antennas.update(antenna.id, { + await this.antennasRepository.update(antenna.id, { name: ps.name, src: ps.src, userListId: userList ? userList.id : null, @@ -120,9 +130,9 @@ export default class extends Endpoint { notify: ps.notify, }); - publishInternalEvent('antennaUpdated', await Antennas.findOneByOrFail({ id: antenna.id })); + this.globalEventService.publishInternalEvent('antennaUpdated', await this.antennasRepository.findOneByOrFail({ id: antenna.id })); - return await Antennas.pack(antenna.id); + return await this.antennasRepository.pack(antenna.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/ap/get.ts b/packages/backend/src/server/api/endpoints/ap/get.ts index 48d66a7d055e..46ee9a392509 100644 --- a/packages/backend/src/server/api/endpoints/ap/get.ts +++ b/packages/backend/src/server/api/endpoints/ap/get.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import Resolver from '@/services/remote/activitypub/resolver.js'; +import { ApResolverService } from '@/services/remote/activitypub/ApResolverService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -35,9 +35,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private apResolverService: ApResolverService, ) { super(meta, paramDef, async (ps, me) => { - const resolver = new Resolver(); + const resolver = this.apResolverService.createResolver(); const object = await resolver.resolve(ps.uri); return object; }); diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index 67f2a1e1b0fe..a4f3cf3e2b31 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -1,19 +1,18 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import config from '@/config/index.js'; -import { createPerson } from '@/services/remote/activitypub/models/person.js'; -import { createNote } from '@/services/remote/activitypub/models/note.js'; -import DbResolver from '@/services/remote/activitypub/db-resolver.js'; -import Resolver from '@/services/remote/activitypub/resolver.js'; import { extractDbHost } from '@/misc/convert-host.js'; import type { Users } from '@/models/index.js'; import { Notes } from '@/models/index.js'; import type { Note } from '@/models/entities/note.js'; import type { CacheableLocalUser, User } from '@/models/entities/user.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; import { isActor, isPost, getApId } from '@/services/remote/activitypub/type.js'; import type { SchemaType } from '@/misc/schema.js'; +import { ApResolverService } from '@/services/remote/activitypub/ApResolverService.js'; +import { ApDbResolverService } from '@/services/remote/activitypub/ApDbResolverService.js'; +import { MetaService } from '@/services/MetaService.js'; +import { ApPersonService } from '@/services/remote/activitypub/models/ApPersonService.js'; +import { ApNoteService } from '@/services/remote/activitypub/models/ApNoteService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -88,9 +87,15 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private metaService: MetaService, + private apResolverService: ApResolverService, + private apDbResolverService: ApDbResolverService, + private apPersonService: ApPersonService, + private apNoteService: ApNoteService, ) { super(meta, paramDef, async (ps, me) => { - const object = await fetchAny(ps.uri, me); + const object = await this.#fetchAny(ps.uri, me); if (object) { return object; } else { @@ -98,63 +103,61 @@ export default class extends Endpoint { } }); } -} -/*** - * URIからUserかNoteを解決する - */ -async function fetchAny(uri: string, me: CacheableLocalUser | null | undefined): Promise | null> { + /*** + * URIからUserかNoteを解決する + */ + async #fetchAny(uri: string, me: CacheableLocalUser | null | undefined): Promise | null> { // ブロックしてたら中断 - const fetchedMeta = await fetchMeta(); - if (fetchedMeta.blockedHosts.includes(extractDbHost(uri))) return null; - - const dbResolver = new DbResolver(); - - let local = await mergePack(me, ...await Promise.all([ - dbResolver.getUserFromApId(uri), - dbResolver.getNoteFromApId(uri), - ])); - if (local != null) return local; - - // リモートから一旦オブジェクトフェッチ - const resolver = new Resolver(); - const object = await resolver.resolve(uri) as any; - - // /@user のような正規id以外で取得できるURIが指定されていた場合、ここで初めて正規URIが確定する - // これはDBに存在する可能性があるため再度DB検索 - if (uri !== object.id) { - local = await mergePack(me, ...await Promise.all([ - dbResolver.getUserFromApId(object.id), - dbResolver.getNoteFromApId(object.id), + const fetchedMeta = await this.metaService.fetch(); + if (fetchedMeta.blockedHosts.includes(extractDbHost(uri))) return null; + + let local = await this.#mergePack(me, ...await Promise.all([ + this.apDbResolverService.getUserFromApId(uri), + this.apDbResolverService.getNoteFromApId(uri), ])); if (local != null) return local; - } - return await mergePack( - me, - isActor(object) ? await createPerson(getApId(object)) : null, - isPost(object) ? await createNote(getApId(object), undefined, true) : null, - ); -} + // リモートから一旦オブジェクトフェッチ + const resolver = this.apResolverService.createResolver(); + const object = await resolver.resolve(uri) as any; + + // /@user のような正規id以外で取得できるURIが指定されていた場合、ここで初めて正規URIが確定する + // これはDBに存在する可能性があるため再度DB検索 + if (uri !== object.id) { + local = await this.#mergePack(me, ...await Promise.all([ + this.apDbResolverService.getUserFromApId(object.id), + this.apDbResolverService.getNoteFromApId(object.id), + ])); + if (local != null) return local; + } -async function mergePack(me: CacheableLocalUser | null | undefined, user: User | null | undefined, note: Note | null | undefined): Promise | null> { - if (user != null) { - return { - type: 'User', - object: await this.usersRepository.pack(user, me, { detail: true }), - }; - } else if (note != null) { - try { - const object = await Notes.pack(note, me, { detail: true }); + return await this.#mergePack( + me, + isActor(object) ? await this.apPersonService.createPerson(getApId(object)) : null, + isPost(object) ? await this.apNoteService.createNote(getApId(object), undefined, true) : null, + ); + } + async #mergePack(me: CacheableLocalUser | null | undefined, user: User | null | undefined, note: Note | null | undefined): Promise | null> { + if (user != null) { return { - type: 'Note', - object, + type: 'User', + object: await this.usersRepository.pack(user, me, { detail: true }), }; - } catch (e) { - return null; + } else if (note != null) { + try { + const object = await Notes.pack(note, me, { detail: true }); + + return { + type: 'Note', + object, + }; + } catch (e) { + return null; + } } - } - return null; + return null; + } } diff --git a/packages/backend/src/server/api/endpoints/app/create.ts b/packages/backend/src/server/api/endpoints/app/create.ts index 984a306ca7d3..3624186bcadb 100644 --- a/packages/backend/src/server/api/endpoints/app/create.ts +++ b/packages/backend/src/server/api/endpoints/app/create.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Apps } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import type { Apps } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; import { unique } from '@/prelude/array.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; @@ -34,6 +34,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('appsRepository') + private appsRepository: typeof Apps, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { @@ -44,7 +47,7 @@ export default class extends Endpoint { const permission = unique(ps.permission.map(v => v.replace(/^(.+)(\/|-)(read|write)$/, '$3:$1'))); // Create account - const app = await Apps.insert({ + const app = await this.appsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), userId: me ? me.id : null, @@ -53,9 +56,9 @@ export default class extends Endpoint { permission, callbackUrl: ps.callbackUrl, secret: secret, - }).then(x => Apps.findOneByOrFail(x.identifiers[0])); + }).then(x => this.appsRepository.findOneByOrFail(x.identifiers[0])); - return await Apps.pack(app, null, { + return await this.appsRepository.pack(app, null, { detail: true, includeSecret: true, }); diff --git a/packages/backend/src/server/api/endpoints/app/show.ts b/packages/backend/src/server/api/endpoints/app/show.ts index 4f8c48e79185..0d2a78b09e8d 100644 --- a/packages/backend/src/server/api/endpoints/app/show.ts +++ b/packages/backend/src/server/api/endpoints/app/show.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Apps } from '@/models/index.js'; +import type { Apps } from '@/models/index.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -33,23 +33,20 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('appsRepository') + private appsRepository: typeof Apps, ) { super(meta, paramDef, async (ps, user, token) => { const isSecure = user != null && token == null; // Lookup app - const ap = await Apps.findOneBy({ id: ps.appId }); + const ap = await this.appsRepository.findOneBy({ id: ps.appId }); if (ap == null) { throw new ApiError(meta.errors.noSuchApp); } - return await Apps.pack(ap, user, { + return await this.appsRepository.pack(ap, user, { detail: true, includeSecret: isSecure && (ap.userId === user!.id), }); diff --git a/packages/backend/src/server/api/endpoints/auth/accept.ts b/packages/backend/src/server/api/endpoints/auth/accept.ts index 543a05797b10..bde5ccd70b10 100644 --- a/packages/backend/src/server/api/endpoints/auth/accept.ts +++ b/packages/backend/src/server/api/endpoints/auth/accept.ts @@ -1,8 +1,8 @@ import * as crypto from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { AuthSessions, AccessTokens, Apps } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import type { AuthSessions, Apps , AccessTokens } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; import { ApiError } from '../../error.js'; @@ -34,11 +34,20 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('appsRepository') + private appsRepository: typeof Apps, + + @Inject('authSessionsRepository') + private authSessionsRepository: typeof AuthSessions, + + @Inject('accessTokensRepository') + private accessTokensRepository: typeof AccessTokens, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { // Fetch token - const session = await AuthSessions + const session = await this.authSessionsRepository .findOneBy({ token: ps.token }); if (session == null) { @@ -49,14 +58,14 @@ export default class extends Endpoint { const accessToken = secureRndstr(32, true); // Fetch exist access token - const exist = await AccessTokens.findOneBy({ + const exist = await this.accessTokensRepository.findOneBy({ appId: session.appId, userId: me.id, }); if (exist == null) { // Lookup app - const app = await Apps.findOneByOrFail({ id: session.appId }); + const app = await this.appsRepository.findOneByOrFail({ id: session.appId }); // Generate Hash const sha256 = crypto.createHash('sha256'); @@ -66,7 +75,7 @@ export default class extends Endpoint { const now = new Date(); // Insert access token doc - await AccessTokens.insert({ + await this.accessTokensRepository.insert({ id: this.idService.genId(), createdAt: now, lastUsedAt: now, @@ -78,7 +87,7 @@ export default class extends Endpoint { } // Update session - await AuthSessions.update(session.id, { + await this.authSessionsRepository.update(session.id, { userId: me.id, }); }); diff --git a/packages/backend/src/server/api/endpoints/auth/session/generate.ts b/packages/backend/src/server/api/endpoints/auth/session/generate.ts index efdbc2ac1fa6..86c479f2ef18 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/generate.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/generate.ts @@ -1,9 +1,10 @@ import { v4 as uuid } from 'uuid'; import { Inject, Injectable } from '@nestjs/common'; -import config from '@/config/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Apps, AuthSessions } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import type { Apps, AuthSessions } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; +import { Config } from '@/config/types.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -48,11 +49,20 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('appsRepository') + private appsRepository: typeof Apps, + + @Inject('authSessionsRepository') + private authSessionsRepository: typeof AuthSessions, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { // Lookup app - const app = await Apps.findOneBy({ + const app = await this.appsRepository.findOneBy({ secret: ps.appSecret, }); @@ -64,16 +74,16 @@ export default class extends Endpoint { const token = uuid(); // Create session token document - const doc = await AuthSessions.insert({ + const doc = await this.authSessionsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), appId: app.id, token: token, - }).then(x => AuthSessions.findOneByOrFail(x.identifiers[0])); + }).then(x => this.authSessionsRepository.findOneByOrFail(x.identifiers[0])); return { token: doc.token, - url: `${config.authUrl}/${doc.token}`, + url: `${this.config.authUrl}/${doc.token}`, }; }); } diff --git a/packages/backend/src/server/api/endpoints/auth/session/show.ts b/packages/backend/src/server/api/endpoints/auth/session/show.ts index 2d3e21a68ebf..9de256e707dc 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/show.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/show.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { AuthSessions } from '@/models/index.js'; +import type { AuthSessions } from '@/models/index.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -50,10 +50,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('authSessionsRepository') + private authSessionsRepository: typeof AuthSessions, ) { super(meta, paramDef, async (ps, me) => { // Lookup session - const session = await AuthSessions.findOneBy({ + const session = await this.authSessionsRepository.findOneBy({ token: ps.token, }); @@ -61,7 +63,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchSession); } - return await AuthSessions.pack(session, me); + return await this.authSessionsRepository.pack(session, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts index 49e6d2d1caec..bfb90019547b 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts @@ -1,7 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users } from '@/models/index.js'; -import { Apps, AuthSessions, AccessTokens } from '@/models/index.js'; +import type { Users , Apps, AccessTokens , AuthSessions } from '@/models/index.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -62,10 +61,19 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + @Inject('appsRepository') + private appsRepository: typeof Apps, + + @Inject('authSessionsRepository') + private authSessionsRepository: typeof AuthSessions, + + @Inject('accessTokensRepository') + private accessTokensRepository: typeof AccessTokens, ) { super(meta, paramDef, async (ps, me) => { // Lookup app - const app = await Apps.findOneBy({ + const app = await this.appsRepository.findOneBy({ secret: ps.appSecret, }); @@ -74,7 +82,7 @@ export default class extends Endpoint { } // Fetch token - const session = await AuthSessions.findOneBy({ + const session = await this.authSessionsRepository.findOneBy({ token: ps.token, appId: app.id, }); @@ -88,13 +96,13 @@ export default class extends Endpoint { } // Lookup access token - const accessToken = await AccessTokens.findOneByOrFail({ + const accessToken = await this.accessTokensRepository.findOneByOrFail({ appId: app.id, userId: session.userId, }); // Delete session - AuthSessions.delete(session.id); + this.authSessionsRepository.delete(session.id); return { accessToken: accessToken.token, diff --git a/packages/backend/src/server/api/endpoints/charts/active-users.ts b/packages/backend/src/server/api/endpoints/charts/active-users.ts index 2baaf3be3a80..7082acde6b1c 100644 --- a/packages/backend/src/server/api/endpoints/charts/active-users.ts +++ b/packages/backend/src/server/api/endpoints/charts/active-users.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type ActiveUsersChart from '@/services/chart/charts/active-users.js'; +import ActiveUsersChart from '@/services/chart/charts/active-users.js'; import { schema } from '@/services/chart/charts/entities/active-users.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/charts/ap-request.ts b/packages/backend/src/server/api/endpoints/charts/ap-request.ts index 91465f22a231..2965f4d875dd 100644 --- a/packages/backend/src/server/api/endpoints/charts/ap-request.ts +++ b/packages/backend/src/server/api/endpoints/charts/ap-request.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type ApRequestChart from '@/services/chart/charts/ap-request.js'; +import ApRequestChart from '@/services/chart/charts/ap-request.js'; import { schema } from '@/services/chart/charts/entities/ap-request.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/charts/drive.ts b/packages/backend/src/server/api/endpoints/charts/drive.ts index 3d2049152461..a66f16507e5a 100644 --- a/packages/backend/src/server/api/endpoints/charts/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/drive.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type DriveChart from '@/services/chart/charts/drive.js'; +import DriveChart from '@/services/chart/charts/drive.js'; import { schema } from '@/services/chart/charts/entities/drive.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/charts/federation.ts b/packages/backend/src/server/api/endpoints/charts/federation.ts index 299b9fc0d0ed..42f24f5ea1d6 100644 --- a/packages/backend/src/server/api/endpoints/charts/federation.ts +++ b/packages/backend/src/server/api/endpoints/charts/federation.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type FederationChart from '@/services/chart/charts/federation.js'; +import FederationChart from '@/services/chart/charts/federation.js'; import { schema } from '@/services/chart/charts/entities/federation.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/charts/hashtag.ts b/packages/backend/src/server/api/endpoints/charts/hashtag.ts index 88238af02a26..165dad9f62ab 100644 --- a/packages/backend/src/server/api/endpoints/charts/hashtag.ts +++ b/packages/backend/src/server/api/endpoints/charts/hashtag.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type HashtagChart from '@/services/chart/charts/hashtag.js'; +import HashtagChart from '@/services/chart/charts/hashtag.js'; import { schema } from '@/services/chart/charts/entities/hashtag.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/charts/instance.ts b/packages/backend/src/server/api/endpoints/charts/instance.ts index 51dd0dcb6144..8f05f84f9f95 100644 --- a/packages/backend/src/server/api/endpoints/charts/instance.ts +++ b/packages/backend/src/server/api/endpoints/charts/instance.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type InstanceChart from '@/services/chart/charts/instance.js'; +import InstanceChart from '@/services/chart/charts/instance.js'; import { schema } from '@/services/chart/charts/entities/instance.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/charts/notes.ts b/packages/backend/src/server/api/endpoints/charts/notes.ts index 6a7683f60e0f..98859b604ad9 100644 --- a/packages/backend/src/server/api/endpoints/charts/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/notes.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type NotesChart from '@/services/chart/charts/notes.js'; +import NotesChart from '@/services/chart/charts/notes.js'; import { schema } from '@/services/chart/charts/entities/notes.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/charts/user/drive.ts b/packages/backend/src/server/api/endpoints/charts/user/drive.ts index 8125028d6118..46cc2f3130a0 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/drive.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type PerUserDriveChart from '@/services/chart/charts/per-user-drive.js'; +import PerUserDriveChart from '@/services/chart/charts/per-user-drive.js'; import { schema } from '@/services/chart/charts/entities/per-user-drive.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/charts/user/following.ts b/packages/backend/src/server/api/endpoints/charts/user/following.ts index f06ed2873bb1..bcee18796515 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/following.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/following.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getJsonSchema } from '@/services/chart/core.js'; -import type PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; +import PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; import { schema } from '@/services/chart/charts/entities/per-user-following.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/charts/user/notes.ts b/packages/backend/src/server/api/endpoints/charts/user/notes.ts index 2359d82912a5..05a03ad81077 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/notes.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; +import PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; import { schema } from '@/services/chart/charts/entities/per-user-notes.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts index 93011051b36d..3aed06312d02 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type PerUserReactionsChart from '@/services/chart/charts/per-user-reactions.js'; +import PerUserReactionsChart from '@/services/chart/charts/per-user-reactions.js'; import { schema } from '@/services/chart/charts/entities/per-user-reactions.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/charts/users.ts b/packages/backend/src/server/api/endpoints/charts/users.ts index 6f620d1f960a..29e3842aa837 100644 --- a/packages/backend/src/server/api/endpoints/charts/users.ts +++ b/packages/backend/src/server/api/endpoints/charts/users.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { getJsonSchema } from '@/services/chart/core.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type UsersChart from '@/services/chart/charts/users.js'; +import UsersChart from '@/services/chart/charts/users.js'; import { schema } from '@/services/chart/charts/entities/users.js'; export const meta = { From ea4678d31d76f6752de26404f3f3fbe5e65c78a9 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 03:26:40 +0900 Subject: [PATCH 096/180] Update show.ts --- packages/backend/src/server/api/endpoints/ap/show.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index a4f3cf3e2b31..b8a0362a996e 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -2,8 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { extractDbHost } from '@/misc/convert-host.js'; -import type { Users } from '@/models/index.js'; -import { Notes } from '@/models/index.js'; +import type { Users , Notes } from '@/models/index.js'; import type { Note } from '@/models/entities/note.js'; import type { CacheableLocalUser, User } from '@/models/entities/user.js'; import { isActor, isPost, getApId } from '@/services/remote/activitypub/type.js'; @@ -147,7 +146,7 @@ export default class extends Endpoint { }; } else if (note != null) { try { - const object = await Notes.pack(note, me, { detail: true }); + const object = await this.notesRepository.pack(note, me, { detail: true }); return { type: 'Note', From 21b2015ddf68925ff0b3a41384551048e5a4160e Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 03:33:02 +0900 Subject: [PATCH 097/180] wip --- .../server/api/endpoints/blocking/create.ts | 8 +- .../server/api/endpoints/blocking/delete.ts | 2 +- .../src/server/api/endpoints/blocking/list.ts | 4 +- .../server/api/endpoints/channels/follow.ts | 2 +- .../server/api/endpoints/channels/followed.ts | 2 +- .../server/api/endpoints/channels/timeline.ts | 10 +- .../server/api/endpoints/channels/unfollow.ts | 2 +- .../src/server/api/endpoints/clips/notes.ts | 10 +- .../endpoints/drive/files/attached-notes.ts | 9 +- .../api/endpoints/federation/followers.ts | 4 +- .../api/endpoints/federation/following.ts | 4 +- .../server/api/endpoints/federation/stats.ts | 4 +- .../server/api/endpoints/following/create.ts | 2 +- .../server/api/endpoints/following/delete.ts | 2 +- .../api/endpoints/following/invalidate.ts | 2 +- .../server/api/endpoints/hashtags/trend.ts | 168 +++++++++--------- .../server/api/endpoints/i/notifications.ts | 4 +- .../server/api/endpoints/messaging/history.ts | 2 +- .../endpoints/messaging/messages/create.ts | 2 +- .../src/server/api/endpoints/mute/create.ts | 4 +- .../src/server/api/endpoints/mute/delete.ts | 4 +- .../src/server/api/endpoints/mute/list.ts | 4 +- .../server/api/endpoints/notes/children.ts | 9 +- .../api/endpoints/notes/conversation.ts | 14 +- .../src/server/api/endpoints/notes/create.ts | 17 +- .../server/api/endpoints/notes/featured.ts | 9 +- .../api/endpoints/notes/global-timeline.ts | 9 +- .../api/endpoints/notes/hybrid-timeline.ts | 12 +- .../api/endpoints/notes/local-timeline.ts | 10 +- .../server/api/endpoints/notes/mentions.ts | 12 +- .../endpoints/notes/polls/recommendation.ts | 12 +- .../server/api/endpoints/notes/polls/vote.ts | 2 +- .../src/server/api/endpoints/notes/renotes.ts | 9 +- .../src/server/api/endpoints/notes/replies.ts | 9 +- .../api/endpoints/notes/search-by-tag.ts | 6 +- .../src/server/api/endpoints/notes/search.ts | 10 +- .../src/server/api/endpoints/notes/show.ts | 12 +- .../src/server/api/endpoints/notes/state.ts | 9 +- .../endpoints/notes/thread-muting/create.ts | 18 +- .../endpoints/notes/thread-muting/delete.ts | 2 +- .../server/api/endpoints/notes/timeline.ts | 14 +- .../server/api/endpoints/notes/translate.ts | 12 +- .../server/api/endpoints/notes/unrenote.ts | 14 +- .../api/endpoints/notes/user-list-timeline.ts | 10 +- .../backend/src/server/api/endpoints/stats.ts | 7 +- .../server/api/endpoints/users/followers.ts | 6 +- .../server/api/endpoints/users/following.ts | 6 +- .../users/get-frequently-replied-users.ts | 12 +- .../server/api/endpoints/users/lists/push.ts | 2 +- .../src/server/api/endpoints/users/notes.ts | 12 +- .../api/endpoints/users/recommendation.ts | 2 +- .../users/search-by-username-and-host.ts | 2 +- .../src/server/api/endpoints/users/stats.ts | 22 +-- 53 files changed, 311 insertions(+), 246 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts index 6f97f0763c70..e3c721c927a3 100644 --- a/packages/backend/src/server/api/endpoints/blocking/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking/create.ts @@ -70,13 +70,13 @@ export default class extends Endpoint { } // Get blockee - const blockee = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; + const blockee = await getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; }); // Check if already blocking - const exist = await Blockings.findOneBy({ + const exist = await this.blockingsRepository.findOneBy({ blockerId: blocker.id, blockeeId: blockee.id, }); diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts index ad74205c726b..f00021f53b0c 100644 --- a/packages/backend/src/server/api/endpoints/blocking/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts @@ -76,7 +76,7 @@ export default class extends Endpoint { }); // Check not blocking - const exist = await Blockings.findOneBy({ + const exist = await this.blockingsRepository.findOneBy({ blockerId: blocker.id, blockeeId: blockee.id, }); diff --git a/packages/backend/src/server/api/endpoints/blocking/list.ts b/packages/backend/src/server/api/endpoints/blocking/list.ts index 8ba56cba6b22..b5635d2fe90b 100644 --- a/packages/backend/src/server/api/endpoints/blocking/list.ts +++ b/packages/backend/src/server/api/endpoints/blocking/list.ts @@ -44,14 +44,14 @@ export default class extends Endpoint { private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(Blockings.createQueryBuilder('blocking'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.blockingsRepository.createQueryBuilder('blocking'), ps.sinceId, ps.untilId) .andWhere('blocking.blockerId = :meId', { meId: me.id }); const blockings = await query .take(ps.limit) .getMany(); - return await Blockings.packMany(blockings, me); + return await this.blockingsRepository.packMany(blockings, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/channels/follow.ts b/packages/backend/src/server/api/endpoints/channels/follow.ts index 4c6f04dcfc82..074e14e0eeb9 100644 --- a/packages/backend/src/server/api/endpoints/channels/follow.ts +++ b/packages/backend/src/server/api/endpoints/channels/follow.ts @@ -44,7 +44,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchChannel); } - await ChannelFollowings.insert({ + await this.channelFollowingsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), followerId: me.id, diff --git a/packages/backend/src/server/api/endpoints/channels/followed.ts b/packages/backend/src/server/api/endpoints/channels/followed.ts index 6799994d3ae7..8f8a2becaf56 100644 --- a/packages/backend/src/server/api/endpoints/channels/followed.ts +++ b/packages/backend/src/server/api/endpoints/channels/followed.ts @@ -44,7 +44,7 @@ export default class extends Endpoint { private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(ChannelFollowings.createQueryBuilder(), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.channelFollowingsRepository.createQueryBuilder(), ps.sinceId, ps.untilId) .andWhere({ followerId: me.id }); const followings = await query diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index 50d2eb463b60..78de50afb7c6 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Notes, Channels } from '@/models/index.js'; +import type { Notes } from '@/models/index.js'; +import { Channels } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; import { QueryService } from '@/services/QueryService.js'; import { ApiError } from '../../error.js'; @@ -46,6 +47,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { @@ -58,7 +62,7 @@ export default class extends Endpoint { } //#region Construct query - const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.channelId = :channelId', { channelId: channel.id }) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') @@ -78,7 +82,7 @@ export default class extends Endpoint { if (me) activeUsersChart.read(me); - return await Notes.packMany(timeline, me); + return await this.notesRepository.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/channels/unfollow.ts b/packages/backend/src/server/api/endpoints/channels/unfollow.ts index e4fd7457a07a..a1fe58b53c73 100644 --- a/packages/backend/src/server/api/endpoints/channels/unfollow.ts +++ b/packages/backend/src/server/api/endpoints/channels/unfollow.ts @@ -42,7 +42,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchChannel); } - await ChannelFollowings.delete({ + await this.channelFollowingsRepository.delete({ followerId: me.id, followeeId: channel.id, }); diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts index 260afb37ed1e..3d9f2b04a450 100644 --- a/packages/backend/src/server/api/endpoints/clips/notes.ts +++ b/packages/backend/src/server/api/endpoints/clips/notes.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ClipNotes, Clips, Notes } from '@/models/index.js'; +import type { Notes } from '@/models/index.js'; +import { ClipNotes, Clips } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { ApiError } from '../../error.js'; @@ -45,6 +46,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { @@ -60,7 +64,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchClip); } - const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) .innerJoin(ClipNotes.metadata.targetName, 'clipNote', 'clipNote.noteId = note.id') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') @@ -85,7 +89,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return await Notes.packMany(notes, me); + return await this.notesRepository.packMany(notes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts index a0788cd2d0d6..c0e0cbf71e6f 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFiles, Notes } from '@/models/index.js'; +import type { Notes } from '@/models/index.js'; +import { DriveFiles } from '@/models/index.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -43,6 +44,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { // Fetch file @@ -55,11 +58,11 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchFile); } - const notes = await Notes.createQueryBuilder('note') + const notes = await this.notesRepository.createQueryBuilder('note') .where(':file = ANY(note.fileIds)', { file: file.id }) .getMany(); - return await Notes.packMany(notes, me, { + return await this.notesRepository.packMany(notes, me, { detail: true, }); }); diff --git a/packages/backend/src/server/api/endpoints/federation/followers.ts b/packages/backend/src/server/api/endpoints/federation/followers.ts index 9986342343dc..e1ccad9d0e30 100644 --- a/packages/backend/src/server/api/endpoints/federation/followers.ts +++ b/packages/backend/src/server/api/endpoints/federation/followers.ts @@ -43,14 +43,14 @@ export default class extends Endpoint { private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.followingsRepository.createQueryBuilder('following'), ps.sinceId, ps.untilId) .andWhere('following.followeeHost = :host', { host: ps.host }); const followings = await query .take(ps.limit) .getMany(); - return await Followings.packMany(followings, me, { populateFollowee: true }); + return await this.followingsRepository.packMany(followings, me, { populateFollowee: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/federation/following.ts b/packages/backend/src/server/api/endpoints/federation/following.ts index c08624c2e262..ace474300967 100644 --- a/packages/backend/src/server/api/endpoints/federation/following.ts +++ b/packages/backend/src/server/api/endpoints/federation/following.ts @@ -43,14 +43,14 @@ export default class extends Endpoint { private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.followingsRepository.createQueryBuilder('following'), ps.sinceId, ps.untilId) .andWhere('following.followerHost = :host', { host: ps.host }); const followings = await query .take(ps.limit) .getMany(); - return await Followings.packMany(followings, me, { populateFollowee: true }); + return await this.followingsRepository.packMany(followings, me, { populateFollowee: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/federation/stats.ts b/packages/backend/src/server/api/endpoints/federation/stats.ts index cde5bc607050..c5aa4384e15a 100644 --- a/packages/backend/src/server/api/endpoints/federation/stats.ts +++ b/packages/backend/src/server/api/endpoints/federation/stats.ts @@ -46,12 +46,12 @@ export default class extends Endpoint { }, take: ps.limit, }), - Followings.count({ + this.followingsRepository.count({ where: { followeeHost: Not(IsNull()), }, }), - Followings.count({ + this.followingsRepository.count({ where: { followerHost: Not(IsNull()), }, diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts index e0146a0cedd9..6cdcd4a76971 100644 --- a/packages/backend/src/server/api/endpoints/following/create.ts +++ b/packages/backend/src/server/api/endpoints/following/create.ts @@ -89,7 +89,7 @@ export default class extends Endpoint { }); // Check if already following - const exist = await Followings.findOneBy({ + const exist = await this.followingsRepository.findOneBy({ followerId: follower.id, followeeId: followee.id, }); diff --git a/packages/backend/src/server/api/endpoints/following/delete.ts b/packages/backend/src/server/api/endpoints/following/delete.ts index a99ea1d6d8c3..f7d4c47fcb87 100644 --- a/packages/backend/src/server/api/endpoints/following/delete.ts +++ b/packages/backend/src/server/api/endpoints/following/delete.ts @@ -76,7 +76,7 @@ export default class extends Endpoint { }); // Check not following - const exist = await Followings.findOneBy({ + const exist = await this.followingsRepository.findOneBy({ followerId: follower.id, followeeId: followee.id, }); diff --git a/packages/backend/src/server/api/endpoints/following/invalidate.ts b/packages/backend/src/server/api/endpoints/following/invalidate.ts index 36379df80146..6d71e376f11e 100644 --- a/packages/backend/src/server/api/endpoints/following/invalidate.ts +++ b/packages/backend/src/server/api/endpoints/following/invalidate.ts @@ -76,7 +76,7 @@ export default class extends Endpoint { }); // Check not following - const exist = await Followings.findOneBy({ + const exist = await this.followingsRepository.findOneBy({ followerId: follower.id, followeeId: followee.id, }); diff --git a/packages/backend/src/server/api/endpoints/hashtags/trend.ts b/packages/backend/src/server/api/endpoints/hashtags/trend.ts index 73f2d8ff510b..919b7dd62edb 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/trend.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/trend.ts @@ -2,8 +2,8 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; -import { Notes } from '@/models/index.js'; -import { Note } from '@/models/entities/note.js'; +import type { Notes } from '@/models/index.js'; +import type { Note } from '@/models/entities/note.js'; import { safeForSql } from '@/misc/safe-for-sql.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; @@ -71,95 +71,95 @@ export default class extends Endpoint { private notesRepository: typeof Notes, ) { super(meta, paramDef, async () => { - const instance = await fetchMeta(true); - const hiddenTags = instance.hiddenTags.map(t => normalizeForSearch(t)); - - const now = new Date(); // 5分単位で丸めた現在日時 - now.setMinutes(Math.round(now.getMinutes() / 5) * 5, 0, 0); - - const tagNotes = await Notes.createQueryBuilder('note') - .where(`note.createdAt > :date`, { date: new Date(now.getTime() - rangeA) }) - .andWhere(new Brackets(qb => { qb - .where(`note.visibility = 'public'`) - .orWhere(`note.visibility = 'home'`); - })) - .andWhere(`note.tags != '{}'`) - .select(['note.tags', 'note.userId']) - .cache(60000) // 1 min - .getMany(); - - if (tagNotes.length === 0) { - return []; - } + const instance = await fetchMeta(true); + const hiddenTags = instance.hiddenTags.map(t => normalizeForSearch(t)); + + const now = new Date(); // 5分単位で丸めた現在日時 + now.setMinutes(Math.round(now.getMinutes() / 5) * 5, 0, 0); + + const tagNotes = await this.notesRepository.createQueryBuilder('note') + .where('note.createdAt > :date', { date: new Date(now.getTime() - rangeA) }) + .andWhere(new Brackets(qb => { qb + .where('note.visibility = \'public\'') + .orWhere('note.visibility = \'home\''); + })) + .andWhere('note.tags != \'{}\'') + .select(['note.tags', 'note.userId']) + .cache(60000) // 1 min + .getMany(); + + if (tagNotes.length === 0) { + return []; + } - const tags: { + const tags: { name: string; users: Note['userId'][]; }[] = []; - for (const note of tagNotes) { - for (const tag of note.tags) { - if (hiddenTags.includes(tag)) continue; - - const x = tags.find(x => x.name === tag); - if (x) { - if (!x.users.includes(note.userId)) { - x.users.push(note.userId); + for (const note of tagNotes) { + for (const tag of note.tags) { + if (hiddenTags.includes(tag)) continue; + + const x = tags.find(x => x.name === tag); + if (x) { + if (!x.users.includes(note.userId)) { + x.users.push(note.userId); + } + } else { + tags.push({ + name: tag, + users: [note.userId], + }); + } } - } else { - tags.push({ - name: tag, - users: [note.userId], - }); } - } - } - // タグを人気順に並べ替え - const hots = tags - .sort((a, b) => b.users.length - a.users.length) - .map(tag => tag.name) - .slice(0, max); - - //#region 2(または3)で話題と判定されたタグそれぞれについて過去の投稿数グラフを取得する - const countPromises: Promise[] = []; - - const range = 20; - - // 10分 - const interval = 1000 * 60 * 10; - - for (let i = 0; i < range; i++) { - countPromises.push(Promise.all(hots.map(tag => Notes.createQueryBuilder('note') - .select('count(distinct note.userId)') - .where(`'{"${safeForSql(tag) ? tag : 'aichan_kawaii'}"}' <@ note.tags`) - .andWhere('note.createdAt < :lt', { lt: new Date(now.getTime() - (interval * i)) }) - .andWhere('note.createdAt > :gt', { gt: new Date(now.getTime() - (interval * (i + 1))) }) - .cache(60000) // 1 min - .getRawOne() - .then(x => parseInt(x.count, 10)) - ))); - } + // タグを人気順に並べ替え + const hots = tags + .sort((a, b) => b.users.length - a.users.length) + .map(tag => tag.name) + .slice(0, max); + + //#region 2(または3)で話題と判定されたタグそれぞれについて過去の投稿数グラフを取得する + const countPromises: Promise[] = []; + + const range = 20; + + // 10分 + const interval = 1000 * 60 * 10; + + for (let i = 0; i < range; i++) { + countPromises.push(Promise.all(hots.map(tag => this.notesRepository.createQueryBuilder('note') + .select('count(distinct note.userId)') + .where(`'{"${safeForSql(tag) ? tag : 'aichan_kawaii'}"}' <@ note.tags`) + .andWhere('note.createdAt < :lt', { lt: new Date(now.getTime() - (interval * i)) }) + .andWhere('note.createdAt > :gt', { gt: new Date(now.getTime() - (interval * (i + 1))) }) + .cache(60000) // 1 min + .getRawOne() + .then(x => parseInt(x.count, 10)), + ))); + } - const countsLog = await Promise.all(countPromises); - //#endregion - - const totalCounts = await Promise.all(hots.map(tag => Notes.createQueryBuilder('note') - .select('count(distinct note.userId)') - .where(`'{"${safeForSql(tag) ? tag : 'aichan_kawaii'}"}' <@ note.tags`) - .andWhere('note.createdAt > :gt', { gt: new Date(now.getTime() - rangeA) }) - .cache(60000 * 60) // 60 min - .getRawOne() - .then(x => parseInt(x.count, 10)) - )); - - const stats = hots.map((tag, i) => ({ - tag, - chart: countsLog.map(counts => counts[i]), - usersCount: totalCounts[i], - })); - - return stats; -}); -} + const countsLog = await Promise.all(countPromises); + //#endregion + + const totalCounts = await Promise.all(hots.map(tag => this.notesRepository.createQueryBuilder('note') + .select('count(distinct note.userId)') + .where(`'{"${safeForSql(tag) ? tag : 'aichan_kawaii'}"}' <@ note.tags`) + .andWhere('note.createdAt > :gt', { gt: new Date(now.getTime() - rangeA) }) + .cache(60000 * 60) // 60 min + .getRawOne() + .then(x => parseInt(x.count, 10)), + )); + + const stats = hots.map((tag, i) => ({ + tag, + chart: countsLog.map(counts => counts[i]), + usersCount: totalCounts[i], + })); + + return stats; + }); + } } diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index 37b7ecd07732..29ef0ae455ab 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -68,11 +68,11 @@ export default class extends Endpoint { if (notificationTypes.every(type => ps.excludeTypes?.includes(type))) { return []; } - const followingQuery = Followings.createQueryBuilder('following') + const followingQuery = this.followingsRepository.createQueryBuilder('following') .select('following.followeeId') .where('following.followerId = :followerId', { followerId: me.id }); - const mutingQuery = Mutings.createQueryBuilder('muting') + const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') .select('muting.muteeId') .where('muting.muterId = :muterId', { muterId: me.id }); diff --git a/packages/backend/src/server/api/endpoints/messaging/history.ts b/packages/backend/src/server/api/endpoints/messaging/history.ts index e995c0a7036b..d4ee0b3cc3cc 100644 --- a/packages/backend/src/server/api/endpoints/messaging/history.ts +++ b/packages/backend/src/server/api/endpoints/messaging/history.ts @@ -37,7 +37,7 @@ export default class extends Endpoint { constructor( ) { super(meta, paramDef, async (ps, me) => { - const mute = await Mutings.findBy({ + const mute = await this.mutingsRepository.findBy({ muterId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts index a4117e3f8c1a..c05f6d90f318 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts @@ -109,7 +109,7 @@ export default class extends Endpoint { }); // Check blocking - const block = await Blockings.findOneBy({ + const block = await this.blockingsRepository.findOneBy({ blockerId: recipientUser.id, blockeeId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/mute/create.ts b/packages/backend/src/server/api/endpoints/mute/create.ts index da613907f96d..8dab6ce9f3b9 100644 --- a/packages/backend/src/server/api/endpoints/mute/create.ts +++ b/packages/backend/src/server/api/endpoints/mute/create.ts @@ -69,7 +69,7 @@ export default class extends Endpoint { }); // Check if already muting - const exist = await Mutings.findOneBy({ + const exist = await this.mutingsRepository.findOneBy({ muterId: muter.id, muteeId: mutee.id, }); @@ -83,7 +83,7 @@ export default class extends Endpoint { } // Create mute - await Mutings.insert({ + await this.mutingsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), expiresAt: ps.expiresAt ? new Date(ps.expiresAt) : null, diff --git a/packages/backend/src/server/api/endpoints/mute/delete.ts b/packages/backend/src/server/api/endpoints/mute/delete.ts index 08ab99180c36..1ff8e80e575c 100644 --- a/packages/backend/src/server/api/endpoints/mute/delete.ts +++ b/packages/backend/src/server/api/endpoints/mute/delete.ts @@ -61,7 +61,7 @@ export default class extends Endpoint { }); // Check not muting - const exist = await Mutings.findOneBy({ + const exist = await this.mutingsRepository.findOneBy({ muterId: muter.id, muteeId: mutee.id, }); @@ -71,7 +71,7 @@ export default class extends Endpoint { } // Delete mute - await Mutings.delete({ + await this.mutingsRepository.delete({ id: exist.id, }); diff --git a/packages/backend/src/server/api/endpoints/mute/list.ts b/packages/backend/src/server/api/endpoints/mute/list.ts index 3b90d637f629..0e9ad879cbbc 100644 --- a/packages/backend/src/server/api/endpoints/mute/list.ts +++ b/packages/backend/src/server/api/endpoints/mute/list.ts @@ -44,14 +44,14 @@ export default class extends Endpoint { private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(Mutings.createQueryBuilder('muting'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.mutingsRepository.createQueryBuilder('muting'), ps.sinceId, ps.untilId) .andWhere('muting.muterId = :meId', { meId: me.id }); const mutings = await query .take(ps.limit) .getMany(); - return await Mutings.packMany(mutings, me); + return await this.mutingsRepository.packMany(mutings, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index b00f093bc349..1f9d499a0f09 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -1,6 +1,6 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { Notes } from '@/models/index.js'; +import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; @@ -35,10 +35,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(new Brackets(qb => { qb .where('note.replyId = :noteId', { noteId: ps.noteId }) .orWhere(new Brackets(qb => { qb @@ -70,7 +73,7 @@ export default class extends Endpoint { const notes = await query.take(ps.limit).getMany(); - return await Notes.packMany(notes, me); + return await this.notesRepository.packMany(notes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/conversation.ts b/packages/backend/src/server/api/endpoints/notes/conversation.ts index a4f6f0fe7358..a2315a69ea74 100644 --- a/packages/backend/src/server/api/endpoints/notes/conversation.ts +++ b/packages/backend/src/server/api/endpoints/notes/conversation.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Note } from '@/models/entities/note.js'; -import { Notes } from '@/models/index.js'; +import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { getNote } from '../../common/getters.js'; @@ -43,11 +43,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; + const note = await getNote(ps.noteId).catch(err => { + if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw err; }); const conversation: Note[] = []; @@ -55,7 +57,7 @@ export default class extends Endpoint { async function get(id: any) { i++; - const p = await Notes.findOneBy({ id }); + const p = await this.notesRepository.findOneBy({ id }); if (p == null) return; if (i > ps.offset!) { @@ -75,7 +77,7 @@ export default class extends Endpoint { await get(note.replyId); } - return await Notes.packMany(conversation, me); + return await this.notesRepository.packMany(conversation, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index c924aaf8ab92..f3c984de3317 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -2,8 +2,8 @@ import ms from 'ms'; import { In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { User } from '@/models/entities/user.js'; -import type { Users } from '@/models/index.js'; -import { DriveFiles, Notes, Channels, Blockings } from '@/models/index.js'; +import type { Users , Notes } from '@/models/index.js'; +import { DriveFiles, Channels, Blockings } from '@/models/index.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import type { Note } from '@/models/entities/note.js'; import type { Channel } from '@/models/entities/channel.js'; @@ -168,6 +168,9 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + @Inject('notesRepository') + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { let visibleUsers: User[] = []; @@ -193,7 +196,7 @@ export default class extends Endpoint { let renote: Note | null = null; if (ps.renoteId != null) { // Fetch renote to note - renote = await Notes.findOneBy({ id: ps.renoteId }); + renote = await this.notesRepository.findOneBy({ id: ps.renoteId }); if (renote == null) { throw new ApiError(meta.errors.noSuchRenoteTarget); @@ -203,7 +206,7 @@ export default class extends Endpoint { // Check blocking if (renote.userId !== me.id) { - const block = await Blockings.findOneBy({ + const block = await this.blockingsRepository.findOneBy({ blockerId: renote.userId, blockeeId: me.id, }); @@ -216,7 +219,7 @@ export default class extends Endpoint { let reply: Note | null = null; if (ps.replyId != null) { // Fetch reply - reply = await Notes.findOneBy({ id: ps.replyId }); + reply = await this.notesRepository.findOneBy({ id: ps.replyId }); if (reply == null) { throw new ApiError(meta.errors.noSuchReplyTarget); @@ -226,7 +229,7 @@ export default class extends Endpoint { // Check blocking if (reply.userId !== me.id) { - const block = await Blockings.findOneBy({ + const block = await this.blockingsRepository.findOneBy({ blockerId: reply.userId, blockeeId: me.id, }); @@ -278,7 +281,7 @@ export default class extends Endpoint { }); return { - createdNote: await Notes.pack(note, me), + createdNote: await this.notesRepository.pack(note, me), }; }); } diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index 81ab8e626c37..6181398d0049 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Notes } from '@/models/index.js'; +import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; @@ -32,13 +32,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { const max = 30; const day = 1000 * 60 * 60 * 24 * 3; // 3日前まで - const query = Notes.createQueryBuilder('note') + const query = this.notesRepository.createQueryBuilder('note') .addSelect('note.score') .where('note.userHost IS NULL') .andWhere('note.score > 0') @@ -68,7 +71,7 @@ export default class extends Endpoint { notes = notes.slice(ps.offset, ps.offset + ps.limit); - return await Notes.packMany(notes, me); + return await this.notesRepository.packMany(notes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index 8e4435dc3a08..86c9b772622f 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { fetchMeta } from '@/misc/fetch-meta.js'; -import { Notes } from '@/models/index.js'; +import type { Notes } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; @@ -49,6 +49,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { @@ -60,7 +63,7 @@ export default class extends Endpoint { } //#region Construct query - const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.visibility = \'public\'') .andWhere('note.channelId IS NULL') @@ -96,7 +99,7 @@ export default class extends Endpoint { } }); - return await Notes.packMany(timeline, me); + return await this.notesRepository.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 49b2a0d8aaf3..d541d5b3174c 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -1,7 +1,8 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { fetchMeta } from '@/misc/fetch-meta.js'; -import { Followings, Notes } from '@/models/index.js'; +import type { Notes } from '@/models/index.js'; +import { Followings } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; @@ -55,6 +56,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { @@ -64,11 +68,11 @@ export default class extends Endpoint { } //#region Construct query - const followingQuery = Followings.createQueryBuilder('following') + const followingQuery = this.followingsRepository.createQueryBuilder('following') .select('following.followeeId') .where('following.followerId = :followerId', { followerId: me.id }); - const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere(new Brackets(qb => { qb.where(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: me.id }) @@ -135,7 +139,7 @@ export default class extends Endpoint { activeUsersChart.read(me); }); - return await Notes.packMany(timeline, me); + return await this.notesRepository.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 8bd7c3459d52..104e098e5474 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -1,8 +1,7 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { fetchMeta } from '@/misc/fetch-meta.js'; -import type { Users } from '@/models/index.js'; -import { Notes } from '@/models/index.js'; +import type { Users , Notes } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; @@ -55,6 +54,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { @@ -66,7 +68,7 @@ export default class extends Endpoint { } //#region Construct query - const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)') .innerJoinAndSelect('note.user', 'user') @@ -116,7 +118,7 @@ export default class extends Endpoint { } }); - return await Notes.packMany(timeline, me); + return await this.notesRepository.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index 8e7e7abb8417..aad901950d3b 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -1,7 +1,8 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import read from '@/services/note/read.js'; -import { Notes, Followings } from '@/models/index.js'; +import type { Notes } from '@/models/index.js'; +import { Followings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; @@ -37,14 +38,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const followingQuery = Followings.createQueryBuilder('following') + const followingQuery = this.followingsRepository.createQueryBuilder('following') .select('following.followeeId') .where('following.followerId = :followerId', { followerId: me.id }); - const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(new Brackets(qb => { qb .where(`'{"${me.id}"}' <@ note.mentions`) .orWhere(`'{"${me.id}"}' <@ note.visibleUserIds`); @@ -79,7 +83,7 @@ export default class extends Endpoint { read(me.id, mentions); - return await Notes.packMany(mentions, me); + return await this.notesRepository.packMany(mentions, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts index a6704fa76973..8ea974442f89 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts @@ -1,6 +1,7 @@ import { Brackets, In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { Polls, Mutings, Notes, PollVotes } from '@/models/index.js'; +import type { Notes } from '@/models/index.js'; +import { Polls, Mutings, PollVotes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -32,7 +33,8 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - + @Inject('notesRepository') + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { const query = Polls.createQueryBuilder('poll') @@ -56,7 +58,7 @@ export default class extends Endpoint { //#endregion //#region mute - const mutingQuery = Mutings.createQueryBuilder('muting') + const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') .select('muting.muteeId') .where('muting.muterId = :muterId', { muterId: me.id }); @@ -74,7 +76,7 @@ export default class extends Endpoint { if (polls.length === 0) return []; - const notes = await Notes.find({ + const notes = await this.notesRepository.find({ where: { id: In(polls.map(poll => poll.noteId)), }, @@ -83,7 +85,7 @@ export default class extends Endpoint { }, }); - return await Notes.packMany(notes, me, { + return await this.notesRepository.packMany(notes, me, { detail: true, }); }); 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 6bfc11b552e5..15dfc52fb249 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -93,7 +93,7 @@ export default class extends Endpoint { // Check blocking if (note.userId !== me.id) { - const block = await Blockings.findOneBy({ + const block = await this.blockingsRepository.findOneBy({ blockerId: note.userId, blockeeId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index 0c81118b01f3..3e1ffabc98a3 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Notes } from '@/models/index.js'; +import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { getNote } from '../../common/getters.js'; @@ -44,6 +44,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { @@ -52,7 +55,7 @@ export default class extends Endpoint { throw e; }); - const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere('note.renoteId = :renoteId', { renoteId: note.id }) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') @@ -72,7 +75,7 @@ export default class extends Endpoint { const renotes = await query.take(ps.limit).getMany(); - return await Notes.packMany(renotes, me); + return await this.notesRepository.packMany(renotes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index 8ebca2c88fdf..342cc6039e1c 100644 --- a/packages/backend/src/server/api/endpoints/notes/replies.ts +++ b/packages/backend/src/server/api/endpoints/notes/replies.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Notes } from '@/models/index.js'; +import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; @@ -34,10 +34,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere('note.replyId = :replyId', { replyId: ps.noteId }) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') @@ -57,7 +60,7 @@ export default class extends Endpoint { const timeline = await query.take(ps.limit).getMany(); - return await Notes.packMany(timeline, me); + return await this.notesRepository.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts index b9f0bd598d41..99d2aabc0b42 100644 --- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts +++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts @@ -1,6 +1,6 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { Notes } from '@/models/index.js'; +import type { Notes } from '@/models/index.js'; import { safeForSql } from '@/misc/safe-for-sql.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -76,7 +76,7 @@ export default class extends Endpoint { private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') .leftJoinAndSelect('user.banner', 'banner') @@ -145,7 +145,7 @@ export default class extends Endpoint { // Search notes const notes = await query.take(ps.limit).getMany(); - return await Notes.packMany(notes, me); + return await this.notesRepository.packMany(notes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index a4af13304035..483aab2199c4 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -1,6 +1,6 @@ import { In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { Notes } from '@/models/index.js'; +import type { Notes } from '@/models/index.js'; import config from '@/config/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; @@ -58,7 +58,7 @@ export default class extends Endpoint { ) { super(meta, paramDef, async (ps, me) => { if (es == null) { - const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId); + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId); if (ps.userId) { query.andWhere('note.userId = :userId', { userId: ps.userId }); @@ -86,7 +86,7 @@ export default class extends Endpoint { const notes = await query.take(ps.limit).getMany(); - return await Notes.packMany(notes, me); + return await this.notesRepository.packMany(notes, me); } else { const userQuery = ps.userId != null ? [{ term: { @@ -137,7 +137,7 @@ export default class extends Endpoint { if (hits.length === 0) return []; // Fetch found notes - const notes = await Notes.find({ + const notes = await this.notesRepository.find({ where: { id: In(hits), }, @@ -146,7 +146,7 @@ export default class extends Endpoint { }, }); - return await Notes.packMany(notes, me); + return await this.notesRepository.packMany(notes, me); } }); } diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts index 211d293d51ff..69bff33324a7 100644 --- a/packages/backend/src/server/api/endpoints/notes/show.ts +++ b/packages/backend/src/server/api/endpoints/notes/show.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Notes } from '@/models/index.js'; +import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../common/getters.js'; import { ApiError } from '../../error.js'; @@ -36,14 +36,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; + const note = await getNote(ps.noteId).catch(err => { + if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw err; }); - return await Notes.pack(note, me, { + return await this.notesRepository.pack(note, me, { detail: true, }); }); diff --git a/packages/backend/src/server/api/endpoints/notes/state.ts b/packages/backend/src/server/api/endpoints/notes/state.ts index 2d8c83318faf..db1bfa7db06c 100644 --- a/packages/backend/src/server/api/endpoints/notes/state.ts +++ b/packages/backend/src/server/api/endpoints/notes/state.ts @@ -1,5 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; -import { NoteFavorites, Notes, NoteThreadMutings, NoteWatchings } from '@/models/index.js'; +import type { Notes } from '@/models/index.js'; +import { NoteFavorites, NoteThreadMutings, NoteWatchings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -39,9 +40,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const note = await Notes.findOneByOrFail({ id: ps.noteId }); + const note = await this.notesRepository.findOneByOrFail({ id: ps.noteId }); const [favorite, watching, threadMuting] = await Promise.all([ NoteFavorites.count({ @@ -58,7 +61,7 @@ export default class extends Endpoint { }, take: 1, }), - NoteThreadMutings.count({ + this.noteThreadMutingsRepository.count({ where: { userId: me.id, threadId: note.threadId || note.id, diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts index 990808b328e5..cbb2b917092b 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Notes, NoteThreadMutings } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import type { Notes } from '@/models/index.js'; +import { NoteThreadMutings } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; import readNote from '@/services/note/read.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../../common/getters.js'; @@ -34,15 +35,18 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; + const note = await getNote(ps.noteId).catch(err => { + if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw err; }); - const mutedNotes = await Notes.find({ + const mutedNotes = await this.notesRepository.find({ where: [{ id: note.threadId || note.id, }, { @@ -52,7 +56,7 @@ export default class extends Endpoint { await readNote(me.id, mutedNotes); - await NoteThreadMutings.insert({ + await this.noteThreadMutingsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), threadId: note.threadId || note.id, diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts index c3db7b34e34c..0ce8d4ae392f 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts @@ -39,7 +39,7 @@ export default class extends Endpoint { throw e; }); - await NoteThreadMutings.delete({ + await this.noteThreadMutingsRepository.delete({ threadId: note.threadId || note.id, userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index bc3088738d56..f7562cb003f3 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -1,6 +1,7 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { Notes, Followings } from '@/models/index.js'; +import type { Notes } from '@/models/index.js'; +import { Followings } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; @@ -45,10 +46,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const hasFollowing = (await Followings.count({ + const hasFollowing = (await this.followingsRepository.count({ where: { followerId: me.id, }, @@ -56,11 +60,11 @@ export default class extends Endpoint { })) !== 0; //#region Construct query - const followingQuery = Followings.createQueryBuilder('following') + const followingQuery = this.followingsRepository.createQueryBuilder('following') .select('following.followeeId') .where('following.followerId = :followerId', { followerId: me.id }); - const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere(new Brackets(qb => { qb .where('note.userId = :meId', { meId: me.id }); @@ -127,7 +131,7 @@ export default class extends Endpoint { activeUsersChart.read(me); }); - return await Notes.packMany(timeline, me); + return await this.notesRepository.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts index 3ec562df791b..107f0c6bb291 100644 --- a/packages/backend/src/server/api/endpoints/notes/translate.ts +++ b/packages/backend/src/server/api/endpoints/notes/translate.ts @@ -4,7 +4,7 @@ import { Inject, Injectable } from '@nestjs/common'; import config from '@/config/index.js'; import { getAgentByUrl } from '@/misc/fetch.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; -import { Notes } from '@/models/index.js'; +import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { getNote } from '../../common/getters.js'; @@ -41,14 +41,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; + const note = await getNote(ps.noteId).catch(err => { + if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw err; }); - if (!(await Notes.isVisibleForMe(note, me ? me.id : null))) { + if (!(await this.notesRepository.isVisibleForMe(note, me ? me.id : null))) { return 204; // TODO: 良い感じのエラー返す } diff --git a/packages/backend/src/server/api/endpoints/notes/unrenote.ts b/packages/backend/src/server/api/endpoints/notes/unrenote.ts index 79943fde6d6f..d22d2480ede1 100644 --- a/packages/backend/src/server/api/endpoints/notes/unrenote.ts +++ b/packages/backend/src/server/api/endpoints/notes/unrenote.ts @@ -1,8 +1,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import deleteNote from '@/services/note/delete.js'; -import type { Users } from '@/models/index.js'; -import { Notes } from '@/models/index.js'; +import type { Users , Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { getNote } from '../../common/getters.js'; import { ApiError } from '../../error.js'; @@ -43,14 +42,17 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + @Inject('notesRepository') + private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; + const note = await getNote(ps.noteId).catch(err => { + if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw err; }); - const renotes = await Notes.findBy({ + const renotes = await this.notesRepository.findBy({ userId: me.id, renoteId: note.id, }); diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index b76b3c9a9cc4..44a55a8bf68e 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -1,6 +1,7 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { UserLists, UserListJoinings, Notes } from '@/models/index.js'; +import type { Notes } from '@/models/index.js'; +import { UserLists, UserListJoinings } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; @@ -55,6 +56,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('notesRepository') + private notesRepository: typeof Notes, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { @@ -68,7 +72,7 @@ export default class extends Endpoint { } //#region Construct query - const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) .innerJoin(UserListJoinings.metadata.targetName, 'userListJoining', 'userListJoining.userId = note.userId') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') @@ -124,7 +128,7 @@ export default class extends Endpoint { activeUsersChart.read(me); - return await Notes.packMany(timeline, me); + return await this.notesRepository.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/stats.ts b/packages/backend/src/server/api/endpoints/stats.ts index 64e59f730712..1ed74e1d9817 100644 --- a/packages/backend/src/server/api/endpoints/stats.ts +++ b/packages/backend/src/server/api/endpoints/stats.ts @@ -1,5 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Instances, NoteReactions, Notes, Users } from '@/models/index.js'; +import type { Notes, Users } from '@/models/index.js'; +import { Instances, NoteReactions } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { } from '@/services/chart/index.js'; import { IsNull } from 'typeorm'; @@ -71,8 +72,8 @@ export default class extends Endpoint { //originalReactionsCount, instances, ] = await Promise.all([ - Notes.count({ cache: 3600000 }), // 1 hour - Notes.count({ where: { userHost: IsNull() }, cache: 3600000 }), + this.notesRepository.count({ cache: 3600000 }), // 1 hour + this.notesRepository.count({ where: { userHost: IsNull() }, cache: 3600000 }), this.usersRepository.count({ cache: 3600000 }), this.usersRepository.count({ where: { host: IsNull() }, cache: 3600000 }), NoteReactions.count({ cache: 3600000 }), // 1 hour diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index 31accbbc5bdf..2993054222c1 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -98,7 +98,7 @@ export default class extends Endpoint { if (me == null) { throw new ApiError(meta.errors.forbidden); } else if (me.id !== user.id) { - const following = await Followings.findOneBy({ + const following = await this.followingsRepository.findOneBy({ followeeId: user.id, followerId: me.id, }); @@ -108,7 +108,7 @@ export default class extends Endpoint { } } - const query = this.queryService.makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.followingsRepository.createQueryBuilder('following'), ps.sinceId, ps.untilId) .andWhere('following.followeeId = :userId', { userId: user.id }) .innerJoinAndSelect('following.follower', 'follower'); @@ -116,7 +116,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return await Followings.packMany(followings, me, { populateFollower: true }); + return await this.followingsRepository.packMany(followings, me, { populateFollower: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index 7a476b7d09a3..00e70ccc17e0 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -98,7 +98,7 @@ export default class extends Endpoint { if (me == null) { throw new ApiError(meta.errors.forbidden); } else if (me.id !== user.id) { - const following = await Followings.findOneBy({ + const following = await this.followingsRepository.findOneBy({ followeeId: user.id, followerId: me.id, }); @@ -108,7 +108,7 @@ export default class extends Endpoint { } } - const query = this.queryService.makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.followingsRepository.createQueryBuilder('following'), ps.sinceId, ps.untilId) .andWhere('following.followerId = :userId', { userId: user.id }) .innerJoinAndSelect('following.followee', 'followee'); @@ -116,7 +116,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return await Followings.packMany(followings, me, { populateFollowee: true }); + return await this.followingsRepository.packMany(followings, me, { populateFollowee: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts index 2d4c6322a97b..17f862bb3f45 100644 --- a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts +++ b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts @@ -1,7 +1,7 @@ import { Not, In, IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { maximum } from '@/prelude/array.js'; -import { Notes, Users } from '@/models/index.js'; +import type { Notes, Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; @@ -63,13 +63,13 @@ export default class extends Endpoint { ) { super(meta, paramDef, async (ps, me) => { // Lookup user - const user = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; + const user = await getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; }); // Fetch recent notes - const recentNotes = await Notes.find({ + const recentNotes = await this.notesRepository.find({ where: { userId: user.id, replyId: Not(IsNull()), @@ -87,7 +87,7 @@ export default class extends Endpoint { } // TODO ミュートを考慮 - const replyTargetNotes = await Notes.find({ + const replyTargetNotes = await this.notesRepository.find({ where: { id: In(recentNotes.map(p => p.replyId)), }, diff --git a/packages/backend/src/server/api/endpoints/users/lists/push.ts b/packages/backend/src/server/api/endpoints/users/lists/push.ts index d486fc4c5a2c..a9e6a2d1af36 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/push.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/push.ts @@ -79,7 +79,7 @@ export default class extends Endpoint { // Check blocking if (user.id !== me.id) { - const block = await Blockings.findOneBy({ + const block = await this.blockingsRepository.findOneBy({ blockerId: user.id, blockeeId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 25ad75b4a151..bc7d58933b6c 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -1,6 +1,6 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { Notes } from '@/models/index.js'; +import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { ApiError } from '../../error.js'; @@ -64,13 +64,13 @@ export default class extends Endpoint { ) { super(meta, paramDef, async (ps, me) => { // Lookup user - const user = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; + const user = await getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; }); //#region Construct query - const query = this.queryService.makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.userId = :userId', { userId: user.id }) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') @@ -127,7 +127,7 @@ export default class extends Endpoint { const timeline = await query.take(ps.limit).getMany(); - return await Notes.packMany(timeline, me); + return await this.notesRepository.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/recommendation.ts b/packages/backend/src/server/api/endpoints/users/recommendation.ts index 0c8f868acceb..864795deb6fb 100644 --- a/packages/backend/src/server/api/endpoints/users/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/users/recommendation.ts @@ -59,7 +59,7 @@ export default class extends Endpoint { this.queryService.generateBlockQueryForUsers(query, me); this.queryService.generateBlockedUserQuery(query, me); - const followingQuery = Followings.createQueryBuilder('following') + const followingQuery = this.followingsRepository.createQueryBuilder('following') .select('following.followeeId') .where('following.followerId = :followerId', { followerId: me.id }); diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index b045ba0daccb..37573c450cd8 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -72,7 +72,7 @@ export default class extends Endpoint { let users: User[] = []; if (me) { - const followingQuery = Followings.createQueryBuilder('following') + const followingQuery = this.followingsRepository.createQueryBuilder('following') .select('following.followeeId') .where('following.followerId = :followerId', { followerId: me.id }); diff --git a/packages/backend/src/server/api/endpoints/users/stats.ts b/packages/backend/src/server/api/endpoints/users/stats.ts index 348b1e44ef64..90c7de7e38b8 100644 --- a/packages/backend/src/server/api/endpoints/users/stats.ts +++ b/packages/backend/src/server/api/endpoints/users/stats.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { Users } from '@/models/index.js'; -import { DriveFiles, Followings, NoteFavorites, NoteReactions, Notes, PageLikes, PollVotes } from '@/models/index.js'; +import type { Users , Notes } from '@/models/index.js'; +import { DriveFiles, Followings, NoteFavorites, NoteReactions, PageLikes, PollVotes } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; @@ -134,21 +134,21 @@ export default class extends Endpoint { } const result = await awaitAll({ - notesCount: Notes.createQueryBuilder('note') + notesCount: this.notesRepository.createQueryBuilder('note') .where('note.userId = :userId', { userId: user.id }) .getCount(), - repliesCount: Notes.createQueryBuilder('note') + repliesCount: this.notesRepository.createQueryBuilder('note') .where('note.userId = :userId', { userId: user.id }) .andWhere('note.replyId IS NOT NULL') .getCount(), - renotesCount: Notes.createQueryBuilder('note') + renotesCount: this.notesRepository.createQueryBuilder('note') .where('note.userId = :userId', { userId: user.id }) .andWhere('note.renoteId IS NOT NULL') .getCount(), - repliedCount: Notes.createQueryBuilder('note') + repliedCount: this.notesRepository.createQueryBuilder('note') .where('note.replyUserId = :userId', { userId: user.id }) .getCount(), - renotedCount: Notes.createQueryBuilder('note') + renotedCount: this.notesRepository.createQueryBuilder('note') .where('note.renoteUserId = :userId', { userId: user.id }) .getCount(), pollVotesCount: PollVotes.createQueryBuilder('vote') @@ -158,19 +158,19 @@ export default class extends Endpoint { .innerJoin('vote.note', 'note') .where('note.userId = :userId', { userId: user.id }) .getCount(), - localFollowingCount: Followings.createQueryBuilder('following') + localFollowingCount: this.followingsRepository.createQueryBuilder('following') .where('following.followerId = :userId', { userId: user.id }) .andWhere('following.followeeHost IS NULL') .getCount(), - remoteFollowingCount: Followings.createQueryBuilder('following') + remoteFollowingCount: this.followingsRepository.createQueryBuilder('following') .where('following.followerId = :userId', { userId: user.id }) .andWhere('following.followeeHost IS NOT NULL') .getCount(), - localFollowersCount: Followings.createQueryBuilder('following') + localFollowersCount: this.followingsRepository.createQueryBuilder('following') .where('following.followeeId = :userId', { userId: user.id }) .andWhere('following.followerHost IS NULL') .getCount(), - remoteFollowersCount: Followings.createQueryBuilder('following') + remoteFollowersCount: this.followingsRepository.createQueryBuilder('following') .where('following.followeeId = :userId', { userId: user.id }) .andWhere('following.followerHost IS NOT NULL') .getCount(), From 8da6c740b291323ec99321ac1605aca37147898b Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 03:35:44 +0900 Subject: [PATCH 098/180] wip --- .../backend/src/server/api/endpoints/i/export-blocking.ts | 5 +++-- .../backend/src/server/api/endpoints/i/export-following.ts | 5 +++-- packages/backend/src/server/api/endpoints/i/export-mute.ts | 5 +++-- packages/backend/src/server/api/endpoints/i/export-notes.ts | 5 +++-- .../backend/src/server/api/endpoints/i/export-user-lists.ts | 5 +++-- .../backend/src/server/api/endpoints/i/import-blocking.ts | 5 +++-- .../backend/src/server/api/endpoints/i/import-following.ts | 5 +++-- .../backend/src/server/api/endpoints/i/import-muting.ts | 5 +++-- .../backend/src/server/api/endpoints/i/import-user-lists.ts | 6 +++--- 9 files changed, 27 insertions(+), 19 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/i/export-blocking.ts b/packages/backend/src/server/api/endpoints/i/export-blocking.ts index a35ba6f0c54f..195e716bdc6b 100644 --- a/packages/backend/src/server/api/endpoints/i/export-blocking.ts +++ b/packages/backend/src/server/api/endpoints/i/export-blocking.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { createExportBlockingJob } from '@/queue/index.js'; +import { QueueService } from '@/queue/queue.service.js'; export const meta = { secure: true, @@ -22,9 +22,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queueService: QueueService, ) { super(meta, paramDef, async (ps, me) => { - createExportBlockingJob(me); + this.queueService.createExportBlockingJob(me); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/export-following.ts b/packages/backend/src/server/api/endpoints/i/export-following.ts index ed3c21a83a5e..2c765a246d89 100644 --- a/packages/backend/src/server/api/endpoints/i/export-following.ts +++ b/packages/backend/src/server/api/endpoints/i/export-following.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { createExportFollowingJob } from '@/queue/index.js'; +import { QueueService } from '@/queue/queue.service.js'; export const meta = { secure: true, @@ -25,9 +25,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queueService: QueueService, ) { super(meta, paramDef, async (ps, me) => { - createExportFollowingJob(me, ps.excludeMuting, ps.excludeInactive); + this.queueService.createExportFollowingJob(me, ps.excludeMuting, ps.excludeInactive); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/export-mute.ts b/packages/backend/src/server/api/endpoints/i/export-mute.ts index 5cc71eec4894..f6277f58a970 100644 --- a/packages/backend/src/server/api/endpoints/i/export-mute.ts +++ b/packages/backend/src/server/api/endpoints/i/export-mute.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { createExportMuteJob } from '@/queue/index.js'; +import { QueueService } from '@/queue/queue.service.js'; export const meta = { secure: true, @@ -22,9 +22,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queueService: QueueService, ) { super(meta, paramDef, async (ps, me) => { - createExportMuteJob(me); + this.queueService.createExportMuteJob(me); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/export-notes.ts b/packages/backend/src/server/api/endpoints/i/export-notes.ts index 10bab872bfb9..224086401301 100644 --- a/packages/backend/src/server/api/endpoints/i/export-notes.ts +++ b/packages/backend/src/server/api/endpoints/i/export-notes.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { createExportNotesJob } from '@/queue/index.js'; +import { QueueService } from '@/queue/queue.service.js'; export const meta = { secure: true, @@ -22,9 +22,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queueService: QueueService, ) { super(meta, paramDef, async (ps, me) => { - createExportNotesJob(me); + this.queueService.createExportNotesJob(me); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/export-user-lists.ts b/packages/backend/src/server/api/endpoints/i/export-user-lists.ts index 63d9dff7cdab..ffefb4857334 100644 --- a/packages/backend/src/server/api/endpoints/i/export-user-lists.ts +++ b/packages/backend/src/server/api/endpoints/i/export-user-lists.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { createExportUserListsJob } from '@/queue/index.js'; +import { QueueService } from '@/queue/queue.service.js'; export const meta = { secure: true, @@ -22,9 +22,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queueService: QueueService, ) { super(meta, paramDef, async (ps, me) => { - createExportUserListsJob(me); + this.queueService.createExportUserListsJob(me); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/import-blocking.ts b/packages/backend/src/server/api/endpoints/i/import-blocking.ts index 3a40a70e3e11..a177467cd2c4 100644 --- a/packages/backend/src/server/api/endpoints/i/import-blocking.ts +++ b/packages/backend/src/server/api/endpoints/i/import-blocking.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { createImportBlockingJob } from '@/queue/index.js'; +import { QueueService } from '@/queue/queue.service.js'; import { DriveFiles } from '@/models/index.js'; import { ApiError } from '../../error.js'; @@ -53,6 +53,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queueService: QueueService, ) { super(meta, paramDef, async (ps, me) => { const file = await DriveFiles.findOneBy({ id: ps.fileId }); @@ -62,7 +63,7 @@ export default class extends Endpoint { if (file.size > 50000) throw new ApiError(meta.errors.tooBigFile); if (file.size === 0) throw new ApiError(meta.errors.emptyFile); - createImportBlockingJob(me, file.id); + this.queueService.createImportBlockingJob(me, file.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/import-following.ts b/packages/backend/src/server/api/endpoints/i/import-following.ts index b1c7fe631b0f..f0c97d825e39 100644 --- a/packages/backend/src/server/api/endpoints/i/import-following.ts +++ b/packages/backend/src/server/api/endpoints/i/import-following.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { createImportFollowingJob } from '@/queue/index.js'; +import { QueueService } from '@/queue/queue.service.js'; import { DriveFiles } from '@/models/index.js'; import { ApiError } from '../../error.js'; @@ -52,6 +52,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queueService: QueueService, ) { super(meta, paramDef, async (ps, me) => { const file = await DriveFiles.findOneBy({ id: ps.fileId }); @@ -61,7 +62,7 @@ export default class extends Endpoint { if (file.size > 50000) throw new ApiError(meta.errors.tooBigFile); if (file.size === 0) throw new ApiError(meta.errors.emptyFile); - createImportFollowingJob(me, file.id); + this.queueService.createImportFollowingJob(me, file.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/import-muting.ts b/packages/backend/src/server/api/endpoints/i/import-muting.ts index 2673543cdb8e..b46bf9d27e8c 100644 --- a/packages/backend/src/server/api/endpoints/i/import-muting.ts +++ b/packages/backend/src/server/api/endpoints/i/import-muting.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { createImportMutingJob } from '@/queue/index.js'; +import { QueueService } from '@/queue/queue.service.js'; import { DriveFiles } from '@/models/index.js'; import { ApiError } from '../../error.js'; @@ -53,6 +53,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private queueService: QueueService, ) { super(meta, paramDef, async (ps, me) => { const file = await DriveFiles.findOneBy({ id: ps.fileId }); @@ -62,7 +63,7 @@ export default class extends Endpoint { if (file.size > 50000) throw new ApiError(meta.errors.tooBigFile); if (file.size === 0) throw new ApiError(meta.errors.emptyFile); - createImportMutingJob(me, file.id); + this.queueService.createImportMutingJob(me, file.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/import-user-lists.ts b/packages/backend/src/server/api/endpoints/i/import-user-lists.ts index 8e6dfc9a5fa2..09517d4575da 100644 --- a/packages/backend/src/server/api/endpoints/i/import-user-lists.ts +++ b/packages/backend/src/server/api/endpoints/i/import-user-lists.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { createImportUserListsJob } from '@/queue/index.js'; +import { QueueService } from '@/queue/queue.service.js'; import { DriveFiles } from '@/models/index.js'; import { ApiError } from '../../error.js'; @@ -52,7 +52,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - + private queueService: QueueService, ) { super(meta, paramDef, async (ps, me) => { const file = await DriveFiles.findOneBy({ id: ps.fileId }); @@ -62,7 +62,7 @@ export default class extends Endpoint { if (file.size > 30000) throw new ApiError(meta.errors.tooBigFile); if (file.size === 0) throw new ApiError(meta.errors.emptyFile); - createImportUserListsJob(me, file.id); + this.queueService.createImportUserListsJob(me, file.id); }); } } From 7d0df81250fa37856024f92f274ce2b4e20e64cf Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 04:12:56 +0900 Subject: [PATCH 099/180] wip --- .../backend/src/models/repositories/note.ts | 324 +---------- .../backend/src/models/repositories/user.ts | 436 +-------------- .../src/server/ActivityPubServerService.ts | 2 +- packages/backend/src/server/ServerService.ts | 2 +- .../backend/src/server/api/SigninService.ts | 2 +- .../src/server/api/common/GetterService.ts | 4 +- .../api/endpoints/admin/abuse-user-reports.ts | 2 +- .../api/endpoints/admin/accounts/create.ts | 2 +- .../api/endpoints/admin/accounts/delete.ts | 4 +- .../admin/drive-capacity-override.ts | 2 +- .../server/api/endpoints/admin/drive/files.ts | 2 +- .../server/api/endpoints/admin/emoji/copy.ts | 2 +- .../api/endpoints/admin/emoji/list-remote.ts | 2 +- .../server/api/endpoints/admin/emoji/list.ts | 2 +- .../endpoints/admin/show-moderation-logs.ts | 2 +- .../server/api/endpoints/admin/show-users.ts | 2 +- .../api/endpoints/admin/suspend-user.ts | 2 +- .../server/api/endpoints/antennas/create.ts | 2 +- .../server/api/endpoints/antennas/notes.ts | 2 +- .../src/server/api/endpoints/antennas/show.ts | 2 +- .../server/api/endpoints/antennas/update.ts | 2 +- .../src/server/api/endpoints/ap/show.ts | 4 +- .../src/server/api/endpoints/app/create.ts | 2 +- .../src/server/api/endpoints/app/show.ts | 2 +- .../server/api/endpoints/auth/session/show.ts | 2 +- .../api/endpoints/auth/session/userkey.ts | 2 +- .../server/api/endpoints/blocking/create.ts | 2 +- .../server/api/endpoints/blocking/delete.ts | 2 +- .../src/server/api/endpoints/blocking/list.ts | 2 +- .../server/api/endpoints/channels/timeline.ts | 2 +- .../src/server/api/endpoints/clips/notes.ts | 2 +- .../endpoints/drive/files/attached-notes.ts | 2 +- .../api/endpoints/federation/followers.ts | 2 +- .../api/endpoints/federation/following.ts | 2 +- .../server/api/endpoints/federation/users.ts | 2 +- .../server/api/endpoints/following/create.ts | 2 +- .../server/api/endpoints/following/delete.ts | 2 +- .../api/endpoints/following/invalidate.ts | 2 +- .../endpoints/following/requests/cancel.ts | 2 +- .../server/api/endpoints/hashtags/users.ts | 2 +- .../backend/src/server/api/endpoints/i.ts | 2 +- .../server/api/endpoints/i/2fa/key-done.ts | 2 +- .../server/api/endpoints/i/2fa/remove-key.ts | 2 +- .../backend/src/server/api/endpoints/i/pin.ts | 2 +- .../src/server/api/endpoints/i/unpin.ts | 2 +- .../server/api/endpoints/i/update-email.ts | 2 +- .../src/server/api/endpoints/i/update.ts | 2 +- .../api/endpoints/messaging/messages.ts | 2 +- .../backend/src/server/api/endpoints/meta.ts | 2 +- .../src/server/api/endpoints/mute/list.ts | 2 +- .../backend/src/server/api/endpoints/notes.ts | 2 +- .../server/api/endpoints/notes/children.ts | 2 +- .../api/endpoints/notes/conversation.ts | 2 +- .../src/server/api/endpoints/notes/create.ts | 2 +- .../server/api/endpoints/notes/featured.ts | 2 +- .../api/endpoints/notes/global-timeline.ts | 2 +- .../api/endpoints/notes/hybrid-timeline.ts | 2 +- .../api/endpoints/notes/local-timeline.ts | 2 +- .../server/api/endpoints/notes/mentions.ts | 2 +- .../endpoints/notes/polls/recommendation.ts | 2 +- .../src/server/api/endpoints/notes/renotes.ts | 2 +- .../src/server/api/endpoints/notes/replies.ts | 2 +- .../api/endpoints/notes/search-by-tag.ts | 2 +- .../src/server/api/endpoints/notes/search.ts | 4 +- .../src/server/api/endpoints/notes/show.ts | 2 +- .../server/api/endpoints/notes/timeline.ts | 2 +- .../api/endpoints/notes/user-list-timeline.ts | 2 +- .../src/server/api/endpoints/page-push.ts | 2 +- .../src/server/api/endpoints/pinned-users.ts | 2 +- .../backend/src/server/api/endpoints/users.ts | 2 +- .../server/api/endpoints/users/followers.ts | 2 +- .../server/api/endpoints/users/following.ts | 2 +- .../users/get-frequently-replied-users.ts | 2 +- .../server/api/endpoints/users/lists/pull.ts | 2 +- .../src/server/api/endpoints/users/notes.ts | 2 +- .../api/endpoints/users/recommendation.ts | 2 +- .../users/search-by-username-and-host.ts | 4 +- .../src/server/api/endpoints/users/search.ts | 2 +- .../src/server/api/endpoints/users/show.ts | 4 +- .../src/server/api/stream/channels/antenna.ts | 2 +- .../src/server/api/stream/channels/channel.ts | 6 +- .../api/stream/channels/global-timeline.ts | 4 +- .../src/server/api/stream/channels/hashtag.ts | 2 +- .../api/stream/channels/home-timeline.ts | 6 +- .../api/stream/channels/hybrid-timeline.ts | 6 +- .../api/stream/channels/local-timeline.ts | 4 +- .../src/server/api/stream/channels/main.ts | 4 +- .../server/api/stream/channels/messaging.ts | 4 +- .../server/api/stream/channels/user-list.ts | 6 +- .../src/server/web/ClientServerService.ts | 10 +- .../src/services/CreateNotificationService.ts | 2 +- packages/backend/src/services/DriveService.ts | 2 +- .../backend/src/services/NoteCreateService.ts | 22 +- .../backend/src/services/NotePiningService.ts | 6 +- packages/backend/src/services/PollService.ts | 2 +- .../backend/src/services/ReactionService.ts | 6 +- .../src/services/UserBlockingService.ts | 22 +- .../src/services/UserFollowingService.ts | 96 ++-- .../backend/src/services/UserListService.ts | 2 +- .../src/services/UserSuspendService.ts | 4 +- .../services/entities/NoteEntityService.ts | 364 +++++++++++++ .../services/entities/UserEntityService.ts | 501 ++++++++++++++++++ .../activitypub/ApDeliverManagerService.ts | 2 +- .../remote/activitypub/ApInboxService.ts | 2 +- .../remote/activitypub/ApRendererService.ts | 10 +- .../activitypub/models/ApPersonService.ts | 2 +- 106 files changed, 1068 insertions(+), 957 deletions(-) create mode 100644 packages/backend/src/services/entities/NoteEntityService.ts create mode 100644 packages/backend/src/services/entities/UserEntityService.ts diff --git a/packages/backend/src/models/repositories/note.ts b/packages/backend/src/models/repositories/note.ts index 3fefab03197c..ad102863753d 100644 --- a/packages/backend/src/models/repositories/note.ts +++ b/packages/backend/src/models/repositories/note.ts @@ -1,326 +1,4 @@ -import { In } from 'typeorm'; -import * as mfm from 'mfm-js'; import { Note } from '@/models/entities/note.js'; -import { User } from '@/models/entities/user.js'; -import { Users, PollVotes, DriveFiles, NoteReactions, Followings, Polls, Channels } from '../index.js'; -import { Packed } from '@/misc/schema.js'; -import { nyaize } from '@/misc/nyaize.js'; -import { awaitAll } from '@/prelude/await-all.js'; -import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '@/misc/reaction-lib.js'; -import { NoteReaction } from '@/models/entities/note-reaction.js'; -import { aggregateNoteEmojis, populateEmojis, prefetchEmojis } from '@/misc/populate-emojis.js'; import { db } from '@/db/postgre.js'; -async function hideNote(packedNote: Packed<'Note'>, meId: User['id'] | null) { - // TODO: isVisibleForMe を使うようにしても良さそう(型違うけど) - let hide = false; - - // visibility が specified かつ自分が指定されていなかったら非表示 - if (packedNote.visibility === 'specified') { - if (meId == null) { - hide = true; - } else if (meId === packedNote.userId) { - hide = false; - } else { - // 指定されているかどうか - const specified = packedNote.visibleUserIds!.some((id: any) => meId === id); - - if (specified) { - hide = false; - } else { - hide = true; - } - } - } - - // visibility が followers かつ自分が投稿者のフォロワーでなかったら非表示 - if (packedNote.visibility === 'followers') { - if (meId == null) { - hide = true; - } else if (meId === packedNote.userId) { - hide = false; - } else if (packedNote.reply && (meId === packedNote.reply.userId)) { - // 自分の投稿に対するリプライ - hide = false; - } else if (packedNote.mentions && packedNote.mentions.some(id => meId === id)) { - // 自分へのメンション - hide = false; - } else { - // フォロワーかどうか - const following = await Followings.findOneBy({ - followeeId: packedNote.userId, - followerId: meId, - }); - - if (following == null) { - hide = true; - } else { - hide = false; - } - } - } - - if (hide) { - packedNote.visibleUserIds = undefined; - packedNote.fileIds = []; - packedNote.files = []; - packedNote.text = null; - packedNote.poll = undefined; - packedNote.cw = null; - packedNote.isHidden = true; - } -} - -async function populatePoll(note: Note, meId: User['id'] | null) { - const poll = await Polls.findOneByOrFail({ noteId: note.id }); - const choices = poll.choices.map(c => ({ - text: c, - votes: poll.votes[poll.choices.indexOf(c)], - isVoted: false, - })); - - if (meId) { - if (poll.multiple) { - const votes = await PollVotes.findBy({ - userId: meId, - noteId: note.id, - }); - - const myChoices = votes.map(v => v.choice); - for (const myChoice of myChoices) { - choices[myChoice].isVoted = true; - } - } else { - const vote = await PollVotes.findOneBy({ - userId: meId, - noteId: note.id, - }); - - if (vote) { - choices[vote.choice].isVoted = true; - } - } - } - - return { - multiple: poll.multiple, - expiresAt: poll.expiresAt, - choices, - }; -} - -async function populateMyReaction(note: Note, meId: User['id'], _hint_?: { - myReactions: Map; -}) { - if (_hint_?.myReactions) { - const reaction = _hint_.myReactions.get(note.id); - if (reaction) { - return convertLegacyReaction(reaction.reaction); - } else if (reaction === null) { - return undefined; - } - // 実装上抜けがあるだけかもしれないので、「ヒントに含まれてなかったら(=undefinedなら)return」のようにはしない - } - - const reaction = await NoteReactions.findOneBy({ - userId: meId, - noteId: note.id, - }); - - if (reaction) { - return convertLegacyReaction(reaction.reaction); - } - - return undefined; -} - -export const NoteRepository = db.getRepository(Note).extend({ - async isVisibleForMe(note: Note, meId: User['id'] | null): Promise { - // This code must always be synchronized with the checks in generateVisibilityQuery. - // visibility が specified かつ自分が指定されていなかったら非表示 - if (note.visibility === 'specified') { - if (meId == null) { - return false; - } else if (meId === note.userId) { - return true; - } else { - // 指定されているかどうか - return note.visibleUserIds.some((id: any) => meId === id); - } - } - - // visibility が followers かつ自分が投稿者のフォロワーでなかったら非表示 - if (note.visibility === 'followers') { - if (meId == null) { - return false; - } else if (meId === note.userId) { - return true; - } else if (note.reply && (meId === note.reply.userId)) { - // 自分の投稿に対するリプライ - return true; - } else if (note.mentions && note.mentions.some(id => meId === id)) { - // 自分へのメンション - return true; - } else { - // フォロワーかどうか - const [following, user] = await Promise.all([ - Followings.count({ - where: { - followeeId: note.userId, - followerId: meId, - }, - take: 1, - }), - Users.findOneByOrFail({ id: meId }), - ]); - - /* If we know the following, everyhting is fine. - - But if we do not know the following, it might be that both the - author of the note and the author of the like are remote users, - in which case we can never know the following. Instead we have - to assume that the users are following each other. - */ - return following > 0 || (note.userHost != null && user.host != null); - } - } - - return true; - }, - - async pack( - src: Note['id'] | Note, - me?: { id: User['id'] } | null | undefined, - options?: { - detail?: boolean; - skipHide?: boolean; - _hint_?: { - myReactions: Map; - }; - } - ): Promise> { - const opts = Object.assign({ - detail: true, - skipHide: false, - }, options); - - const meId = me ? me.id : null; - const note = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - const host = note.userHost; - - let text = note.text; - - if (note.name && (note.url ?? note.uri)) { - text = `【${note.name}】\n${(note.text || '').trim()}\n\n${note.url ?? note.uri}`; - } - - const channel = note.channelId - ? note.channel - ? note.channel - : await Channels.findOneBy({ id: note.channelId }) - : null; - - const reactionEmojiNames = Object.keys(note.reactions).filter(x => x?.startsWith(':')).map(x => decodeReaction(x).reaction).map(x => x.replace(/:/g, '')); - - const packed: Packed<'Note'> = await awaitAll({ - id: note.id, - createdAt: note.createdAt.toISOString(), - userId: note.userId, - user: Users.pack(note.user ?? note.userId, me, { - detail: false, - }), - text: text, - cw: note.cw, - visibility: note.visibility, - localOnly: note.localOnly || undefined, - visibleUserIds: note.visibility === 'specified' ? note.visibleUserIds : undefined, - renoteCount: note.renoteCount, - repliesCount: note.repliesCount, - reactions: convertLegacyReactions(note.reactions), - tags: note.tags.length > 0 ? note.tags : undefined, - emojis: populateEmojis(note.emojis.concat(reactionEmojiNames), host), - fileIds: note.fileIds, - files: DriveFiles.packMany(note.fileIds), - replyId: note.replyId, - renoteId: note.renoteId, - channelId: note.channelId || undefined, - channel: channel ? { - id: channel.id, - name: channel.name, - } : undefined, - mentions: note.mentions.length > 0 ? note.mentions : undefined, - uri: note.uri || undefined, - url: note.url || undefined, - - ...(opts.detail ? { - reply: note.replyId ? this.pack(note.reply || note.replyId, me, { - detail: false, - _hint_: options?._hint_, - }) : undefined, - - renote: note.renoteId ? this.pack(note.renote || note.renoteId, me, { - detail: true, - _hint_: options?._hint_, - }) : undefined, - - poll: note.hasPoll ? populatePoll(note, meId) : undefined, - - ...(meId ? { - myReaction: populateMyReaction(note, meId, options?._hint_), - } : {}), - } : {}), - }); - - if (packed.user.isCat && packed.text) { - const tokens = packed.text ? mfm.parse(packed.text) : []; - mfm.inspect(tokens, node => { - if (node.type === 'text') { - // TODO: quoteなtextはskip - node.props.text = nyaize(node.props.text); - } - }); - packed.text = mfm.toString(tokens); - } - - if (!opts.skipHide) { - await hideNote(packed, meId); - } - - return packed; - }, - - async packMany( - notes: Note[], - me?: { id: User['id'] } | null | undefined, - options?: { - detail?: boolean; - skipHide?: boolean; - } - ) { - if (notes.length === 0) return []; - - const meId = me ? me.id : null; - const myReactionsMap = new Map(); - if (meId) { - const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!); - const targets = [...notes.map(n => n.id), ...renoteIds]; - const myReactions = await NoteReactions.findBy({ - userId: meId, - noteId: In(targets), - }); - - for (const target of targets) { - myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) || null); - } - } - - await prefetchEmojis(aggregateNoteEmojis(notes)); - - return await Promise.all(notes.map(n => this.pack(n, me, { - ...options, - _hint_: { - myReactions: myReactionsMap, - }, - }))); - }, -}); +export const NoteRepository = db.getRepository(Note); diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts index 5c46ae27a3cc..a07a8e5fc23b 100644 --- a/packages/backend/src/models/repositories/user.ts +++ b/packages/backend/src/models/repositories/user.ts @@ -1,436 +1,4 @@ -import { EntityRepository, Repository, In, Not } from 'typeorm'; -import Ajv from 'ajv'; -import { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; -import config from '@/config/index.js'; -import { Packed } from '@/misc/schema.js'; -import { awaitAll, Promiseable } from '@/prelude/await-all.js'; -import { populateEmojis } from '@/misc/populate-emojis.js'; -import { getAntennas } from '@/misc/antenna-cache.js'; -import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js'; -import { Cache } from '@/misc/cache.js'; +import { User } from '@/models/entities/user.js'; import { db } from '@/db/postgre.js'; -import { Instance } from '../entities/instance.js'; -import { Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserSecurityKeys, UserGroupJoinings, Pages, Announcements, AnnouncementReads, Antennas, AntennaNotes, ChannelFollowings, Instances, DriveFiles } from '../index.js'; -const userInstanceCache = new Cache(1000 * 60 * 60 * 3); - -type IsUserDetailed = Detailed extends true ? Packed<'UserDetailed'> : Packed<'UserLite'>; -type IsMeAndIsUserDetailed = - Detailed extends true ? - ExpectsMe extends true ? Packed<'MeDetailed'> : - ExpectsMe extends false ? Packed<'UserDetailedNotMe'> : - Packed<'UserDetailed'> : - Packed<'UserLite'>; - -const ajv = new Ajv(); - -const localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const; -const passwordSchema = { type: 'string', minLength: 1 } as const; -const nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const; -const descriptionSchema = { type: 'string', minLength: 1, maxLength: 500 } as const; -const locationSchema = { type: 'string', minLength: 1, maxLength: 50 } as const; -const birthdaySchema = { type: 'string', pattern: /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.toString().slice(1, -1) } as const; - -function isLocalUser(user: User): user is ILocalUser; -function isLocalUser(user: T): user is T & { host: null; }; -function isLocalUser(user: User | { host: User['host'] }): boolean { - return user.host == null; -} - -function isRemoteUser(user: User): user is IRemoteUser; -function isRemoteUser(user: T): user is T & { host: string; }; -function isRemoteUser(user: User | { host: User['host'] }): boolean { - return !isLocalUser(user); -} - -export const UserRepository = db.getRepository(User).extend({ - localUsernameSchema, - passwordSchema, - nameSchema, - descriptionSchema, - locationSchema, - birthdaySchema, - - //#region Validators - validateLocalUsername: ajv.compile(localUsernameSchema), - validatePassword: ajv.compile(passwordSchema), - validateName: ajv.compile(nameSchema), - validateDescription: ajv.compile(descriptionSchema), - validateLocation: ajv.compile(locationSchema), - validateBirthday: ajv.compile(birthdaySchema), - //#endregion - - async getRelation(me: User['id'], target: User['id']) { - return awaitAll({ - id: target, - isFollowing: Followings.count({ - where: { - followerId: me, - followeeId: target, - }, - take: 1, - }).then(n => n > 0), - isFollowed: Followings.count({ - where: { - followerId: target, - followeeId: me, - }, - take: 1, - }).then(n => n > 0), - hasPendingFollowRequestFromYou: FollowRequests.count({ - where: { - followerId: me, - followeeId: target, - }, - take: 1, - }).then(n => n > 0), - hasPendingFollowRequestToYou: FollowRequests.count({ - where: { - followerId: target, - followeeId: me, - }, - take: 1, - }).then(n => n > 0), - isBlocking: Blockings.count({ - where: { - blockerId: me, - blockeeId: target, - }, - take: 1, - }).then(n => n > 0), - isBlocked: Blockings.count({ - where: { - blockerId: target, - blockeeId: me, - }, - take: 1, - }).then(n => n > 0), - isMuted: Mutings.count({ - where: { - muterId: me, - muteeId: target, - }, - take: 1, - }).then(n => n > 0), - }); - }, - - async getHasUnreadMessagingMessage(userId: User['id']): Promise { - const mute = await Mutings.findBy({ - muterId: userId, - }); - - const joinings = await UserGroupJoinings.findBy({ userId: userId }); - - const groupQs = Promise.all(joinings.map(j => MessagingMessages.createQueryBuilder('message') - .where('message.groupId = :groupId', { groupId: j.userGroupId }) - .andWhere('message.userId != :userId', { userId: userId }) - .andWhere('NOT (:userId = ANY(message.reads))', { userId: userId }) - .andWhere('message.createdAt > :joinedAt', { joinedAt: j.createdAt }) // 自分が加入する前の会話については、未読扱いしない - .getOne().then(x => x != null))); - - const [withUser, withGroups] = await Promise.all([ - MessagingMessages.count({ - where: { - recipientId: userId, - isRead: false, - ...(mute.length > 0 ? { userId: Not(In(mute.map(x => x.muteeId))) } : {}), - }, - take: 1, - }).then(count => count > 0), - groupQs, - ]); - - return withUser || withGroups.some(x => x); - }, - - async getHasUnreadAnnouncement(userId: User['id']): Promise { - const reads = await AnnouncementReads.findBy({ - userId: userId, - }); - - const count = await Announcements.countBy(reads.length > 0 ? { - id: Not(In(reads.map(read => read.announcementId))), - } : {}); - - return count > 0; - }, - - async getHasUnreadAntenna(userId: User['id']): Promise { - const myAntennas = (await getAntennas()).filter(a => a.userId === userId); - - const unread = myAntennas.length > 0 ? await AntennaNotes.findOneBy({ - antennaId: In(myAntennas.map(x => x.id)), - read: false, - }) : null; - - return unread != null; - }, - - async getHasUnreadChannel(userId: User['id']): Promise { - const channels = await ChannelFollowings.findBy({ followerId: userId }); - - const unread = channels.length > 0 ? await NoteUnreads.findOneBy({ - userId: userId, - noteChannelId: In(channels.map(x => x.followeeId)), - }) : null; - - return unread != null; - }, - - async getHasUnreadNotification(userId: User['id']): Promise { - const mute = await Mutings.findBy({ - muterId: userId, - }); - const mutedUserIds = mute.map(m => m.muteeId); - - const count = await Notifications.count({ - where: { - notifieeId: userId, - ...(mutedUserIds.length > 0 ? { notifierId: Not(In(mutedUserIds)) } : {}), - isRead: false, - }, - take: 1, - }); - - return count > 0; - }, - - async getHasPendingReceivedFollowRequest(userId: User['id']): Promise { - const count = await FollowRequests.countBy({ - followeeId: userId, - }); - - return count > 0; - }, - - getOnlineStatus(user: User): 'unknown' | 'online' | 'active' | 'offline' { - if (user.hideOnlineStatus) return 'unknown'; - if (user.lastActiveDate == null) return 'unknown'; - const elapsed = Date.now() - user.lastActiveDate.getTime(); - return ( - elapsed < USER_ONLINE_THRESHOLD ? 'online' : - elapsed < USER_ACTIVE_THRESHOLD ? 'active' : - 'offline' - ); - }, - - async getAvatarUrl(user: User): Promise { - if (user.avatar) { - return DriveFiles.getPublicUrl(user.avatar, true) || this.getIdenticonUrl(user.id); - } else if (user.avatarId) { - const avatar = await DriveFiles.findOneByOrFail({ id: user.avatarId }); - return DriveFiles.getPublicUrl(avatar, true) || this.getIdenticonUrl(user.id); - } else { - return this.getIdenticonUrl(user.id); - } - }, - - getAvatarUrlSync(user: User): string { - if (user.avatar) { - return DriveFiles.getPublicUrl(user.avatar, true) || this.getIdenticonUrl(user.id); - } else { - return this.getIdenticonUrl(user.id); - } - }, - - getIdenticonUrl(userId: User['id']): string { - return `${config.url}/identicon/${userId}`; - }, - - async pack( - src: User['id'] | User, - me?: { id: User['id'] } | null | undefined, - options?: { - detail?: D, - includeSecrets?: boolean, - }, - ): Promise> { - const opts = Object.assign({ - detail: false, - includeSecrets: false, - }, options); - - let user: User; - - if (typeof src === 'object') { - user = src; - if (src.avatar === undefined && src.avatarId) src.avatar = await DriveFiles.findOneBy({ id: src.avatarId }) ?? null; - if (src.banner === undefined && src.bannerId) src.banner = await DriveFiles.findOneBy({ id: src.bannerId }) ?? null; - } else { - user = await this.findOneOrFail({ - where: { id: src }, - relations: { - avatar: true, - banner: true, - }, - }); - } - - const meId = me ? me.id : null; - const isMe = meId === user.id; - - const relation = meId && !isMe && opts.detail ? await this.getRelation(meId, user.id) : null; - const pins = opts.detail ? await UserNotePinings.createQueryBuilder('pin') - .where('pin.userId = :userId', { userId: user.id }) - .innerJoinAndSelect('pin.note', 'note') - .orderBy('pin.id', 'DESC') - .getMany() : []; - const profile = opts.detail ? await UserProfiles.findOneByOrFail({ userId: user.id }) : null; - - const followingCount = profile == null ? null : - (profile.ffVisibility === 'public') || isMe ? user.followingCount : - (profile.ffVisibility === 'followers') && (relation && relation.isFollowing) ? user.followingCount : - null; - - const followersCount = profile == null ? null : - (profile.ffVisibility === 'public') || isMe ? user.followersCount : - (profile.ffVisibility === 'followers') && (relation && relation.isFollowing) ? user.followersCount : - null; - - const falsy = opts.detail ? false : undefined; - - const packed = { - id: user.id, - name: user.name, - username: user.username, - host: user.host, - avatarUrl: this.getAvatarUrlSync(user), - avatarBlurhash: user.avatar?.blurhash || null, - avatarColor: null, // 後方互換性のため - isAdmin: user.isAdmin || falsy, - isModerator: user.isModerator || falsy, - isBot: user.isBot || falsy, - isCat: user.isCat || falsy, - instance: user.host ? userInstanceCache.fetch(user.host, - () => Instances.findOneBy({ host: user.host! }), - v => v != null, - ).then(instance => instance ? { - name: instance.name, - softwareName: instance.softwareName, - softwareVersion: instance.softwareVersion, - iconUrl: instance.iconUrl, - faviconUrl: instance.faviconUrl, - themeColor: instance.themeColor, - } : undefined) : undefined, - emojis: populateEmojis(user.emojis, user.host), - onlineStatus: this.getOnlineStatus(user), - driveCapacityOverrideMb: user.driveCapacityOverrideMb, - - ...(opts.detail ? { - url: profile!.url, - uri: user.uri, - createdAt: user.createdAt.toISOString(), - updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null, - lastFetchedAt: user.lastFetchedAt ? user.lastFetchedAt.toISOString() : null, - bannerUrl: user.banner ? DriveFiles.getPublicUrl(user.banner, false) : null, - bannerBlurhash: user.banner?.blurhash || null, - bannerColor: null, // 後方互換性のため - isLocked: user.isLocked, - isSilenced: user.isSilenced || falsy, - isSuspended: user.isSuspended || falsy, - description: profile!.description, - location: profile!.location, - birthday: profile!.birthday, - lang: profile!.lang, - fields: profile!.fields, - followersCount: followersCount || 0, - followingCount: followingCount || 0, - notesCount: user.notesCount, - pinnedNoteIds: pins.map(pin => pin.noteId), - pinnedNotes: Notes.packMany(pins.map(pin => pin.note!), me, { - detail: true, - }), - pinnedPageId: profile!.pinnedPageId, - pinnedPage: profile!.pinnedPageId ? Pages.pack(profile!.pinnedPageId, me) : null, - publicReactions: profile!.publicReactions, - ffVisibility: profile!.ffVisibility, - twoFactorEnabled: profile!.twoFactorEnabled, - usePasswordLessLogin: profile!.usePasswordLessLogin, - securityKeys: profile!.twoFactorEnabled - ? UserSecurityKeys.countBy({ - userId: user.id, - }).then(result => result >= 1) - : false, - } : {}), - - ...(opts.detail && isMe ? { - avatarId: user.avatarId, - bannerId: user.bannerId, - injectFeaturedNote: profile!.injectFeaturedNote, - receiveAnnouncementEmail: profile!.receiveAnnouncementEmail, - alwaysMarkNsfw: profile!.alwaysMarkNsfw, - autoSensitive: profile!.autoSensitive, - carefulBot: profile!.carefulBot, - autoAcceptFollowed: profile!.autoAcceptFollowed, - noCrawle: profile!.noCrawle, - isExplorable: user.isExplorable, - isDeleted: user.isDeleted, - hideOnlineStatus: user.hideOnlineStatus, - hasUnreadSpecifiedNotes: NoteUnreads.count({ - where: { userId: user.id, isSpecified: true }, - take: 1, - }).then(count => count > 0), - hasUnreadMentions: NoteUnreads.count({ - where: { userId: user.id, isMentioned: true }, - take: 1, - }).then(count => count > 0), - hasUnreadAnnouncement: this.getHasUnreadAnnouncement(user.id), - hasUnreadAntenna: this.getHasUnreadAntenna(user.id), - hasUnreadChannel: this.getHasUnreadChannel(user.id), - hasUnreadMessagingMessage: this.getHasUnreadMessagingMessage(user.id), - hasUnreadNotification: this.getHasUnreadNotification(user.id), - hasPendingReceivedFollowRequest: this.getHasPendingReceivedFollowRequest(user.id), - integrations: profile!.integrations, - mutedWords: profile!.mutedWords, - mutedInstances: profile!.mutedInstances, - mutingNotificationTypes: profile!.mutingNotificationTypes, - emailNotificationTypes: profile!.emailNotificationTypes, - showTimelineReplies: user.showTimelineReplies || falsy, - } : {}), - - ...(opts.includeSecrets ? { - email: profile!.email, - emailVerified: profile!.emailVerified, - securityKeysList: profile!.twoFactorEnabled - ? UserSecurityKeys.find({ - where: { - userId: user.id, - }, - select: { - id: true, - name: true, - lastUsed: true, - }, - }) - : [], - } : {}), - - ...(relation ? { - isFollowing: relation.isFollowing, - isFollowed: relation.isFollowed, - hasPendingFollowRequestFromYou: relation.hasPendingFollowRequestFromYou, - hasPendingFollowRequestToYou: relation.hasPendingFollowRequestToYou, - isBlocking: relation.isBlocking, - isBlocked: relation.isBlocked, - isMuted: relation.isMuted, - } : {}), - } as Promiseable> as Promiseable>; - - return await awaitAll(packed); - }, - - packMany( - users: (User['id'] | User)[], - me?: { id: User['id'] } | null | undefined, - options?: { - detail?: D, - includeSecrets?: boolean, - }, - ): Promise[]> { - return Promise.all(users.map(u => this.pack(u, me, options))); - }, - - isLocalUser, - isRemoteUser, -}); +export const UserRepository = db.getRepository(User); diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index dc2d1d8291cb..5d8986fb04e5 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -477,7 +477,7 @@ export class ActivityPubServerService { const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); - if (this.usersRepository.isLocalUser(user)) { + if (this.userEntityService.isLocalUser(user)) { ctx.body = this.apRendererService.renderActivity(this.apRendererService.renderKey(user, keypair)); ctx.set('Cache-Control', 'public, max-age=180'); this.#setResponseType(ctx); diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index b2fc83c9bb88..1bef68aecb88 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -131,7 +131,7 @@ export class ServerService { emailVerifyCode: null, }); - this.globalEventService.publishMainStream(profile.userId, 'meUpdated', await this.usersRepository.pack(profile.userId, { id: profile.userId }, { + this.globalEventService.publishMainStream(profile.userId, 'meUpdated', await this.userEntityService.pack(profile.userId, { id: profile.userId }, { detail: true, includeSecrets: true, })); diff --git a/packages/backend/src/server/api/SigninService.ts b/packages/backend/src/server/api/SigninService.ts index 9a1e08b5e339..20ed6447f535 100644 --- a/packages/backend/src/server/api/SigninService.ts +++ b/packages/backend/src/server/api/SigninService.ts @@ -54,7 +54,7 @@ export class SigninService { }).then(x => this.signinsRepository.findOneByOrFail(x.identifiers[0])); // Publish signin event - this.globalEventService.publishMainStream(user.id, 'signin', await this.signinsRepository.pack(record)); + this.globalEventService.publishMainStream(user.id, 'signin', await this.signinEntityService.pack(record)); })(); } } diff --git a/packages/backend/src/server/api/common/GetterService.ts b/packages/backend/src/server/api/common/GetterService.ts index 09cf2af011f2..dfb181d9326a 100644 --- a/packages/backend/src/server/api/common/GetterService.ts +++ b/packages/backend/src/server/api/common/GetterService.ts @@ -48,7 +48,7 @@ export class GetterService { public async getRemoteUser(userId: User['id']) { const user = await this.getUser(userId); - if (!this.usersRepository.isRemoteUser(user)) { + if (!this.userEntityService.isRemoteUser(user)) { throw new Error('user is not a remote user'); } @@ -61,7 +61,7 @@ export class GetterService { public async getLocalUser(userId: User['id']) { const user = await this.getUser(userId); - if (!this.usersRepository.isLocalUser(user)) { + if (!this.userEntityService.isLocalUser(user)) { throw new Error('user is not a local user'); } diff --git a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts index effb8a9e45d3..6dccc073fff2 100644 --- a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts +++ b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts @@ -114,7 +114,7 @@ export default class extends Endpoint { const reports = await query.take(ps.limit).getMany(); - return await this.abuseUserReportsRepository.packMany(reports); + return await this.abuseUserReportEntityService.packMany(reports); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts index 4a281200bcc1..d09d84bebcf4 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts @@ -50,7 +50,7 @@ export default class extends Endpoint { password: ps.password, }); - const res = await this.usersRepository.pack(account, account, { + const res = await this.userEntityService.pack(account, account, { detail: true, includeSecrets: true, }); diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts index 1c618af6e30c..806e764c293d 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts @@ -46,7 +46,7 @@ export default class extends Endpoint { throw new Error('cannot suspend moderator'); } - if (this.usersRepository.isLocalUser(user)) { + if (this.userEntityService.isLocalUser(user)) { // 物理削除する前にDelete activityを送信する await this.userSuspendService.doPostSuspend(user).catch(err => {}); @@ -63,7 +63,7 @@ export default class extends Endpoint { isDeleted: true, }); - if (this.usersRepository.isLocalUser(user)) { + if (this.userEntityService.isLocalUser(user)) { // Terminate streaming this.globalEventService.publishUserEvent(user.id, 'terminate', {}); } diff --git a/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts b/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts index fcb28b141dca..119328056d09 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts @@ -35,7 +35,7 @@ export default class extends Endpoint { throw new Error('user not found'); } - if (!this.usersRepository.isLocalUser(user)) { + if (!this.userEntityService.isLocalUser(user)) { throw new Error('user is not local user'); } diff --git a/packages/backend/src/server/api/endpoints/admin/drive/files.ts b/packages/backend/src/server/api/endpoints/admin/drive/files.ts index c1c8ccf2bfc3..e3dbb772eaa8 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/files.ts @@ -75,7 +75,7 @@ export default class extends Endpoint { const files = await query.take(ps.limit).getMany(); - return await this.driveFilesRepository.packMany(files, { detail: true, withUser: true, self: true }); + return await this.driveFileEntityService.packMany(files, { detail: true, withUser: true, self: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index 2f1a0f014179..1a194240f9f1 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -90,7 +90,7 @@ export default class extends Endpoint { await this.db.queryResultCache!.remove(['meta_emojis']); this.globalEventService.publishBroadcastStream('emojiAdded', { - emoji: await this.emojisRepository.pack(copied.id), + emoji: await this.emojiEntityService.pack(copied.id), }); return { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts index 313c794beb7a..e801e61e6ada 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts @@ -96,7 +96,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return this.emojisRepository.packMany(emojis); + return this.emojiEntityService.packMany(emojis); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts index 5b3af9e86450..71dbc8a0cd7a 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts @@ -94,7 +94,7 @@ export default class extends Endpoint { emojis = await q.take(ps.limit).getMany(); } - return this.emojisRepository.packMany(emojis); + return this.emojiEntityService.packMany(emojis); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts index b9e500785dbe..0de737615711 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts @@ -73,7 +73,7 @@ export default class extends Endpoint { const reports = await query.take(ps.limit).getMany(); - return await this.moderationLogsRepository.packMany(reports); + return await this.moderationLogEntityService.packMany(reports); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/show-users.ts b/packages/backend/src/server/api/endpoints/admin/show-users.ts index dd8b21eb343b..c643685b1879 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-users.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts @@ -86,7 +86,7 @@ export default class extends Endpoint { const users = await query.getMany(); - return await this.usersRepository.packMany(users, me, { detail: true }); + return await this.userEntityService.packMany(users, me, { detail: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts index 7aadf35eb735..3215d13f580b 100644 --- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts @@ -64,7 +64,7 @@ export default class extends Endpoint { }); // Terminate streaming - if (this.usersRepository.isLocalUser(user)) { + if (this.userEntityService.isLocalUser(user)) { this.globalEventService.publishUserEvent(user.id, 'terminate', {}); } diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index dcad185e0b89..cb89c127feaa 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -120,7 +120,7 @@ export default class extends Endpoint { this.globalEventService.publishInternalEvent('antennaCreated', antenna); - return await this.antennasRepository.pack(antenna); + return await this.antennaEntityService.pack(antenna); }); } } diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index 2bde5fa464ed..b2a5be4e1cff 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -96,7 +96,7 @@ export default class extends Endpoint { this.noteReadService.read(me.id, notes); } - return await this.notesRepository.packMany(notes, me); + return await this.noteEntityService.packMany(notes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/antennas/show.ts b/packages/backend/src/server/api/endpoints/antennas/show.ts index c358a4e78fbe..65b3eea31ec3 100644 --- a/packages/backend/src/server/api/endpoints/antennas/show.ts +++ b/packages/backend/src/server/api/endpoints/antennas/show.ts @@ -51,7 +51,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchAntenna); } - return await this.antennasRepository.pack(antenna); + return await this.antennaEntityService.pack(antenna); }); } } diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts index 2e470cfb2071..44776ab17116 100644 --- a/packages/backend/src/server/api/endpoints/antennas/update.ts +++ b/packages/backend/src/server/api/endpoints/antennas/update.ts @@ -132,7 +132,7 @@ export default class extends Endpoint { this.globalEventService.publishInternalEvent('antennaUpdated', await this.antennasRepository.findOneByOrFail({ id: antenna.id })); - return await this.antennasRepository.pack(antenna.id); + return await this.antennaEntityService.pack(antenna.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index b8a0362a996e..85636c7fae0c 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -142,11 +142,11 @@ export default class extends Endpoint { if (user != null) { return { type: 'User', - object: await this.usersRepository.pack(user, me, { detail: true }), + object: await this.userEntityService.pack(user, me, { detail: true }), }; } else if (note != null) { try { - const object = await this.notesRepository.pack(note, me, { detail: true }); + const object = await this.noteEntityService.pack(note, me, { detail: true }); return { type: 'Note', diff --git a/packages/backend/src/server/api/endpoints/app/create.ts b/packages/backend/src/server/api/endpoints/app/create.ts index 3624186bcadb..21b33777cf4a 100644 --- a/packages/backend/src/server/api/endpoints/app/create.ts +++ b/packages/backend/src/server/api/endpoints/app/create.ts @@ -58,7 +58,7 @@ export default class extends Endpoint { secret: secret, }).then(x => this.appsRepository.findOneByOrFail(x.identifiers[0])); - return await this.appsRepository.pack(app, null, { + return await this.appEntityService.pack(app, null, { detail: true, includeSecret: true, }); diff --git a/packages/backend/src/server/api/endpoints/app/show.ts b/packages/backend/src/server/api/endpoints/app/show.ts index 0d2a78b09e8d..036921cc6bff 100644 --- a/packages/backend/src/server/api/endpoints/app/show.ts +++ b/packages/backend/src/server/api/endpoints/app/show.ts @@ -46,7 +46,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchApp); } - return await this.appsRepository.pack(ap, user, { + return await this.appEntityService.pack(ap, user, { detail: true, includeSecret: isSecure && (ap.userId === user!.id), }); diff --git a/packages/backend/src/server/api/endpoints/auth/session/show.ts b/packages/backend/src/server/api/endpoints/auth/session/show.ts index 9de256e707dc..f5f40629384b 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/show.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/show.ts @@ -63,7 +63,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchSession); } - return await this.authSessionsRepository.pack(session, me); + return await this.authSessionEntityService.pack(session, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts index bfb90019547b..56df7f6132c0 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts @@ -106,7 +106,7 @@ export default class extends Endpoint { return { accessToken: accessToken.token, - user: await this.usersRepository.pack(session.userId, null, { + user: await this.userEntityService.pack(session.userId, null, { detail: true, }), }; diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts index e3c721c927a3..c9702251314b 100644 --- a/packages/backend/src/server/api/endpoints/blocking/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking/create.ts @@ -92,7 +92,7 @@ export default class extends Endpoint { noteUserId: blockee.id, }); - return await this.usersRepository.pack(blockee.id, blocker, { + return await this.userEntityService.pack(blockee.id, blocker, { detail: true, }); }); diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts index f00021f53b0c..9c6c3dca1a62 100644 --- a/packages/backend/src/server/api/endpoints/blocking/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts @@ -88,7 +88,7 @@ export default class extends Endpoint { // Delete blocking await deleteBlocking(blocker, blockee); - return await this.usersRepository.pack(blockee.id, blocker, { + return await this.userEntityService.pack(blockee.id, blocker, { detail: true, }); }); diff --git a/packages/backend/src/server/api/endpoints/blocking/list.ts b/packages/backend/src/server/api/endpoints/blocking/list.ts index b5635d2fe90b..45bb1975371d 100644 --- a/packages/backend/src/server/api/endpoints/blocking/list.ts +++ b/packages/backend/src/server/api/endpoints/blocking/list.ts @@ -51,7 +51,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return await this.blockingsRepository.packMany(blockings, me); + return await this.blockingEntityService.packMany(blockings, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index 78de50afb7c6..57d2b2c55d5c 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -82,7 +82,7 @@ export default class extends Endpoint { if (me) activeUsersChart.read(me); - return await this.notesRepository.packMany(timeline, me); + return await this.noteEntityService.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts index 3d9f2b04a450..5063a185a125 100644 --- a/packages/backend/src/server/api/endpoints/clips/notes.ts +++ b/packages/backend/src/server/api/endpoints/clips/notes.ts @@ -89,7 +89,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return await this.notesRepository.packMany(notes, me); + return await this.noteEntityService.packMany(notes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts index c0e0cbf71e6f..c3f9a6df2f17 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts @@ -62,7 +62,7 @@ export default class extends Endpoint { .where(':file = ANY(note.fileIds)', { file: file.id }) .getMany(); - return await this.notesRepository.packMany(notes, me, { + return await this.noteEntityService.packMany(notes, me, { detail: true, }); }); diff --git a/packages/backend/src/server/api/endpoints/federation/followers.ts b/packages/backend/src/server/api/endpoints/federation/followers.ts index e1ccad9d0e30..fcb24b4550a1 100644 --- a/packages/backend/src/server/api/endpoints/federation/followers.ts +++ b/packages/backend/src/server/api/endpoints/federation/followers.ts @@ -50,7 +50,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return await this.followingsRepository.packMany(followings, me, { populateFollowee: true }); + return await this.followingEntityService.packMany(followings, me, { populateFollowee: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/federation/following.ts b/packages/backend/src/server/api/endpoints/federation/following.ts index ace474300967..53ac1269983c 100644 --- a/packages/backend/src/server/api/endpoints/federation/following.ts +++ b/packages/backend/src/server/api/endpoints/federation/following.ts @@ -50,7 +50,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return await this.followingsRepository.packMany(followings, me, { populateFollowee: true }); + return await this.followingEntityService.packMany(followings, me, { populateFollowee: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/federation/users.ts b/packages/backend/src/server/api/endpoints/federation/users.ts index 6c9317907dcb..46976039e2de 100644 --- a/packages/backend/src/server/api/endpoints/federation/users.ts +++ b/packages/backend/src/server/api/endpoints/federation/users.ts @@ -47,7 +47,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return await this.usersRepository.packMany(users, me, { detail: true }); + return await this.userEntityService.packMany(users, me, { detail: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts index 6cdcd4a76971..9c296597f1f6 100644 --- a/packages/backend/src/server/api/endpoints/following/create.ts +++ b/packages/backend/src/server/api/endpoints/following/create.ts @@ -108,7 +108,7 @@ export default class extends Endpoint { throw e; } - return await this.usersRepository.pack(followee.id, me); + return await this.userEntityService.pack(followee.id, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/following/delete.ts b/packages/backend/src/server/api/endpoints/following/delete.ts index f7d4c47fcb87..125422119c28 100644 --- a/packages/backend/src/server/api/endpoints/following/delete.ts +++ b/packages/backend/src/server/api/endpoints/following/delete.ts @@ -87,7 +87,7 @@ export default class extends Endpoint { await deleteFollowing(follower, followee); - return await this.usersRepository.pack(followee.id, me); + return await this.userEntityService.pack(followee.id, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/following/invalidate.ts b/packages/backend/src/server/api/endpoints/following/invalidate.ts index 6d71e376f11e..129f244cb446 100644 --- a/packages/backend/src/server/api/endpoints/following/invalidate.ts +++ b/packages/backend/src/server/api/endpoints/following/invalidate.ts @@ -87,7 +87,7 @@ export default class extends Endpoint { await deleteFollowing(follower, followee); - return await this.usersRepository.pack(followee.id, me); + return await this.userEntityService.pack(followee.id, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts index 1080f4ce395a..5327b5c53a76 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts @@ -65,7 +65,7 @@ export default class extends Endpoint { throw e; } - return await this.usersRepository.pack(followee.id, me); + return await this.userEntityService.pack(followee.id, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts index 274c944837b1..70982fc63537 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/users.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts @@ -68,7 +68,7 @@ export default class extends Endpoint { const users = await query.take(ps.limit).getMany(); - return await this.usersRepository.packMany(users, me, { detail: true }); + return await this.userEntityService.packMany(users, me, { detail: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index 65c2f3b7458d..b6bfbd13c460 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -34,7 +34,7 @@ export default class extends Endpoint { const isSecure = token == null; // ここで渡ってきている user はキャッシュされていて古い可能性もあるので id だけ渡す - return await this.usersRepository.pack(user.id, user, { + return await this.userEntityService.pack(user.id, user, { detail: true, includeSecrets: isSecure, }); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts index acc5a1d7824c..f55d0bc69b89 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts @@ -140,7 +140,7 @@ export default class extends Endpoint { }); // Publish meUpdated event - publishMainStream(me.id, 'meUpdated', await this.usersRepository.pack(me.id, me, { + publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { detail: true, includeSecrets: true, })); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts index 5decc8d3a11c..de4ddb2c78bd 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts @@ -44,7 +44,7 @@ export default class extends Endpoint { }); // Publish meUpdated event - publishMainStream(me.id, 'meUpdated', await this.usersRepository.pack(me.id, me, { + publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { detail: true, includeSecrets: true, })); diff --git a/packages/backend/src/server/api/endpoints/i/pin.ts b/packages/backend/src/server/api/endpoints/i/pin.ts index 8b56a61a0708..47db5c1b31a3 100644 --- a/packages/backend/src/server/api/endpoints/i/pin.ts +++ b/packages/backend/src/server/api/endpoints/i/pin.ts @@ -61,7 +61,7 @@ export default class extends Endpoint { throw e; }); - return await this.usersRepository.pack(me.id, me, { + return await this.userEntityService.pack(me.id, me, { detail: true, }); }); diff --git a/packages/backend/src/server/api/endpoints/i/unpin.ts b/packages/backend/src/server/api/endpoints/i/unpin.ts index 54e1238e05bf..46b0bb8c8a2f 100644 --- a/packages/backend/src/server/api/endpoints/i/unpin.ts +++ b/packages/backend/src/server/api/endpoints/i/unpin.ts @@ -47,7 +47,7 @@ export default class extends Endpoint { throw e; }); - return await this.usersRepository.pack(me.id, me, { + return await this.userEntityService.pack(me.id, me, { detail: true, }); }); diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts index c6f730a76f34..6c9201034e0c 100644 --- a/packages/backend/src/server/api/endpoints/i/update-email.ts +++ b/packages/backend/src/server/api/endpoints/i/update-email.ts @@ -75,7 +75,7 @@ export default class extends Endpoint { emailVerifyCode: null, }); - const iObj = await this.usersRepository.pack(me.id, me, { + const iObj = await this.userEntityService.pack(me.id, me, { detail: true, includeSecrets: true, }); diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 4fb29162da09..9e131ae40611 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -244,7 +244,7 @@ export default class extends Endpoint { if (Object.keys(updates).length > 0) await this.usersRepository.update(user.id, updates); if (Object.keys(profileUpdates).length > 0) await UserProfiles.update(user.id, profileUpdates); - const iObj = await this.usersRepository.pack(user.id, user, { + const iObj = await this.userEntityService.pack(user.id, user, { detail: true, includeSecrets: isSecure, }); diff --git a/packages/backend/src/server/api/endpoints/messaging/messages.ts b/packages/backend/src/server/api/endpoints/messaging/messages.ts index af02431e59e7..3414d5694bd0 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages.ts @@ -108,7 +108,7 @@ export default class extends Endpoint { readUserMessagingMessage(me.id, recipient.id, messages.filter(m => m.recipientId === me.id).map(x => x.id)); // リモートユーザーとのメッセージだったら既読配信 - if (this.usersRepository.isLocalUser(me) && this.usersRepository.isRemoteUser(recipient)) { + if (this.userEntityService.isLocalUser(me) && this.userEntityService.isRemoteUser(recipient)) { deliverReadActivity(me, recipient, messages); } } diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 8e156c6cd0d2..ddbf2514a53a 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -400,7 +400,7 @@ export default class extends Endpoint { }; if (ps.detail) { - const proxyAccount = instance.proxyAccountId ? await this.usersRepository.pack(instance.proxyAccountId).catch(() => null) : null; + const proxyAccount = instance.proxyAccountId ? await this.userEntityService.pack(instance.proxyAccountId).catch(() => null) : null; response.proxyAccountName = proxyAccount ? proxyAccount.username : null; response.features = { diff --git a/packages/backend/src/server/api/endpoints/mute/list.ts b/packages/backend/src/server/api/endpoints/mute/list.ts index 0e9ad879cbbc..80a356e00957 100644 --- a/packages/backend/src/server/api/endpoints/mute/list.ts +++ b/packages/backend/src/server/api/endpoints/mute/list.ts @@ -51,7 +51,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return await this.mutingsRepository.packMany(mutings, me); + return await this.mutingEntityService.packMany(mutings, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes.ts b/packages/backend/src/server/api/endpoints/notes.ts index db47fd89aac1..844b20cd1273 100644 --- a/packages/backend/src/server/api/endpoints/notes.ts +++ b/packages/backend/src/server/api/endpoints/notes.ts @@ -84,7 +84,7 @@ export default class extends Endpoint { const notes = await query.take(ps.limit).getMany(); - return await this.notesRepository.packMany(notes); + return await this.noteEntityService.packMany(notes); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index 1f9d499a0f09..9ce24a542fbc 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -73,7 +73,7 @@ export default class extends Endpoint { const notes = await query.take(ps.limit).getMany(); - return await this.notesRepository.packMany(notes, me); + return await this.noteEntityService.packMany(notes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/conversation.ts b/packages/backend/src/server/api/endpoints/notes/conversation.ts index a2315a69ea74..ac75dacd5052 100644 --- a/packages/backend/src/server/api/endpoints/notes/conversation.ts +++ b/packages/backend/src/server/api/endpoints/notes/conversation.ts @@ -77,7 +77,7 @@ export default class extends Endpoint { await get(note.replyId); } - return await this.notesRepository.packMany(conversation, me); + return await this.noteEntityService.packMany(conversation, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index f3c984de3317..fcd82e60dac6 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -281,7 +281,7 @@ export default class extends Endpoint { }); return { - createdNote: await this.notesRepository.pack(note, me), + createdNote: await this.noteEntityService.pack(note, me), }; }); } diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index 6181398d0049..06cb679f7864 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -71,7 +71,7 @@ export default class extends Endpoint { notes = notes.slice(ps.offset, ps.offset + ps.limit); - return await this.notesRepository.packMany(notes, me); + return await this.noteEntityService.packMany(notes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index 86c9b772622f..ce119a3e1554 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -99,7 +99,7 @@ export default class extends Endpoint { } }); - return await this.notesRepository.packMany(timeline, me); + return await this.noteEntityService.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index d541d5b3174c..36ec6f56168b 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -139,7 +139,7 @@ export default class extends Endpoint { activeUsersChart.read(me); }); - return await this.notesRepository.packMany(timeline, me); + return await this.noteEntityService.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 104e098e5474..aec538419c31 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -118,7 +118,7 @@ export default class extends Endpoint { } }); - return await this.notesRepository.packMany(timeline, me); + return await this.noteEntityService.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index aad901950d3b..be3794c39534 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -83,7 +83,7 @@ export default class extends Endpoint { read(me.id, mentions); - return await this.notesRepository.packMany(mentions, me); + return await this.noteEntityService.packMany(mentions, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts index 8ea974442f89..2150fa0fa380 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts @@ -85,7 +85,7 @@ export default class extends Endpoint { }, }); - return await this.notesRepository.packMany(notes, me, { + return await this.noteEntityService.packMany(notes, me, { detail: true, }); }); diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index 3e1ffabc98a3..2166c6c0328a 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -75,7 +75,7 @@ export default class extends Endpoint { const renotes = await query.take(ps.limit).getMany(); - return await this.notesRepository.packMany(renotes, me); + return await this.noteEntityService.packMany(renotes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index 342cc6039e1c..f9e2d0688ec9 100644 --- a/packages/backend/src/server/api/endpoints/notes/replies.ts +++ b/packages/backend/src/server/api/endpoints/notes/replies.ts @@ -60,7 +60,7 @@ export default class extends Endpoint { const timeline = await query.take(ps.limit).getMany(); - return await this.notesRepository.packMany(timeline, me); + return await this.noteEntityService.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts index 99d2aabc0b42..d225bf3f0da9 100644 --- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts +++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts @@ -145,7 +145,7 @@ export default class extends Endpoint { // Search notes const notes = await query.take(ps.limit).getMany(); - return await this.notesRepository.packMany(notes, me); + return await this.noteEntityService.packMany(notes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index 483aab2199c4..e70963f4948a 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -86,7 +86,7 @@ export default class extends Endpoint { const notes = await query.take(ps.limit).getMany(); - return await this.notesRepository.packMany(notes, me); + return await this.noteEntityService.packMany(notes, me); } else { const userQuery = ps.userId != null ? [{ term: { @@ -146,7 +146,7 @@ export default class extends Endpoint { }, }); - return await this.notesRepository.packMany(notes, me); + return await this.noteEntityService.packMany(notes, me); } }); } diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts index 69bff33324a7..3e9bfd825cb6 100644 --- a/packages/backend/src/server/api/endpoints/notes/show.ts +++ b/packages/backend/src/server/api/endpoints/notes/show.ts @@ -45,7 +45,7 @@ export default class extends Endpoint { throw err; }); - return await this.notesRepository.pack(note, me, { + return await this.noteEntityService.pack(note, me, { detail: true, }); }); diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index f7562cb003f3..48bd6043d2bf 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -131,7 +131,7 @@ export default class extends Endpoint { activeUsersChart.read(me); }); - return await this.notesRepository.packMany(timeline, me); + return await this.noteEntityService.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 44a55a8bf68e..b8e4df6d59f1 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -128,7 +128,7 @@ export default class extends Endpoint { activeUsersChart.read(me); - return await this.notesRepository.packMany(timeline, me); + return await this.noteEntityService.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/page-push.ts b/packages/backend/src/server/api/endpoints/page-push.ts index 067974169121..a784d5d7bf22 100644 --- a/packages/backend/src/server/api/endpoints/page-push.ts +++ b/packages/backend/src/server/api/endpoints/page-push.ts @@ -46,7 +46,7 @@ export default class extends Endpoint { event: ps.event, var: ps.var, userId: me.id, - user: await this.usersRepository.pack(me.id, { id: page.userId }, { + user: await this.userEntityService.pack(me.id, { id: page.userId }, { detail: true, }), }); diff --git a/packages/backend/src/server/api/endpoints/pinned-users.ts b/packages/backend/src/server/api/endpoints/pinned-users.ts index 1b57f3921f50..dd0b19ebbc5a 100644 --- a/packages/backend/src/server/api/endpoints/pinned-users.ts +++ b/packages/backend/src/server/api/endpoints/pinned-users.ts @@ -46,7 +46,7 @@ export default class extends Endpoint { host: acct.host ?? IsNull(), }))); - return await this.usersRepository.packMany(users.filter(x => x !== undefined) as User[], me, { detail: true }); + return await this.userEntityService.packMany(users.filter(x => x !== undefined) as User[], me, { detail: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/users.ts b/packages/backend/src/server/api/endpoints/users.ts index a1ad632814c3..1100c231f6a8 100644 --- a/packages/backend/src/server/api/endpoints/users.ts +++ b/packages/backend/src/server/api/endpoints/users.ts @@ -87,7 +87,7 @@ export default class extends Endpoint { const users = await query.getMany(); - return await this.usersRepository.packMany(users, me, { detail: true }); + return await this.userEntityService.packMany(users, me, { detail: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index 2993054222c1..4915ef11d97e 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -116,7 +116,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return await this.followingsRepository.packMany(followings, me, { populateFollower: true }); + return await this.followingEntityService.packMany(followings, me, { populateFollower: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index 00e70ccc17e0..61e18c09ab27 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -116,7 +116,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return await this.followingsRepository.packMany(followings, me, { populateFollowee: true }); + return await this.followingEntityService.packMany(followings, me, { populateFollowee: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts index 17f862bb3f45..015f24839d65 100644 --- a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts +++ b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts @@ -116,7 +116,7 @@ export default class extends Endpoint { // Make replies object (includes weights) const repliesObj = await Promise.all(topRepliedthis.usersRepository.map(async (user) => ({ - user: await this.usersRepository.pack(user, me, { detail: true }), + user: await this.userEntityService.pack(user, me, { detail: true }), weight: repliedUsers[user] / peak, }))); diff --git a/packages/backend/src/server/api/endpoints/users/lists/pull.ts b/packages/backend/src/server/api/endpoints/users/lists/pull.ts index c89bbbff30dc..8e57f6f881d3 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/pull.ts @@ -68,7 +68,7 @@ export default class extends Endpoint { // Pull the user await UserListJoinings.delete({ userListId: userList.id, userId: user.id }); - publishUserListStream(userList.id, 'userRemoved', await this.usersRepository.pack(user)); + publishUserListStream(userList.id, 'userRemoved', await this.userEntityService.pack(user)); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index bc7d58933b6c..8917cbd8a27d 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -127,7 +127,7 @@ export default class extends Endpoint { const timeline = await query.take(ps.limit).getMany(); - return await this.notesRepository.packMany(timeline, me); + return await this.noteEntityService.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/recommendation.ts b/packages/backend/src/server/api/endpoints/users/recommendation.ts index 864795deb6fb..cc8918ddeece 100644 --- a/packages/backend/src/server/api/endpoints/users/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/users/recommendation.ts @@ -70,7 +70,7 @@ export default class extends Endpoint { const users = await query.take(ps.limit).skip(ps.offset).getMany(); - return await this.usersRepository.packMany(users, me, { detail: true }); + return await this.userEntityService.packMany(users, me, { detail: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index 37573c450cd8..39da94d790aa 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -67,7 +67,7 @@ export default class extends Endpoint { const users = await q.take(ps.limit).getMany(); - return await this.usersRepository.packMany(users, me, { detail: ps.detail }); + return await this.userEntityService.packMany(users, me, { detail: ps.detail }); } else if (ps.username) { let users: User[] = []; @@ -120,7 +120,7 @@ export default class extends Endpoint { .getMany(); } - return await this.usersRepository.packMany(users, me, { detail: !!ps.detail }); + return await this.userEntityService.packMany(users, me, { detail: !!ps.detail }); } return []; diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index 0e3028d9f2f2..32b1e94d292a 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -129,7 +129,7 @@ export default class extends Endpoint { } } - return await this.usersRepository.packMany(users, me, { detail: ps.detail }); + return await this.userEntityService.packMany(users, me, { detail: ps.detail }); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts index 50835c14dd77..d53b45f58953 100644 --- a/packages/backend/src/server/api/endpoints/users/show.ts +++ b/packages/backend/src/server/api/endpoints/users/show.ts @@ -112,7 +112,7 @@ export default class extends Endpoint { _users.push(users.find(x => x.id === id)!); } - return await Promise.all(_users.map(u => this.usersRepository.pack(u, me, { + return await Promise.all(_users.map(u => this.userEntityService.pack(u, me, { detail: true, }))); } else { @@ -134,7 +134,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchUser); } - return await this.usersRepository.pack(user, me, { + return await this.userEntityService.pack(user, me, { detail: true, }); } diff --git a/packages/backend/src/server/api/stream/channels/antenna.ts b/packages/backend/src/server/api/stream/channels/antenna.ts index c49786a43e40..891480705fed 100644 --- a/packages/backend/src/server/api/stream/channels/antenna.ts +++ b/packages/backend/src/server/api/stream/channels/antenna.ts @@ -29,7 +29,7 @@ class AntennaChannel extends Channel { private async onEvent(data: StreamMessages['antenna']['payload']) { if (data.type === 'note') { - const note = await this.notesRepository.pack(data.body.id, this.user, { detail: true }); + const note = await this.noteEntityService.pack(data.body.id, this.user, { detail: true }); // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する if (isUserRelated(note, this.muting)) return; diff --git a/packages/backend/src/server/api/stream/channels/channel.ts b/packages/backend/src/server/api/stream/channels/channel.ts index 31bace164a91..d46464669b64 100644 --- a/packages/backend/src/server/api/stream/channels/channel.ts +++ b/packages/backend/src/server/api/stream/channels/channel.ts @@ -40,13 +40,13 @@ class ChannelChannel extends Channel { // リプライなら再pack if (note.replyId != null) { - note.reply = await this.notesRepository.pack(note.replyId, this.user, { + note.reply = await this.noteEntityService.pack(note.replyId, this.user, { detail: true, }); } // Renoteなら再pack if (note.renoteId != null) { - note.renote = await this.notesRepository.pack(note.renoteId, this.user, { + note.renote = await this.noteEntityService.pack(note.renoteId, this.user, { detail: true, }); } @@ -80,7 +80,7 @@ class ChannelChannel extends Channel { if (now.getTime() - date.getTime() > 5000) delete this.typers[userId]; } - const users = await this.usersRepository.packMany(Object.keys(this.typers), null, { detail: false }); + const users = await this.userEntityService.packMany(Object.keys(this.typers), null, { detail: false }); this.send({ type: 'typers', diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index d4078ebae273..161d9342be2e 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -39,13 +39,13 @@ class GlobalTimelineChannel extends Channel { // リプライなら再pack if (note.replyId != null) { - note.reply = await this.notesRepository.pack(note.replyId, this.user, { + note.reply = await this.noteEntityService.pack(note.replyId, this.user, { detail: true, }); } // Renoteなら再pack if (note.renoteId != null) { - note.renote = await this.notesRepository.pack(note.renoteId, this.user, { + note.renote = await this.noteEntityService.pack(note.renoteId, this.user, { detail: true, }); } diff --git a/packages/backend/src/server/api/stream/channels/hashtag.ts b/packages/backend/src/server/api/stream/channels/hashtag.ts index e365f4b26e56..898af4dec818 100644 --- a/packages/backend/src/server/api/stream/channels/hashtag.ts +++ b/packages/backend/src/server/api/stream/channels/hashtag.ts @@ -37,7 +37,7 @@ class HashtagChannel extends Channel { // Renoteなら再pack if (note.renoteId != null) { - note.renote = await this.notesRepository.pack(note.renoteId, this.user, { + note.renote = await this.noteEntityService.pack(note.renoteId, this.user, { detail: true, }); } diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index 56cdbde5b18b..8cc7ff20aba6 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -38,7 +38,7 @@ class HomeTimelineChannel extends Channel { if (isInstanceMuted(note, new Set(this.userProfile?.mutedInstances ?? []))) return; if (['followers', 'specified'].includes(note.visibility)) { - note = await this.notesRepository.pack(note.id, this.user!, { + note = await this.noteEntityService.pack(note.id, this.user!, { detail: true, }); @@ -48,13 +48,13 @@ class HomeTimelineChannel extends Channel { } else { // リプライなら再pack if (note.replyId != null) { - note.reply = await this.notesRepository.pack(note.replyId, this.user!, { + note.reply = await this.noteEntityService.pack(note.replyId, this.user!, { detail: true, }); } // Renoteなら再pack if (note.renoteId != null) { - note.renote = await this.notesRepository.pack(note.renoteId, this.user!, { + note.renote = await this.noteEntityService.pack(note.renoteId, this.user!, { detail: true, }); } diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index 5cf334546e68..c0d671c322fa 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -45,7 +45,7 @@ class HybridTimelineChannel extends Channel { )) return; if (['followers', 'specified'].includes(note.visibility)) { - note = await this.notesRepository.pack(note.id, this.user!, { + note = await this.noteEntityService.pack(note.id, this.user!, { detail: true, }); @@ -55,13 +55,13 @@ class HybridTimelineChannel extends Channel { } else { // リプライなら再pack if (note.replyId != null) { - note.reply = await this.notesRepository.pack(note.replyId, this.user!, { + note.reply = await this.noteEntityService.pack(note.replyId, this.user!, { detail: true, }); } // Renoteなら再pack if (note.renoteId != null) { - note.renote = await this.notesRepository.pack(note.renoteId, this.user!, { + note.renote = await this.noteEntityService.pack(note.renoteId, this.user!, { detail: true, }); } diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index 42516d5c7504..86066a43d386 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -39,13 +39,13 @@ class LocalTimelineChannel extends Channel { // リプライなら再pack if (note.replyId != null) { - note.reply = await this.notesRepository.pack(note.replyId, this.user, { + note.reply = await this.noteEntityService.pack(note.replyId, this.user, { detail: true, }); } // Renoteなら再pack if (note.renoteId != null) { - note.renote = await this.notesRepository.pack(note.renoteId, this.user, { + note.renote = await this.noteEntityService.pack(note.renoteId, this.user, { detail: true, }); } diff --git a/packages/backend/src/server/api/stream/channels/main.ts b/packages/backend/src/server/api/stream/channels/main.ts index 34cb78b17836..73794d2e8479 100644 --- a/packages/backend/src/server/api/stream/channels/main.ts +++ b/packages/backend/src/server/api/stream/channels/main.ts @@ -27,7 +27,7 @@ class MainChannel extends Channel { if (data.body.userId && this.muting.has(data.body.userId)) return; if (data.body.note && data.body.note.isHidden) { - const note = await this.notesRepository.pack(data.body.note.id, this.user, { + const note = await this.noteEntityService.pack(data.body.note.id, this.user, { detail: true, }); this.connection.cacheNote(note); @@ -40,7 +40,7 @@ class MainChannel extends Channel { if (this.muting.has(data.body.userId)) return; if (data.body.isHidden) { - const note = await this.notesRepository.pack(data.body.id, this.user, { + const note = await this.noteEntityService.pack(data.body.id, this.user, { detail: true, }); this.connection.cacheNote(note); diff --git a/packages/backend/src/server/api/stream/channels/messaging.ts b/packages/backend/src/server/api/stream/channels/messaging.ts index 8cf490033cb4..c7b1ed223109 100644 --- a/packages/backend/src/server/api/stream/channels/messaging.ts +++ b/packages/backend/src/server/api/stream/channels/messaging.ts @@ -79,7 +79,7 @@ class MessagingChannel extends Channel { readUserMessagingMessage(this.user!.id, this.otherpartyId, [body.id]); // リモートユーザーからのメッセージだったら既読配信 - if (this.usersRepository.isLocalUser(this.user!) && this.usersRepository.isRemoteUser(this.otherparty!)) { + if (this.userEntityService.isLocalUser(this.user!) && this.userEntityService.isRemoteUser(this.otherparty!)) { this.messagingMessagesRepository.findOneBy({ id: body.id }).then(message => { if (message) deliverReadActivity(this.user as ILocalUser, this.otherparty as IRemoteUser, message); }); @@ -99,7 +99,7 @@ class MessagingChannel extends Channel { if (now.getTime() - date.getTime() > 5000) delete this.typers[userId]; } - const users = await this.usersRepository.packMany(Object.keys(this.typers), null, { detail: false }); + const users = await this.userEntityService.packMany(Object.keys(this.typers), null, { detail: false }); this.send({ type: 'typers', diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts index 7c8f475d99a4..91a5b3e85269 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -60,7 +60,7 @@ class UserListChannel extends Channel { if (!this.listUsers.includes(note.userId)) return; if (['followers', 'specified'].includes(note.visibility)) { - note = await this.notesRepository.pack(note.id, this.user, { + note = await this.noteEntityService.pack(note.id, this.user, { detail: true, }); @@ -70,13 +70,13 @@ class UserListChannel extends Channel { } else { // リプライなら再pack if (note.replyId != null) { - note.reply = await this.notesRepository.pack(note.replyId, this.user, { + note.reply = await this.noteEntityService.pack(note.replyId, this.user, { detail: true, }); } // Renoteなら再pack if (note.renoteId != null) { - note.renote = await this.notesRepository.pack(note.renoteId, this.user, { + note.renote = await this.noteEntityService.pack(note.renoteId, this.user, { detail: true, }); } diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 23115fc2886f..2a4ffac574ce 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -382,7 +382,7 @@ export class ClientServerService { }); if (note) { - const _note = await this.notesRepository.pack(note); + const _note = await this.noteEntityService.pack(note); const profile = await this.userProfilesRepository.findOneByOrFail({ userId: note.userId }); const meta = await this.metaService.fetch(); await ctx.render('note', { @@ -420,7 +420,7 @@ export class ClientServerService { }); if (page) { - const _page = await this.pagesRepository.pack(page); + const _page = await this.pageEntityService.pack(page); const profile = await this.userProfilesRepository.findOneByOrFail({ userId: page.userId }); const meta = await this.metaService.fetch(); await ctx.render('page', { @@ -452,7 +452,7 @@ export class ClientServerService { }); if (clip) { - const _clip = await this.clipsRepository.pack(clip); + const _clip = await this.clipEntityService.pack(clip); const profile = await this.userProfilesRepository.findOneByOrFail({ userId: clip.userId }); const meta = await this.metaService.fetch(); await ctx.render('clip', { @@ -477,7 +477,7 @@ export class ClientServerService { const post = await this.galleryPostsRepository.findOneBy({ id: ctx.params.post }); if (post) { - const _post = await this.galleryPostsRepository.pack(post); + const _post = await this.galleryPostEntityService.pack(post); const profile = await this.userProfilesRepository.findOneByOrFail({ userId: post.userId }); const meta = await this.metaService.fetch(); await ctx.render('gallery-post', { @@ -504,7 +504,7 @@ export class ClientServerService { }); if (channel) { - const _channel = await this.channelsRepository.pack(channel); + const _channel = await this.channelEntityService.pack(channel); const meta = await this.metaService.fetch(); await ctx.render('channel', { channel: _channel, diff --git a/packages/backend/src/services/CreateNotificationService.ts b/packages/backend/src/services/CreateNotificationService.ts index 083c180ab15d..3c5dd532c8e7 100644 --- a/packages/backend/src/services/CreateNotificationService.ts +++ b/packages/backend/src/services/CreateNotificationService.ts @@ -50,7 +50,7 @@ export class CreateNotificationService { } as Partial) .then(x => this.notificationsRepository.findOneByOrFail(x.identifiers[0])); - const packed = await this.notificationsRepository.pack(notification, {}); + const packed = await this.notificationEntityService.pack(notification, {}); // Publish notification event this.globalEventServie.publishMainStream(notifieeId, 'notification', packed); diff --git a/packages/backend/src/services/DriveService.ts b/packages/backend/src/services/DriveService.ts index 06c0142a207d..3a13b6c80dee 100644 --- a/packages/backend/src/services/DriveService.ts +++ b/packages/backend/src/services/DriveService.ts @@ -573,7 +573,7 @@ export class DriveService { this.#registerLogger.succ(`drive file has been created ${file.id}`); if (user) { - this.driveFilesRepository.pack(file, { self: true }).then(packedFile => { + this.driveFileEntityService.pack(file, { self: true }).then(packedFile => { // Publish driveFileCreated event this.globalEventService.publishMainStream(user.id, 'driveFileCreated', packedFile); this.globalEventService.publishDriveStream(user.id, 'fileCreated', packedFile); diff --git a/packages/backend/src/services/NoteCreateService.ts b/packages/backend/src/services/NoteCreateService.ts index 7f9b4d69bda0..edb5f806017c 100644 --- a/packages/backend/src/services/NoteCreateService.ts +++ b/packages/backend/src/services/NoteCreateService.ts @@ -387,7 +387,7 @@ export class NoteCreateService { this.perUserNotesChart.update(user, note, true); // Register host - if (this.usersRepository.isRemoteUser(user)) { + if (this.userEntityService.isRemoteUser(user)) { this.federatedInstanceService.registerOrFetchInstanceDoc(user.host).then(i => { Instances.increment({ id: i.id }, 'notesCount', 1); this.instanceChart.updateNote(i.host, note, true); @@ -464,7 +464,7 @@ export class NoteCreateService { } if (!silent) { - if (this.usersRepository.isLocalUser(user)) this.activeUsersChart.write(user); + if (this.userEntityService.isLocalUser(user)) this.activeUsersChart.write(user); // 未読通知を作成 if (data.visibility === 'specified') { @@ -472,7 +472,7 @@ export class NoteCreateService { for (const u of data.visibleUsers) { // ローカルユーザーのみ - if (!this.usersRepository.isLocalUser(u)) continue; + if (!this.userEntityService.isLocalUser(u)) continue; insertNoteUnread(u.id, note, { isSpecified: true, @@ -482,7 +482,7 @@ export class NoteCreateService { } else { for (const u of mentionedUsers) { // ローカルユーザーのみ - if (!this.usersRepository.isLocalUser(u)) continue; + if (!this.userEntityService.isLocalUser(u)) continue; insertNoteUnread(u.id, note, { isSpecified: false, @@ -492,7 +492,7 @@ export class NoteCreateService { } // Pack the note - const noteObj = await this.notesRepository.pack(note); + const noteObj = await this.noteEntityService.pack(note); this.globalEventServie.publishNotesStream(noteObj); @@ -566,26 +566,26 @@ export class NoteCreateService { }); //#region AP deliver - if (this.usersRepository.isLocalUser(user)) { + if (this.userEntityService.isLocalUser(user)) { (async () => { const noteActivity = await this.#renderNoteOrRenoteActivity(data, note); const dm = new DeliverManager(user, noteActivity); // メンションされたリモートユーザーに配送 - for (const u of mentionedUsers.filter(u => this.usersRepository.isRemoteUser(u))) { + for (const u of mentionedUsers.filter(u => this.userEntityService.isRemoteUser(u))) { dm.addDirectRecipe(u as IRemoteUser); } // 投稿がリプライかつ投稿者がローカルユーザーかつリプライ先の投稿の投稿者がリモートユーザーなら配送 if (data.reply && data.reply.userHost !== null) { const u = await this.usersRepository.findOneBy({ id: data.reply.userId }); - if (u && this.usersRepository.isRemoteUser(u)) dm.addDirectRecipe(u); + if (u && this.userEntityService.isRemoteUser(u)) dm.addDirectRecipe(u); } // 投稿がRenoteかつ投稿者がローカルユーザーかつRenote元の投稿の投稿者がリモートユーザーなら配送 if (data.renote && data.renote.userHost !== null) { const u = await this.usersRepository.findOneBy({ id: data.renote.userId }); - if (u && this.usersRepository.isRemoteUser(u)) dm.addDirectRecipe(u); + if (u && this.userEntityService.isRemoteUser(u)) dm.addDirectRecipe(u); } // フォロワーに配送 @@ -636,7 +636,7 @@ export class NoteCreateService { } async #createMentionedEvents(mentionedUsers: MinimumUser[], note: Note, nm: NotificationManager) { - for (const u of mentionedUsers.filter(u => this.usersRepository.isLocalUser(u))) { + for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) { const threadMuted = await NoteThreadMutings.findOneBy({ userId: u.id, threadId: note.threadId || note.id, @@ -646,7 +646,7 @@ export class NoteCreateService { continue; } - const detailPackedNote = await this.notesRepository.pack(note, u, { + const detailPackedNote = await this.noteEntityService.pack(note, u, { detail: true, }); diff --git a/packages/backend/src/services/NotePiningService.ts b/packages/backend/src/services/NotePiningService.ts index 5883b9ab9c2a..2558da10958a 100644 --- a/packages/backend/src/services/NotePiningService.ts +++ b/packages/backend/src/services/NotePiningService.ts @@ -64,7 +64,7 @@ export class NotePiningService { } as UserNotePining); // Deliver to remote followers - if (this.usersRepository.isLocalUser(user)) { + if (this.userEntityService.isLocalUser(user)) { deliverPinnedChange(user.id, note.id, true); } } @@ -91,7 +91,7 @@ export class NotePiningService { }); // Deliver to remote followers - if (this.usersRepository.isLocalUser(user)) { + if (this.userEntityService.isLocalUser(user)) { deliverPinnedChange(user.id, noteId, false); } } @@ -100,7 +100,7 @@ export class NotePiningService { const user = await this.usersRepository.findOneBy({ id: userId }); if (user == null) throw new Error('user not found'); - if (!this.usersRepository.isLocalUser(user)) return; + if (!this.userEntityService.isLocalUser(user)) return; const target = `${this.config.url}/users/${user.id}/collections/featured`; const item = `${this.config.url}/notes/${noteId}`; diff --git a/packages/backend/src/services/PollService.ts b/packages/backend/src/services/PollService.ts index 8c3900612478..7e780cf8bde9 100644 --- a/packages/backend/src/services/PollService.ts +++ b/packages/backend/src/services/PollService.ts @@ -104,7 +104,7 @@ export class PollService { const user = await this.usersRepository.findOneBy({ id: note.userId }); if (user == null) throw new Error('note not found'); - if (this.usersRepository.isLocalUser(user)) { + if (this.userEntityService.isLocalUser(user)) { const content = renderActivity(renderUpdate(await renderNote(note, false), user)); deliverToFollowers(user, content); this.relayService.deliverToRelays(user, content); diff --git a/packages/backend/src/services/ReactionService.ts b/packages/backend/src/services/ReactionService.ts index 5ed8d2555a2b..c6450d8a90df 100644 --- a/packages/backend/src/services/ReactionService.ts +++ b/packages/backend/src/services/ReactionService.ts @@ -133,7 +133,7 @@ export class ReactionService { } //#region 配信 - if (this.usersRepository.isLocalUser(user) && !note.localOnly) { + if (this.userEntityService.isLocalUser(user) && !note.localOnly) { const content = renderActivity(await renderLike(record, note)); const dm = new DeliverManager(user, content); if (note.userHost !== null) { @@ -145,7 +145,7 @@ export class ReactionService { dm.addFollowersRecipe(); } else if (note.visibility === 'specified') { const visibleUsers = await Promise.all(note.visibleUserIds.map(id => this.usersRepository.findOneBy({ id }))); - for (const u of visibleUsers.filter(u => u && this.usersRepository.isRemoteUser(u))) { + for (const u of visibleUsers.filter(u => u && this.userEntityService.isRemoteUser(u))) { dm.addDirectRecipe(u as IRemoteUser); } } @@ -190,7 +190,7 @@ export class ReactionService { }); //#region 配信 - if (this.usersRepository.isLocalUser(user) && !note.localOnly) { + if (this.userEntityService.isLocalUser(user) && !note.localOnly) { const content = renderActivity(renderUndo(await renderLike(exist, note), user)); const dm = new DeliverManager(user, content); if (note.userHost !== null) { diff --git a/packages/backend/src/services/UserBlockingService.ts b/packages/backend/src/services/UserBlockingService.ts index d719d281d2a2..f4835d43f72a 100644 --- a/packages/backend/src/services/UserBlockingService.ts +++ b/packages/backend/src/services/UserBlockingService.ts @@ -64,7 +64,7 @@ export class UserBlockingService { await this.blockingsRepository.insert(blocking); - if (this.usersRepository.isLocalUser(blocker) && this.usersRepository.isRemoteUser(blockee)) { + if (this.userEntityService.isLocalUser(blocker) && this.userEntityService.isRemoteUser(blockee)) { const content = renderActivity(renderBlock(blocking)); this.queueService.deliver(blocker, content, blockee.inbox); } @@ -85,14 +85,14 @@ export class UserBlockingService { followerId: follower.id, }); - if (this.usersRepository.isLocalUser(followee)) { - this.usersRepository.pack(followee, followee, { + if (this.userEntityService.isLocalUser(followee)) { + this.userEntityService.pack(followee, followee, { detail: true, }).then(packed => this.globalEventServie.publishMainStream(followee.id, 'meUpdated', packed)); } - if (this.usersRepository.isLocalUser(follower)) { - this.usersRepository.pack(followee, follower, { + if (this.userEntityService.isLocalUser(follower)) { + this.userEntityService.pack(followee, follower, { detail: true, }).then(async packed => { this.globalEventServie.publishUserEvent(follower.id, 'unfollow', packed); @@ -108,13 +108,13 @@ export class UserBlockingService { } // リモートにフォローリクエストをしていたらUndoFollow送信 - if (this.usersRepository.isLocalUser(follower) && this.usersRepository.isRemoteUser(followee)) { + if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { const content = renderActivity(renderUndo(renderFollow(follower, followee), follower)); this.queueService.deliver(follower, content, followee.inbox); } // リモートからフォローリクエストを受けていたらReject送信 - if (this.usersRepository.isRemoteUser(follower) && this.usersRepository.isLocalUser(followee)) { + if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { const content = renderActivity(renderReject(renderFollow(follower, followee, request.requestId!), followee)); this.queueService.deliver(followee, content, follower.inbox); } @@ -138,8 +138,8 @@ export class UserBlockingService { ]); // Publish unfollow event - if (this.usersRepository.isLocalUser(follower)) { - this.usersRepository.pack(followee, follower, { + if (this.userEntityService.isLocalUser(follower)) { + this.userEntityService.pack(followee, follower, { detail: true, }).then(async packed => { this.globalEventServie.publishUserEvent(follower.id, 'unfollow', packed); @@ -155,7 +155,7 @@ export class UserBlockingService { } // リモートにフォローをしていたらUndoFollow送信 - if (this.usersRepository.isLocalUser(follower) && this.usersRepository.isRemoteUser(followee)) { + if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { const content = renderActivity(renderUndo(renderFollow(follower, followee), follower)); this.queueService.deliver(follower, content, followee.inbox); } @@ -193,7 +193,7 @@ export class UserBlockingService { await this.blockingsRepository.delete(blocking.id); // deliver if remote bloking - if (this.usersRepository.isLocalUser(blocker) && this.usersRepository.isRemoteUser(blockee)) { + if (this.userEntityService.isLocalUser(blocker) && this.userEntityService.isRemoteUser(blockee)) { const content = renderActivity(renderUndo(renderBlock(blocking), blocker)); this.queueService.deliver(blocker, content, blockee.inbox); } diff --git a/packages/backend/src/services/UserFollowingService.ts b/packages/backend/src/services/UserFollowingService.ts index f150482df5ff..d80b4864d355 100644 --- a/packages/backend/src/services/UserFollowingService.ts +++ b/packages/backend/src/services/UserFollowingService.ts @@ -6,17 +6,17 @@ import renderFollow from '@/services/remote/activitypub/renderer/follow.js'; import renderAccept from '@/services/remote/activitypub/renderer/accept.js'; import renderReject from '@/services/remote/activitypub/renderer/reject.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; -import type { QueueService } from '@/queue/queue.service.js'; -import type PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; -import type { GlobalEventService } from '@/services/GlobalEventService.js'; -import type { IdService } from '@/services/IdService.js'; +import { QueueService } from '@/queue/queue.service.js'; +import PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { IdService } from '@/services/IdService.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import renderUndo from '@/services/remote/activitypub/renderer/undo.js'; import type { Packed } from '@/misc/schema.js'; -import type InstanceChart from '@/services/chart/charts/instance.js'; -import type { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; -import type { WebhookService } from '@/services/webhookService.js'; -import type { CreateNotificationService } from '@/services/CreateNotificationService.js'; +import InstanceChart from '@/services/chart/charts/instance.js'; +import { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; +import { WebhookService } from '@/services/webhookService.js'; +import { CreateNotificationService } from '@/services/CreateNotificationService.js'; import Logger from './logger.js'; const logger = new Logger('following/create'); @@ -84,12 +84,12 @@ export class UserFollowingService { }), ]); - if (this.usersRepository.isRemoteUser(follower) && this.usersRepository.isLocalUser(followee) && blocked) { + if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee) && blocked) { // リモートフォローを受けてブロックしていた場合は、エラーにするのではなくRejectを送り返しておしまい。 const content = renderActivity(renderReject(renderFollow(follower, followee, requestId), followee)); this.queueService.deliver(followee , content, follower.inbox); return; - } else if (this.usersRepository.isRemoteUser(follower) && this.usersRepository.isLocalUser(followee) && blocking) { + } else if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee) && blocking) { // リモートフォローを受けてブロックされているはずの場合だったら、ブロック解除しておく。 await this.blockingsRepository.delete(blocking.id); } else { @@ -104,7 +104,7 @@ export class UserFollowingService { // フォロワーがBotであり、フォロー対象がBotからのフォローに慎重である or // フォロワーがローカルユーザーであり、フォロー対象がリモートユーザーである // 上記のいずれかに当てはまる場合はすぐフォローせずにフォローリクエストを発行しておく - if (followee.isLocked || (followeeProfile.carefulBot && follower.isBot) || (this.usersRepository.isLocalUser(follower) && this.usersRepository.isRemoteUser(followee))) { + if (followee.isLocked || (followeeProfile.carefulBot && follower.isBot) || (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee))) { let autoAccept = false; // 鍵アカウントであっても、既にフォローされていた場合はスルー @@ -117,7 +117,7 @@ export class UserFollowingService { } // フォローしているユーザーは自動承認オプション - if (!autoAccept && (this.usersRepository.isLocalUser(followee) && followeeProfile.autoAcceptFollowed)) { + if (!autoAccept && (this.userEntityService.isLocalUser(followee) && followeeProfile.autoAcceptFollowed)) { const followed = await this.followingsRepository.findOneBy({ followerId: followee.id, followeeId: follower.id, @@ -134,7 +134,7 @@ export class UserFollowingService { await this.insertFollowingDoc(followee, follower); - if (this.usersRepository.isRemoteUser(follower) && this.usersRepository.isLocalUser(followee)) { + if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { const content = renderActivity(renderAccept(renderFollow(follower, followee, requestId), followee)); this.queueService.deliver(followee, content, follower.inbox); } @@ -160,13 +160,13 @@ export class UserFollowingService { // 非正規化 followerHost: follower.host, - followerInbox: this.usersRepository.isRemoteUser(follower) ? follower.inbox : null, - followerSharedInbox: this.usersRepository.isRemoteUser(follower) ? follower.sharedInbox : null, + followerInbox: this.userEntityService.isRemoteUser(follower) ? follower.inbox : null, + followerSharedInbox: this.userEntityService.isRemoteUser(follower) ? follower.sharedInbox : null, followeeHost: followee.host, - followeeInbox: this.usersRepository.isRemoteUser(followee) ? followee.inbox : null, - followeeSharedInbox: this.usersRepository.isRemoteUser(followee) ? followee.sharedInbox : null, + followeeInbox: this.userEntityService.isRemoteUser(followee) ? followee.inbox : null, + followeeSharedInbox: this.userEntityService.isRemoteUser(followee) ? followee.sharedInbox : null, }).catch(e => { - if (isDuplicateKeyValueError(e) && this.usersRepository.isRemoteUser(follower) && this.usersRepository.isLocalUser(followee)) { + if (isDuplicateKeyValueError(e) && this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { logger.info(`Insert duplicated ignore. ${follower.id} => ${followee.id}`); alreadyFollowed = true; } else { @@ -201,12 +201,12 @@ export class UserFollowingService { //#endregion //#region Update instance stats - if (this.usersRepository.isRemoteUser(follower) && this.usersRepository.isLocalUser(followee)) { + if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { this.federatedInstanceService.registerOrFetchInstanceDoc(follower.host).then(i => { this.instancesRepository.increment({ id: i.id }, 'followingCount', 1); this.instanceChart.updateFollowing(i.host, true); }); - } else if (this.usersRepository.isLocalUser(follower) && this.usersRepository.isRemoteUser(followee)) { + } else if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { this.federatedInstanceService.registerOrFetchInstanceDoc(followee.host).then(i => { this.instancesRepository.increment({ id: i.id }, 'followersCount', 1); this.instanceChart.updateFollowers(i.host, true); @@ -217,8 +217,8 @@ export class UserFollowingService { this.perUserFollowingChart.update(follower, followee, true); // Publish follow event - if (this.usersRepository.isLocalUser(follower)) { - this.usersRepository.pack(followee.id, follower, { + if (this.userEntityService.isLocalUser(follower)) { + this.userEntityService.pack(followee.id, follower, { detail: true, }).then(async packed => { this.globalEventServie.publishUserEvent(follower.id, 'follow', packed as Packed<'UserDetailedNotMe'>); @@ -234,8 +234,8 @@ export class UserFollowingService { } // Publish followed event - if (this.usersRepository.isLocalUser(followee)) { - this.usersRepository.pack(follower.id, followee).then(async packed => { + if (this.userEntityService.isLocalUser(followee)) { + this.userEntityService.pack(follower.id, followee).then(async packed => { this.globalEventServie.publishMainStream(followee.id, 'followed', packed); const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === followee.id && x.on.includes('followed')); @@ -277,8 +277,8 @@ export class UserFollowingService { this.decrementFollowing(follower, followee); // Publish unfollow event - if (!silent && this.usersRepository.isLocalUser(follower)) { - this.usersRepository.pack(followee.id, follower, { + if (!silent && this.userEntityService.isLocalUser(follower)) { + this.userEntityService.pack(followee.id, follower, { detail: true, }).then(async packed => { this.globalEventServie.publishUserEvent(follower.id, 'unfollow', packed); @@ -293,12 +293,12 @@ export class UserFollowingService { }); } - if (this.usersRepository.isLocalUser(follower) && this.usersRepository.isRemoteUser(followee)) { + if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { const content = renderActivity(renderUndo(renderFollow(follower, followee), follower)); this.queueService.deliver(follower, content, followee.inbox); } - if (this.usersRepository.isLocalUser(followee) && this.usersRepository.isRemoteUser(follower)) { + if (this.userEntityService.isLocalUser(followee) && this.userEntityService.isRemoteUser(follower)) { // local user has null host const content = renderActivity(renderReject(renderFollow(follower, followee), followee)); this.queueService.deliver(followee, content, follower.inbox); @@ -317,12 +317,12 @@ export class UserFollowingService { //#endregion //#region Update instance stats - if (this.usersRepository.isRemoteUser(follower) && this.usersRepository.isLocalUser(followee)) { + if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { this.federatedInstanceService.registerOrFetchInstanceDoc(follower.host).then(i => { this.instancesRepository.decrement({ id: i.id }, 'followingCount', 1); this.instanceChart.updateFollowing(i.host, false); }); - } else if (this.usersRepository.isLocalUser(follower) && this.usersRepository.isRemoteUser(followee)) { + } else if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { this.federatedInstanceService.registerOrFetchInstanceDoc(followee.host).then(i => { this.instancesRepository.decrement({ id: i.id }, 'followersCount', 1); this.instanceChart.updateFollowers(i.host, false); @@ -368,18 +368,18 @@ export class UserFollowingService { // 非正規化 followerHost: follower.host, - followerInbox: this.usersRepository.isRemoteUser(follower) ? follower.inbox : undefined, - followerSharedInbox: this.usersRepository.isRemoteUser(follower) ? follower.sharedInbox : undefined, + followerInbox: this.userEntityService.isRemoteUser(follower) ? follower.inbox : undefined, + followerSharedInbox: this.userEntityService.isRemoteUser(follower) ? follower.sharedInbox : undefined, followeeHost: followee.host, - followeeInbox: this.usersRepository.isRemoteUser(followee) ? followee.inbox : undefined, - followeeSharedInbox: this.usersRepository.isRemoteUser(followee) ? followee.sharedInbox : undefined, + followeeInbox: this.userEntityService.isRemoteUser(followee) ? followee.inbox : undefined, + followeeSharedInbox: this.userEntityService.isRemoteUser(followee) ? followee.sharedInbox : undefined, }).then(x => this.followRequestsRepository.findOneByOrFail(x.identifiers[0])); // Publish receiveRequest event - if (this.usersRepository.isLocalUser(followee)) { - this.usersRepository.pack(follower.id, followee).then(packed => this.globalEventServie.publishMainStream(followee.id, 'receiveFollowRequest', packed)); + if (this.userEntityService.isLocalUser(followee)) { + this.userEntityService.pack(follower.id, followee).then(packed => this.globalEventServie.publishMainStream(followee.id, 'receiveFollowRequest', packed)); - this.usersRepository.pack(followee.id, followee, { + this.userEntityService.pack(followee.id, followee, { detail: true, }).then(packed => this.globalEventServie.publishMainStream(followee.id, 'meUpdated', packed)); @@ -390,7 +390,7 @@ export class UserFollowingService { }); } - if (this.usersRepository.isLocalUser(follower) && this.usersRepository.isRemoteUser(followee)) { + if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { const content = renderActivity(renderFollow(follower, followee)); this.queueService.deliver(follower, content, followee.inbox); } @@ -404,10 +404,10 @@ export class UserFollowingService { id: User['id']; host: User['host']; uri: User['host'] }, ): Promise { - if (this.usersRepository.isRemoteUser(followee)) { + if (this.userEntityService.isRemoteUser(followee)) { const content = renderActivity(renderUndo(renderFollow(follower, followee), follower)); - if (this.usersRepository.isLocalUser(follower)) { // 本来このチェックは不要だけどTSに怒られるので + if (this.userEntityService.isLocalUser(follower)) { // 本来このチェックは不要だけどTSに怒られるので this.queueService.deliver(follower, content, followee.inbox); } } @@ -426,7 +426,7 @@ export class UserFollowingService { followerId: follower.id, }); - this.usersRepository.pack(followee.id, followee, { + this.userEntityService.pack(followee.id, followee, { detail: true, }).then(packed => this.globalEventServie.publishMainStream(followee.id, 'meUpdated', packed)); } @@ -448,12 +448,12 @@ export class UserFollowingService { await this.insertFollowingDoc(followee, follower); - if (this.usersRepository.isRemoteUser(follower) && this.usersRepository.isLocalUser(followee)) { + if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { const content = renderActivity(renderAccept(renderFollow(follower, followee, request.requestId!), followee)); this.queueService.deliver(followee, content, follower.inbox); } - this.usersRepository.pack(followee.id, followee, { + this.userEntityService.pack(followee.id, followee, { detail: true, }).then(packed => this.globalEventServie.publishMainStream(followee.id, 'meUpdated', packed)); } @@ -477,13 +477,13 @@ export class UserFollowingService { * API following/request/reject */ public async rejectFollowRequest(user: Local, follower: Both): Promise { - if (this.usersRepository.isRemoteUser(follower)) { + if (this.userEntityService.isRemoteUser(follower)) { this.#deliverReject(user, follower); } await this.#removeFollowRequest(user, follower); - if (this.usersRepository.isLocalUser(follower)) { + if (this.userEntityService.isLocalUser(follower)) { this.#publishUnfollow(user, follower); } } @@ -492,13 +492,13 @@ export class UserFollowingService { * API following/reject */ public async rejectFollow(user: Local, follower: Both): Promise { - if (this.usersRepository.isRemoteUser(follower)) { + if (this.userEntityService.isRemoteUser(follower)) { this.#deliverReject(user, follower); } await this.#removeFollow(user, follower); - if (this.usersRepository.isLocalUser(follower)) { + if (this.userEntityService.isLocalUser(follower)) { this.#publishUnfollow(user, follower); } } @@ -558,7 +558,7 @@ export class UserFollowingService { * Publish unfollow to local */ async #publishUnfollow(followee: Both, follower: Local): Promise { - const packedFollowee = await this.usersRepository.pack(followee.id, follower, { + const packedFollowee = await this.userEntityService.pack(followee.id, follower, { detail: true, }); diff --git a/packages/backend/src/services/UserListService.ts b/packages/backend/src/services/UserListService.ts index 6c3970703021..c320c073866f 100644 --- a/packages/backend/src/services/UserListService.ts +++ b/packages/backend/src/services/UserListService.ts @@ -31,7 +31,7 @@ export class UserListService { userListId: list.id, } as UserListJoining); - this.globalEventServie.publishUserListStream(list.id, 'userAdded', await this.usersRepository.pack(target)); + this.globalEventServie.publishUserListStream(list.id, 'userAdded', await this.userEntityService.pack(target)); // このインスタンス内にこのリモートユーザーをフォローしているユーザーがいなくても投稿を受け取るためにダミーのユーザーがフォローしたということにする if (Users.isRemoteUser(target)) { diff --git a/packages/backend/src/services/UserSuspendService.ts b/packages/backend/src/services/UserSuspendService.ts index 66706af62c76..78f5b3a17d3d 100644 --- a/packages/backend/src/services/UserSuspendService.ts +++ b/packages/backend/src/services/UserSuspendService.ts @@ -30,7 +30,7 @@ export class UserSuspendService { public async doPostSuspend(user: { id: User['id']; host: User['host'] }): Promise { this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true }); - if (this.usersRepository.isLocalUser(user)) { + if (this.userEntityService.isLocalUser(user)) { // 知り得る全SharedInboxにDelete配信 const content = renderActivity(renderDelete(`${this.config.url}/users/${user.id}`, user)); @@ -59,7 +59,7 @@ export class UserSuspendService { public async doPostUnsuspend(user: User): Promise { this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false }); - if (this.usersRepository.isLocalUser(user)) { + if (this.userEntityService.isLocalUser(user)) { // 知り得る全SharedInboxにUndo Delete配信 const content = renderActivity(renderUndo(renderDelete(`${this.config.url}/users/${user.id}`, user), user)); diff --git a/packages/backend/src/services/entities/NoteEntityService.ts b/packages/backend/src/services/entities/NoteEntityService.ts new file mode 100644 index 000000000000..5696891eecee --- /dev/null +++ b/packages/backend/src/services/entities/NoteEntityService.ts @@ -0,0 +1,364 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { DataSource, In } from 'typeorm'; +import * as mfm from 'mfm-js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Notes , Polls, PollVotes , DriveFiles , Channels , Followings , Users , NoteReactions } from '@/models/index.js'; +import { Config } from '@/config/types.js'; +import type { Packed } from '@/misc/schema.js'; +import { nyaize } from '@/misc/nyaize.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '@/misc/reaction-lib.js'; +import type { User } from '@/models/entities/user'; +import type { Note } from '@/models/entities/note'; +import type { NoteReaction } from '@/models/entities/note-reaction'; +import { aggregateNoteEmojis, populateEmojis, prefetchEmojis } from '@/misc/populate-emojis'; +import { UserEntityService } from './UserEntityService.js'; + +@Injectable() +export class NoteEntityService { + constructor( + @Inject(DI_SYMBOLS.db) + private db: DataSource, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + @Inject('pollsRepository') + private pollsRepository: typeof Polls, + + @Inject('pollVotesRepository') + private pollVotesRepository: typeof PollVotes, + + @Inject('noteReactionsRepository') + private noteReactionsRepository: typeof NoteReactions, + + @Inject('channelsRepository') + private channelsRepository: typeof Channels, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + // 循環参照のため / for circular dependency + @Inject(forwardRef(() => UserEntityService)) + private userEntityService: UserEntityService, + ) { + } + + async #hideNote(packedNote: Packed<'Note'>, meId: User['id'] | null) { + // TODO: isVisibleForMe を使うようにしても良さそう(型違うけど) + let hide = false; + + // visibility が specified かつ自分が指定されていなかったら非表示 + if (packedNote.visibility === 'specified') { + if (meId == null) { + hide = true; + } else if (meId === packedNote.userId) { + hide = false; + } else { + // 指定されているかどうか + const specified = packedNote.visibleUserIds!.some((id: any) => meId === id); + + if (specified) { + hide = false; + } else { + hide = true; + } + } + } + + // visibility が followers かつ自分が投稿者のフォロワーでなかったら非表示 + if (packedNote.visibility === 'followers') { + if (meId == null) { + hide = true; + } else if (meId === packedNote.userId) { + hide = false; + } else if (packedNote.reply && (meId === packedNote.reply.userId)) { + // 自分の投稿に対するリプライ + hide = false; + } else if (packedNote.mentions && packedNote.mentions.some(id => meId === id)) { + // 自分へのメンション + hide = false; + } else { + // フォロワーかどうか + const following = await this.followingsRepository.findOneBy({ + followeeId: packedNote.userId, + followerId: meId, + }); + + if (following == null) { + hide = true; + } else { + hide = false; + } + } + } + + if (hide) { + packedNote.visibleUserIds = undefined; + packedNote.fileIds = []; + packedNote.files = []; + packedNote.text = null; + packedNote.poll = undefined; + packedNote.cw = null; + packedNote.isHidden = true; + } + } + + async #populatePoll(note: Note, meId: User['id'] | null) { + const poll = await this.pollsRepository.findOneByOrFail({ noteId: note.id }); + const choices = poll.choices.map(c => ({ + text: c, + votes: poll.votes[poll.choices.indexOf(c)], + isVoted: false, + })); + + if (meId) { + if (poll.multiple) { + const votes = await this.pollVotesRepository.findBy({ + userId: meId, + noteId: note.id, + }); + + const myChoices = votes.map(v => v.choice); + for (const myChoice of myChoices) { + choices[myChoice].isVoted = true; + } + } else { + const vote = await this.pollVotesRepository.findOneBy({ + userId: meId, + noteId: note.id, + }); + + if (vote) { + choices[vote.choice].isVoted = true; + } + } + } + + return { + multiple: poll.multiple, + expiresAt: poll.expiresAt, + choices, + }; + } + + async #populateMyReaction(note: Note, meId: User['id'], _hint_?: { + myReactions: Map; + }) { + if (_hint_?.myReactions) { + const reaction = _hint_.myReactions.get(note.id); + if (reaction) { + return convertLegacyReaction(reaction.reaction); + } else if (reaction === null) { + return undefined; + } + // 実装上抜けがあるだけかもしれないので、「ヒントに含まれてなかったら(=undefinedなら)return」のようにはしない + } + + const reaction = await this.noteReactionsRepository.findOneBy({ + userId: meId, + noteId: note.id, + }); + + if (reaction) { + return convertLegacyReaction(reaction.reaction); + } + + return undefined; + } + + public async isVisibleForMe(note: Note, meId: User['id'] | null): Promise { + // This code must always be synchronized with the checks in generateVisibilityQuery. + // visibility が specified かつ自分が指定されていなかったら非表示 + if (note.visibility === 'specified') { + if (meId == null) { + return false; + } else if (meId === note.userId) { + return true; + } else { + // 指定されているかどうか + return note.visibleUserIds.some((id: any) => meId === id); + } + } + + // visibility が followers かつ自分が投稿者のフォロワーでなかったら非表示 + if (note.visibility === 'followers') { + if (meId == null) { + return false; + } else if (meId === note.userId) { + return true; + } else if (note.reply && (meId === note.reply.userId)) { + // 自分の投稿に対するリプライ + return true; + } else if (note.mentions && note.mentions.some(id => meId === id)) { + // 自分へのメンション + return true; + } else { + // フォロワーかどうか + const [following, user] = await Promise.all([ + this.followingsRepository.count({ + where: { + followeeId: note.userId, + followerId: meId, + }, + take: 1, + }), + this.usersRepository.findOneByOrFail({ id: meId }), + ]); + + /* If we know the following, everyhting is fine. + + But if we do not know the following, it might be that both the + author of the note and the author of the like are remote users, + in which case we can never know the following. Instead we have + to assume that the users are following each other. + */ + return following > 0 || (note.userHost != null && user.host != null); + } + } + + return true; + } + + public async pack( + src: Note['id'] | Note, + me?: { id: User['id'] } | null | undefined, + options?: { + detail?: boolean; + skipHide?: boolean; + _hint_?: { + myReactions: Map; + }; + }, + ): Promise> { + const opts = Object.assign({ + detail: true, + skipHide: false, + }, options); + + const meId = me ? me.id : null; + const note = typeof src === 'object' ? src : await this.notesRepository.findOneByOrFail({ id: src }); + const host = note.userHost; + + let text = note.text; + + if (note.name && (note.url ?? note.uri)) { + text = `【${note.name}】\n${(note.text || '').trim()}\n\n${note.url ?? note.uri}`; + } + + const channel = note.channelId + ? note.channel + ? note.channel + : await this.channelsRepository.findOneBy({ id: note.channelId }) + : null; + + const reactionEmojiNames = Object.keys(note.reactions).filter(x => x.startsWith(':')).map(x => decodeReaction(x).reaction).map(x => x.replace(/:/g, '')); + + const packed: Packed<'Note'> = await awaitAll({ + id: note.id, + createdAt: note.createdAt.toISOString(), + userId: note.userId, + user: this.userEntityService.pack(note.user ?? note.userId, me, { + detail: false, + }), + text: text, + cw: note.cw, + visibility: note.visibility, + localOnly: note.localOnly || undefined, + visibleUserIds: note.visibility === 'specified' ? note.visibleUserIds : undefined, + renoteCount: note.renoteCount, + repliesCount: note.repliesCount, + reactions: convertLegacyReactions(note.reactions), + tags: note.tags.length > 0 ? note.tags : undefined, + emojis: populateEmojis(note.emojis.concat(reactionEmojiNames), host), + fileIds: note.fileIds, + files: this.driveFileEntityService.packMany(note.fileIds), + replyId: note.replyId, + renoteId: note.renoteId, + channelId: note.channelId || undefined, + channel: channel ? { + id: channel.id, + name: channel.name, + } : undefined, + mentions: note.mentions.length > 0 ? note.mentions : undefined, + uri: note.uri || undefined, + url: note.url || undefined, + + ...(opts.detail ? { + reply: note.replyId ? this.pack(note.reply || note.replyId, me, { + detail: false, + _hint_: options?._hint_, + }) : undefined, + + renote: note.renoteId ? this.pack(note.renote || note.renoteId, me, { + detail: true, + _hint_: options?._hint_, + }) : undefined, + + poll: note.hasPoll ? this.#populatePoll(note, meId) : undefined, + + ...(meId ? { + myReaction: this.#populateMyReaction(note, meId, options?._hint_), + } : {}), + } : {}), + }); + + if (packed.user.isCat && packed.text) { + const tokens = packed.text ? mfm.parse(packed.text) : []; + mfm.inspect(tokens, node => { + if (node.type === 'text') { + // TODO: quoteなtextはskip + node.props.text = nyaize(node.props.text); + } + }); + packed.text = mfm.toString(tokens); + } + + if (!opts.skipHide) { + await this.#hideNote(packed, meId); + } + + return packed; + } + + public async packMany( + notes: Note[], + me?: { id: User['id'] } | null | undefined, + options?: { + detail?: boolean; + skipHide?: boolean; + }, + ) { + if (notes.length === 0) return []; + + const meId = me ? me.id : null; + const myReactionsMap = new Map(); + if (meId) { + const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!); + const targets = [...notes.map(n => n.id), ...renoteIds]; + const myReactions = await this.noteReactionsRepository.findBy({ + userId: meId, + noteId: In(targets), + }); + + for (const target of targets) { + myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) || null); + } + } + + await prefetchEmojis(aggregateNoteEmojis(notes)); + + return await Promise.all(notes.map(n => this.pack(n, me, { + ...options, + _hint_: { + myReactions: myReactionsMap, + }, + }))); + } +} diff --git a/packages/backend/src/services/entities/UserEntityService.ts b/packages/backend/src/services/entities/UserEntityService.ts new file mode 100644 index 000000000000..2be6e03a7708 --- /dev/null +++ b/packages/backend/src/services/entities/UserEntityService.ts @@ -0,0 +1,501 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { EntityRepository, Repository, In, Not } from 'typeorm'; +import Ajv from 'ajv'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { Pages } from '@/models/index.js'; +import type { AntennaNotes, Instances, MessagingMessages, UserSecurityKeys , Blockings, Mutings , Followings, FollowRequests, Users, DriveFiles, NoteUnreads, ChannelFollowings, Notifications, UserNotePinings, UserProfiles , AnnouncementReads, Announcements, UserGroupJoinings } from '@/models/index.js'; +import { Config } from '@/config/types.js'; +import type { Packed } from '@/misc/schema.js'; +import type { Promiseable } from '@/prelude/await-all.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import { populateEmojis } from '@/misc/populate-emojis.js'; +import { getAntennas } from '@/misc/antenna-cache.js'; +import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js'; +import { Cache } from '@/misc/cache.js'; +import type { Instance } from '@/models/entities/instance.js'; +import type { ILocalUser, IRemoteUser, User } from '@/models/entities/user.js'; +import { NoteEntityService } from './NoteEntityService.js'; + +const userInstanceCache = new Cache(1000 * 60 * 60 * 3); + +type IsUserDetailed = Detailed extends true ? Packed<'UserDetailed'> : Packed<'UserLite'>; +type IsMeAndIsUserDetailed = + Detailed extends true ? + ExpectsMe extends true ? Packed<'MeDetailed'> : + ExpectsMe extends false ? Packed<'UserDetailedNotMe'> : + Packed<'UserDetailed'> : + Packed<'UserLite'>; + +const ajv = new Ajv(); + +const localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const; +const passwordSchema = { type: 'string', minLength: 1 } as const; +const nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const; +const descriptionSchema = { type: 'string', minLength: 1, maxLength: 500 } as const; +const locationSchema = { type: 'string', minLength: 1, maxLength: 50 } as const; +const birthdaySchema = { type: 'string', pattern: /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.toString().slice(1, -1) } as const; + +function isLocalUser(user: User): user is ILocalUser; +function isLocalUser(user: T): user is T & { host: null; }; +function isLocalUser(user: User | { host: User['host'] }): boolean { + return user.host == null; +} + +function isRemoteUser(user: User): user is IRemoteUser; +function isRemoteUser(user: T): user is T & { host: string; }; +function isRemoteUser(user: User | { host: User['host'] }): boolean { + return !isLocalUser(user); +} + +@Injectable() +export class UserEntityService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('userSecurityKeysRepository') + private userSecurityKeysRepository: typeof UserSecurityKeys, + + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + @Inject('followRequestsRepository') + private followRequestsRepository: typeof FollowRequests, + + @Inject('blockingsRepository') + private blockingsRepository: typeof Blockings, + + @Inject('mutingsRepository') + private mutingsRepository: typeof Mutings, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + @Inject('noteUnreadRepository') + private noteUnreadRepository: typeof NoteUnreads, + + @Inject('channelFollowingsRepository') + private channelFollowingsRepository: typeof ChannelFollowings, + + @Inject('notificationsRepository') + private notificationsRepository: typeof Notifications, + + @Inject('userNotePiningsRepository') + private userNotePiningsRepository: typeof UserNotePinings, + + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + @Inject('instancesRepository') + private instancesRepository: typeof Instances, + + @Inject('announcementReadsRepository') + private announcementReadsRepository: typeof AnnouncementReads, + + @Inject('messagingMessagesRepository') + private messagingMessagesRepository: typeof MessagingMessages, + + @Inject('userGroupJoiningsRepository') + private userGroupJoiningsRepository: typeof UserGroupJoinings, + + @Inject('announcementsRepository') + private announcementsRepository: typeof Announcements, + + @Inject('antennaNotesRepository') + private antennaNotesRepository: typeof AntennaNotes, + + @Inject('pagesRepository') + private pagesRepository: typeof Pages, + + // 循環参照のため / for circular dependency + @Inject(forwardRef(() => NoteEntityService)) + private noteEntityService: NoteEntityService, + ) { + } + + //#region Validators + public validateLocalUsername = ajv.compile(localUsernameSchema); + public validatePassword = ajv.compile(passwordSchema); + public validateName = ajv.compile(nameSchema); + public validateDescription = ajv.compile(descriptionSchema); + public validateLocation = ajv.compile(locationSchema); + public validateBirthday = ajv.compile(birthdaySchema); + //#endregion + + public isLocalUser = isLocalUser; + public isRemoteUser = isRemoteUser; + + public async getRelation(me: User['id'], target: User['id']) { + return awaitAll({ + id: target, + isFollowing: this.followingsRepository.count({ + where: { + followerId: me, + followeeId: target, + }, + take: 1, + }).then(n => n > 0), + isFollowed: this.followingsRepository.count({ + where: { + followerId: target, + followeeId: me, + }, + take: 1, + }).then(n => n > 0), + hasPendingFollowRequestFromYou: this.followRequestsRepository.count({ + where: { + followerId: me, + followeeId: target, + }, + take: 1, + }).then(n => n > 0), + hasPendingFollowRequestToYou: this.followRequestsRepository.count({ + where: { + followerId: target, + followeeId: me, + }, + take: 1, + }).then(n => n > 0), + isBlocking: this.blockingsRepository.count({ + where: { + blockerId: me, + blockeeId: target, + }, + take: 1, + }).then(n => n > 0), + isBlocked: this.blockingsRepository.count({ + where: { + blockerId: target, + blockeeId: me, + }, + take: 1, + }).then(n => n > 0), + isMuted: this.mutingsRepository.count({ + where: { + muterId: me, + muteeId: target, + }, + take: 1, + }).then(n => n > 0), + }); + } + + public async getHasUnreadMessagingMessage(userId: User['id']): Promise { + const mute = await this.mutingsRepository.findBy({ + muterId: userId, + }); + + const joinings = await this.userGroupJoiningsRepository.findBy({ userId: userId }); + + const groupQs = Promise.all(joinings.map(j => this.messagingMessagesRepository.createQueryBuilder('message') + .where('message.groupId = :groupId', { groupId: j.userGroupId }) + .andWhere('message.userId != :userId', { userId: userId }) + .andWhere('NOT (:userId = ANY(message.reads))', { userId: userId }) + .andWhere('message.createdAt > :joinedAt', { joinedAt: j.createdAt }) // 自分が加入する前の会話については、未読扱いしない + .getOne().then(x => x != null))); + + const [withUser, withGroups] = await Promise.all([ + this.messagingMessagesRepository.count({ + where: { + recipientId: userId, + isRead: false, + ...(mute.length > 0 ? { userId: Not(In(mute.map(x => x.muteeId))) } : {}), + }, + take: 1, + }).then(count => count > 0), + groupQs, + ]); + + return withUser || withGroups.some(x => x); + } + + public async getHasUnreadAnnouncement(userId: User['id']): Promise { + const reads = await this.announcementReadsRepository.findBy({ + userId: userId, + }); + + const count = await this.announcementsRepository.countBy(reads.length > 0 ? { + id: Not(In(reads.map(read => read.announcementId))), + } : {}); + + return count > 0; + } + + public async getHasUnreadAntenna(userId: User['id']): Promise { + const myAntennas = (await getAntennas()).filter(a => a.userId === userId); + + const unread = myAntennas.length > 0 ? await this.antennaNotesRepository.findOneBy({ + antennaId: In(myAntennas.map(x => x.id)), + read: false, + }) : null; + + return unread != null; + } + + public async getHasUnreadChannel(userId: User['id']): Promise { + const channels = await this.channelFollowingsRepository.findBy({ followerId: userId }); + + const unread = channels.length > 0 ? await this.noteUnreadRepository.findOneBy({ + userId: userId, + noteChannelId: In(channels.map(x => x.followeeId)), + }) : null; + + return unread != null; + } + + public async getHasUnreadNotification(userId: User['id']): Promise { + const mute = await this.mutingsRepository.findBy({ + muterId: userId, + }); + const mutedUserIds = mute.map(m => m.muteeId); + + const count = await this.notificationsRepository.count({ + where: { + notifieeId: userId, + ...(mutedUserIds.length > 0 ? { notifierId: Not(In(mutedUserIds)) } : {}), + isRead: false, + }, + take: 1, + }); + + return count > 0; + } + + public async getHasPendingReceivedFollowRequest(userId: User['id']): Promise { + const count = await this.followRequestsRepository.countBy({ + followeeId: userId, + }); + + return count > 0; + } + + public getOnlineStatus(user: User): 'unknown' | 'online' | 'active' | 'offline' { + if (user.hideOnlineStatus) return 'unknown'; + if (user.lastActiveDate == null) return 'unknown'; + const elapsed = Date.now() - user.lastActiveDate.getTime(); + return ( + elapsed < USER_ONLINE_THRESHOLD ? 'online' : + elapsed < USER_ACTIVE_THRESHOLD ? 'active' : + 'offline' + ); + } + + public async getAvatarUrl(user: User): Promise { + if (user.avatar) { + return this.driveFilesRepository.getPublicUrl(user.avatar, true) || this.getIdenticonUrl(user.id); + } else if (user.avatarId) { + const avatar = await this.driveFilesRepository.findOneByOrFail({ id: user.avatarId }); + return this.driveFilesRepository.getPublicUrl(avatar, true) || this.getIdenticonUrl(user.id); + } else { + return this.getIdenticonUrl(user.id); + } + } + + public getAvatarUrlSync(user: User): string { + if (user.avatar) { + return this.driveFilesRepository.getPublicUrl(user.avatar, true) || this.getIdenticonUrl(user.id); + } else { + return this.getIdenticonUrl(user.id); + } + } + + public getIdenticonUrl(userId: User['id']): string { + return `${this.config.url}/identicon/${userId}`; + } + + public async pack( + src: User['id'] | User, + me?: { id: User['id'] } | null | undefined, + options?: { + detail?: D, + includeSecrets?: boolean, + }, + ): Promise> { + const opts = Object.assign({ + detail: false, + includeSecrets: false, + }, options); + + let user: User; + + if (typeof src === 'object') { + user = src; + if (src.avatar === undefined && src.avatarId) src.avatar = await this.driveFilesRepository.findOneBy({ id: src.avatarId }) ?? null; + if (src.banner === undefined && src.bannerId) src.banner = await this.driveFilesRepository.findOneBy({ id: src.bannerId }) ?? null; + } else { + user = await this.usersRepository.findOneOrFail({ + where: { id: src }, + relations: { + avatar: true, + banner: true, + }, + }); + } + + const meId = me ? me.id : null; + const isMe = meId === user.id; + + const relation = meId && !isMe && opts.detail ? await this.getRelation(meId, user.id) : null; + const pins = opts.detail ? await this.userNotePiningsRepository.createQueryBuilder('pin') + .where('pin.userId = :userId', { userId: user.id }) + .innerJoinAndSelect('pin.note', 'note') + .orderBy('pin.id', 'DESC') + .getMany() : []; + const profile = opts.detail ? await this.userProfilesRepository.findOneByOrFail({ userId: user.id }) : null; + + const followingCount = profile == null ? null : + (profile.ffVisibility === 'public') || isMe ? user.followingCount : + (profile.ffVisibility === 'followers') && (relation && relation.isFollowing) ? user.followingCount : + null; + + const followersCount = profile == null ? null : + (profile.ffVisibility === 'public') || isMe ? user.followersCount : + (profile.ffVisibility === 'followers') && (relation && relation.isFollowing) ? user.followersCount : + null; + + const falsy = opts.detail ? false : undefined; + + const packed = { + id: user.id, + name: user.name, + username: user.username, + host: user.host, + avatarUrl: this.getAvatarUrlSync(user), + avatarBlurhash: user.avatar?.blurhash || null, + avatarColor: null, // 後方互換性のため + isAdmin: user.isAdmin || falsy, + isModerator: user.isModerator || falsy, + isBot: user.isBot || falsy, + isCat: user.isCat || falsy, + instance: user.host ? userInstanceCache.fetch(user.host, + () => this.instancesRepository.findOneBy({ host: user.host! }), + v => v != null, + ).then(instance => instance ? { + name: instance.name, + softwareName: instance.softwareName, + softwareVersion: instance.softwareVersion, + iconUrl: instance.iconUrl, + faviconUrl: instance.faviconUrl, + themeColor: instance.themeColor, + } : undefined) : undefined, + emojis: populateEmojis(user.emojis, user.host), + onlineStatus: this.getOnlineStatus(user), + driveCapacityOverrideMb: user.driveCapacityOverrideMb, + + ...(opts.detail ? { + url: profile!.url, + uri: user.uri, + createdAt: user.createdAt.toISOString(), + updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null, + lastFetchedAt: user.lastFetchedAt ? user.lastFetchedAt.toISOString() : null, + bannerUrl: user.banner ? this.driveFilesRepository.getPublicUrl(user.banner, false) : null, + bannerBlurhash: user.banner?.blurhash || null, + bannerColor: null, // 後方互換性のため + isLocked: user.isLocked, + isSilenced: user.isSilenced || falsy, + isSuspended: user.isSuspended || falsy, + description: profile!.description, + location: profile!.location, + birthday: profile!.birthday, + lang: profile!.lang, + fields: profile!.fields, + followersCount: followersCount || 0, + followingCount: followingCount || 0, + notesCount: user.notesCount, + pinnedNoteIds: pins.map(pin => pin.noteId), + pinnedNotes: this.noteEntityService.packMany(pins.map(pin => pin.note!), me, { + detail: true, + }), + pinnedPageId: profile!.pinnedPageId, + pinnedPage: profile!.pinnedPageId ? Pages.pack(profile!.pinnedPageId, me) : null, + publicReactions: profile!.publicReactions, + ffVisibility: profile!.ffVisibility, + twoFactorEnabled: profile!.twoFactorEnabled, + usePasswordLessLogin: profile!.usePasswordLessLogin, + securityKeys: profile!.twoFactorEnabled + ? this.userSecurityKeysRepository.countBy({ + userId: user.id, + }).then(result => result >= 1) + : false, + } : {}), + + ...(opts.detail && isMe ? { + avatarId: user.avatarId, + bannerId: user.bannerId, + injectFeaturedNote: profile!.injectFeaturedNote, + receiveAnnouncementEmail: profile!.receiveAnnouncementEmail, + alwaysMarkNsfw: profile!.alwaysMarkNsfw, + autoSensitive: profile!.autoSensitive, + carefulBot: profile!.carefulBot, + autoAcceptFollowed: profile!.autoAcceptFollowed, + noCrawle: profile!.noCrawle, + isExplorable: user.isExplorable, + isDeleted: user.isDeleted, + hideOnlineStatus: user.hideOnlineStatus, + hasUnreadSpecifiedNotes: this.noteUnreadRepository.count({ + where: { userId: user.id, isSpecified: true }, + take: 1, + }).then(count => count > 0), + hasUnreadMentions: this.noteUnreadRepository.count({ + where: { userId: user.id, isMentioned: true }, + take: 1, + }).then(count => count > 0), + hasUnreadAnnouncement: this.getHasUnreadAnnouncement(user.id), + hasUnreadAntenna: this.getHasUnreadAntenna(user.id), + hasUnreadChannel: this.getHasUnreadChannel(user.id), + hasUnreadMessagingMessage: this.getHasUnreadMessagingMessage(user.id), + hasUnreadNotification: this.getHasUnreadNotification(user.id), + hasPendingReceivedFollowRequest: this.getHasPendingReceivedFollowRequest(user.id), + integrations: profile!.integrations, + mutedWords: profile!.mutedWords, + mutedInstances: profile!.mutedInstances, + mutingNotificationTypes: profile!.mutingNotificationTypes, + emailNotificationTypes: profile!.emailNotificationTypes, + showTimelineReplies: user.showTimelineReplies || falsy, + } : {}), + + ...(opts.includeSecrets ? { + email: profile!.email, + emailVerified: profile!.emailVerified, + securityKeysList: profile!.twoFactorEnabled + ? this.userSecurityKeysRepository.find({ + where: { + userId: user.id, + }, + select: { + id: true, + name: true, + lastUsed: true, + }, + }) + : [], + } : {}), + + ...(relation ? { + isFollowing: relation.isFollowing, + isFollowed: relation.isFollowed, + hasPendingFollowRequestFromYou: relation.hasPendingFollowRequestFromYou, + hasPendingFollowRequestToYou: relation.hasPendingFollowRequestToYou, + isBlocking: relation.isBlocking, + isBlocked: relation.isBlocked, + isMuted: relation.isMuted, + } : {}), + } as Promiseable> as Promiseable>; + + return await awaitAll(packed); + } + + public packMany( + users: (User['id'] | User)[], + me?: { id: User['id'] } | null | undefined, + options?: { + detail?: D, + includeSecrets?: boolean, + }, + ): Promise[]> { + return Promise.all(users.map(u => this.pack(u, me, options))); + } +} diff --git a/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts b/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts index 5d75d83a2306..e503d98e47e9 100644 --- a/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts +++ b/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts @@ -134,7 +134,7 @@ class DeliverManager { * Execute delivers */ public async execute() { - if (!this.usersRepository.isLocalUser(this.actor)) return; + if (!this.userEntityService.isLocalUser(this.actor)) return; const inboxes = new Set(); diff --git a/packages/backend/src/services/remote/activitypub/ApInboxService.ts b/packages/backend/src/services/remote/activitypub/ApInboxService.ts index 4f2c130745ad..573a4115a3d7 100644 --- a/packages/backend/src/services/remote/activitypub/ApInboxService.ts +++ b/packages/backend/src/services/remote/activitypub/ApInboxService.ts @@ -525,7 +525,7 @@ export class ApInboxService { return 'skip: follower not found'; } - if (!this.usersRepository.isLocalUser(follower)) { + if (!this.userEntityService.isLocalUser(follower)) { return 'skip: follower is not a local user'; } diff --git a/packages/backend/src/services/remote/activitypub/ApRendererService.ts b/packages/backend/src/services/remote/activitypub/ApRendererService.ts index 8efd87240101..480e1676c3b7 100644 --- a/packages/backend/src/services/remote/activitypub/ApRendererService.ts +++ b/packages/backend/src/services/remote/activitypub/ApRendererService.ts @@ -184,7 +184,7 @@ export class ApRendererService { */ public async renderFollowUser(id: User['id']) { const user = await this.usersRepository.findOneByOrFail({ id: id }); - return this.usersRepository.isLocalUser(user) ? `${this.config.url}/users/${user.id}` : user.uri; + return this.userEntityService.isLocalUser(user) ? `${this.config.url}/users/${user.id}` : user.uri; } public renderFollow( @@ -195,8 +195,8 @@ export class ApRendererService { const follow = { id: requestId ?? `${this.config.url}/follows/${follower.id}/${followee.id}`, type: 'Follow', - actor: this.usersRepository.isLocalUser(follower) ? `${this.config.url}/users/${follower.id}` : follower.uri, - object: this.usersRepository.isLocalUser(followee) ? `${this.config.url}/users/${followee.id}` : followee.uri, + actor: this.userEntityService.isLocalUser(follower) ? `${this.config.url}/users/${follower.id}` : follower.uri, + object: this.userEntityService.isLocalUser(followee) ? `${this.config.url}/users/${followee.id}` : followee.uri, } as any; return follow; @@ -259,8 +259,8 @@ export class ApRendererService { public renderMention(mention: User) { return { type: 'Mention', - href: this.usersRepository.isRemoteUser(mention) ? mention.uri : `${this.config.url}/users/${(mention as ILocalUser).id}`, - name: this.usersRepository.isRemoteUser(mention) ? `@${mention.username}@${mention.host}` : `@${(mention as ILocalUser).username}`, + href: this.userEntityService.isRemoteUser(mention) ? mention.uri : `${this.config.url}/users/${(mention as ILocalUser).id}`, + name: this.userEntityService.isRemoteUser(mention) ? `@${mention.username}@${mention.host}` : `@${(mention as ILocalUser).username}`, }; } diff --git a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts index 7cbd91bae568..289582fb065a 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts @@ -516,7 +516,7 @@ export class ApPersonService { public async updateFeatured(userId: User['id']) { const user = await this.usersRepository.findOneByOrFail({ id: userId }); - if (!this.usersRepository.isRemoteUser(user)) return; + if (!this.userEntityService.isRemoteUser(user)) return; if (!user.featured) return; this.#logger.info(`Updating the featured: ${user.uri}`); From 2c179578b969561ed9bb1ae414e7f3522818ee97 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 04:22:14 +0900 Subject: [PATCH 100/180] wip --- .../models/repositories/abuse-user-report.ts | 38 ---------- .../backend/src/models/repositories/note.ts | 4 - .../backend/src/models/repositories/relay.ts | 5 -- .../backend/src/models/repositories/user.ts | 4 - .../entities/AbuseUserReportEntityService.ts | 49 +++++++++++++ .../entities/NotificationEntityService.ts} | 73 ++++++++++++------- 6 files changed, 94 insertions(+), 79 deletions(-) delete mode 100644 packages/backend/src/models/repositories/abuse-user-report.ts delete mode 100644 packages/backend/src/models/repositories/note.ts delete mode 100644 packages/backend/src/models/repositories/relay.ts delete mode 100644 packages/backend/src/models/repositories/user.ts create mode 100644 packages/backend/src/services/entities/AbuseUserReportEntityService.ts rename packages/backend/src/{models/repositories/notification.ts => services/entities/NotificationEntityService.ts} (53%) diff --git a/packages/backend/src/models/repositories/abuse-user-report.ts b/packages/backend/src/models/repositories/abuse-user-report.ts deleted file mode 100644 index 36d7ab90c5b5..000000000000 --- a/packages/backend/src/models/repositories/abuse-user-report.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { Users } from '../index.js'; -import { AbuseUserReport } from '@/models/entities/abuse-user-report.js'; -import { awaitAll } from '@/prelude/await-all.js'; - -export const AbuseUserReportRepository = db.getRepository(AbuseUserReport).extend({ - async pack( - src: AbuseUserReport['id'] | AbuseUserReport, - ) { - const report = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - return await awaitAll({ - id: report.id, - createdAt: report.createdAt.toISOString(), - comment: report.comment, - resolved: report.resolved, - reporterId: report.reporterId, - targetUserId: report.targetUserId, - assigneeId: report.assigneeId, - reporter: Users.pack(report.reporter || report.reporterId, null, { - detail: true, - }), - targetUser: Users.pack(report.targetUser || report.targetUserId, null, { - detail: true, - }), - assignee: report.assigneeId ? Users.pack(report.assignee || report.assigneeId, null, { - detail: true, - }) : null, - forwarded: report.forwarded, - }); - }, - - packMany( - reports: any[], - ) { - return Promise.all(reports.map(x => this.pack(x))); - }, -}); diff --git a/packages/backend/src/models/repositories/note.ts b/packages/backend/src/models/repositories/note.ts deleted file mode 100644 index ad102863753d..000000000000 --- a/packages/backend/src/models/repositories/note.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Note } from '@/models/entities/note.js'; -import { db } from '@/db/postgre.js'; - -export const NoteRepository = db.getRepository(Note); diff --git a/packages/backend/src/models/repositories/relay.ts b/packages/backend/src/models/repositories/relay.ts deleted file mode 100644 index fa1c8f4d8dad..000000000000 --- a/packages/backend/src/models/repositories/relay.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { Relay } from '@/models/entities/relay.js'; - -export const RelayRepository = db.getRepository(Relay).extend({ -}); diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts deleted file mode 100644 index a07a8e5fc23b..000000000000 --- a/packages/backend/src/models/repositories/user.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { User } from '@/models/entities/user.js'; -import { db } from '@/db/postgre.js'; - -export const UserRepository = db.getRepository(User); diff --git a/packages/backend/src/services/entities/AbuseUserReportEntityService.ts b/packages/backend/src/services/entities/AbuseUserReportEntityService.ts new file mode 100644 index 000000000000..73cbc3f02521 --- /dev/null +++ b/packages/backend/src/services/entities/AbuseUserReportEntityService.ts @@ -0,0 +1,49 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { AbuseUserReports } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { AbuseUserReport } from '@/models/entities/abuse-user-report.js'; +import { UserEntityService } from './UserEntityService.js'; + +@Injectable() +export class AbuseUserReportEntityService { + constructor( + @Inject('abuseUserReportRepository') + private abuseUserReportRepository: typeof AbuseUserReports, + + private userEntityService: UserEntityService, + ) { + } + + public async pack( + src: AbuseUserReport['id'] | AbuseUserReport, + ) { + const report = typeof src === 'object' ? src : await this.abuseUserReportRepository.findOneByOrFail({ id: src }); + + return await awaitAll({ + id: report.id, + createdAt: report.createdAt.toISOString(), + comment: report.comment, + resolved: report.resolved, + reporterId: report.reporterId, + targetUserId: report.targetUserId, + assigneeId: report.assigneeId, + reporter: this.userEntityService.pack(report.reporter || report.reporterId, null, { + detail: true, + }), + targetUser: this.userEntityService.pack(report.targetUser || report.targetUserId, null, { + detail: true, + }), + assignee: report.assigneeId ? this.userEntityService.pack(report.assignee || report.assigneeId, null, { + detail: true, + }) : null, + forwarded: report.forwarded, + }); + } + + public packMany( + reports: any[], + ) { + return Promise.all(reports.map(x => this.pack(x))); + } +} diff --git a/packages/backend/src/models/repositories/notification.ts b/packages/backend/src/services/entities/NotificationEntityService.ts similarity index 53% rename from packages/backend/src/models/repositories/notification.ts rename to packages/backend/src/services/entities/NotificationEntityService.ts index 42b47ab1503b..86945113ba07 100644 --- a/packages/backend/src/models/repositories/notification.ts +++ b/packages/backend/src/services/entities/NotificationEntityService.ts @@ -1,26 +1,43 @@ -import { In, Repository } from 'typeorm'; -import { Users, Notes, UserGroupInvitations, AccessTokens, NoteReactions } from '../index.js'; -import { Notification } from '@/models/entities/notification.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { In } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { AccessTokens, NoteReactions , Notifications } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; -import { Packed } from '@/misc/schema.js'; -import { Note } from '@/models/entities/note.js'; -import { NoteReaction } from '@/models/entities/note-reaction.js'; -import { User } from '@/models/entities/user.js'; +import type { Notification } from '@/models/entities/notification.js'; import { aggregateNoteEmojis, prefetchEmojis } from '@/misc/populate-emojis.js'; -import { notificationTypes } from '@/types.js'; -import { db } from '@/db/postgre.js'; +import type { NoteReaction } from '@/models/entities/note-reaction.js'; +import type { Note } from '@/models/entities/note.js'; +import type { Packed } from '@/misc/schema.js'; +import { UserEntityService } from './UserEntityService.js'; +import { NoteEntityService } from './NoteEntityService.js'; -export const NotificationRepository = db.getRepository(Notification).extend({ - async pack( +@Injectable() +export class NotificationEntityService { + constructor( + @Inject('notificationsRepository') + private notificationsRepository: typeof Notifications, + + @Inject('noteReactionsRepository') + private noteReactionsRepository: typeof NoteReactions, + + @Inject('accessTokensRepository') + private accessTokensRepository: typeof AccessTokens, + + private userEntityService: UserEntityService, + private noteEntityService: NoteEntityService, + ) { + } + + public async pack( src: Notification['id'] | Notification, options: { _hintForEachNotes_?: { myReactions: Map; }; - } + }, ): Promise> { - const notification = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - const token = notification.appAccessTokenId ? await AccessTokens.findOneByOrFail({ id: notification.appAccessTokenId }) : null; + const notification = typeof src === 'object' ? src : await this.notificationsRepository.findOneByOrFail({ id: src }); + const token = notification.appAccessTokenId ? await this.accessTokensRepository.findOneByOrFail({ id: notification.appAccessTokenId }) : null; return await awaitAll({ id: notification.id, @@ -28,47 +45,47 @@ export const NotificationRepository = db.getRepository(Notification).extend({ type: notification.type, isRead: notification.isRead, userId: notification.notifierId, - user: notification.notifierId ? Users.pack(notification.notifier || notification.notifierId) : null, + user: notification.notifierId ? this.userEntityService.pack(notification.notifier || notification.notifierId) : null, ...(notification.type === 'mention' ? { - note: Notes.pack(notification.note || notification.noteId!, { id: notification.notifieeId }, { + note: this.noteEntityService.pack(notification.note ?? notification.noteId!, { id: notification.notifieeId }, { detail: true, _hint_: options._hintForEachNotes_, }), } : {}), ...(notification.type === 'reply' ? { - note: Notes.pack(notification.note || notification.noteId!, { id: notification.notifieeId }, { + note: this.noteEntityService.pack(notification.note ?? notification.noteId!, { id: notification.notifieeId }, { detail: true, _hint_: options._hintForEachNotes_, }), } : {}), ...(notification.type === 'renote' ? { - note: Notes.pack(notification.note || notification.noteId!, { id: notification.notifieeId }, { + note: this.noteEntityService.pack(notification.note ?? notification.noteId!, { id: notification.notifieeId }, { detail: true, _hint_: options._hintForEachNotes_, }), } : {}), ...(notification.type === 'quote' ? { - note: Notes.pack(notification.note || notification.noteId!, { id: notification.notifieeId }, { + note: this.noteEntityService.pack(notification.note ?? notification.noteId!, { id: notification.notifieeId }, { detail: true, _hint_: options._hintForEachNotes_, }), } : {}), ...(notification.type === 'reaction' ? { - note: Notes.pack(notification.note || notification.noteId!, { id: notification.notifieeId }, { + note: this.noteEntityService.pack(notification.note ?? notification.noteId!, { id: notification.notifieeId }, { detail: true, _hint_: options._hintForEachNotes_, }), reaction: notification.reaction, } : {}), ...(notification.type === 'pollVote' ? { - note: Notes.pack(notification.note || notification.noteId!, { id: notification.notifieeId }, { + note: this.noteEntityService.pack(notification.note ?? notification.noteId!, { id: notification.notifieeId }, { detail: true, _hint_: options._hintForEachNotes_, }), choice: notification.choice, } : {}), ...(notification.type === 'pollEnded' ? { - note: Notes.pack(notification.note || notification.noteId!, { id: notification.notifieeId }, { + note: this.noteEntityService.pack(notification.note ?? notification.noteId!, { id: notification.notifieeId }, { detail: true, _hint_: options._hintForEachNotes_, }), @@ -82,11 +99,11 @@ export const NotificationRepository = db.getRepository(Notification).extend({ icon: notification.customIcon || token?.iconUrl, } : {}), }); - }, + } - async packMany( + public async packMany( notifications: Notification[], - meId: User['id'] + meId: User['id'], ) { if (notifications.length === 0) return []; @@ -95,7 +112,7 @@ export const NotificationRepository = db.getRepository(Notification).extend({ const myReactionsMap = new Map(); const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!); const targets = [...noteIds, ...renoteIds]; - const myReactions = await NoteReactions.findBy({ + const myReactions = await this.noteReactionsRepository.findBy({ userId: meId, noteId: In(targets), }); @@ -111,5 +128,5 @@ export const NotificationRepository = db.getRepository(Notification).extend({ myReactions: myReactionsMap, }, }))); - }, -}); + } +} From 216957d5b5a23be026bc2bd7f0c3d5b485e2ff3b Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 04:28:24 +0900 Subject: [PATCH 101/180] wip --- .../entities/DriveFileEntityService.ts} | 110 +++++++++++------- 1 file changed, 66 insertions(+), 44 deletions(-) rename packages/backend/src/{models/repositories/drive-file.ts => services/entities/DriveFileEntityService.ts} (62%) diff --git a/packages/backend/src/models/repositories/drive-file.ts b/packages/backend/src/services/entities/DriveFileEntityService.ts similarity index 62% rename from packages/backend/src/models/repositories/drive-file.ts rename to packages/backend/src/services/entities/DriveFileEntityService.ts index 0d589d4f1134..b95103b277e4 100644 --- a/packages/backend/src/models/repositories/drive-file.ts +++ b/packages/backend/src/services/entities/DriveFileEntityService.ts @@ -1,14 +1,16 @@ -import { db } from '@/db/postgre.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; -import { User } from '@/models/entities/user.js'; +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { DataSource, In } from 'typeorm'; +import * as mfm from 'mfm-js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Notes , DriveFiles } from '@/models/index.js'; +import { Config } from '@/config/types.js'; +import type { Packed } from '@/misc/schema.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { User } from '@/models/entities/user'; +import type { DriveFile } from '@/models/entities/drive-file.js'; +import { appendQuery, query } from '@/prelude/url.js'; import { toPuny } from '@/misc/convert-host.js'; -import { awaitAll, Promiseable } from '@/prelude/await-all.js'; -import { Packed } from '@/misc/schema.js'; -import config from '@/config/index.js'; -import { query, appendQuery } from '@/prelude/url.js'; -import { Meta } from '@/models/entities/meta.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import { Users, DriveFolders } from '../index.js'; +import { UserEntityService } from './UserEntityService.js'; type PackOptions = { detail?: boolean, @@ -16,8 +18,28 @@ type PackOptions = { withUser?: boolean, }; -export const DriveFileRepository = db.getRepository(DriveFile).extend({ - validateFileName(name: string): boolean { +@Injectable() +export class DriveFileEntityService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject(DI_SYMBOLS.db) + private db: DataSource, + + @Inject('notesRepository') + private notesRepository: typeof Notes, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + // 循環参照のため / for circular dependency + @Inject(forwardRef(() => UserEntityService)) + private userEntityService: UserEntityService, + ) { + } + + public validateFileName(name: string): boolean { return ( (name.trim().length > 0) && (name.length <= 200) && @@ -25,9 +47,9 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({ (name.indexOf('/') === -1) && (name.indexOf('..') === -1) ); - }, + } - getPublicProperties(file: DriveFile): DriveFile['properties'] { + public getPublicProperties(file: DriveFile): DriveFile['properties'] { if (file.properties.orientation != null) { // TODO //const properties = structuredClone(file.properties); @@ -40,35 +62,35 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({ } return file.properties; - }, + } - getPublicUrl(file: DriveFile, thumbnail = false): string | null { + public getPublicUrl(file: DriveFile, thumbnail = false): string | null { // リモートかつメディアプロキシ - if (file.uri != null && file.userHost != null && config.mediaProxy != null) { - return appendQuery(config.mediaProxy, query({ + if (file.uri != null && file.userHost != null && this.config.mediaProxy != null) { + return appendQuery(this.config.mediaProxy, query({ url: file.uri, thumbnail: thumbnail ? '1' : undefined, })); } // リモートかつ期限切れはローカルプロキシを試みる - if (file.uri != null && file.isLink && config.proxyRemoteFiles) { + if (file.uri != null && file.isLink && this.config.proxyRemoteFiles) { const key = thumbnail ? file.thumbnailAccessKey : file.webpublicAccessKey; if (key && !key.match('/')) { // 古いものはここにオブジェクトストレージキーが入ってるので除外 - return `${config.url}/files/${key}`; + return `${this.config.url}/files/${key}`; } } const isImage = file.type && ['image/png', 'image/apng', 'image/gif', 'image/jpeg', 'image/webp', 'image/svg+xml'].includes(file.type); return thumbnail ? (file.thumbnailUrl || (isImage ? (file.webpublicUrl || file.url) : null)) : (file.webpublicUrl || file.url); - }, + } - async calcDriveUsageOf(user: User['id'] | { id: User['id'] }): Promise { + public async calcDriveUsageOf(user: User['id'] | { id: User['id'] }): Promise { const id = typeof user === 'object' ? user.id : user; - const { sum } = await this + const { sum } = await this.driveFilesRepository .createQueryBuilder('file') .where('file.userId = :id', { id: id }) .andWhere('file.isLink = FALSE') @@ -76,10 +98,10 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({ .getRawOne(); return parseInt(sum, 10) || 0; - }, + } - async calcDriveUsageOfHost(host: string): Promise { - const { sum } = await this + public async calcDriveUsageOfHost(host: string): Promise { + const { sum } = await this.driveFilesRepository .createQueryBuilder('file') .where('file.userHost = :host', { host: toPuny(host) }) .andWhere('file.isLink = FALSE') @@ -87,10 +109,10 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({ .getRawOne(); return parseInt(sum, 10) || 0; - }, + } - async calcDriveUsageOfLocal(): Promise { - const { sum } = await this + public async calcDriveUsageOfLocal(): Promise { + const { sum } = await this.driveFilesRepository .createQueryBuilder('file') .where('file.userHost IS NULL') .andWhere('file.isLink = FALSE') @@ -98,10 +120,10 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({ .getRawOne(); return parseInt(sum, 10) || 0; - }, + } - async calcDriveUsageOfRemote(): Promise { - const { sum } = await this + public async calcDriveUsageOfRemote(): Promise { + const { sum } = await this.driveFilesRepository .createQueryBuilder('file') .where('file.userHost IS NOT NULL') .andWhere('file.isLink = FALSE') @@ -109,9 +131,9 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({ .getRawOne(); return parseInt(sum, 10) || 0; - }, + } - async pack( + public async pack( src: DriveFile['id'] | DriveFile, options?: PackOptions, ): Promise> { @@ -120,7 +142,7 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({ self: false, }, options); - const file = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); + const file = typeof src === 'object' ? src : await this.driveFilesRepository.findOneByOrFail({ id: src }); return await awaitAll>({ id: file.id, @@ -140,11 +162,11 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({ detail: true, }) : null, userId: opts.withUser ? file.userId : null, - user: (opts.withUser && file.userId) ? Users.pack(file.userId) : null, + user: (opts.withUser && file.userId) ? this.userEntityService.pack(file.userId) : null, }); - }, + } - async packNullable( + public async packNullable( src: DriveFile['id'] | DriveFile, options?: PackOptions, ): Promise | null> { @@ -153,7 +175,7 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({ self: false, }, options); - const file = typeof src === 'object' ? src : await this.findOneBy({ id: src }); + const file = typeof src === 'object' ? src : await this.driveFilesRepository.findOneBy({ id: src }); if (file == null) return null; return await awaitAll>({ @@ -174,15 +196,15 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({ detail: true, }) : null, userId: opts.withUser ? file.userId : null, - user: (opts.withUser && file.userId) ? Users.pack(file.userId) : null, + user: (opts.withUser && file.userId) ? this.userEntityService.pack(file.userId) : null, }); - }, + } - async packMany( + public async packMany( files: (DriveFile['id'] | DriveFile)[], options?: PackOptions, ): Promise[]> { const items = await Promise.all(files.map(f => this.packNullable(f, options))); return items.filter((x): x is Packed<'DriveFile'> => x != null); - }, -}); + } +} From d8c5982e2fe0e4e2afb8d7454036c17f06f11cdd Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 05:36:21 +0900 Subject: [PATCH 102/180] wip --- .../src/models/repositories/antenna.ts | 32 --------- .../backend/src/models/repositories/app.ts | 39 ----------- .../src/models/repositories/auth-session.ts | 20 ------ .../src/models/repositories/blocking.ts | 31 --------- .../src/models/repositories/channel.ts | 41 ------------ .../backend/src/models/repositories/clip.ts | 30 --------- .../src/models/repositories/drive-folder.ts | 42 ------------ .../backend/src/models/repositories/emoji.ts | 27 -------- .../src/models/repositories/follow-request.ts | 19 ------ .../src/models/repositories/gallery-like.ts | 24 ------- .../src/models/repositories/gallery-post.ts | 39 ----------- .../src/models/repositories/hashtag.ts | 25 ------- .../models/repositories/messaging-message.ts | 39 ----------- .../models/repositories/moderation-logs.ts | 29 -------- .../backend/src/models/repositories/muting.ts | 32 --------- .../src/models/repositories/note-favorite.ts | 27 -------- .../src/models/repositories/note-reaction.ts | 32 --------- .../src/models/repositories/page-like.ts | 25 ------- .../backend/src/models/repositories/signin.ts | 10 --- .../repositories/user-group-invitation.ts | 22 ------- .../src/models/repositories/user-group.ts | 24 ------- .../src/models/repositories/user-list.ts | 23 ------- .../services/entities/AntennaEntityService.ts | 47 +++++++++++++ .../src/services/entities/AppEntityService.ts | 52 +++++++++++++++ .../entities/AuthSessionEntityService.ts | 34 ++++++++++ .../entities/BlockingEntityService.ts | 42 ++++++++++++ .../services/entities/ChannelEntityService.ts | 66 +++++++++++++++++++ .../services/entities/ClipEntityService.ts | 43 ++++++++++++ .../entities/DriveFileEntityService.ts | 7 +- .../entities/DriveFolderEntityService.ts | 59 +++++++++++++++++ .../services/entities/EmojiEntityService.ts | 43 ++++++++++++ .../entities/FollowRequestEntityService.ts | 34 ++++++++++ .../entities/FollowingEntityService.ts} | 61 ++++++++++------- .../entities/GalleryLikeEntityService.ts | 42 ++++++++++++ .../entities/GalleryPostEntityService.ts | 57 ++++++++++++++++ .../services/entities/HashtagEntityService.ts | 41 ++++++++++++ .../entities/InstanceEntityService.ts} | 38 +++++++---- .../entities/MessagingMessageEntityService.ts | 55 ++++++++++++++++ .../entities/ModerationLogEntityService.ts | 44 +++++++++++++ .../services/entities/MutingEntityService.ts | 45 +++++++++++++ .../services/entities/NoteEntityService.ts | 11 ++-- .../entities/NoteFavoriteEntityService.ts | 42 ++++++++++++ .../entities/NoteReactionEntityService.ts | 47 +++++++++++++ .../entities/PageEntityService.ts} | 54 ++++++++++----- .../entities/PageLikeEntityService.ts | 41 ++++++++++++ .../services/entities/SigninEntityService.ts | 27 ++++++++ .../entities/UserGroupEntityService.ts | 42 ++++++++++++ .../UserGroupInvitationEntityService.ts | 39 +++++++++++ .../entities/UserListEntityService.ts | 41 ++++++++++++ 49 files changed, 1096 insertions(+), 690 deletions(-) delete mode 100644 packages/backend/src/models/repositories/antenna.ts delete mode 100644 packages/backend/src/models/repositories/app.ts delete mode 100644 packages/backend/src/models/repositories/auth-session.ts delete mode 100644 packages/backend/src/models/repositories/blocking.ts delete mode 100644 packages/backend/src/models/repositories/channel.ts delete mode 100644 packages/backend/src/models/repositories/clip.ts delete mode 100644 packages/backend/src/models/repositories/drive-folder.ts delete mode 100644 packages/backend/src/models/repositories/emoji.ts delete mode 100644 packages/backend/src/models/repositories/follow-request.ts delete mode 100644 packages/backend/src/models/repositories/gallery-like.ts delete mode 100644 packages/backend/src/models/repositories/gallery-post.ts delete mode 100644 packages/backend/src/models/repositories/hashtag.ts delete mode 100644 packages/backend/src/models/repositories/messaging-message.ts delete mode 100644 packages/backend/src/models/repositories/moderation-logs.ts delete mode 100644 packages/backend/src/models/repositories/muting.ts delete mode 100644 packages/backend/src/models/repositories/note-favorite.ts delete mode 100644 packages/backend/src/models/repositories/note-reaction.ts delete mode 100644 packages/backend/src/models/repositories/page-like.ts delete mode 100644 packages/backend/src/models/repositories/signin.ts delete mode 100644 packages/backend/src/models/repositories/user-group-invitation.ts delete mode 100644 packages/backend/src/models/repositories/user-group.ts delete mode 100644 packages/backend/src/models/repositories/user-list.ts create mode 100644 packages/backend/src/services/entities/AntennaEntityService.ts create mode 100644 packages/backend/src/services/entities/AppEntityService.ts create mode 100644 packages/backend/src/services/entities/AuthSessionEntityService.ts create mode 100644 packages/backend/src/services/entities/BlockingEntityService.ts create mode 100644 packages/backend/src/services/entities/ChannelEntityService.ts create mode 100644 packages/backend/src/services/entities/ClipEntityService.ts create mode 100644 packages/backend/src/services/entities/DriveFolderEntityService.ts create mode 100644 packages/backend/src/services/entities/EmojiEntityService.ts create mode 100644 packages/backend/src/services/entities/FollowRequestEntityService.ts rename packages/backend/src/{models/repositories/following.ts => services/entities/FollowingEntityService.ts} (52%) create mode 100644 packages/backend/src/services/entities/GalleryLikeEntityService.ts create mode 100644 packages/backend/src/services/entities/GalleryPostEntityService.ts create mode 100644 packages/backend/src/services/entities/HashtagEntityService.ts rename packages/backend/src/{models/repositories/instance.ts => services/entities/InstanceEntityService.ts} (59%) create mode 100644 packages/backend/src/services/entities/MessagingMessageEntityService.ts create mode 100644 packages/backend/src/services/entities/ModerationLogEntityService.ts create mode 100644 packages/backend/src/services/entities/MutingEntityService.ts create mode 100644 packages/backend/src/services/entities/NoteFavoriteEntityService.ts create mode 100644 packages/backend/src/services/entities/NoteReactionEntityService.ts rename packages/backend/src/{models/repositories/page.ts => services/entities/PageEntityService.ts} (56%) create mode 100644 packages/backend/src/services/entities/PageLikeEntityService.ts create mode 100644 packages/backend/src/services/entities/SigninEntityService.ts create mode 100644 packages/backend/src/services/entities/UserGroupEntityService.ts create mode 100644 packages/backend/src/services/entities/UserGroupInvitationEntityService.ts create mode 100644 packages/backend/src/services/entities/UserListEntityService.ts diff --git a/packages/backend/src/models/repositories/antenna.ts b/packages/backend/src/models/repositories/antenna.ts deleted file mode 100644 index 70180e2dec74..000000000000 --- a/packages/backend/src/models/repositories/antenna.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { Antenna } from '@/models/entities/antenna.js'; -import { Packed } from '@/misc/schema.js'; -import { AntennaNotes, UserGroupJoinings } from '../index.js'; - -export const AntennaRepository = db.getRepository(Antenna).extend({ - async pack( - src: Antenna['id'] | Antenna, - ): Promise> { - const antenna = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - const hasUnreadNote = (await AntennaNotes.findOneBy({ antennaId: antenna.id, read: false })) != null; - const userGroupJoining = antenna.userGroupJoiningId ? await UserGroupJoinings.findOneBy({ id: antenna.userGroupJoiningId }) : null; - - return { - id: antenna.id, - createdAt: antenna.createdAt.toISOString(), - name: antenna.name, - keywords: antenna.keywords, - excludeKeywords: antenna.excludeKeywords, - src: antenna.src, - userListId: antenna.userListId, - userGroupId: userGroupJoining ? userGroupJoining.userGroupId : null, - users: antenna.users, - caseSensitive: antenna.caseSensitive, - notify: antenna.notify, - withReplies: antenna.withReplies, - withFile: antenna.withFile, - hasUnreadNote, - }; - }, -}); diff --git a/packages/backend/src/models/repositories/app.ts b/packages/backend/src/models/repositories/app.ts deleted file mode 100644 index e08dd6f0e3e0..000000000000 --- a/packages/backend/src/models/repositories/app.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { App } from '@/models/entities/app.js'; -import { AccessTokens } from '../index.js'; -import { Packed } from '@/misc/schema.js'; -import { User } from '../entities/user.js'; - -export const AppRepository = db.getRepository(App).extend({ - async pack( - src: App['id'] | App, - me?: { id: User['id'] } | null | undefined, - options?: { - detail?: boolean, - includeSecret?: boolean, - includeProfileImageIds?: boolean - } - ): Promise> { - const opts = Object.assign({ - detail: false, - includeSecret: false, - includeProfileImageIds: false, - }, options); - - const app = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - return { - id: app.id, - name: app.name, - callbackUrl: app.callbackUrl, - permission: app.permission, - ...(opts.includeSecret ? { secret: app.secret } : {}), - ...(me ? { - isAuthorized: await AccessTokens.countBy({ - appId: app.id, - userId: me.id, - }).then(count => count > 0), - } : {}), - }; - }, -}); diff --git a/packages/backend/src/models/repositories/auth-session.ts b/packages/backend/src/models/repositories/auth-session.ts deleted file mode 100644 index 3f1f6f489784..000000000000 --- a/packages/backend/src/models/repositories/auth-session.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { Apps } from '../index.js'; -import { AuthSession } from '@/models/entities/auth-session.js'; -import { awaitAll } from '@/prelude/await-all.js'; -import { User } from '@/models/entities/user.js'; - -export const AuthSessionRepository = db.getRepository(AuthSession).extend({ - async pack( - src: AuthSession['id'] | AuthSession, - me?: { id: User['id'] } | null | undefined - ) { - const session = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - return await awaitAll({ - id: session.id, - app: Apps.pack(session.appId, me), - token: session.token, - }); - }, -}); diff --git a/packages/backend/src/models/repositories/blocking.ts b/packages/backend/src/models/repositories/blocking.ts deleted file mode 100644 index 1d569fb87590..000000000000 --- a/packages/backend/src/models/repositories/blocking.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { Users } from '../index.js'; -import { Blocking } from '@/models/entities/blocking.js'; -import { awaitAll } from '@/prelude/await-all.js'; -import { Packed } from '@/misc/schema.js'; -import { User } from '@/models/entities/user.js'; - -export const BlockingRepository = db.getRepository(Blocking).extend({ - async pack( - src: Blocking['id'] | Blocking, - me?: { id: User['id'] } | null | undefined - ): Promise> { - const blocking = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - return await awaitAll({ - id: blocking.id, - createdAt: blocking.createdAt.toISOString(), - blockeeId: blocking.blockeeId, - blockee: Users.pack(blocking.blockeeId, me, { - detail: true, - }), - }); - }, - - packMany( - blockings: any[], - me: { id: User['id'] } - ) { - return Promise.all(blockings.map(x => this.pack(x, me))); - }, -}); diff --git a/packages/backend/src/models/repositories/channel.ts b/packages/backend/src/models/repositories/channel.ts deleted file mode 100644 index 213ac3671ab0..000000000000 --- a/packages/backend/src/models/repositories/channel.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { Channel } from '@/models/entities/channel.js'; -import { Packed } from '@/misc/schema.js'; -import { DriveFiles, ChannelFollowings, NoteUnreads } from '../index.js'; -import { User } from '@/models/entities/user.js'; - -export const ChannelRepository = db.getRepository(Channel).extend({ - async pack( - src: Channel['id'] | Channel, - me?: { id: User['id'] } | null | undefined, - ): Promise> { - const channel = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - const meId = me ? me.id : null; - - const banner = channel.bannerId ? await DriveFiles.findOneBy({ id: channel.bannerId }) : null; - - const hasUnreadNote = meId ? (await NoteUnreads.findOneBy({ noteChannelId: channel.id, userId: meId })) != null : undefined; - - const following = meId ? await ChannelFollowings.findOneBy({ - followerId: meId, - followeeId: channel.id, - }) : null; - - return { - id: channel.id, - createdAt: channel.createdAt.toISOString(), - lastNotedAt: channel.lastNotedAt ? channel.lastNotedAt.toISOString() : null, - name: channel.name, - description: channel.description, - userId: channel.userId, - bannerUrl: banner ? DriveFiles.getPublicUrl(banner, false) : null, - usersCount: channel.usersCount, - notesCount: channel.notesCount, - - ...(me ? { - isFollowing: following != null, - hasUnreadNote, - } : {}), - }; - }, -}); diff --git a/packages/backend/src/models/repositories/clip.ts b/packages/backend/src/models/repositories/clip.ts deleted file mode 100644 index b4a342905ef0..000000000000 --- a/packages/backend/src/models/repositories/clip.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { Clip } from '@/models/entities/clip.js'; -import { Packed } from '@/misc/schema.js'; -import { Users } from '../index.js'; -import { awaitAll } from '@/prelude/await-all.js'; - -export const ClipRepository = db.getRepository(Clip).extend({ - async pack( - src: Clip['id'] | Clip, - ): Promise> { - const clip = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - return await awaitAll({ - id: clip.id, - createdAt: clip.createdAt.toISOString(), - userId: clip.userId, - user: Users.pack(clip.user || clip.userId), - name: clip.name, - description: clip.description, - isPublic: clip.isPublic, - }); - }, - - packMany( - clips: Clip[], - ) { - return Promise.all(clips.map(x => this.pack(x))); - }, -}); - diff --git a/packages/backend/src/models/repositories/drive-folder.ts b/packages/backend/src/models/repositories/drive-folder.ts deleted file mode 100644 index ab5f3dab634b..000000000000 --- a/packages/backend/src/models/repositories/drive-folder.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { DriveFolders, DriveFiles } from '../index.js'; -import { DriveFolder } from '@/models/entities/drive-folder.js'; -import { awaitAll } from '@/prelude/await-all.js'; -import { Packed } from '@/misc/schema.js'; - -export const DriveFolderRepository = db.getRepository(DriveFolder).extend({ - async pack( - src: DriveFolder['id'] | DriveFolder, - options?: { - detail: boolean - } - ): Promise> { - const opts = Object.assign({ - detail: false, - }, options); - - const folder = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - return await awaitAll({ - id: folder.id, - createdAt: folder.createdAt.toISOString(), - name: folder.name, - parentId: folder.parentId, - - ...(opts.detail ? { - foldersCount: DriveFolders.countBy({ - parentId: folder.id, - }), - filesCount: DriveFiles.countBy({ - folderId: folder.id, - }), - - ...(folder.parentId ? { - parent: this.pack(folder.parentId, { - detail: true, - }), - } : {}), - } : {}), - }); - }, -}); diff --git a/packages/backend/src/models/repositories/emoji.ts b/packages/backend/src/models/repositories/emoji.ts deleted file mode 100644 index a0d390d793a8..000000000000 --- a/packages/backend/src/models/repositories/emoji.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { Emoji } from '@/models/entities/emoji.js'; -import { Packed } from '@/misc/schema.js'; - -export const EmojiRepository = db.getRepository(Emoji).extend({ - async pack( - src: Emoji['id'] | Emoji, - ): Promise> { - const emoji = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - return { - id: emoji.id, - aliases: emoji.aliases, - name: emoji.name, - category: emoji.category, - host: emoji.host, - // || emoji.originalUrl してるのは後方互換性のため - url: emoji.publicUrl || emoji.originalUrl, - }; - }, - - packMany( - emojis: any[], - ) { - return Promise.all(emojis.map(x => this.pack(x))); - }, -}); diff --git a/packages/backend/src/models/repositories/follow-request.ts b/packages/backend/src/models/repositories/follow-request.ts deleted file mode 100644 index c4a7203aa1e6..000000000000 --- a/packages/backend/src/models/repositories/follow-request.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { FollowRequest } from '@/models/entities/follow-request.js'; -import { Users } from '../index.js'; -import { User } from '@/models/entities/user.js'; - -export const FollowRequestRepository = db.getRepository(FollowRequest).extend({ - async pack( - src: FollowRequest['id'] | FollowRequest, - me?: { id: User['id'] } | null | undefined - ) { - const request = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - return { - id: request.id, - follower: await Users.pack(request.followerId, me), - followee: await Users.pack(request.followeeId, me), - }; - }, -}); diff --git a/packages/backend/src/models/repositories/gallery-like.ts b/packages/backend/src/models/repositories/gallery-like.ts deleted file mode 100644 index 08ca4962b8fa..000000000000 --- a/packages/backend/src/models/repositories/gallery-like.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { GalleryLike } from '@/models/entities/gallery-like.js'; -import { GalleryPosts } from '../index.js'; - -export const GalleryLikeRepository = db.getRepository(GalleryLike).extend({ - async pack( - src: GalleryLike['id'] | GalleryLike, - me?: any - ) { - const like = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - return { - id: like.id, - post: await GalleryPosts.pack(like.post || like.postId, me), - }; - }, - - packMany( - likes: any[], - me: any - ) { - return Promise.all(likes.map(x => this.pack(x, me))); - }, -}); diff --git a/packages/backend/src/models/repositories/gallery-post.ts b/packages/backend/src/models/repositories/gallery-post.ts deleted file mode 100644 index bb8d40b75ef9..000000000000 --- a/packages/backend/src/models/repositories/gallery-post.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { GalleryPost } from '@/models/entities/gallery-post.js'; -import { Packed } from '@/misc/schema.js'; -import { Users, DriveFiles, GalleryLikes } from '../index.js'; -import { awaitAll } from '@/prelude/await-all.js'; -import { User } from '@/models/entities/user.js'; - -export const GalleryPostRepository = db.getRepository(GalleryPost).extend({ - async pack( - src: GalleryPost['id'] | GalleryPost, - me?: { id: User['id'] } | null | undefined, - ): Promise> { - const meId = me ? me.id : null; - const post = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - return await awaitAll({ - id: post.id, - createdAt: post.createdAt.toISOString(), - updatedAt: post.updatedAt.toISOString(), - userId: post.userId, - user: Users.pack(post.user || post.userId, me), - title: post.title, - description: post.description, - fileIds: post.fileIds, - files: DriveFiles.packMany(post.fileIds), - tags: post.tags.length > 0 ? post.tags : undefined, - isSensitive: post.isSensitive, - likedCount: post.likedCount, - isLiked: meId ? await GalleryLikes.findOneBy({ postId: post.id, userId: meId }).then(x => x != null) : undefined, - }); - }, - - packMany( - posts: GalleryPost[], - me?: { id: User['id'] } | null | undefined, - ) { - return Promise.all(posts.map(x => this.pack(x, me))); - }, -}); diff --git a/packages/backend/src/models/repositories/hashtag.ts b/packages/backend/src/models/repositories/hashtag.ts deleted file mode 100644 index e6c0e36f00ba..000000000000 --- a/packages/backend/src/models/repositories/hashtag.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { Hashtag } from '@/models/entities/hashtag.js'; -import { Packed } from '@/misc/schema.js'; - -export const HashtagRepository = db.getRepository(Hashtag).extend({ - async pack( - src: Hashtag, - ): Promise> { - return { - tag: src.name, - mentionedUsersCount: src.mentionedUsersCount, - mentionedLocalUsersCount: src.mentionedLocalUsersCount, - mentionedRemoteUsersCount: src.mentionedRemoteUsersCount, - attachedUsersCount: src.attachedUsersCount, - attachedLocalUsersCount: src.attachedLocalUsersCount, - attachedRemoteUsersCount: src.attachedRemoteUsersCount, - }; - }, - - packMany( - hashtags: Hashtag[], - ) { - return Promise.all(hashtags.map(x => this.pack(x))); - }, -}); diff --git a/packages/backend/src/models/repositories/messaging-message.ts b/packages/backend/src/models/repositories/messaging-message.ts deleted file mode 100644 index 6c51c93ff788..000000000000 --- a/packages/backend/src/models/repositories/messaging-message.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { MessagingMessage } from '@/models/entities/messaging-message.js'; -import { Users, DriveFiles, UserGroups } from '../index.js'; -import { Packed } from '@/misc/schema.js'; -import { User } from '@/models/entities/user.js'; - -export const MessagingMessageRepository = db.getRepository(MessagingMessage).extend({ - async pack( - src: MessagingMessage['id'] | MessagingMessage, - me?: { id: User['id'] } | null | undefined, - options?: { - populateRecipient?: boolean, - populateGroup?: boolean, - } - ): Promise> { - const opts = options || { - populateRecipient: true, - populateGroup: true, - }; - - const message = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - return { - id: message.id, - createdAt: message.createdAt.toISOString(), - text: message.text, - userId: message.userId, - user: await Users.pack(message.user || message.userId, me), - recipientId: message.recipientId, - recipient: message.recipientId && opts.populateRecipient ? await Users.pack(message.recipient || message.recipientId, me) : undefined, - groupId: message.groupId, - group: message.groupId && opts.populateGroup ? await UserGroups.pack(message.group || message.groupId) : undefined, - fileId: message.fileId, - file: message.fileId ? await DriveFiles.pack(message.fileId) : null, - isRead: message.isRead, - reads: message.reads, - }; - }, -}); diff --git a/packages/backend/src/models/repositories/moderation-logs.ts b/packages/backend/src/models/repositories/moderation-logs.ts deleted file mode 100644 index 1488b1eabee2..000000000000 --- a/packages/backend/src/models/repositories/moderation-logs.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { Users } from '../index.js'; -import { ModerationLog } from '@/models/entities/moderation-log.js'; -import { awaitAll } from '@/prelude/await-all.js'; - -export const ModerationLogRepository = db.getRepository(ModerationLog).extend({ - async pack( - src: ModerationLog['id'] | ModerationLog, - ) { - const log = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - return await awaitAll({ - id: log.id, - createdAt: log.createdAt.toISOString(), - type: log.type, - info: log.info, - userId: log.userId, - user: Users.pack(log.user || log.userId, null, { - detail: true, - }), - }); - }, - - packMany( - reports: any[], - ) { - return Promise.all(reports.map(x => this.pack(x))); - }, -}); diff --git a/packages/backend/src/models/repositories/muting.ts b/packages/backend/src/models/repositories/muting.ts deleted file mode 100644 index 7891b10fb01e..000000000000 --- a/packages/backend/src/models/repositories/muting.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { Users } from '../index.js'; -import { Muting } from '@/models/entities/muting.js'; -import { awaitAll } from '@/prelude/await-all.js'; -import { Packed } from '@/misc/schema.js'; -import { User } from '@/models/entities/user.js'; - -export const MutingRepository = db.getRepository(Muting).extend({ - async pack( - src: Muting['id'] | Muting, - me?: { id: User['id'] } | null | undefined - ): Promise> { - const muting = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - return await awaitAll({ - id: muting.id, - createdAt: muting.createdAt.toISOString(), - expiresAt: muting.expiresAt ? muting.expiresAt.toISOString() : null, - muteeId: muting.muteeId, - mutee: Users.pack(muting.muteeId, me, { - detail: true, - }), - }); - }, - - packMany( - mutings: any[], - me: { id: User['id'] } - ) { - return Promise.all(mutings.map(x => this.pack(x, me))); - }, -}); diff --git a/packages/backend/src/models/repositories/note-favorite.ts b/packages/backend/src/models/repositories/note-favorite.ts deleted file mode 100644 index 9bd97f9880da..000000000000 --- a/packages/backend/src/models/repositories/note-favorite.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { NoteFavorite } from '@/models/entities/note-favorite.js'; -import { Notes } from '../index.js'; -import { User } from '@/models/entities/user.js'; - -export const NoteFavoriteRepository = db.getRepository(NoteFavorite).extend({ - async pack( - src: NoteFavorite['id'] | NoteFavorite, - me?: { id: User['id'] } | null | undefined - ) { - const favorite = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - return { - id: favorite.id, - createdAt: favorite.createdAt.toISOString(), - noteId: favorite.noteId, - note: await Notes.pack(favorite.note || favorite.noteId, me), - }; - }, - - packMany( - favorites: any[], - me: { id: User['id'] } - ) { - return Promise.all(favorites.map(x => this.pack(x, me))); - }, -}); diff --git a/packages/backend/src/models/repositories/note-reaction.ts b/packages/backend/src/models/repositories/note-reaction.ts deleted file mode 100644 index 4deae51c9319..000000000000 --- a/packages/backend/src/models/repositories/note-reaction.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { NoteReaction } from '@/models/entities/note-reaction.js'; -import { Notes, Users } from '../index.js'; -import { Packed } from '@/misc/schema.js'; -import { convertLegacyReaction } from '@/misc/reaction-lib.js'; -import { User } from '@/models/entities/user.js'; - -export const NoteReactionRepository = db.getRepository(NoteReaction).extend({ - async pack( - src: NoteReaction['id'] | NoteReaction, - me?: { id: User['id'] } | null | undefined, - options?: { - withNote: boolean; - }, - ): Promise> { - const opts = Object.assign({ - withNote: false, - }, options); - - const reaction = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - return { - id: reaction.id, - createdAt: reaction.createdAt.toISOString(), - user: await Users.pack(reaction.user ?? reaction.userId, me), - type: convertLegacyReaction(reaction.reaction), - ...(opts.withNote ? { - note: await Notes.pack(reaction.note ?? reaction.noteId, me), - } : {}), - }; - }, -}); diff --git a/packages/backend/src/models/repositories/page-like.ts b/packages/backend/src/models/repositories/page-like.ts deleted file mode 100644 index 87d6accc34fe..000000000000 --- a/packages/backend/src/models/repositories/page-like.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { PageLike } from '@/models/entities/page-like.js'; -import { Pages } from '../index.js'; -import { User } from '@/models/entities/user.js'; - -export const PageLikeRepository = db.getRepository(PageLike).extend({ - async pack( - src: PageLike['id'] | PageLike, - me?: { id: User['id'] } | null | undefined - ) { - const like = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - return { - id: like.id, - page: await Pages.pack(like.page || like.pageId, me), - }; - }, - - packMany( - likes: any[], - me: { id: User['id'] } - ) { - return Promise.all(likes.map(x => this.pack(x, me))); - }, -}); diff --git a/packages/backend/src/models/repositories/signin.ts b/packages/backend/src/models/repositories/signin.ts deleted file mode 100644 index 94410ec58a2c..000000000000 --- a/packages/backend/src/models/repositories/signin.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { Signin } from '@/models/entities/signin.js'; - -export const SigninRepository = db.getRepository(Signin).extend({ - async pack( - src: Signin, - ) { - return src; - }, -}); diff --git a/packages/backend/src/models/repositories/user-group-invitation.ts b/packages/backend/src/models/repositories/user-group-invitation.ts deleted file mode 100644 index 79ad019c996e..000000000000 --- a/packages/backend/src/models/repositories/user-group-invitation.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { UserGroupInvitation } from '@/models/entities/user-group-invitation.js'; -import { UserGroups } from '../index.js'; - -export const UserGroupInvitationRepository = db.getRepository(UserGroupInvitation).extend({ - async pack( - src: UserGroupInvitation['id'] | UserGroupInvitation, - ) { - const invitation = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - return { - id: invitation.id, - group: await UserGroups.pack(invitation.userGroup || invitation.userGroupId), - }; - }, - - packMany( - invitations: any[], - ) { - return Promise.all(invitations.map(x => this.pack(x))); - }, -}); diff --git a/packages/backend/src/models/repositories/user-group.ts b/packages/backend/src/models/repositories/user-group.ts deleted file mode 100644 index 6eb9234244e7..000000000000 --- a/packages/backend/src/models/repositories/user-group.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { UserGroup } from '@/models/entities/user-group.js'; -import { UserGroupJoinings } from '../index.js'; -import { Packed } from '@/misc/schema.js'; - -export const UserGroupRepository = db.getRepository(UserGroup).extend({ - async pack( - src: UserGroup['id'] | UserGroup, - ): Promise> { - const userGroup = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - const users = await UserGroupJoinings.findBy({ - userGroupId: userGroup.id, - }); - - return { - id: userGroup.id, - createdAt: userGroup.createdAt.toISOString(), - name: userGroup.name, - ownerId: userGroup.userId, - userIds: users.map(x => x.userId), - }; - }, -}); diff --git a/packages/backend/src/models/repositories/user-list.ts b/packages/backend/src/models/repositories/user-list.ts deleted file mode 100644 index 2b6f411ef62b..000000000000 --- a/packages/backend/src/models/repositories/user-list.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { db } from '@/db/postgre.js'; -import { UserList } from '@/models/entities/user-list.js'; -import { UserListJoinings } from '../index.js'; -import { Packed } from '@/misc/schema.js'; - -export const UserListRepository = db.getRepository(UserList).extend({ - async pack( - src: UserList['id'] | UserList, - ): Promise> { - const userList = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); - - const users = await UserListJoinings.findBy({ - userListId: userList.id, - }); - - return { - id: userList.id, - createdAt: userList.createdAt.toISOString(), - name: userList.name, - userIds: users.map(x => x.userId), - }; - }, -}); diff --git a/packages/backend/src/services/entities/AntennaEntityService.ts b/packages/backend/src/services/entities/AntennaEntityService.ts new file mode 100644 index 000000000000..4f613cf0e67b --- /dev/null +++ b/packages/backend/src/services/entities/AntennaEntityService.ts @@ -0,0 +1,47 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { AntennaNotes, Antennas, UserGroupJoinings } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { Antenna } from '@/models/entities/antenna.js'; + +@Injectable() +export class AntennaEntityService { + constructor( + @Inject('antennaRepository') + private antennaRepository: typeof Antennas, + + @Inject('antennaNotesRepository') + private antennaNotesRepository: typeof AntennaNotes, + + @Inject('userGroupJoiningsRepository') + private userGroupJoiningsRepository: typeof UserGroupJoinings, + ) { + } + + public async pack( + src: Antenna['id'] | Antenna, + ): Promise> { + const antenna = typeof src === 'object' ? src : await this.antennaRepository.findOneByOrFail({ id: src }); + + const hasUnreadNote = (await this.antennaNotesRepository.findOneBy({ antennaId: antenna.id, read: false })) != null; + const userGroupJoining = antenna.userGroupJoiningId ? await this.userGroupJoiningsRepository.findOneBy({ id: antenna.userGroupJoiningId }) : null; + + return { + id: antenna.id, + createdAt: antenna.createdAt.toISOString(), + name: antenna.name, + keywords: antenna.keywords, + excludeKeywords: antenna.excludeKeywords, + src: antenna.src, + userListId: antenna.userListId, + userGroupId: userGroupJoining ? userGroupJoining.userGroupId : null, + users: antenna.users, + caseSensitive: antenna.caseSensitive, + notify: antenna.notify, + withReplies: antenna.withReplies, + withFile: antenna.withFile, + hasUnreadNote, + }; + } +} diff --git a/packages/backend/src/services/entities/AppEntityService.ts b/packages/backend/src/services/entities/AppEntityService.ts new file mode 100644 index 000000000000..0d468bb5181a --- /dev/null +++ b/packages/backend/src/services/entities/AppEntityService.ts @@ -0,0 +1,52 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { AccessTokens, Apps } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { App } from '@/models/entities/app.js'; +import type { User } from '@/models/entities/user.js'; +import { UserEntityService } from './UserEntityService.js'; + +@Injectable() +export class AppEntityService { + constructor( + @Inject('appsRepository') + private appsRepository: typeof Apps, + + @Inject('accessTokensRepository') + private accessTokensRepository: typeof AccessTokens, + ) { + } + + public async pack( + src: App['id'] | App, + me?: { id: User['id'] } | null | undefined, + options?: { + detail?: boolean, + includeSecret?: boolean, + includeProfileImageIds?: boolean + }, + ): Promise> { + const opts = Object.assign({ + detail: false, + includeSecret: false, + includeProfileImageIds: false, + }, options); + + const app = typeof src === 'object' ? src : await this.appsRepository.findOneByOrFail({ id: src }); + + return { + id: app.id, + name: app.name, + callbackUrl: app.callbackUrl, + permission: app.permission, + ...(opts.includeSecret ? { secret: app.secret } : {}), + ...(me ? { + isAuthorized: await this.accessTokensRepository.countBy({ + appId: app.id, + userId: me.id, + }).then(count => count > 0), + } : {}), + }; + } +} diff --git a/packages/backend/src/services/entities/AuthSessionEntityService.ts b/packages/backend/src/services/entities/AuthSessionEntityService.ts new file mode 100644 index 000000000000..438205658f4c --- /dev/null +++ b/packages/backend/src/services/entities/AuthSessionEntityService.ts @@ -0,0 +1,34 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { Apps } from '@/models/index.js'; +import type { AuthSessions } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { AuthSession } from '@/models/entities/auth-session.js'; +import type { User } from '@/models/entities/user.js'; +import { UserEntityService } from './UserEntityService.js'; +import { AppEntityService } from './AppEntityService.js'; + +@Injectable() +export class AuthSessionEntityService { + constructor( + @Inject('authSessionsRepository') + private authSessionsRepository: typeof AuthSessions, + + private appEntityService: AppEntityService, + ) { + } + + public async pack( + src: AuthSession['id'] | AuthSession, + me?: { id: User['id'] } | null | undefined, + ) { + const session = typeof src === 'object' ? src : await this.authSessionsRepository.findOneByOrFail({ id: src }); + + return await awaitAll({ + id: session.id, + app: this.appEntityService.pack(session.appId, me), + token: session.token, + }); + } +} diff --git a/packages/backend/src/services/entities/BlockingEntityService.ts b/packages/backend/src/services/entities/BlockingEntityService.ts new file mode 100644 index 000000000000..c53956e239e8 --- /dev/null +++ b/packages/backend/src/services/entities/BlockingEntityService.ts @@ -0,0 +1,42 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Blockings } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { Blocking } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import { UserEntityService } from './UserEntityService.js'; + +@Injectable() +export class BlockingEntityService { + constructor( + @Inject('blockingsRepository') + private blockingsRepository: typeof Blockings, + + private userEntityService: UserEntityService, + ) { + } + + public async pack( + src: Blocking['id'] | Blocking, + me?: { id: User['id'] } | null | undefined, + ): Promise> { + const blocking = typeof src === 'object' ? src : await this.blockingsRepository.findOneByOrFail({ id: src }); + + return await awaitAll({ + id: blocking.id, + createdAt: blocking.createdAt.toISOString(), + blockeeId: blocking.blockeeId, + blockee: this.userEntityService.pack(blocking.blockeeId, me, { + detail: true, + }), + }); + } + + public packMany( + blockings: any[], + me: { id: User['id'] }, + ) { + return Promise.all(blockings.map(x => this.pack(x, me))); + } +} diff --git a/packages/backend/src/services/entities/ChannelEntityService.ts b/packages/backend/src/services/entities/ChannelEntityService.ts new file mode 100644 index 000000000000..e7a763905ae9 --- /dev/null +++ b/packages/backend/src/services/entities/ChannelEntityService.ts @@ -0,0 +1,66 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { ChannelFollowings, Channels, DriveFiles, NoteUnreads } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import type { Channel } from '@/models/entities/channel.js'; +import { UserEntityService } from './UserEntityService.js'; +import { DriveFileEntityService } from './DriveFileEntityService.js'; + +@Injectable() +export class ChannelEntityService { + constructor( + @Inject('channelsRepository') + private channelsRepository: typeof Channels, + + @Inject('channelFollowingsRepository') + private channelFollowingsRepository: typeof ChannelFollowings, + + @Inject('noteUnreadsRepository') + private noteUnreadsRepository: typeof NoteUnreads, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private userEntityService: UserEntityService, + private driveFileEntityService: DriveFileEntityService, + ) { + } + + public async pack( + src: Channel['id'] | Channel, + me?: { id: User['id'] } | null | undefined, + ): Promise> { + const channel = typeof src === 'object' ? src : await this.channelsRepository.findOneByOrFail({ id: src }); + const meId = me ? me.id : null; + + const banner = channel.bannerId ? await this.driveFilesRepository.findOneBy({ id: channel.bannerId }) : null; + + const hasUnreadNote = meId ? (await this.noteUnreadsRepository.findOneBy({ noteChannelId: channel.id, userId: meId })) != null : undefined; + + const following = meId ? await this.channelFollowingsRepository.findOneBy({ + followerId: meId, + followeeId: channel.id, + }) : null; + + return { + id: channel.id, + createdAt: channel.createdAt.toISOString(), + lastNotedAt: channel.lastNotedAt ? channel.lastNotedAt.toISOString() : null, + name: channel.name, + description: channel.description, + userId: channel.userId, + bannerUrl: banner ? this.driveFileEntityService.getPublicUrl(banner, false) : null, + usersCount: channel.usersCount, + notesCount: channel.notesCount, + + ...(me ? { + isFollowing: following != null, + hasUnreadNote, + } : {}), + }; + } +} + diff --git a/packages/backend/src/services/entities/ClipEntityService.ts b/packages/backend/src/services/entities/ClipEntityService.ts new file mode 100644 index 000000000000..a69a88df8e74 --- /dev/null +++ b/packages/backend/src/services/entities/ClipEntityService.ts @@ -0,0 +1,43 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Clips } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import type { Clip } from '@/models/entities/clip.js'; +import { UserEntityService } from './UserEntityService.js'; + +@Injectable() +export class ClipEntityService { + constructor( + @Inject('clipsRepository') + private clipsRepository: typeof Clips, + + private userEntityService: UserEntityService, + ) { + } + + public async pack( + src: Clip['id'] | Clip, + ): Promise> { + const clip = typeof src === 'object' ? src : await this.clipsRepository.findOneByOrFail({ id: src }); + + return await awaitAll({ + id: clip.id, + createdAt: clip.createdAt.toISOString(), + userId: clip.userId, + user: this.userEntityService.pack(clip.user || clip.userId), + name: clip.name, + description: clip.description, + isPublic: clip.isPublic, + }); + } + + public packMany( + clips: Clip[], + ) { + return Promise.all(clips.map(x => this.pack(x))); + } +} + diff --git a/packages/backend/src/services/entities/DriveFileEntityService.ts b/packages/backend/src/services/entities/DriveFileEntityService.ts index b95103b277e4..9d13a429c9ac 100644 --- a/packages/backend/src/services/entities/DriveFileEntityService.ts +++ b/packages/backend/src/services/entities/DriveFileEntityService.ts @@ -11,6 +11,7 @@ import type { DriveFile } from '@/models/entities/drive-file.js'; import { appendQuery, query } from '@/prelude/url.js'; import { toPuny } from '@/misc/convert-host.js'; import { UserEntityService } from './UserEntityService.js'; +import { DriveFolderEntityService } from './DriveFolderEntityService.js'; type PackOptions = { detail?: boolean, @@ -36,6 +37,8 @@ export class DriveFileEntityService { // 循環参照のため / for circular dependency @Inject(forwardRef(() => UserEntityService)) private userEntityService: UserEntityService, + + private driveFolderEntityService: DriveFolderEntityService, ) { } @@ -158,7 +161,7 @@ export class DriveFileEntityService { thumbnailUrl: this.getPublicUrl(file, true), comment: file.comment, folderId: file.folderId, - folder: opts.detail && file.folderId ? DriveFolders.pack(file.folderId, { + folder: opts.detail && file.folderId ? this.driveFolderEntityService.pack(file.folderId, { detail: true, }) : null, userId: opts.withUser ? file.userId : null, @@ -192,7 +195,7 @@ export class DriveFileEntityService { thumbnailUrl: this.getPublicUrl(file, true), comment: file.comment, folderId: file.folderId, - folder: opts.detail && file.folderId ? DriveFolders.pack(file.folderId, { + folder: opts.detail && file.folderId ? this.driveFolderEntityService.pack(file.folderId, { detail: true, }) : null, userId: opts.withUser ? file.userId : null, diff --git a/packages/backend/src/services/entities/DriveFolderEntityService.ts b/packages/backend/src/services/entities/DriveFolderEntityService.ts new file mode 100644 index 000000000000..d3244270d81d --- /dev/null +++ b/packages/backend/src/services/entities/DriveFolderEntityService.ts @@ -0,0 +1,59 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { DriveFiles, DriveFolders } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import type { DriveFolder } from '@/models/entities/drive-folder.js'; +import { UserEntityService } from './UserEntityService.js'; + +@Injectable() +export class DriveFolderEntityService { + constructor( + @Inject('driveFoldersRepository') + private driveFoldersRepository: typeof DriveFolders, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private userEntityService: UserEntityService, + ) { + } + + public async pack( + src: DriveFolder['id'] | DriveFolder, + options?: { + detail: boolean + }, + ): Promise> { + const opts = Object.assign({ + detail: false, + }, options); + + const folder = typeof src === 'object' ? src : await this.driveFoldersRepository.findOneByOrFail({ id: src }); + + return await awaitAll({ + id: folder.id, + createdAt: folder.createdAt.toISOString(), + name: folder.name, + parentId: folder.parentId, + + ...(opts.detail ? { + foldersCount: this.driveFoldersRepository.countBy({ + parentId: folder.id, + }), + filesCount: this.driveFilesRepository.countBy({ + folderId: folder.id, + }), + + ...(folder.parentId ? { + parent: this.pack(folder.parentId, { + detail: true, + }), + } : {}), + } : {}), + }); + } +} + diff --git a/packages/backend/src/services/entities/EmojiEntityService.ts b/packages/backend/src/services/entities/EmojiEntityService.ts new file mode 100644 index 000000000000..90b3ee40b4df --- /dev/null +++ b/packages/backend/src/services/entities/EmojiEntityService.ts @@ -0,0 +1,43 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Emojis } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import type { Emoji } from '@/models/entities/emoji.js'; +import { UserEntityService } from './UserEntityService.js'; + +@Injectable() +export class EmojiEntityService { + constructor( + @Inject('emojisRepository') + private emojisRepository: typeof Emojis, + + private userEntityService: UserEntityService, + ) { + } + + public async pack( + src: Emoji['id'] | Emoji, + ): Promise> { + const emoji = typeof src === 'object' ? src : await this.emojisRepository.findOneByOrFail({ id: src }); + + return { + id: emoji.id, + aliases: emoji.aliases, + name: emoji.name, + category: emoji.category, + host: emoji.host, + // || emoji.originalUrl してるのは後方互換性のため + url: emoji.publicUrl || emoji.originalUrl, + }; + } + + public packMany( + emojis: any[], + ) { + return Promise.all(emojis.map(x => this.pack(x))); + } +} + diff --git a/packages/backend/src/services/entities/FollowRequestEntityService.ts b/packages/backend/src/services/entities/FollowRequestEntityService.ts new file mode 100644 index 000000000000..12d4a310cc1a --- /dev/null +++ b/packages/backend/src/services/entities/FollowRequestEntityService.ts @@ -0,0 +1,34 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { FollowRequests } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import type { FollowRequest } from '@/models/entities/follow-request.js'; +import { UserEntityService } from './UserEntityService.js'; + +@Injectable() +export class FollowRequestEntityService { + constructor( + @Inject('followRequestsRepository') + private followRequestsRepository: typeof FollowRequests, + + private userEntityService: UserEntityService, + ) { + } + + public async pack( + src: FollowRequest['id'] | FollowRequest, + me?: { id: User['id'] } | null | undefined, + ) { + const request = typeof src === 'object' ? src : await this.followRequestsRepository.findOneByOrFail({ id: src }); + + return { + id: request.id, + follower: await this.userEntityService.pack(request.followerId, me), + followee: await this.userEntityService.pack(request.followeeId, me), + }; + } +} + diff --git a/packages/backend/src/models/repositories/following.ts b/packages/backend/src/services/entities/FollowingEntityService.ts similarity index 52% rename from packages/backend/src/models/repositories/following.ts rename to packages/backend/src/services/entities/FollowingEntityService.ts index 46109244fa3d..774deecd16e3 100644 --- a/packages/backend/src/models/repositories/following.ts +++ b/packages/backend/src/services/entities/FollowingEntityService.ts @@ -1,9 +1,12 @@ -import { db } from '@/db/postgre.js'; -import { Users } from '../index.js'; -import { Following } from '@/models/entities/following.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Followings } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; -import { Packed } from '@/misc/schema.js'; -import { User } from '@/models/entities/user.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import type { Following } from '@/models/entities/following.js'; +import { UserEntityService } from './UserEntityService.js'; type LocalFollowerFollowing = Following & { followerHost: null; @@ -29,32 +32,41 @@ type RemoteFolloweeFollowing = Following & { followeeSharedInbox: string; }; -export const FollowingRepository = db.getRepository(Following).extend({ - isLocalFollower(following: Following): following is LocalFollowerFollowing { +@Injectable() +export class FollowingEntityService { + constructor( + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + private userEntityService: UserEntityService, + ) { + } + + public isLocalFollower(following: Following): following is LocalFollowerFollowing { return following.followerHost == null; - }, + } - isRemoteFollower(following: Following): following is RemoteFollowerFollowing { + public isRemoteFollower(following: Following): following is RemoteFollowerFollowing { return following.followerHost != null; - }, + } - isLocalFollowee(following: Following): following is LocalFolloweeFollowing { + public isLocalFollowee(following: Following): following is LocalFolloweeFollowing { return following.followeeHost == null; - }, + } - isRemoteFollowee(following: Following): following is RemoteFolloweeFollowing { + public isRemoteFollowee(following: Following): following is RemoteFolloweeFollowing { return following.followeeHost != null; - }, + } - async pack( + public async pack( src: Following['id'] | Following, me?: { id: User['id'] } | null | undefined, opts?: { populateFollowee?: boolean; populateFollower?: boolean; - } + }, ): Promise> { - const following = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); + const following = typeof src === 'object' ? src : await this.followingsRepository.findOneByOrFail({ id: src }); if (opts == null) opts = {}; @@ -63,23 +75,24 @@ export const FollowingRepository = db.getRepository(Following).extend({ createdAt: following.createdAt.toISOString(), followeeId: following.followeeId, followerId: following.followerId, - followee: opts.populateFollowee ? Users.pack(following.followee || following.followeeId, me, { + followee: opts.populateFollowee ? this.userEntityService.pack(following.followee || following.followeeId, me, { detail: true, }) : undefined, - follower: opts.populateFollower ? Users.pack(following.follower || following.followerId, me, { + follower: opts.populateFollower ? this.userEntityService.pack(following.follower || following.followerId, me, { detail: true, }) : undefined, }); - }, + } - packMany( + public packMany( followings: any[], me?: { id: User['id'] } | null | undefined, opts?: { populateFollowee?: boolean; populateFollower?: boolean; - } + }, ) { return Promise.all(followings.map(x => this.pack(x, me, opts))); - }, -}); + } +} + diff --git a/packages/backend/src/services/entities/GalleryLikeEntityService.ts b/packages/backend/src/services/entities/GalleryLikeEntityService.ts new file mode 100644 index 000000000000..66e94c094473 --- /dev/null +++ b/packages/backend/src/services/entities/GalleryLikeEntityService.ts @@ -0,0 +1,42 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { GalleryPosts } from '@/models/index.js'; +import type { GalleryLikes } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import type { GalleryLike } from '@/models/entities/gallery-like.js'; +import { UserEntityService } from './UserEntityService.js'; +import { GalleryPostEntityService } from './GalleryPostEntityService.js'; + +@Injectable() +export class GalleryLikeEntityService { + constructor( + @Inject('galleryLikesRepository') + private galleryLikesRepository: typeof GalleryLikes, + + private galleryPostEntityService: GalleryPostEntityService, + ) { + } + + public async pack( + src: GalleryLike['id'] | GalleryLike, + me?: any, + ) { + const like = typeof src === 'object' ? src : await this.galleryLikesRepository.findOneByOrFail({ id: src }); + + return { + id: like.id, + post: await this.galleryPostEntityService.pack(like.post || like.postId, me), + }; + } + + public packMany( + likes: any[], + me: any, + ) { + return Promise.all(likes.map(x => this.pack(x, me))); + } +} + diff --git a/packages/backend/src/services/entities/GalleryPostEntityService.ts b/packages/backend/src/services/entities/GalleryPostEntityService.ts new file mode 100644 index 000000000000..bc0e8731f32b --- /dev/null +++ b/packages/backend/src/services/entities/GalleryPostEntityService.ts @@ -0,0 +1,57 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { GalleryLikes, GalleryPosts } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import type { GalleryPost } from '@/models/entities/gallery-post.js'; +import { UserEntityService } from './UserEntityService.js'; +import { DriveFileEntityService } from './DriveFileEntityService.js'; + +@Injectable() +export class GalleryPostEntityService { + constructor( + @Inject('galleryPostsRepository') + private galleryPostsRepository: typeof GalleryPosts, + + @Inject('galleryLikesRepository') + private galleryLikesRepository: typeof GalleryLikes, + + private userEntityService: UserEntityService, + private driveFileEntityService: DriveFileEntityService, + ) { + } + + public async pack( + src: GalleryPost['id'] | GalleryPost, + me?: { id: User['id'] } | null | undefined, + ): Promise> { + const meId = me ? me.id : null; + const post = typeof src === 'object' ? src : await this.galleryPostsRepository.findOneByOrFail({ id: src }); + + return await awaitAll({ + id: post.id, + createdAt: post.createdAt.toISOString(), + updatedAt: post.updatedAt.toISOString(), + userId: post.userId, + user: this.userEntityService.pack(post.user || post.userId, me), + title: post.title, + description: post.description, + fileIds: post.fileIds, + files: this.driveFileEntityService.packMany(post.fileIds), + tags: post.tags.length > 0 ? post.tags : undefined, + isSensitive: post.isSensitive, + likedCount: post.likedCount, + isLiked: meId ? await this.galleryLikesRepository.findOneBy({ postId: post.id, userId: meId }).then(x => x != null) : undefined, + }); + } + + public packMany( + posts: GalleryPost[], + me?: { id: User['id'] } | null | undefined, + ) { + return Promise.all(posts.map(x => this.pack(x, me))); + } +} + diff --git a/packages/backend/src/services/entities/HashtagEntityService.ts b/packages/backend/src/services/entities/HashtagEntityService.ts new file mode 100644 index 000000000000..5d2756a6b295 --- /dev/null +++ b/packages/backend/src/services/entities/HashtagEntityService.ts @@ -0,0 +1,41 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Hashtags } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import type { Hashtag } from '@/models/entities/hashtag.js'; +import { UserEntityService } from './UserEntityService.js'; + +@Injectable() +export class HashtagEntityService { + constructor( + @Inject('hashtagsRepository') + private hashtagsRepository: typeof Hashtags, + + private userEntityService: UserEntityService, + ) { + } + + public async pack( + src: Hashtag, + ): Promise> { + return { + tag: src.name, + mentionedUsersCount: src.mentionedUsersCount, + mentionedLocalUsersCount: src.mentionedLocalUsersCount, + mentionedRemoteUsersCount: src.mentionedRemoteUsersCount, + attachedUsersCount: src.attachedUsersCount, + attachedLocalUsersCount: src.attachedLocalUsersCount, + attachedRemoteUsersCount: src.attachedRemoteUsersCount, + }; + } + + public packMany( + hashtags: Hashtag[], + ) { + return Promise.all(hashtags.map(x => this.pack(x))); + } +} + diff --git a/packages/backend/src/models/repositories/instance.ts b/packages/backend/src/services/entities/InstanceEntityService.ts similarity index 59% rename from packages/backend/src/models/repositories/instance.ts rename to packages/backend/src/services/entities/InstanceEntityService.ts index 5f0fd8d58210..73e4c249613b 100644 --- a/packages/backend/src/models/repositories/instance.ts +++ b/packages/backend/src/services/entities/InstanceEntityService.ts @@ -1,13 +1,28 @@ -import { db } from '@/db/postgre.js'; -import { Instance } from '@/models/entities/instance.js'; -import { Packed } from '@/misc/schema.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Instances } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import type { Instance } from '@/models/entities/instance.js'; +import { MetaService } from '../MetaService.js'; +import { UserEntityService } from './UserEntityService.js'; -export const InstanceRepository = db.getRepository(Instance).extend({ - async pack( +@Injectable() +export class InstanceEntityService { + constructor( + @Inject('instancesRepository') + private instancesRepository: typeof Instances, + + private metaService: MetaService, + ) { + } + + public async pack( instance: Instance, ): Promise> { - const meta = await fetchMeta(); + const meta = await this.metaService.fetch(); return { id: instance.id, caughtAt: instance.caughtAt.toISOString(), @@ -33,11 +48,12 @@ export const InstanceRepository = db.getRepository(Instance).extend({ themeColor: instance.themeColor, infoUpdatedAt: instance.infoUpdatedAt ? instance.infoUpdatedAt.toISOString() : null, }; - }, + } - packMany( + public packMany( instances: Instance[], ) { return Promise.all(instances.map(x => this.pack(x))); - }, -}); + } +} + diff --git a/packages/backend/src/services/entities/MessagingMessageEntityService.ts b/packages/backend/src/services/entities/MessagingMessageEntityService.ts new file mode 100644 index 000000000000..7354ba42e067 --- /dev/null +++ b/packages/backend/src/services/entities/MessagingMessageEntityService.ts @@ -0,0 +1,55 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { MessagingMessages } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import type { MessagingMessage } from '@/models/entities/messaging-message.js'; +import { UserEntityService } from './UserEntityService.js'; +import { DriveFileEntityService } from './DriveFileEntityService.js'; + +@Injectable() +export class MessagingMessageEntityService { + constructor( + @Inject('messagingMessagesRepository') + private messagingMessagesRepository: typeof MessagingMessages, + + private userEntityService: UserEntityService, + private driveFileEntityService: DriveFileEntityService, + ) { + } + + public async pack( + src: MessagingMessage['id'] | MessagingMessage, + me?: { id: User['id'] } | null | undefined, + options?: { + populateRecipient?: boolean, + populateGroup?: boolean, + }, + ): Promise> { + const opts = options || { + populateRecipient: true, + populateGroup: true, + }; + + const message = typeof src === 'object' ? src : await this.messagingMessagesRepository.findOneByOrFail({ id: src }); + + return { + id: message.id, + createdAt: message.createdAt.toISOString(), + text: message.text, + userId: message.userId, + user: await this.userEntityService.pack(message.user || message.userId, me), + recipientId: message.recipientId, + recipient: message.recipientId && opts.populateRecipient ? await this.userEntityService.pack(message.recipient || message.recipientId, me) : undefined, + groupId: message.groupId, + group: message.groupId && opts.populateGroup ? await this.userGroupEntityService.pack(message.group || message.groupId) : undefined, + fileId: message.fileId, + file: message.fileId ? await this.driveFileEntityService.pack(message.fileId) : null, + isRead: message.isRead, + reads: message.reads, + }; + } +} + diff --git a/packages/backend/src/services/entities/ModerationLogEntityService.ts b/packages/backend/src/services/entities/ModerationLogEntityService.ts new file mode 100644 index 000000000000..89483ddb8489 --- /dev/null +++ b/packages/backend/src/services/entities/ModerationLogEntityService.ts @@ -0,0 +1,44 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { ModerationLogs } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import type { ModerationLog } from '@/models/entities/moderation-log.js'; +import { UserEntityService } from './UserEntityService.js'; + +@Injectable() +export class ModerationLogEntityService { + constructor( + @Inject('moderationLogsRepository') + private moderationLogsRepository: typeof ModerationLogs, + + private userEntityService: UserEntityService, + ) { + } + + public async pack( + src: ModerationLog['id'] | ModerationLog, + ) { + const log = typeof src === 'object' ? src : await this.moderationLogsRepository.findOneByOrFail({ id: src }); + + return await awaitAll({ + id: log.id, + createdAt: log.createdAt.toISOString(), + type: log.type, + info: log.info, + userId: log.userId, + user: this.userEntityService.pack(log.user || log.userId, null, { + detail: true, + }), + }); + } + + public packMany( + reports: any[], + ) { + return Promise.all(reports.map(x => this.pack(x))); + } +} + diff --git a/packages/backend/src/services/entities/MutingEntityService.ts b/packages/backend/src/services/entities/MutingEntityService.ts new file mode 100644 index 000000000000..6773e913b844 --- /dev/null +++ b/packages/backend/src/services/entities/MutingEntityService.ts @@ -0,0 +1,45 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Mutings } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import type { Muting } from '@/models/entities/muting.js'; +import { UserEntityService } from './UserEntityService.js'; + +@Injectable() +export class MutingEntityService { + constructor( + @Inject('mutingsRepository') + private mutingsRepository: typeof Mutings, + + private userEntityService: UserEntityService, + ) { + } + + public async pack( + src: Muting['id'] | Muting, + me?: { id: User['id'] } | null | undefined, + ): Promise> { + const muting = typeof src === 'object' ? src : await this.mutingsRepository.findOneByOrFail({ id: src }); + + return await awaitAll({ + id: muting.id, + createdAt: muting.createdAt.toISOString(), + expiresAt: muting.expiresAt ? muting.expiresAt.toISOString() : null, + muteeId: muting.muteeId, + mutee: this.userEntityService.pack(muting.muteeId, me, { + detail: true, + }), + }); + } + + public packMany( + mutings: any[], + me: { id: User['id'] }, + ) { + return Promise.all(mutings.map(x => this.pack(x, me))); + } +} + diff --git a/packages/backend/src/services/entities/NoteEntityService.ts b/packages/backend/src/services/entities/NoteEntityService.ts index 5696891eecee..2f950efe6ca5 100644 --- a/packages/backend/src/services/entities/NoteEntityService.ts +++ b/packages/backend/src/services/entities/NoteEntityService.ts @@ -8,11 +8,12 @@ import type { Packed } from '@/misc/schema.js'; import { nyaize } from '@/misc/nyaize.js'; import { awaitAll } from '@/prelude/await-all.js'; import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '@/misc/reaction-lib.js'; -import type { User } from '@/models/entities/user'; -import type { Note } from '@/models/entities/note'; -import type { NoteReaction } from '@/models/entities/note-reaction'; -import { aggregateNoteEmojis, populateEmojis, prefetchEmojis } from '@/misc/populate-emojis'; +import type { User } from '@/models/entities/user.js'; +import type { Note } from '@/models/entities/note.js'; +import type { NoteReaction } from '@/models/entities/note-reaction.js'; +import { aggregateNoteEmojis, populateEmojis, prefetchEmojis } from '@/misc/populate-emojis.js'; import { UserEntityService } from './UserEntityService.js'; +import { DriveFileEntityService } from './DriveFileEntityService.js'; @Injectable() export class NoteEntityService { @@ -47,6 +48,8 @@ export class NoteEntityService { // 循環参照のため / for circular dependency @Inject(forwardRef(() => UserEntityService)) private userEntityService: UserEntityService, + + private driveFileEntityService: DriveFileEntityService, ) { } diff --git a/packages/backend/src/services/entities/NoteFavoriteEntityService.ts b/packages/backend/src/services/entities/NoteFavoriteEntityService.ts new file mode 100644 index 000000000000..9f39a3b810ed --- /dev/null +++ b/packages/backend/src/services/entities/NoteFavoriteEntityService.ts @@ -0,0 +1,42 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { NoteFavorites } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import type { NoteFavorite } from '@/models/entities/note-favorite.js'; +import { UserEntityService } from './UserEntityService.js'; +import { NoteEntityService } from './NoteEntityService.js'; + +@Injectable() +export class NoteFavoriteEntityService { + constructor( + @Inject('noteFavoritesRepository') + private noteFavoritesRepository: typeof NoteFavorites, + + private noteEntityService: NoteEntityService, + ) { + } + + public async pack( + src: NoteFavorite['id'] | NoteFavorite, + me?: { id: User['id'] } | null | undefined, + ) { + const favorite = typeof src === 'object' ? src : await this.noteFavoritesRepository.findOneByOrFail({ id: src }); + + return { + id: favorite.id, + createdAt: favorite.createdAt.toISOString(), + noteId: favorite.noteId, + note: await this.noteEntityService.pack(favorite.note || favorite.noteId, me), + }; + } + + public packMany( + favorites: any[], + me: { id: User['id'] }, + ) { + return Promise.all(favorites.map(x => this.pack(x, me))); + } +} diff --git a/packages/backend/src/services/entities/NoteReactionEntityService.ts b/packages/backend/src/services/entities/NoteReactionEntityService.ts new file mode 100644 index 000000000000..5a0220d53ed6 --- /dev/null +++ b/packages/backend/src/services/entities/NoteReactionEntityService.ts @@ -0,0 +1,47 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { NoteReactions } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import type { NoteReaction } from '@/models/entities/note-reaction.js'; +import { convertLegacyReaction } from '@/misc/reaction-lib.js'; +import { UserEntityService } from './UserEntityService.js'; +import { NoteEntityService } from './NoteEntityService.js'; + +@Injectable() +export class NoteReactionEntityService { + constructor( + @Inject('noteReactionsRepository') + private noteReactionsRepository: typeof NoteReactions, + + private userEntityService: UserEntityService, + private noteEntityService: NoteEntityService, + ) { + } + + public async pack( + src: NoteReaction['id'] | NoteReaction, + me?: { id: User['id'] } | null | undefined, + options?: { + withNote: boolean; + }, + ): Promise> { + const opts = Object.assign({ + withNote: false, + }, options); + + const reaction = typeof src === 'object' ? src : await this.noteReactionsRepository.findOneByOrFail({ id: src }); + + return { + id: reaction.id, + createdAt: reaction.createdAt.toISOString(), + user: await this.userEntityService.pack(reaction.user ?? reaction.userId, me), + type: convertLegacyReaction(reaction.reaction), + ...(opts.withNote ? { + note: await this.noteEntityService.pack(reaction.note ?? reaction.noteId, me), + } : {}), + }; + } +} diff --git a/packages/backend/src/models/repositories/page.ts b/packages/backend/src/services/entities/PageEntityService.ts similarity index 56% rename from packages/backend/src/models/repositories/page.ts rename to packages/backend/src/services/entities/PageEntityService.ts index 092b26b3964b..2d8c08833d5e 100644 --- a/packages/backend/src/models/repositories/page.ts +++ b/packages/backend/src/services/entities/PageEntityService.ts @@ -1,24 +1,43 @@ -import { db } from '@/db/postgre.js'; -import { Page } from '@/models/entities/page.js'; -import { Packed } from '@/misc/schema.js'; +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DriveFiles } from '@/models/index.js'; +import type { Pages , PageLikes } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; -import { User } from '@/models/entities/user.js'; -import { Users, DriveFiles, PageLikes } from '../index.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import type { Page } from '@/models/entities/page.js'; +import type { DriveFile } from '@/models/entities/drive-file.js'; +import { UserEntityService } from './UserEntityService.js'; -export const PageRepository = db.getRepository(Page).extend({ - async pack( +@Injectable() +export class PageEntityService { + constructor( + @Inject('pagesRepository') + private pagesRepository: typeof Pages, + + @Inject('pageLikesRepository') + private pageLikesRepository: typeof PageLikes, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private userEntityService: UserEntityService, + ) { + } + + public async pack( src: Page['id'] | Page, me?: { id: User['id'] } | null | undefined, ): Promise> { const meId = me ? me.id : null; - const page = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src }); + const page = typeof src === 'object' ? src : await this.pagesRepository.findOneByOrFail({ id: src }); const attachedFiles: Promise[] = []; const collectFile = (xs: any[]) => { for (const x of xs) { if (x.type === 'image') { - attachedFiles.push(DriveFiles.findOneBy({ + attachedFiles.push(this.driveFilesRepository.findOneBy({ id: x.fileId, userId: page.userId, })); @@ -51,7 +70,7 @@ export const PageRepository = db.getRepository(Page).extend({ }; migrate(page.content); if (migrated) { - this.update(page.id, { + this.pagesRepository.update(page.id, { content: page.content, }); } @@ -61,7 +80,7 @@ export const PageRepository = db.getRepository(Page).extend({ createdAt: page.createdAt.toISOString(), updatedAt: page.updatedAt.toISOString(), userId: page.userId, - user: Users.pack(page.user || page.userId, me), // { detail: true } すると無限ループするので注意 + user: this.userEntityService.pack(page.user || page.userId, me), // { detail: true } すると無限ループするので注意 content: page.content, variables: page.variables, title: page.title, @@ -75,14 +94,15 @@ export const PageRepository = db.getRepository(Page).extend({ eyeCatchingImage: page.eyeCatchingImageId ? await DriveFiles.pack(page.eyeCatchingImageId) : null, attachedFiles: DriveFiles.packMany((await Promise.all(attachedFiles)).filter((x): x is DriveFile => x != null)), likedCount: page.likedCount, - isLiked: meId ? await PageLikes.findOneBy({ pageId: page.id, userId: meId }).then(x => x != null) : undefined, + isLiked: meId ? await this.pageLikesRepository.findOneBy({ pageId: page.id, userId: meId }).then(x => x != null) : undefined, }); - }, + } - packMany( + public packMany( pages: Page[], me?: { id: User['id'] } | null | undefined, ) { return Promise.all(pages.map(x => this.pack(x, me))); - }, -}); + } +} + diff --git a/packages/backend/src/services/entities/PageLikeEntityService.ts b/packages/backend/src/services/entities/PageLikeEntityService.ts new file mode 100644 index 000000000000..d4a54c18eac9 --- /dev/null +++ b/packages/backend/src/services/entities/PageLikeEntityService.ts @@ -0,0 +1,41 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { PageLikes } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import type { PageLike } from '@/models/entities/page-like.js'; +import { UserEntityService } from './UserEntityService.js'; +import { PageEntityService } from './PageEntityService.js'; + +@Injectable() +export class PageLikeEntityService { + constructor( + @Inject('pageLikesRepository') + private pageLikesRepository: typeof PageLikes, + + private pageEntityService: PageEntityService, + ) { + } + + public async pack( + src: PageLike['id'] | PageLike, + me?: { id: User['id'] } | null | undefined, + ) { + const like = typeof src === 'object' ? src : await this.pageLikesRepository.findOneByOrFail({ id: src }); + + return { + id: like.id, + page: await this.pageEntityService.pack(like.page || like.pageId, me), + }; + } + + public packMany( + likes: any[], + me: { id: User['id'] }, + ) { + return Promise.all(likes.map(x => this.pack(x, me))); + } +} + diff --git a/packages/backend/src/services/entities/SigninEntityService.ts b/packages/backend/src/services/entities/SigninEntityService.ts new file mode 100644 index 000000000000..7f52e0e9332b --- /dev/null +++ b/packages/backend/src/services/entities/SigninEntityService.ts @@ -0,0 +1,27 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Signins } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import type { Signin } from '@/models/entities/signin.js'; +import { UserEntityService } from './UserEntityService.js'; + +@Injectable() +export class SigninEntityService { + constructor( + @Inject('signinRepository') + private signinRepository: typeof Signins, + + private userEntityService: UserEntityService, + ) { + } + + public async pack( + src: Signin, + ) { + return src; + } +} + diff --git a/packages/backend/src/services/entities/UserGroupEntityService.ts b/packages/backend/src/services/entities/UserGroupEntityService.ts new file mode 100644 index 000000000000..78c9b9985930 --- /dev/null +++ b/packages/backend/src/services/entities/UserGroupEntityService.ts @@ -0,0 +1,42 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { UserGroupJoinings, UserGroups } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import type { UserGroup } from '@/models/entities/user-group.js'; +import { UserEntityService } from './UserEntityService.js'; + +@Injectable() +export class UserGroupEntityService { + constructor( + @Inject('userGroupsRepository') + private userGroupsRepository: typeof UserGroups, + + @Inject('userGroupJoiningsRepository') + private userGroupJoiningsRepository: typeof UserGroupJoinings, + + private userEntityService: UserEntityService, + ) { + } + + public async pack( + src: UserGroup['id'] | UserGroup, + ): Promise> { + const userGroup = typeof src === 'object' ? src : await this.userGroupsRepository.findOneByOrFail({ id: src }); + + const users = await this.userGroupJoiningsRepository.findBy({ + userGroupId: userGroup.id, + }); + + return { + id: userGroup.id, + createdAt: userGroup.createdAt.toISOString(), + name: userGroup.name, + ownerId: userGroup.userId, + userIds: users.map(x => x.userId), + }; + } +} + diff --git a/packages/backend/src/services/entities/UserGroupInvitationEntityService.ts b/packages/backend/src/services/entities/UserGroupInvitationEntityService.ts new file mode 100644 index 000000000000..a8c144790237 --- /dev/null +++ b/packages/backend/src/services/entities/UserGroupInvitationEntityService.ts @@ -0,0 +1,39 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { UserGroupInvitations } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import type { UserGroupInvitation } from '@/models/entities/user-group-invitation.js'; +import { UserEntityService } from './UserEntityService.js'; +import { UserGroupEntityService } from './UserGroupEntityService.js'; + +@Injectable() +export class UserGroupInvitationEntityService { + constructor( + @Inject('userGroupInvitationsRepository') + private userGroupInvitationsRepository: typeof UserGroupInvitations, + + private userGroupEntityService: UserGroupEntityService, + ) { + } + + public async pack( + src: UserGroupInvitation['id'] | UserGroupInvitation, + ) { + const invitation = typeof src === 'object' ? src : await this.userGroupInvitationsRepository.findOneByOrFail({ id: src }); + + return { + id: invitation.id, + group: await this.userGroupEntityService.pack(invitation.userGroup || invitation.userGroupId), + }; + } + + public packMany( + invitations: any[], + ) { + return Promise.all(invitations.map(x => this.pack(x))); + } +} + diff --git a/packages/backend/src/services/entities/UserListEntityService.ts b/packages/backend/src/services/entities/UserListEntityService.ts new file mode 100644 index 000000000000..ba5ce69c652e --- /dev/null +++ b/packages/backend/src/services/entities/UserListEntityService.ts @@ -0,0 +1,41 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { UserListJoinings, UserLists } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { } from '@/models/entities/blocking.js'; +import type { User } from '@/models/entities/user.js'; +import type { UserList } from '@/models/entities/user-list.js'; +import { UserEntityService } from './UserEntityService.js'; + +@Injectable() +export class UserListEntityService { + constructor( + @Inject('userListsRepository') + private userListsRepository: typeof UserLists, + + @Inject('userListJoiningsRepository') + private userListJoiningsRepository: typeof UserListJoinings, + + private userEntityService: UserEntityService, + ) { + } + + public async pack( + src: UserList['id'] | UserList, + ): Promise> { + const userList = typeof src === 'object' ? src : await this.userListsRepository.findOneByOrFail({ id: src }); + + const users = await this.userListJoiningsRepository.findBy({ + userListId: userList.id, + }); + + return { + id: userList.id, + createdAt: userList.createdAt.toISOString(), + name: userList.name, + userIds: users.map(x => x.userId), + }; + } +} + From e722f1d9e6c813863942fc01aae42f823f140762 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 05:42:22 +0900 Subject: [PATCH 103/180] Update index.ts --- packages/backend/src/models/index.ts | 123 +++++++++++++-------------- 1 file changed, 61 insertions(+), 62 deletions(-) diff --git a/packages/backend/src/models/index.ts b/packages/backend/src/models/index.ts index 3f73269318f6..e6ae6892337b 100644 --- a/packages/backend/src/models/index.ts +++ b/packages/backend/src/models/index.ts @@ -12,50 +12,50 @@ import { NoteWatching } from './entities/note-watching.js'; import { NoteThreadMuting } from './entities/note-thread-muting.js'; import { NoteUnread } from './entities/note-unread.js'; import { RegistrationTicket } from './entities/registration-tickets.js'; -import { UserRepository } from './repositories/user.js'; -import { NoteRepository } from './repositories/note.js'; -import { DriveFileRepository } from './repositories/drive-file.js'; -import { DriveFolderRepository } from './repositories/drive-folder.js'; +import { User } from './entities/user.js'; +import { Note } from './entities/note.js'; +import { DriveFile } from './entities/drive-file.js'; +import { DriveFolder } from './entities/drive-folder.js'; import { AccessToken } from './entities/access-token.js'; import { UserNotePining } from './entities/user-note-pining.js'; -import { SigninRepository } from './repositories/signin.js'; -import { MessagingMessageRepository } from './repositories/messaging-message.js'; -import { UserListRepository } from './repositories/user-list.js'; +import { Signin } from './entities/signin.js'; +import { MessagingMessage } from './entities/messaging-message.js'; +import { UserList } from './entities/user-list.js'; import { UserListJoining } from './entities/user-list-joining.js'; -import { UserGroupRepository } from './repositories/user-group.js'; +import { UserGroup } from './entities/user-group.js'; import { UserGroupJoining } from './entities/user-group-joining.js'; -import { UserGroupInvitationRepository } from './repositories/user-group-invitation.js'; -import { FollowRequestRepository } from './repositories/follow-request.js'; -import { MutingRepository } from './repositories/muting.js'; -import { BlockingRepository } from './repositories/blocking.js'; -import { NoteReactionRepository } from './repositories/note-reaction.js'; -import { NotificationRepository } from './repositories/notification.js'; -import { NoteFavoriteRepository } from './repositories/note-favorite.js'; +import { UserGroupInvitation } from './entities/user-group-invitation.js'; +import { FollowRequest } from './entities/follow-request.js'; +import { Muting } from './entities/muting.js'; +import { Blocking } from './entities/blocking.js'; +import { NoteReaction } from './entities/note-reaction.js'; +import { Notification } from './entities/notification.js'; +import { NoteFavorite } from './entities/note-favorite.js'; import { UserPublickey } from './entities/user-publickey.js'; import { UserKeypair } from './entities/user-keypair.js'; -import { AppRepository } from './repositories/app.js'; -import { FollowingRepository } from './repositories/following.js'; -import { AbuseUserReportRepository } from './repositories/abuse-user-report.js'; -import { AuthSessionRepository } from './repositories/auth-session.js'; +import { App } from './entities/app.js'; +import { Following } from './entities/following.js'; +import { AbuseUserReport } from './entities/abuse-user-report.js'; +import { AuthSession } from './entities/auth-session.js'; import { UserProfile } from './entities/user-profile.js'; import { AttestationChallenge } from './entities/attestation-challenge.js'; import { UserSecurityKey } from './entities/user-security-key.js'; -import { HashtagRepository } from './repositories/hashtag.js'; -import { PageRepository } from './repositories/page.js'; -import { PageLikeRepository } from './repositories/page-like.js'; -import { GalleryPostRepository } from './repositories/gallery-post.js'; -import { GalleryLikeRepository } from './repositories/gallery-like.js'; -import { ModerationLogRepository } from './repositories/moderation-logs.js'; +import { Hashtag } from './entities/hashtag.js'; +import { Page } from './entities/page.js'; +import { PageLike } from './entities/page-like.js'; +import { GalleryPost } from './entities/gallery-post.js'; +import { GalleryLike } from './entities/gallery-like.js'; +import { ModerationLog } from './entities/moderation-log.js'; import { UsedUsername } from './entities/used-username.js'; -import { ClipRepository } from './repositories/clip.js'; +import { Clip } from './entities/clip.js'; import { ClipNote } from './entities/clip-note.js'; -import { AntennaRepository } from './repositories/antenna.js'; +import { Antenna } from './entities/antenna.js'; import { AntennaNote } from './entities/antenna-note.js'; import { PromoNote } from './entities/promo-note.js'; import { PromoRead } from './entities/promo-read.js'; -import { EmojiRepository } from './repositories/emoji.js'; -import { RelayRepository } from './repositories/relay.js'; -import { ChannelRepository } from './repositories/channel.js'; +import { Emoji } from './entities/emoji.js'; +import { Relay } from './entities/relay.js'; +import { Channel } from './entities/channel.js'; import { MutedNote } from './entities/muted-note.js'; import { ChannelFollowing } from './entities/channel-following.js'; import { ChannelNotePining } from './entities/channel-note-pining.js'; @@ -63,68 +63,67 @@ import { RegistryItem } from './entities/registry-item.js'; import { Ad } from './entities/ad.js'; import { PasswordResetRequest } from './entities/password-reset-request.js'; import { UserPending } from './entities/user-pending.js'; -import { InstanceRepository } from './repositories/instance.js'; import { Webhook } from './entities/webhook.js'; import { UserIp } from './entities/user-ip.js'; export const Announcements = db.getRepository(Announcement); export const AnnouncementReads = db.getRepository(AnnouncementRead); -export const Apps = (AppRepository); -export const Notes = (NoteRepository); -export const NoteFavorites = (NoteFavoriteRepository); +export const Apps = db.getRepository(App); +export const Notes = db.getRepository(Note); +export const NoteFavorites = db.getRepository(NoteFavorite); export const NoteWatchings = db.getRepository(NoteWatching); export const NoteThreadMutings = db.getRepository(NoteThreadMuting); -export const NoteReactions = (NoteReactionRepository); +export const NoteReactions = db.getRepository(NoteReaction); export const NoteUnreads = db.getRepository(NoteUnread); export const Polls = db.getRepository(Poll); export const PollVotes = db.getRepository(PollVote); -export const Users = (UserRepository); +export const Users = db.getRepository(User); export const UserProfiles = db.getRepository(UserProfile); export const UserKeypairs = db.getRepository(UserKeypair); export const UserPendings = db.getRepository(UserPending); export const AttestationChallenges = db.getRepository(AttestationChallenge); export const UserSecurityKeys = db.getRepository(UserSecurityKey); export const UserPublickeys = db.getRepository(UserPublickey); -export const UserLists = (UserListRepository); +export const UserLists = db.getRepository(UserList); export const UserListJoinings = db.getRepository(UserListJoining); -export const UserGroups = (UserGroupRepository); +export const UserGroups = db.getRepository(UserGroup); export const UserGroupJoinings = db.getRepository(UserGroupJoining); -export const UserGroupInvitations = (UserGroupInvitationRepository); +export const UserGroupInvitations = db.getRepository(UserGroupInvitation); export const UserNotePinings = db.getRepository(UserNotePining); export const UserIps = db.getRepository(UserIp); export const UsedUsernames = db.getRepository(UsedUsername); -export const Followings = (FollowingRepository); -export const FollowRequests = (FollowRequestRepository); -export const Instances = (InstanceRepository); -export const Emojis = (EmojiRepository); -export const DriveFiles = (DriveFileRepository); -export const DriveFolders = (DriveFolderRepository); -export const Notifications = (NotificationRepository); +export const Followings = db.getRepository(Following); +export const FollowRequests = db.getRepository(FollowRequest); +export const Instances = db.getRepository(Instance); +export const Emojis = db.getRepository(Emoji); +export const DriveFiles = db.getRepository(DriveFile); +export const DriveFolders = db.getRepository(DriveFolder); +export const Notifications = db.getRepository(Notification); export const Metas = db.getRepository(Meta); -export const Mutings = (MutingRepository); -export const Blockings = (BlockingRepository); +export const Mutings = db.getRepository(Muting); +export const Blockings = db.getRepository(Blocking); export const SwSubscriptions = db.getRepository(SwSubscription); -export const Hashtags = (HashtagRepository); -export const AbuseUserReports = (AbuseUserReportRepository); +export const Hashtags = db.getRepository(Hashtag); +export const AbuseUserReports = db.getRepository(AbuseUserReport); export const RegistrationTickets = db.getRepository(RegistrationTicket); -export const AuthSessions = (AuthSessionRepository); +export const AuthSessions = db.getRepository(AuthSession); export const AccessTokens = db.getRepository(AccessToken); -export const Signins = (SigninRepository); -export const MessagingMessages = (MessagingMessageRepository); -export const Pages = (PageRepository); -export const PageLikes = (PageLikeRepository); -export const GalleryPosts = (GalleryPostRepository); -export const GalleryLikes = (GalleryLikeRepository); -export const ModerationLogs = (ModerationLogRepository); -export const Clips = (ClipRepository); +export const Signins = db.getRepository(Signin); +export const MessagingMessages = db.getRepository(MessagingMessage); +export const Pages = db.getRepository(Page); +export const PageLikes = db.getRepository(PageLike); +export const GalleryPosts = db.getRepository(GalleryPost); +export const GalleryLikes = db.getRepository(GalleryLike); +export const ModerationLogs = db.getRepository(ModerationLog); +export const Clips = db.getRepository(Clip); export const ClipNotes = db.getRepository(ClipNote); -export const Antennas = (AntennaRepository); +export const Antennas = db.getRepository(Antenna); export const AntennaNotes = db.getRepository(AntennaNote); export const PromoNotes = db.getRepository(PromoNote); export const PromoReads = db.getRepository(PromoRead); -export const Relays = (RelayRepository); +export const Relays = db.getRepository(Relay); export const MutedNotes = db.getRepository(MutedNote); -export const Channels = (ChannelRepository); +export const Channels = db.getRepository(Channel); export const ChannelFollowings = db.getRepository(ChannelFollowing); export const ChannelNotePinings = db.getRepository(ChannelNotePining); export const RegistryItems = db.getRepository(RegistryItem); From d30d2935fcc600a2356062e08cca2b1d052a04d1 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 05:51:26 +0900 Subject: [PATCH 104/180] wip --- .../src/server/NodeinfoServerService.ts | 8 +++-- packages/backend/src/server/ServerService.ts | 4 ++- .../src/server/api/ApiServerService.ts | 7 +++-- .../backend/src/server/api/SigninService.ts | 2 ++ .../src/server/api/SignupApiService.ts | 8 +++-- .../api/endpoints/admin/accounts/create.ts | 2 ++ .../server/api/endpoints/admin/emoji/add.ts | 9 +++--- .../server/api/endpoints/admin/emoji/copy.ts | 2 ++ .../server/api/endpoints/antennas/create.ts | 2 ++ .../src/server/api/endpoints/antennas/list.ts | 7 +++-- .../src/server/api/endpoints/antennas/show.ts | 3 ++ .../server/api/endpoints/antennas/update.ts | 4 ++- .../src/server/api/endpoints/ap/show.ts | 4 +++ .../backend/src/server/api/endpoints/meta.ts | 29 ++++++++++++------- .../src/server/api/endpoints/page-push.ts | 17 ++++++----- 15 files changed, 74 insertions(+), 34 deletions(-) diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index 7f94d96518fa..b80e12dc7509 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -2,12 +2,13 @@ import { Inject, Injectable } from '@nestjs/common'; import Router from '@koa/router'; import { IsNull, MoreThan } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Notes } from '@/models/index.js'; -import { Users } from '@/models/index.js'; +import type { Notes , Users } from '@/models/index.js'; + import { Config } from '@/config/types.js'; import { MetaService } from '@/services/MetaService.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const'; import { Cache } from '@/misc/cache.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; const nodeinfo2_1path = '/nodeinfo/2.1'; const nodeinfo2_0path = '/nodeinfo/2.0'; @@ -24,6 +25,7 @@ export class NodeinfoServerService { @Inject('notesRepository') private notesRepository: typeof Notes, + private userEntityService: UserEntityService, private metaService: MetaService, ) { } @@ -57,7 +59,7 @@ export class NodeinfoServerService { this.notesRepository.count({ where: { userHost: IsNull() } }), ]); - const proxyAccount = meta.proxyAccountId ? await Users.pack(meta.proxyAccountId).catch(() => null) : null; + const proxyAccount = meta.proxyAccountId ? await this.userEntityService.pack(meta.proxyAccountId).catch(() => null) : null; return { software: { diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 1bef68aecb88..2848a95c1af3 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -17,6 +17,7 @@ import { envOption } from '@/env.js'; import * as Acct from '@/misc/acct.js'; import { genIdenticon } from '@/misc/gen-identicon.js'; import { createTemp } from '@/misc/create-temp.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { ActivityPubServerService } from './ActivityPubServerService.js'; import { NodeinfoServerService } from './NodeinfoServerService.js'; import { ApiServerService } from './api/ApiServerService.js'; @@ -40,6 +41,7 @@ export class ServerService { @Inject('userProfilesRepository') private userProfilesRepository: typeof UserProfiles, + private userEntityService: UserEntityService, private apiServerService: ApiServerService, private streamingApiServerService: StreamingApiServerService, private activityPubServerService: ActivityPubServerService, @@ -104,7 +106,7 @@ export class ServerService { }); if (user) { - ctx.redirect(this.usersRepository.getAvatarUrlSync(user)); + ctx.redirect(this.userEntityService.getAvatarUrlSync(user)); } else { ctx.redirect('/static-assets/user-unknown.png'); } diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts index 836918d7088f..6312b6832b7e 100644 --- a/packages/backend/src/server/api/ApiServerService.ts +++ b/packages/backend/src/server/api/ApiServerService.ts @@ -6,8 +6,10 @@ import bodyParser from 'koa-bodyparser'; import cors from '@koa/cors'; import { ModuleRef } from '@nestjs/core'; import { Config } from '@/config/types.js'; -import { Users , Instances, AccessTokens } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { Instances, AccessTokens } from '@/models/index.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; import endpoints from './endpoints.js'; import discord from './service/discord.js'; import github from './service/github.js'; @@ -27,6 +29,7 @@ export class ApiServerService { @Inject('usersRepository') private usersRepository: typeof Users, + private userEntityService: UserEntityService, private apiCallService: ApiCallService, private signupApiServiceService: SignupApiService, private signinApiServiceService: SigninApiService, @@ -127,7 +130,7 @@ export class ApiServerService { ctx.body = { ok: true, token: token.token, - user: await Users.pack(token.userId, null, { detail: true }), + user: await this.userEntityService.pack(token.userId, null, { detail: true }), }; } else { ctx.body = { diff --git a/packages/backend/src/server/api/SigninService.ts b/packages/backend/src/server/api/SigninService.ts index 20ed6447f535..0e5f3b29246c 100644 --- a/packages/backend/src/server/api/SigninService.ts +++ b/packages/backend/src/server/api/SigninService.ts @@ -5,6 +5,7 @@ import { Config } from '@/config/types.js'; import { IdService } from '@/services/IdService.js'; import type { ILocalUser } from '@/models/entities/user.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { SigninEntityService } from '@/services/entities/SigninEntityService.js'; import type Koa from 'koa'; @Injectable() @@ -16,6 +17,7 @@ export class SigninService { @Inject('signinsRepository') private signinsRepository: typeof Signins, + private signinEntityService: SigninEntityService, private idService: IdService, private globalEventService: GlobalEventService, ) { diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts index bd8f0f284182..790b12ae6b95 100644 --- a/packages/backend/src/server/api/SignupApiService.ts +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -2,13 +2,14 @@ import { Inject, Injectable } from '@nestjs/common'; import rndstr from 'rndstr'; import bcrypt from 'bcryptjs'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { RegistrationTickets , UserPendings, UserProfiles } from '@/models/index.js'; -import { Users } from '@/models/index.js'; +import type { RegistrationTickets , UserPendings, UserProfiles , Users } from '@/models/index.js'; + import { Config } from '@/config/types.js'; import { MetaService } from '@/services/MetaService.js'; import { CaptchaService } from '@/services/CaptchaService.js'; import { IdService } from '@/services/IdService.js'; import { SignupService } from '@/services/SignupService.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { SigninService } from '../SigninService.js'; import type Koa from 'koa'; @@ -30,6 +31,7 @@ export class SignupApiService { @Inject('registrationTicketsRepository') private registrationTicketsRepository: typeof RegistrationTickets, + private userEntityService: UserEntityService, private idService: IdService, private metaService: MetaService, private captchaService: CaptchaService, @@ -125,7 +127,7 @@ export class SignupApiService { username, password, host, }); - const res = await Users.pack(account, account, { + const res = await this.userEntityService.pack(account, account, { detail: true, includeSecrets: true, }); diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts index d09d84bebcf4..e9e640d071ed 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts @@ -3,6 +3,7 @@ import { IsNull } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Users } from '@/models/index.js'; import { SignupService } from '@/services/SignupService.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; export const meta = { tags: ['admin'], @@ -36,6 +37,7 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, + private userEntityService: UserEntityService, private signupService: SignupService, ) { super(meta, paramDef, async (ps, _me) => { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index e688081e5516..4cfeabaec992 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -2,12 +2,12 @@ import { Inject, Injectable } from '@nestjs/common'; import rndstr from 'rndstr'; import { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFiles } from '@/models/index.js'; -import { Emojis } from '@/models/index.js'; +import type { DriveFiles , Emojis } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ModerationLogService } from '@/services/ModerationLogService.js'; +import { EmojiEntityService } from '@/services/entities/EmojiEntityService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -48,6 +48,7 @@ export default class extends Endpoint { @Inject('emojisRepository') private emojisRepository: typeof Emojis, + private emojiEntityService: EmojiEntityService, private idService: IdService, private globalEventService: GlobalEventService, private moderationLogService: ModerationLogService, @@ -69,12 +70,12 @@ export default class extends Endpoint { originalUrl: file.url, publicUrl: file.webpublicUrl ?? file.url, type: file.webpublicType ?? file.type, - }).then(x => Emojis.findOneByOrFail(x.identifiers[0])); + }).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0])); await this.db.queryResultCache!.remove(['meta_emojis']); this.globalEventService.publishBroadcastStream('emojiAdded', { - emoji: await Emojis.pack(emoji.id), + emoji: await this.emojiEntityService.pack(emoji.id), }); this.moderationLogService.insertModerationLog(me, 'addEmoji', { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index 1a194240f9f1..1d335d063065 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -7,6 +7,7 @@ import type { DriveFile } from '@/models/entities/drive-file.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import { DriveService } from '@/services/DriveService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { EmojiEntityService } from '@/services/entities/EmojiEntityService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -56,6 +57,7 @@ export default class extends Endpoint { @Inject('emojisRepository') private emojisRepository: typeof Emojis, + private emojiEntityService: EmojiEntityService, private idService: IdService, private globalEventService: GlobalEventService, private driveService: DriveService, diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index cb89c127feaa..e0b90946bbab 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/services/IdService.js'; import type { UserLists, UserGroupJoinings , Antennas } from '@/models/index.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { AntennaEntityService } from '@/services/entities/AntennaEntityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -74,6 +75,7 @@ export default class extends Endpoint { @Inject('userGroupJoiningsRepository') private userGroupJoiningsRepository: typeof UserGroupJoinings, + private antennaEntityService: AntennaEntityService, private idService: IdService, private globalEventService: GlobalEventService, ) { diff --git a/packages/backend/src/server/api/endpoints/antennas/list.ts b/packages/backend/src/server/api/endpoints/antennas/list.ts index eb6d964cc517..6b6cd874f2f6 100644 --- a/packages/backend/src/server/api/endpoints/antennas/list.ts +++ b/packages/backend/src/server/api/endpoints/antennas/list.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Antennas } from '@/models/index.js'; +import type { Antennas } from '@/models/index.js'; +import { AntennaEntityService } from '@/services/entities/AntennaEntityService.js'; export const meta = { tags: ['antennas', 'account'], @@ -32,13 +33,15 @@ export default class extends Endpoint { constructor( @Inject('antennasRepository') private antennasRepository: typeof Antennas, + + private antennaEntityService: AntennaEntityService, ) { super(meta, paramDef, async (ps, me) => { const antennas = await this.antennasRepository.findBy({ userId: me.id, }); - return await Promise.all(antennas.map(x => Antennas.pack(x))); + return await Promise.all(antennas.map(x => this.antennaEntityService.pack(x))); }); } } diff --git a/packages/backend/src/server/api/endpoints/antennas/show.ts b/packages/backend/src/server/api/endpoints/antennas/show.ts index 65b3eea31ec3..6e3d0b6ad921 100644 --- a/packages/backend/src/server/api/endpoints/antennas/show.ts +++ b/packages/backend/src/server/api/endpoints/antennas/show.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Antennas } from '@/models/index.js'; +import { AntennaEntityService } from '@/services/entities/AntennaEntityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -39,6 +40,8 @@ export default class extends Endpoint { constructor( @Inject('antennasRepository') private antennasRepository: typeof Antennas, + + private antennaEntityService: AntennaEntityService, ) { super(meta, paramDef, async (ps, me) => { // Fetch the antenna diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts index 44776ab17116..153db8684413 100644 --- a/packages/backend/src/server/api/endpoints/antennas/update.ts +++ b/packages/backend/src/server/api/endpoints/antennas/update.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Antennas , UserLists, UserGroupJoinings } from '@/models/index.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { AntennaEntityService } from '@/services/entities/AntennaEntityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -79,7 +80,8 @@ export default class extends Endpoint { @Inject('userGroupJoiningsRepository') private userGroupJoiningsRepository: typeof UserGroupJoinings, - + + private antennaEntityService: AntennaEntityService, private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index 85636c7fae0c..61b2072b1307 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -12,6 +12,8 @@ import { ApDbResolverService } from '@/services/remote/activitypub/ApDbResolverS import { MetaService } from '@/services/MetaService.js'; import { ApPersonService } from '@/services/remote/activitypub/models/ApPersonService.js'; import { ApNoteService } from '@/services/remote/activitypub/models/ApNoteService.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -87,6 +89,8 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + private userEntityService: UserEntityService, + private noteEntityService: NoteEntityService, private metaService: MetaService, private apResolverService: ApResolverService, private apDbResolverService: ApDbResolverService, diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index ddbf2514a53a..0aacc9b6c90f 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -1,11 +1,15 @@ import { IsNull, MoreThan } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import config from '@/config/index.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import { Ads, Emojis, Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { Ads, Emojis } from '@/models/index.js'; import { DB_MAX_NOTE_TEXT_LENGTH } from '@/misc/hard-limits.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { EmojiEntityService } from '@/services/entities/EmojiEntityService.js'; +import { MetaService } from '@/services/MetaService.js'; +import { Config } from '@/config/types.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; export const meta = { tags: ['meta'], @@ -27,7 +31,6 @@ export const meta = { version: { type: 'string', optional: false, nullable: false, - example: config.version, }, name: { type: 'string', @@ -308,14 +311,18 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private userEntityService: UserEntityService, + private emojiEntityService: EmojiEntityService, + private metaService: MetaService, ) { super(meta, paramDef, async (ps, me) => { - const instance = await fetchMeta(true); + const instance = await this.metaService.fetch(true); const emojis = await Emojis.find({ where: { @@ -341,10 +348,10 @@ export default class extends Endpoint { maintainerName: instance.maintainerName, maintainerEmail: instance.maintainerEmail, - version: config.version, + version: this.config.version, name: instance.name, - uri: config.url, + uri: this.config.url, description: instance.description, langs: instance.langs, tosUrl: instance.ToSUrl, @@ -369,7 +376,7 @@ export default class extends Endpoint { backgroundImageUrl: instance.backgroundImageUrl, logoImageUrl: instance.logoImageUrl, maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, // 後方互換性のため - emojis: await Emojis.packMany(emojis), + emojis: await this.emojiEntityService.packMany(emojis), defaultLightTheme: instance.defaultLightTheme, defaultDarkTheme: instance.defaultDarkTheme, ads: ads.map(ad => ({ @@ -408,7 +415,7 @@ export default class extends Endpoint { localTimeLine: !instance.disableLocalTimeline, globalTimeLine: !instance.disableGlobalTimeline, emailRequiredForSignup: instance.emailRequiredForSignup, - elasticsearch: config.elasticsearch ? true : false, + elasticsearch: this.config.elasticsearch ? true : false, hcaptcha: instance.enableHcaptcha, recaptcha: instance.enableRecaptcha, objectStorage: instance.useObjectStorage, diff --git a/packages/backend/src/server/api/endpoints/page-push.ts b/packages/backend/src/server/api/endpoints/page-push.ts index a784d5d7bf22..2aa872897867 100644 --- a/packages/backend/src/server/api/endpoints/page-push.ts +++ b/packages/backend/src/server/api/endpoints/page-push.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; -import { publishMainStream } from '@/services/stream.js'; -import type { Users } from '@/models/index.js'; -import { Pages } from '@/models/index.js'; +import type { Users , Pages } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../error.js'; export const meta = { @@ -32,16 +32,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('pagesRepository') + private pagesRepository: typeof Pages, + + private userEntityService: UserEntityService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { - const page = await Pages.findOneBy({ id: ps.pageId }); + const page = await this.pagesRepository.findOneBy({ id: ps.pageId }); if (page == null) { throw new ApiError(meta.errors.noSuchPage); } - publishMainStream(page.userId, 'pageEvent', { + this.globalEventService.publishMainStream(page.userId, 'pageEvent', { pageId: ps.pageId, event: ps.event, var: ps.var, From c618070126ba1433d77bc54942353e7d4206d39b Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 06:06:33 +0900 Subject: [PATCH 105/180] wip --- .../src/server/api/endpoints/app/create.ts | 2 ++ .../src/server/api/endpoints/app/show.ts | 3 +++ .../server/api/endpoints/auth/session/show.ts | 3 +++ .../api/endpoints/auth/session/userkey.ts | 3 +++ .../server/api/endpoints/blocking/create.ts | 10 +++++++-- .../server/api/endpoints/blocking/delete.ts | 15 ++++++++----- .../server/api/endpoints/channels/create.ts | 20 ++++++++++++------ .../server/api/endpoints/channels/featured.ts | 18 ++++++++-------- .../server/api/endpoints/channels/followed.ts | 14 ++++++------- .../server/api/endpoints/channels/owned.ts | 15 +++++++------ .../src/server/api/endpoints/channels/show.ts | 14 ++++++------- .../server/api/endpoints/channels/update.ts | 21 +++++++++++-------- .../src/server/api/endpoints/clips/create.ts | 15 ++++++++----- .../src/server/api/endpoints/clips/list.ts | 14 ++++++------- .../src/server/api/endpoints/clips/show.ts | 14 ++++++------- .../src/server/api/endpoints/clips/update.ts | 13 ++++++++---- .../server/api/endpoints/drive/files/find.ts | 4 +++- .../server/api/endpoints/drive/files/show.ts | 14 +++++++++---- .../api/endpoints/drive/files/update.ts | 21 +++++++++++++------ .../src/server/api/endpoints/drive/folders.ts | 11 +++++++--- .../api/endpoints/drive/folders/create.ts | 17 +++++++++------ .../api/endpoints/drive/folders/find.ts | 11 +++++++--- .../api/endpoints/drive/folders/show.ts | 11 +++++++--- .../api/endpoints/drive/folders/update.ts | 21 ++++++++++++------- 24 files changed, 194 insertions(+), 110 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/app/create.ts b/packages/backend/src/server/api/endpoints/app/create.ts index 21b33777cf4a..b3da01e6ee8d 100644 --- a/packages/backend/src/server/api/endpoints/app/create.ts +++ b/packages/backend/src/server/api/endpoints/app/create.ts @@ -4,6 +4,7 @@ import type { Apps } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; import { unique } from '@/prelude/array.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; +import { AppEntityService } from '@/services/entities/AppEntityService.js'; export const meta = { tags: ['app'], @@ -37,6 +38,7 @@ export default class extends Endpoint { @Inject('appsRepository') private appsRepository: typeof Apps, + private appEntityService: AppEntityService, private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/app/show.ts b/packages/backend/src/server/api/endpoints/app/show.ts index 036921cc6bff..753cd0b0143e 100644 --- a/packages/backend/src/server/api/endpoints/app/show.ts +++ b/packages/backend/src/server/api/endpoints/app/show.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Apps } from '@/models/index.js'; +import { AppEntityService } from '@/services/entities/AppEntityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -35,6 +36,8 @@ export default class extends Endpoint { constructor( @Inject('appsRepository') private appsRepository: typeof Apps, + + private appEntityService: AppEntityService, ) { super(meta, paramDef, async (ps, user, token) => { const isSecure = user != null && token == null; diff --git a/packages/backend/src/server/api/endpoints/auth/session/show.ts b/packages/backend/src/server/api/endpoints/auth/session/show.ts index f5f40629384b..da878d7e7ce5 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/show.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/show.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { AuthSessions } from '@/models/index.js'; +import { AuthSessionEntityService } from '@/services/entities/AuthSessionEntityService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -52,6 +53,8 @@ export default class extends Endpoint { constructor( @Inject('authSessionsRepository') private authSessionsRepository: typeof AuthSessions, + + private authSessionEntityService: AuthSessionEntityService, ) { super(meta, paramDef, async (ps, me) => { // Lookup session diff --git a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts index 56df7f6132c0..828afaa99621 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users , Apps, AccessTokens , AuthSessions } from '@/models/index.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -70,6 +71,8 @@ export default class extends Endpoint { @Inject('accessTokensRepository') private accessTokensRepository: typeof AccessTokens, + + private userEntityService: UserEntityService, ) { super(meta, paramDef, async (ps, me) => { // Lookup app diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts index c9702251314b..eefa1d21d029 100644 --- a/packages/backend/src/server/api/endpoints/blocking/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking/create.ts @@ -2,8 +2,9 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import create from '@/services/blocking/create.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users } from '@/models/index.js'; -import { Blockings, NoteWatchings } from '@/models/index.js'; +import type { Users , Blockings } from '@/models/index.js'; +import { NoteWatchings } from '@/models/index.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; @@ -60,6 +61,11 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + @Inject('blockingsRepository') + private blockingsRepository: typeof Blockings, + + private userEntityService: UserEntityService, ) { super(meta, paramDef, async (ps, me) => { const blocker = await this.usersRepository.findOneByOrFail({ id: me.id }); diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts index 9c6c3dca1a62..989cb1998c90 100644 --- a/packages/backend/src/server/api/endpoints/blocking/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts @@ -2,8 +2,8 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import deleteBlocking from '@/services/blocking/delete.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users } from '@/models/index.js'; -import { Blockings } from '@/models/index.js'; +import type { Users , Blockings } from '@/models/index.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; @@ -60,6 +60,11 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + @Inject('blockingsRepository') + private blockingsRepository: typeof Blockings, + + private userEntityService: UserEntityService, ) { super(meta, paramDef, async (ps, me) => { const blocker = await this.usersRepository.findOneByOrFail({ id: me.id }); @@ -70,9 +75,9 @@ export default class extends Endpoint { } // Get blockee - const blockee = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; + const blockee = await getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; }); // Check not blocking diff --git a/packages/backend/src/server/api/endpoints/channels/create.ts b/packages/backend/src/server/api/endpoints/channels/create.ts index 7c5b67088ede..06dd391e5769 100644 --- a/packages/backend/src/server/api/endpoints/channels/create.ts +++ b/packages/backend/src/server/api/endpoints/channels/create.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Channels, DriveFiles } from '@/models/index.js'; +import type { Channels, DriveFiles } from '@/models/index.js'; import type { Channel } from '@/models/entities/channel.js'; -import type { IdService } from '@/services/IdService.js'; +import { IdService } from '@/services/IdService.js'; +import { ChannelEntityService } from '@/services/entities/ChannelEntityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -41,12 +42,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + @Inject('channelsRepository') + private channelsRepository: typeof Channels, + private idService: IdService, + private channelEntityService: ChannelEntityService, ) { super(meta, paramDef, async (ps, me) => { let banner = null; if (ps.bannerId != null) { - banner = await DriveFiles.findOneBy({ + banner = await this.driveFilesRepository.findOneBy({ id: ps.bannerId, userId: me.id, }); @@ -56,16 +64,16 @@ export default class extends Endpoint { } } - const channel = await Channels.insert({ + const channel = await this.channelsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), userId: me.id, name: ps.name, description: ps.description || null, bannerId: banner ? banner.id : null, - } as Channel).then(x => Channels.findOneByOrFail(x.identifiers[0])); + } as Channel).then(x => this.channelsRepository.findOneByOrFail(x.identifiers[0])); - return await Channels.pack(channel, me); + return await this.channelEntityService.pack(channel, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/channels/featured.ts b/packages/backend/src/server/api/endpoints/channels/featured.ts index 483c8a47bd35..d3ce826296d3 100644 --- a/packages/backend/src/server/api/endpoints/channels/featured.ts +++ b/packages/backend/src/server/api/endpoints/channels/featured.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Channels } from '@/models/index.js'; +import type { Channels } from '@/models/index.js'; +import { ChannelEntityService } from '@/services/entities/ChannelEntityService.js'; export const meta = { tags: ['channels'], @@ -28,20 +29,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('channelsRepository') + private channelsRepository: typeof Channels, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private channelEntityService: ChannelEntityService, ) { super(meta, paramDef, async (ps, me) => { - const query = Channels.createQueryBuilder('channel') - .where('channel.lastNotedAt IS NOT NULL') - .orderBy('channel.lastNotedAt', 'DESC'); + const query = this.channelsRepository.createQueryBuilder('channel') + .where('channel.lastNotedAt IS NOT NULL') + .orderBy('channel.lastNotedAt', 'DESC'); const channels = await query.take(10).getMany(); - return await Promise.all(channels.map(x => Channels.pack(x, me))); + return await Promise.all(channels.map(x => this.channelEntityService.pack(x, me))); }); } } diff --git a/packages/backend/src/server/api/endpoints/channels/followed.ts b/packages/backend/src/server/api/endpoints/channels/followed.ts index 8f8a2becaf56..0384c001a140 100644 --- a/packages/backend/src/server/api/endpoints/channels/followed.ts +++ b/packages/backend/src/server/api/endpoints/channels/followed.ts @@ -1,7 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Channels, ChannelFollowings } from '@/models/index.js'; +import type { ChannelFollowings } from '@/models/index.js'; +import { Channels } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { ChannelEntityService } from '@/services/entities/ChannelEntityService.js'; export const meta = { tags: ['channels', 'account'], @@ -35,12 +37,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('channelFollowingsRepository') + private channelFollowingsRepository: typeof ChannelFollowings, + private channelEntityService: ChannelEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { @@ -51,7 +51,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return await Promise.all(followings.map(x => Channels.pack(x.followeeId, me))); + return await Promise.all(followings.map(x => this.channelEntityService.pack(x.followeeId, me))); }); } } diff --git a/packages/backend/src/server/api/endpoints/channels/owned.ts b/packages/backend/src/server/api/endpoints/channels/owned.ts index 33e2efcae95f..3642d4ae1902 100644 --- a/packages/backend/src/server/api/endpoints/channels/owned.ts +++ b/packages/backend/src/server/api/endpoints/channels/owned.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Channels } from '@/models/index.js'; +import type { Channels } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { ChannelEntityService } from '@/services/entities/ChannelEntityService.js'; export const meta = { tags: ['channels', 'account'], @@ -35,23 +36,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('channelsRepository') + private channelsRepository: typeof Channels, + private channelEntityService: ChannelEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(Channels.createQueryBuilder(), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.channelsRepository.createQueryBuilder(), ps.sinceId, ps.untilId) .andWhere({ userId: me.id }); const channels = await query .take(ps.limit) .getMany(); - return await Promise.all(channels.map(x => Channels.pack(x, me))); + return await Promise.all(channels.map(x => this.channelEntityService.pack(x, me))); }); } } diff --git a/packages/backend/src/server/api/endpoints/channels/show.ts b/packages/backend/src/server/api/endpoints/channels/show.ts index c5a3dacec807..a04d8d868fa6 100644 --- a/packages/backend/src/server/api/endpoints/channels/show.ts +++ b/packages/backend/src/server/api/endpoints/channels/show.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Channels } from '@/models/index.js'; +import type { Channels } from '@/models/index.js'; +import { ChannelEntityService } from '@/services/entities/ChannelEntityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -35,14 +36,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('channelsRepository') + private channelsRepository: typeof Channels, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private channelEntityService: ChannelEntityService, ) { super(meta, paramDef, async (ps, me) => { - const channel = await Channels.findOneBy({ + const channel = await this.channelsRepository.findOneBy({ id: ps.channelId, }); @@ -50,7 +50,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchChannel); } - return await Channels.pack(channel, me); + return await this.channelEntityService.pack(channel, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/channels/update.ts b/packages/backend/src/server/api/endpoints/channels/update.ts index 4e0396c416a3..88397b516833 100644 --- a/packages/backend/src/server/api/endpoints/channels/update.ts +++ b/packages/backend/src/server/api/endpoints/channels/update.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Channels, DriveFiles } from '@/models/index.js'; +import type { DriveFiles , Channels } from '@/models/index.js'; +import { ChannelEntityService } from '@/services/entities/ChannelEntityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -52,14 +53,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('channelsRepository') + private channelsRepository: typeof Channels, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private channelEntityService: ChannelEntityService, ) { super(meta, paramDef, async (ps, me) => { - const channel = await Channels.findOneBy({ + const channel = await this.channelsRepository.findOneBy({ id: ps.channelId, }); @@ -74,7 +77,7 @@ export default class extends Endpoint { // eslint:disable-next-line:no-unnecessary-initializer let banner = undefined; if (ps.bannerId != null) { - banner = await DriveFiles.findOneBy({ + banner = await this.driveFilesRepository.findOneBy({ id: ps.bannerId, userId: me.id, }); @@ -86,13 +89,13 @@ export default class extends Endpoint { banner = null; } - await Channels.update(channel.id, { + await this.channelsRepository.update(channel.id, { ...(ps.name !== undefined ? { name: ps.name } : {}), ...(ps.description !== undefined ? { description: ps.description } : {}), ...(banner ? { bannerId: banner.id } : {}), }); - return await Channels.pack(channel.id, me); + return await this.channelEntityService.pack(channel.id, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/clips/create.ts b/packages/backend/src/server/api/endpoints/clips/create.ts index 703f0d80cea9..a33d9981609f 100644 --- a/packages/backend/src/server/api/endpoints/clips/create.ts +++ b/packages/backend/src/server/api/endpoints/clips/create.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { IdService } from '@/services/IdService.js'; -import { Clips } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; +import type { Clips } from '@/models/index.js'; +import { ClipEntityService } from '@/services/entities/ClipEntityService.js'; export const meta = { tags: ['clips'], @@ -31,19 +32,23 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('clipsRepository') + private clipsRepository: typeof Clips, + + private clipEntityService: ClipEntityService, private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { - const clip = await Clips.insert({ + const clip = await this.clipsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), userId: me.id, name: ps.name, isPublic: ps.isPublic, description: ps.description, - }).then(x => Clips.findOneByOrFail(x.identifiers[0])); + }).then(x => this.clipsRepository.findOneByOrFail(x.identifiers[0])); - return await Clips.pack(clip); + return await this.clipEntityService.pack(clip); }); } } diff --git a/packages/backend/src/server/api/endpoints/clips/list.ts b/packages/backend/src/server/api/endpoints/clips/list.ts index c0155d3ff163..6c659f04ea41 100644 --- a/packages/backend/src/server/api/endpoints/clips/list.ts +++ b/packages/backend/src/server/api/endpoints/clips/list.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Clips } from '@/models/index.js'; +import type { Clips } from '@/models/index.js'; +import { ClipEntityService } from '@/services/entities/ClipEntityService.js'; export const meta = { tags: ['clips', 'account'], @@ -30,18 +31,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('clipsRepository') + private clipsRepository: typeof Clips, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private clipEntityService: ClipEntityService, ) { super(meta, paramDef, async (ps, me) => { - const clips = await Clips.findBy({ + const clips = await this.clipsRepository.findBy({ userId: me.id, }); - return await Promise.all(clips.map(x => Clips.pack(x))); + return await Promise.all(clips.map(x => this.clipEntityService.pack(x))); }); } } diff --git a/packages/backend/src/server/api/endpoints/clips/show.ts b/packages/backend/src/server/api/endpoints/clips/show.ts index 7dbe68e091fd..17fcdd35af32 100644 --- a/packages/backend/src/server/api/endpoints/clips/show.ts +++ b/packages/backend/src/server/api/endpoints/clips/show.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Clips } from '@/models/index.js'; +import type { Clips } from '@/models/index.js'; +import { ClipEntityService } from '@/services/entities/ClipEntityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -37,15 +38,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('clipsRepository') + private clipsRepository: typeof Clips, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private clipEntityService: ClipEntityService, ) { super(meta, paramDef, async (ps, me) => { // Fetch the clip - const clip = await Clips.findOneBy({ + const clip = await this.clipsRepository.findOneBy({ id: ps.clipId, }); @@ -57,7 +57,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchClip); } - return await Clips.pack(clip); + return await this.clipEntityService.pack(clip); }); } } diff --git a/packages/backend/src/server/api/endpoints/clips/update.ts b/packages/backend/src/server/api/endpoints/clips/update.ts index 943634a403c5..adace87d7c89 100644 --- a/packages/backend/src/server/api/endpoints/clips/update.ts +++ b/packages/backend/src/server/api/endpoints/clips/update.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Clips } from '@/models/index.js'; +import type { Clips } from '@/models/index.js'; +import { ClipEntityService } from '@/services/entities/ClipEntityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -40,10 +41,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('clipsRepository') + private clipsRepository: typeof Clips, + + private clipEntityService: ClipEntityService, ) { super(meta, paramDef, async (ps, me) => { // Fetch the clip - const clip = await Clips.findOneBy({ + const clip = await this.clipsRepository.findOneBy({ id: ps.clipId, userId: me.id, }); @@ -52,13 +57,13 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchClip); } - await Clips.update(clip.id, { + await this.clipsRepository.update(clip.id, { name: ps.name, description: ps.description, isPublic: ps.isPublic, }); - return await Clips.pack(clip.id); + return await this.clipEntityService.pack(clip.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/drive/files/find.ts b/packages/backend/src/server/api/endpoints/drive/files/find.ts index 0db6b1d005cf..3d9ef5e9fe80 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/find.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/find.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFiles } from '@/models/index.js'; +import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; export const meta = { requireCredential: true, @@ -36,6 +37,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private driveFileEntityService: DriveFileEntityService, ) { super(meta, paramDef, async (ps, me) => { const files = await DriveFiles.findBy({ @@ -44,7 +46,7 @@ export default class extends Endpoint { folderId: ps.folderId ?? IsNull(), }); - return await Promise.all(files.map(file => DriveFiles.pack(file, { self: true }))); + return await Promise.all(files.map(file => this.driveFileEntityService.pack(file, { self: true }))); }); } } diff --git a/packages/backend/src/server/api/endpoints/drive/files/show.ts b/packages/backend/src/server/api/endpoints/drive/files/show.ts index d8057822ff59..c694a07c612a 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/show.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/show.ts @@ -1,7 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import type { DriveFile } from '@/models/entities/drive-file.js'; -import { DriveFiles, Users } from '@/models/index.js'; +import type { DriveFiles } from '@/models/index.js'; +import { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -56,14 +58,18 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private driveFileEntityService: DriveFileEntityService, ) { super(meta, paramDef, async (ps, me) => { let file: DriveFile | null = null; if (ps.fileId) { - file = await DriveFiles.findOneBy({ id: ps.fileId }); + file = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); } else if (ps.url) { - file = await DriveFiles.findOne({ + file = await this.driveFilesRepository.findOne({ where: [{ url: ps.url, }, { @@ -82,7 +88,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.accessDenied); } - return await DriveFiles.pack(file, { + return await this.driveFileEntityService.pack(file, { detail: true, withUser: true, self: true, diff --git a/packages/backend/src/server/api/endpoints/drive/files/update.ts b/packages/backend/src/server/api/endpoints/drive/files/update.ts index da45da62dd36..354e9d4dc348 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts @@ -1,8 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { publishDriveStream } from '@/services/stream.js'; -import { DriveFiles, DriveFolders, Users } from '@/models/index.js'; +import type { DriveFiles , DriveFolders } from '@/models/index.js'; +import { Users } from '@/models/index.js'; import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -63,9 +65,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + @Inject('driveFoldersRepository') + private driveFoldersRepository: typeof DriveFolders, + + private driveFileEntityService: DriveFileEntityService, ) { super(meta, paramDef, async (ps, me) => { - const file = await DriveFiles.findOneBy({ id: ps.fileId }); + const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (file == null) { throw new ApiError(meta.errors.noSuchFile); @@ -76,7 +85,7 @@ export default class extends Endpoint { } if (ps.name) file.name = ps.name; - if (!DriveFiles.validateFileName(file.name)) { + if (!this.driveFileEntityService.validateFileName(file.name)) { throw new ApiError(meta.errors.invalidFileName); } @@ -88,7 +97,7 @@ export default class extends Endpoint { if (ps.folderId === null) { file.folderId = null; } else { - const folder = await DriveFolders.findOneBy({ + const folder = await this.driveFoldersRepository.findOneBy({ id: ps.folderId, userId: me.id, }); @@ -101,14 +110,14 @@ export default class extends Endpoint { } } - await DriveFiles.update(file.id, { + await this.driveFilesRepository.update(file.id, { name: file.name, comment: file.comment, folderId: file.folderId, isSensitive: file.isSensitive, }); - const fileObj = await DriveFiles.pack(file, { self: true }); + const fileObj = await this.driveFileEntityService.pack(file, { self: true }); // Publish fileUpdated event publishDriveStream(me.id, 'fileUpdated', fileObj); diff --git a/packages/backend/src/server/api/endpoints/drive/folders.ts b/packages/backend/src/server/api/endpoints/drive/folders.ts index 1ef7deec1fb1..736a174eb71d 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFolders } from '@/models/index.js'; +import type { DriveFolders } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { DriveFolderEntityService } from '@/services/entities/DriveFolderEntityService.js'; export const meta = { tags: ['drive'], @@ -36,10 +37,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('driveFoldersRepository') + private driveFoldersRepository: typeof DriveFolders, + + private driveFolderEntityService: DriveFolderEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(DriveFolders.createQueryBuilder('folder'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.driveFoldersRepository.createQueryBuilder('folder'), ps.sinceId, ps.untilId) .andWhere('folder.userId = :userId', { userId: me.id }); if (ps.folderId) { @@ -50,7 +55,7 @@ export default class extends Endpoint { const folders = await query.take(ps.limit).getMany(); - return await Promise.all(folders.map(folder => DriveFolders.pack(folder))); + return await Promise.all(folders.map(folder => this.driveFolderEntityService.pack(folder))); }); } } diff --git a/packages/backend/src/server/api/endpoints/drive/folders/create.ts b/packages/backend/src/server/api/endpoints/drive/folders/create.ts index 894b0a759786..c019ba66cdff 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/create.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { publishDriveStream } from '@/services/stream.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFolders } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import type { DriveFolders } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; +import { DriveFolderEntityService } from '@/services/entities/DriveFolderEntityService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -40,6 +41,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('driveFoldersRepository') + private driveFoldersRepository: typeof DriveFolders, + + private driveFolderEntityService: DriveFolderEntityService, private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { @@ -47,7 +52,7 @@ export default class extends Endpoint { let parent = null; if (ps.parentId) { // Fetch parent folder - parent = await DriveFolders.findOneBy({ + parent = await this.driveFoldersRepository.findOneBy({ id: ps.parentId, userId: me.id, }); @@ -58,15 +63,15 @@ export default class extends Endpoint { } // Create folder - const folder = await DriveFolders.insert({ + const folder = await this.driveFoldersRepository.insert({ id: this.idService.genId(), createdAt: new Date(), name: ps.name, parentId: parent !== null ? parent.id : null, userId: me.id, - }).then(x => DriveFolders.findOneByOrFail(x.identifiers[0])); + }).then(x => this.driveFoldersRepository.findOneByOrFail(x.identifiers[0])); - const folderObj = await DriveFolders.pack(folder); + const folderObj = await this.driveFolderEntityService.pack(folder); // Publish folderCreated event publishDriveStream(me.id, 'folderCreated', folderObj); diff --git a/packages/backend/src/server/api/endpoints/drive/folders/find.ts b/packages/backend/src/server/api/endpoints/drive/folders/find.ts index 65b70783347f..cd21c940d464 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/find.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/find.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFolders } from '@/models/index.js'; +import type { DriveFolders } from '@/models/index.js'; +import { DriveFolderEntityService } from '@/services/entities/DriveFolderEntityService'; export const meta = { tags: ['drive'], @@ -34,15 +35,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('driveFoldersRepository') + private driveFoldersRepository: typeof DriveFolders, + + private driveFolderEntityService: DriveFolderEntityService, ) { super(meta, paramDef, async (ps, me) => { - const folders = await DriveFolders.findBy({ + const folders = await this.driveFoldersRepository.findBy({ name: ps.name, userId: me.id, parentId: ps.parentId ?? IsNull(), }); - return await Promise.all(folders.map(folder => DriveFolders.pack(folder))); + return await Promise.all(folders.map(folder => this.driveFolderEntityService.pack(folder))); }); } } diff --git a/packages/backend/src/server/api/endpoints/drive/folders/show.ts b/packages/backend/src/server/api/endpoints/drive/folders/show.ts index d422ded6a9b4..69294b1031cc 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/show.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/show.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFolders } from '@/models/index.js'; +import type { DriveFolders } from '@/models/index.js'; +import { DriveFolderEntityService } from '@/services/entities/DriveFolderEntityService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -37,10 +38,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('driveFoldersRepository') + private driveFoldersRepository: typeof DriveFolders, + + private driveFolderEntityService: DriveFolderEntityService, ) { super(meta, paramDef, async (ps, me) => { // Get folder - const folder = await DriveFolders.findOneBy({ + const folder = await this.driveFoldersRepository.findOneBy({ id: ps.folderId, userId: me.id, }); @@ -49,7 +54,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchFolder); } - return await DriveFolders.pack(folder, { + return await this.driveFolderEntityService.pack(folder, { detail: true, }); }); diff --git a/packages/backend/src/server/api/endpoints/drive/folders/update.ts b/packages/backend/src/server/api/endpoints/drive/folders/update.ts index 35dd9960b2c4..4c19c27c98e2 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/update.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { publishDriveStream } from '@/services/stream.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFolders } from '@/models/index.js'; +import type { DriveFolders } from '@/models/index.js'; +import { DriveFolderEntityService } from '@/services/entities/DriveFolderEntityService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -52,10 +53,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('driveFoldersRepository') + private driveFoldersRepository: typeof DriveFolders, + + private driveFolderEntityService: DriveFolderEntityService, ) { super(meta, paramDef, async (ps, me) => { // Fetch folder - const folder = await DriveFolders.findOneBy({ + const folder = await this.driveFoldersRepository.findOneBy({ id: ps.folderId, userId: me.id, }); @@ -73,7 +78,7 @@ export default class extends Endpoint { folder.parentId = null; } else { // Get parent folder - const parent = await DriveFolders.findOneBy({ + const parent = await this.driveFoldersRepository.findOneBy({ id: ps.parentId, userId: me.id, }); @@ -83,9 +88,9 @@ export default class extends Endpoint { } // Check if the circular reference will occur - async function checkCircle(folderId: string): Promise { + const checkCircle = async (folderId: string): Promise => { // Fetch folder - const folder2 = await DriveFolders.findOneBy({ + const folder2 = await this.driveFoldersRepository.findOneBy({ id: folderId, }); @@ -96,7 +101,7 @@ export default class extends Endpoint { } else { return false; } - } + }; if (parent.parentId !== null) { if (await checkCircle(parent.parentId)) { @@ -109,12 +114,12 @@ export default class extends Endpoint { } // Update - DriveFolders.update(folder.id, { + this.driveFoldersRepository.update(folder.id, { name: folder.name, parentId: folder.parentId, }); - const folderObj = await DriveFolders.pack(folder); + const folderObj = await this.driveFolderEntityService.pack(folder); // Publish folderUpdated event publishDriveStream(me.id, 'folderUpdated', folderObj); From 657192c5036d9aa46fe6c3051343866fef79489e Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 06:13:37 +0900 Subject: [PATCH 106/180] wipo --- .../api/endpoints/federation/show-instance.ts | 16 +++++----- .../server/api/endpoints/following/create.ts | 23 +++++++++----- .../server/api/endpoints/following/delete.ts | 23 +++++++++----- .../api/endpoints/following/invalidate.ts | 23 +++++++++----- .../endpoints/following/requests/cancel.ts | 31 +++++++++++-------- .../api/endpoints/following/requests/list.ts | 11 +++++-- .../src/services/UserFollowingService.ts | 2 ++ 7 files changed, 81 insertions(+), 48 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/federation/show-instance.ts b/packages/backend/src/server/api/endpoints/federation/show-instance.ts index 9a876461a587..9cdde7d0da5c 100644 --- a/packages/backend/src/server/api/endpoints/federation/show-instance.ts +++ b/packages/backend/src/server/api/endpoints/federation/show-instance.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Instances } from '@/models/index.js'; +import type { Instances } from '@/models/index.js'; import { toPuny } from '@/misc/convert-host.js'; +import { InstanceEntityService } from '@/services/entities/InstanceEntityService'; export const meta = { tags: ['federation'], @@ -30,17 +31,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('instancesRepository') + private instancesRepository: typeof Instances, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private instanceEntityService: InstanceEntityService, ) { super(meta, paramDef, async (ps, me) => { - const instance = await Instances - .findOneBy({ host: toPuny(ps.host) }); + const instance = await this.instancesRepository + .findOneBy({ host: toPuny(ps.host) }); - return instance ? await Instances.pack(instance) : null; + return instance ? await this.instanceEntityService.pack(instance) : null; }); } } diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts index 9c296597f1f6..c4c93800c946 100644 --- a/packages/backend/src/server/api/endpoints/following/create.ts +++ b/packages/backend/src/server/api/endpoints/following/create.ts @@ -1,12 +1,12 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import create from '@/services/following/create.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users } from '@/models/index.js'; -import { Followings } from '@/models/index.js'; +import type { Users , Followings } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { UserFollowingService } from '@/services/UserFollowingService.js'; import { ApiError } from '../../error.js'; -import { getUser } from '../../common/getters.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['following', 'users'], @@ -73,6 +73,13 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + private userEntityService: UserEntityService, + private getterService: GetterService, + private userFollowingService: UserFollowingService, ) { super(meta, paramDef, async (ps, me) => { const follower = me; @@ -83,9 +90,9 @@ export default class extends Endpoint { } // Get followee - const followee = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; + const followee = await this.getterService.getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; }); // Check if already following @@ -99,7 +106,7 @@ export default class extends Endpoint { } try { - await create(follower, followee); + await this.userFollowingService.follow(follower, followee); } catch (e) { if (e instanceof IdentifiableError) { if (e.id === '710e8fb0-b8c3-4922-be49-d5d93d8e6a6e') throw new ApiError(meta.errors.blocking); diff --git a/packages/backend/src/server/api/endpoints/following/delete.ts b/packages/backend/src/server/api/endpoints/following/delete.ts index 125422119c28..c5f95a4f9a7e 100644 --- a/packages/backend/src/server/api/endpoints/following/delete.ts +++ b/packages/backend/src/server/api/endpoints/following/delete.ts @@ -1,11 +1,11 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import deleteFollowing from '@/services/following/delete.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users } from '@/models/index.js'; -import { Followings } from '@/models/index.js'; +import type { Users , Followings } from '@/models/index.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { UserFollowingService } from '@/services/UserFollowingService.js'; import { ApiError } from '../../error.js'; -import { getUser } from '../../common/getters.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['following', 'users'], @@ -60,6 +60,13 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + private userEntityService: UserEntityService, + private getterService: GetterService, + private userFollowingService: UserFollowingService, ) { super(meta, paramDef, async (ps, me) => { const follower = me; @@ -70,9 +77,9 @@ export default class extends Endpoint { } // Get followee - const followee = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; + const followee = await this.getterService.getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; }); // Check not following @@ -85,7 +92,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.notFollowing); } - await deleteFollowing(follower, followee); + await this.userFollowingService.unfollow(follower, followee); return await this.userEntityService.pack(followee.id, me); }); diff --git a/packages/backend/src/server/api/endpoints/following/invalidate.ts b/packages/backend/src/server/api/endpoints/following/invalidate.ts index 129f244cb446..27f221a960bb 100644 --- a/packages/backend/src/server/api/endpoints/following/invalidate.ts +++ b/packages/backend/src/server/api/endpoints/following/invalidate.ts @@ -1,11 +1,11 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import deleteFollowing from '@/services/following/delete.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users } from '@/models/index.js'; -import { Followings } from '@/models/index.js'; +import type { Users , Followings } from '@/models/index.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { UserFollowingService } from '@/services/UserFollowingService.js'; import { ApiError } from '../../error.js'; -import { getUser } from '../../common/getters.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['following', 'users'], @@ -60,6 +60,13 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + private userEntityService: UserEntityService, + private getterService: GetterService, + private userFollowingService: UserFollowingService, ) { super(meta, paramDef, async (ps, me) => { const followee = me; @@ -70,9 +77,9 @@ export default class extends Endpoint { } // Get follower - const follower = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; + const follower = await this.getterService.getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; }); // Check not following @@ -85,7 +92,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.notFollowing); } - await deleteFollowing(follower, followee); + await this.userFollowingService.unfollow(follower, followee); return await this.userEntityService.pack(followee.id, me); }); diff --git a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts index 5327b5c53a76..16401d59610e 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts @@ -1,10 +1,11 @@ import { Inject, Injectable } from '@nestjs/common'; -import cancelFollowRequest from '@/services/following/requests/cancel.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users } from '@/models/index.js'; +import type { Followings, Users } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { GetterService } from '@/server/api/common/GetterService.js'; +import { UserFollowingService } from '@/services/UserFollowingService.js'; import { ApiError } from '../../../error.js'; -import { getUser } from '../../../common/getters.js'; export const meta = { tags: ['following', 'account'], @@ -46,23 +47,27 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + private userEntityService: UserEntityService, + private getterService: GetterService, + private userFollowingService: UserFollowingService, ) { super(meta, paramDef, async (ps, me) => { // Fetch followee - const followee = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; + const followee = await this.getterService.getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; }); try { - await cancelFollowRequest(followee, me); - } catch (e) { - if (e instanceof IdentifiableError) { - if (e.id === '17447091-ce07-46dd-b331-c1fd4f15b1e7') throw new ApiError(meta.errors.followRequestNotFound); + await this.userFollowingService.cancelFollowRequest(followee, me); + } catch (err) { + if (err instanceof IdentifiableError) { + if (err.id === '17447091-ce07-46dd-b331-c1fd4f15b1e7') throw new ApiError(meta.errors.followRequestNotFound); } - throw e; + throw err; } return await this.userEntityService.pack(followee.id, me); diff --git a/packages/backend/src/server/api/endpoints/following/requests/list.ts b/packages/backend/src/server/api/endpoints/following/requests/list.ts index aed3ff6ced74..195c6da63131 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/list.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/list.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { FollowRequests } from '@/models/index.js'; +import type { FollowRequests } from '@/models/index.js'; +import { FollowRequestEntityService } from '@/services/entities/FollowRequestEntityService'; export const meta = { tags: ['following', 'account'], @@ -46,13 +47,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('followRequestsRepository') + private followRequestsRepository: typeof FollowRequests, + + private followRequestEntityService: FollowRequestEntityService, ) { super(meta, paramDef, async (ps, me) => { - const reqs = await FollowRequests.findBy({ + const reqs = await this.followRequestsRepository.findBy({ followeeId: me.id, }); - return await Promise.all(reqs.map(req => FollowRequests.pack(req))); + return await Promise.all(reqs.map(req => this.followRequestEntityService.pack(req))); }); } } diff --git a/packages/backend/src/services/UserFollowingService.ts b/packages/backend/src/services/UserFollowingService.ts index d80b4864d355..bf18549200a0 100644 --- a/packages/backend/src/services/UserFollowingService.ts +++ b/packages/backend/src/services/UserFollowingService.ts @@ -18,6 +18,7 @@ import { FederatedInstanceService } from '@/services/FederatedInstanceService.js import { WebhookService } from '@/services/webhookService.js'; import { CreateNotificationService } from '@/services/CreateNotificationService.js'; import Logger from './logger.js'; +import { UserEntityService } from './entities/UserEntityService.js'; const logger = new Logger('following/create'); @@ -55,6 +56,7 @@ export class UserFollowingService { @Inject('instancesRepository') private instancesRepository: typeof Instances, + private userEntityService: UserEntityService, private idService: IdService, private queueService: QueueService, private globalEventServie: GlobalEventService, From c814c7cfe47907f524701c0190b2d55915abc1a5 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 07:02:52 +0900 Subject: [PATCH 107/180] wip --- .../api/endpoints/gallery/posts/create.ts | 17 ++++++--- .../api/endpoints/gallery/posts/delete.ts | 8 +++-- .../api/endpoints/gallery/posts/show.ts | 14 ++++---- .../api/endpoints/gallery/posts/update.ts | 18 +++++++--- .../src/server/api/endpoints/hashtags/show.ts | 11 ++++-- .../server/api/endpoints/i/2fa/key-done.ts | 36 +++++++++++++------ .../server/api/endpoints/i/2fa/remove-key.ts | 21 +++++++---- .../server/api/endpoints/i/authorized-apps.ts | 12 +++++-- .../server/api/endpoints/i/signin-history.ts | 11 ++++-- .../server/api/endpoints/i/update-email.ts | 28 ++++++++++----- 10 files changed, 121 insertions(+), 55 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts index d3a32e135de2..396215959924 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts @@ -1,10 +1,12 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFiles, GalleryPosts } from '@/models/index.js'; +import type { GalleryPosts } from '@/models/index.js'; +import { DriveFiles } from '@/models/index.js'; import { GalleryPost } from '@/models/entities/gallery-post.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; -import { genId } from '../../../../../misc/gen-id.js'; +import { IdService } from '@/services/IdService.js'; +import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -47,6 +49,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('galleryPostsRepository') + private galleryPostsRepository: typeof GalleryPosts, + + private galleryPostEntityService: GalleryPostEntityService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const files = (await Promise.all(ps.fileIds.map(fileId => @@ -60,7 +67,7 @@ export default class extends Endpoint { throw new Error(); } - const post = await GalleryPosts.insert(new GalleryPost({ + const post = await this.galleryPostsRepository.insert(new GalleryPost({ id: this.idService.genId(), createdAt: new Date(), updatedAt: new Date(), @@ -69,9 +76,9 @@ export default class extends Endpoint { userId: me.id, isSensitive: ps.isSensitive, fileIds: files.map(file => file.id), - })).then(x => GalleryPosts.findOneByOrFail(x.identifiers[0])); + })).then(x => this.galleryPostsRepository.findOneByOrFail(x.identifiers[0])); - return await GalleryPosts.pack(post, me); + return await this.galleryPostEntityService.pack(post, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts b/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts index 742fd8c671cd..952211c0c1e0 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GalleryPosts } from '@/models/index.js'; +import type { GalleryPosts } from '@/models/index.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -31,9 +31,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('galleryPostsRepository') + private galleryPostsRepository: typeof GalleryPosts, ) { super(meta, paramDef, async (ps, me) => { - const post = await GalleryPosts.findOneBy({ + const post = await this.galleryPostsRepository.findOneBy({ id: ps.postId, userId: me.id, }); @@ -42,7 +44,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchPost); } - await GalleryPosts.delete(post.id); + await this.galleryPostsRepository.delete(post.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts b/packages/backend/src/server/api/endpoints/gallery/posts/show.ts index f16a2aaa0e43..a247f224f130 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/show.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GalleryPosts } from '@/models/index.js'; +import type { GalleryPosts } from '@/models/index.js'; +import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -35,14 +36,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('galleryPostsRepository') + private galleryPostsRepository: typeof GalleryPosts, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private galleryPostEntityService: GalleryPostEntityService, ) { super(meta, paramDef, async (ps, me) => { - const post = await GalleryPosts.findOneBy({ + const post = await this.galleryPostsRepository.findOneBy({ id: ps.postId, }); @@ -50,7 +50,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchPost); } - return await GalleryPosts.pack(post, me); + return await this.galleryPostEntityService.pack(post, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts index 56cfbcfd0931..ec89b1fd1712 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts @@ -1,9 +1,10 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFiles, GalleryPosts } from '@/models/index.js'; +import type { DriveFiles, GalleryPosts } from '@/models/index.js'; import { GalleryPost } from '@/models/entities/gallery-post.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; +import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -47,10 +48,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('galleryPostsRepository') + private galleryPostsRepository: typeof GalleryPosts, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private galleryPostEntityService: GalleryPostEntityService, ) { super(meta, paramDef, async (ps, me) => { const files = (await Promise.all(ps.fileIds.map(fileId => - DriveFiles.findOneBy({ + this.driveFilesRepository.findOneBy({ id: fileId, userId: me.id, }), @@ -60,7 +68,7 @@ export default class extends Endpoint { throw new Error(); } - await GalleryPosts.update({ + await this.galleryPostsRepository.update({ id: ps.postId, userId: me.id, }, { @@ -71,9 +79,9 @@ export default class extends Endpoint { fileIds: files.map(file => file.id), }); - const post = await GalleryPosts.findOneByOrFail({ id: ps.postId }); + const post = await this.galleryPostsRepository.findOneByOrFail({ id: ps.postId }); - return await GalleryPosts.pack(post, me); + return await this.galleryPostEntityService.pack(post, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/hashtags/show.ts b/packages/backend/src/server/api/endpoints/hashtags/show.ts index f51cef9661fc..4dd474faa3b9 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/show.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/show.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Hashtags } from '@/models/index.js'; +import type { Hashtags } from '@/models/index.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; +import { HashtagEntityService } from '@/services/entities/HashtagEntityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -36,14 +37,18 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('hashtagsRepository') + private hashtagsRepository: typeof Hashtags, + + private hashtagEntityService: HashtagEntityService, ) { super(meta, paramDef, async (ps, me) => { - const hashtag = await Hashtags.findOneBy({ name: normalizeForSearch(ps.tag) }); + const hashtag = await this.hashtagsRepository.findOneBy({ name: normalizeForSearch(ps.tag) }); if (hashtag == null) { throw new ApiError(meta.errors.noSuchHashtag); } - return await Hashtags.pack(hashtag); + return await this.hashtagEntityService.pack(hashtag); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts index f55d0bc69b89..b88c4c74fb8f 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts @@ -4,18 +4,19 @@ import * as cbor from 'cbor'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { - Users } from '@/models/index.js'; + Users , + UserProfiles } from '@/models/index.js'; import { - UserProfiles, UserSecurityKeys, AttestationChallenges, } from '@/models/index.js'; -import config from '@/config/index.js'; -import { publishMainStream } from '@/services/stream.js'; -import { procedures, hash } from '../../../2fa.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { Config } from '@/config/types.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { GlobalEventService } from '@/services/GlobalEventService'; +import { TwoFactorAuthenticationService } from '@/services/TwoFactorAuthenticationService'; const cborDecodeFirst = promisify(cbor.decodeFirst) as any; -const rpIdHashReal = hash(Buffer.from(config.hostname, 'utf-8')); export const meta = { requireCredential: true, @@ -39,12 +40,23 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + @Inject('usersRepository') private usersRepository: typeof Users, + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + private userEntityService: UserEntityService, + private globalEventService: GlobalEventService, + private twoFactorAuthenticationService: TwoFactorAuthenticationService, ) { super(meta, paramDef, async (ps, me) => { - const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); + const rpIdHashReal = this.twoFactorAuthenticationService.hash(Buffer.from(this.config.hostname, 'utf-8')); + + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); @@ -62,11 +74,11 @@ export default class extends Endpoint { if (clientData.type !== 'webauthn.create') { throw new Error('not a creation attestation'); } - if (clientData.origin !== config.scheme + '://' + config.host) { + if (clientData.origin !== this.config.scheme + '://' + this.config.host) { throw new Error('origin mismatch'); } - const clientDataJSONHash = hash(Buffer.from(ps.clientDataJSON, 'utf-8')); + const clientDataJSONHash = this.twoFactorAuthenticationService.hash(Buffer.from(ps.clientDataJSON, 'utf-8')); const attestation = await cborDecodeFirst(ps.attestationObject); @@ -91,6 +103,8 @@ export default class extends Endpoint { throw new Error('alg mismatch'); } + const procedures = this.twoFactorAuthenticationService.getProcedures(); + if (!(procedures as any)[attestation.fmt]) { throw new Error('unsupported fmt'); } @@ -109,7 +123,7 @@ export default class extends Endpoint { userId: me.id, id: ps.challengeId, registrationChallenge: true, - challenge: hash(clientData.challenge).toString('hex'), + challenge: this.twoFactorAuthenticationService.hash(clientData.challenge).toString('hex'), }); if (!attestationChallenge) { @@ -140,7 +154,7 @@ export default class extends Endpoint { }); // Publish meUpdated event - publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { + this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { detail: true, includeSecrets: true, })); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts index de4ddb2c78bd..1e8487bd2ccb 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts @@ -1,9 +1,10 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users } from '@/models/index.js'; -import { UserProfiles, UserSecurityKeys } from '@/models/index.js'; +import type { Users , UserProfiles , UserSecurityKeys } from '@/models/index.js'; import { publishMainStream } from '@/services/stream.js'; +import { UserEntityService } from '@/services/entities/UserEntityService'; +import { GlobalEventService } from '@/services/GlobalEventService'; export const meta = { requireCredential: true, @@ -24,11 +25,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('userSecurityKeysRepository') + private userSecurityKeysRepository: typeof UserSecurityKeys, + + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + private userEntityService: UserEntityService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { - const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); @@ -38,13 +45,13 @@ export default class extends Endpoint { } // Make sure we only delete the user's own creds - await UserSecurityKeys.delete({ + await this.userSecurityKeysRepository.delete({ userId: me.id, id: ps.credentialId, }); // Publish meUpdated event - publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { + this.globalEventService.publishMainStream(me.id, 'meUpdated', await this.userEntityService.pack(me.id, me, { detail: true, includeSecrets: true, })); diff --git a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts index 8f865a64e59c..1a187158d857 100644 --- a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts +++ b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts @@ -1,6 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { AccessTokens, Apps } from '@/models/index.js'; +import type { AccessTokens } from '@/models/index.js'; +import { Apps } from '@/models/index.js'; +import { AppEntityService } from '@/services/entities/AppEntityService'; export const meta = { requireCredential: true, @@ -22,10 +24,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('accessTokensRepository') + private accessTokensRepository: typeof AccessTokens, + + private appEntityService: AppEntityService, ) { super(meta, paramDef, async (ps, me) => { // Get tokens - const tokens = await AccessTokens.find({ + const tokens = await this.accessTokensRepository.find({ where: { userId: me.id, }, @@ -36,7 +42,7 @@ export default class extends Endpoint { }, }); - return await Promise.all(tokens.map(token => Apps.pack(token.appId, me, { + return await Promise.all(tokens.map(token => this.appEntityService.pack(token.appId, me, { detail: true, }))); }); diff --git a/packages/backend/src/server/api/endpoints/i/signin-history.ts b/packages/backend/src/server/api/endpoints/i/signin-history.ts index 8deb32c100b0..aa9b85dcb36f 100644 --- a/packages/backend/src/server/api/endpoints/i/signin-history.ts +++ b/packages/backend/src/server/api/endpoints/i/signin-history.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Signins } from '@/models/index.js'; +import type { Signins } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { SigninEntityService } from '@/services/entities/SigninEntityService'; export const meta = { requireCredential: true, @@ -23,15 +24,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('signinsRepository') + private signinsRepository: typeof Signins, + + private signinEntityService: SigninEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(Signins.createQueryBuilder('signin'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.signinsRepository.createQueryBuilder('signin'), ps.sinceId, ps.untilId) .andWhere('signin.userId = :meId', { meId: me.id }); const history = await query.take(ps.limit).getMany(); - return await Promise.all(history.map(record => Signins.pack(record))); + return await Promise.all(history.map(record => this.signinEntityService.pack(record))); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts index 6c9201034e0c..f00cf53bc35b 100644 --- a/packages/backend/src/server/api/endpoints/i/update-email.ts +++ b/packages/backend/src/server/api/endpoints/i/update-email.ts @@ -2,13 +2,15 @@ import { Inject, Injectable } from '@nestjs/common'; import rndstr from 'rndstr'; import ms from 'ms'; import bcrypt from 'bcryptjs'; -import config from '@/config/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { publishMainStream } from '@/services/stream.js'; import type { Users } from '@/models/index.js'; import { UserProfiles } from '@/models/index.js'; -import { sendEmail } from '@/services/send-email.js'; import { validateEmailForAccount } from '@/services/validate-email-for-account.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { EmailService } from '@/services/EmailService.js'; +import { Config } from '@/config/types.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -49,11 +51,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + @Inject('usersRepository') private usersRepository: typeof Users, + + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + private userEntityService: UserEntityService, + private emailService: EmailService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { - const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); @@ -69,7 +81,7 @@ export default class extends Endpoint { } } - await UserProfiles.update(me.id, { + await this.userProfilesRepository.update(me.id, { email: ps.email, emailVerified: false, emailVerifyCode: null, @@ -81,7 +93,7 @@ export default class extends Endpoint { }); // Publish meUpdated event - publishMainStream(me.id, 'meUpdated', iObj); + this.globalEventService.publishMainStream(me.id, 'meUpdated', iObj); if (ps.email != null) { const code = rndstr('a-z0-9', 16); @@ -90,9 +102,9 @@ export default class extends Endpoint { emailVerifyCode: code, }); - const link = `${config.url}/verify-email/${code}`; + const link = `${this.config.url}/verify-email/${code}`; - sendEmail(ps.email, 'Email verification', + this.emailService.sendEmail(ps.email, 'Email verification', `To verify email, please click this link:
${link}`, `To verify email, please click this link: ${link}`); } From 50603d55b48b9a1a060dec32f1269657ad007e03 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 07:15:23 +0900 Subject: [PATCH 108/180] wip --- .../server/api/endpoints/messaging/history.ts | 19 ++++++++--- .../api/endpoints/messaging/messages.ts | 34 ++++++++++++------- .../src/server/api/endpoints/my/apps.ts | 11 ++++-- .../src/server/api/endpoints/notes/clips.ts | 28 ++++++++------- .../src/server/api/endpoints/notes/create.ts | 3 ++ .../server/api/endpoints/notes/reactions.ts | 11 ++++-- .../src/server/api/endpoints/notes/show.ts | 8 +++-- 7 files changed, 77 insertions(+), 37 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/messaging/history.ts b/packages/backend/src/server/api/endpoints/messaging/history.ts index d4ee0b3cc3cc..148c9f790d3c 100644 --- a/packages/backend/src/server/api/endpoints/messaging/history.ts +++ b/packages/backend/src/server/api/endpoints/messaging/history.ts @@ -2,7 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Brackets } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { MessagingMessage } from '@/models/entities/messaging-message.js'; -import { MessagingMessages, Mutings, UserGroupJoinings } from '@/models/index.js'; +import type { Mutings , UserGroupJoinings , MessagingMessages } from '@/models/index.js'; +import { MessagingMessageEntityService } from '@/services/entities/MessagingMessageEntityService'; export const meta = { tags: ['messaging'], @@ -35,13 +36,23 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('messagingMessagesRepository') + private messagingMessagesRepository: typeof MessagingMessages, + + @Inject('mutingsRepository') + private mutingsRepository: typeof Mutings, + + @Inject('userGroupJoiningsRepository') + private userGroupJoiningsRepository: typeof UserGroupJoinings, + + private messagingMessageEntityService: MessagingMessageEntityService, ) { super(meta, paramDef, async (ps, me) => { const mute = await this.mutingsRepository.findBy({ muterId: me.id, }); - const groups = ps.group ? await UserGroupJoinings.findBy({ + const groups = ps.group ? await this.userGroupJoiningsRepository.findBy({ userId: me.id, }).then(xs => xs.map(x => x.userGroupId)) : []; @@ -56,7 +67,7 @@ export default class extends Endpoint { ? history.map(m => m.groupId!) : history.map(m => (m.userId === me.id) ? m.recipientId! : m.userId!); - const query = MessagingMessages.createQueryBuilder('message') + const query = this.messagingMessagesRepository.createQueryBuilder('message') .orderBy('message.createdAt', 'DESC'); if (ps.group) { @@ -92,7 +103,7 @@ export default class extends Endpoint { } } - return await Promise.all(history.map(h => MessagingMessages.pack(h.id, me))); + return await Promise.all(history.map(h => this.messagingMessageEntityService.pack(h.id, me))); }); } } diff --git a/packages/backend/src/server/api/endpoints/messaging/messages.ts b/packages/backend/src/server/api/endpoints/messaging/messages.ts index 3414d5694bd0..a1553f47ee75 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages.ts @@ -1,12 +1,14 @@ import { Inject, Injectable } from '@nestjs/common'; import { Brackets } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users } from '@/models/index.js'; -import { MessagingMessages, UserGroups, UserGroupJoinings } from '@/models/index.js'; +import type { Users , MessagingMessages, UserGroupJoinings } from '@/models/index.js'; +import { UserGroups } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { MessagingMessageEntityService } from '@/services/entities/MessagingMessageEntityService.js'; import { ApiError } from '../../error.js'; -import { getUser } from '../../common/getters.js'; import { readUserMessagingMessage, readGroupMessagingMessage, deliverReadActivity } from '../../common/read-messaging-message.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['messaging'], @@ -74,20 +76,26 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('messagingMessagesRepository') + private messagingMessagesRepository: typeof MessagingMessages, + @Inject('userGroupJoiningsRepository') + private userGroupJoiningsRepository: typeof UserGroupJoinings, + + private messagingMessageEntityService: MessagingMessageEntityService, + private userEntityService: UserEntityService, private queryService: QueryService, + private getterService: GetterService, ) { super(meta, paramDef, async (ps, me) => { if (ps.userId != null) { // Fetch recipient (user) - const recipient = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; + const recipient = await this.getterService.getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; }); - const query = this.queryService.makePaginationQuery(MessagingMessages.createQueryBuilder('message'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.messagingMessagesRepository.createQueryBuilder('message'), ps.sinceId, ps.untilId) .andWhere(new Brackets(qb => { qb .where(new Brackets(qb => { qb .where('message.userId = :meId') @@ -113,7 +121,7 @@ export default class extends Endpoint { } } - return await Promise.all(messages.map(message => MessagingMessages.pack(message, me, { + return await Promise.all(messages.map(message => this.messagingMessageEntityService.pack(message, me, { populateRecipient: false, }))); } else if (ps.groupId != null) { @@ -125,7 +133,7 @@ export default class extends Endpoint { } // check joined - const joining = await UserGroupJoinings.findOneBy({ + const joining = await this.userGroupJoiningsRepository.findOneBy({ userId: me.id, userGroupId: recipientGroup.id, }); @@ -134,7 +142,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.groupAccessDenied); } - const query = this.queryService.makePaginationQuery(MessagingMessages.createQueryBuilder('message'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.messagingMessagesRepository.createQueryBuilder('message'), ps.sinceId, ps.untilId) .andWhere('message.groupId = :groupId', { groupId: recipientGroup.id }); const messages = await query.take(ps.limit).getMany(); @@ -144,7 +152,7 @@ export default class extends Endpoint { readGroupMessagingMessage(me.id, recipientGroup.id, messages.map(x => x.id)); } - return await Promise.all(messages.map(message => MessagingMessages.pack(message, me, { + return await Promise.all(messages.map(message => this.messagingMessageEntityService.pack(message, me, { populateGroup: false, }))); } diff --git a/packages/backend/src/server/api/endpoints/my/apps.ts b/packages/backend/src/server/api/endpoints/my/apps.ts index 7e4a6b3923f8..ccfdbfdc6c78 100644 --- a/packages/backend/src/server/api/endpoints/my/apps.ts +++ b/packages/backend/src/server/api/endpoints/my/apps.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Apps } from '@/models/index.js'; +import type { Apps } from '@/models/index.js'; +import { AppEntityService } from '@/services/entities/AppEntityService'; export const meta = { tags: ['account', 'app'], @@ -31,19 +32,23 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('appsRepository') + private appsRepository: typeof Apps, + + private appEntityService: AppEntityService, ) { super(meta, paramDef, async (ps, me) => { const query = { userId: me.id, }; - const apps = await Apps.find({ + const apps = await this.appsRepository.find({ where: query, take: ps.limit, skip: ps.offset, }); - return await Promise.all(apps.map(app => Apps.pack(app, me, { + return await Promise.all(apps.map(app => this.appEntityService.pack(app, me, { detail: true, }))); }); diff --git a/packages/backend/src/server/api/endpoints/notes/clips.ts b/packages/backend/src/server/api/endpoints/notes/clips.ts index 48034dd21de4..bce0bf4f1700 100644 --- a/packages/backend/src/server/api/endpoints/notes/clips.ts +++ b/packages/backend/src/server/api/endpoints/notes/clips.ts @@ -1,9 +1,10 @@ import { In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { ClipNotes, Clips } from '@/models/index.js'; +import type { ClipNotes, Clips } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { getNote } from '../../common/getters.js'; +import { ClipEntityService } from '@/services/entities/ClipEntityService.js'; import { ApiError } from '../../error.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['clips', 'notes'], @@ -41,28 +42,31 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('clipsRepository') + private clipsRepository: typeof Clips, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('clipNotesRepository') + private clipNotesRepository: typeof ClipNotes, + + private clipEntityService: ClipEntityService, + private getterService: GetterService, ) { super(meta, paramDef, async (ps, me) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; + const note = await this.getterService.getNote(ps.noteId).catch(err => { + if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw err; }); - const clipNotes = await ClipNotes.findBy({ + const clipNotes = await this.clipNotesRepository.findBy({ noteId: note.id, }); - const clips = await Clips.findBy({ + const clips = await this.clipsRepository.findBy({ id: In(clipNotes.map(x => x.clipId)), isPublic: true, }); - return await Promise.all(clips.map(x => Clips.pack(x))); + return await Promise.all(clips.map(x => this.clipEntityService.pack(x))); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index fcd82e60dac6..300962939c00 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -10,6 +10,7 @@ import type { Channel } from '@/models/entities/channel.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { noteService } from '@/services/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { noteVisibilities } from '../../../../types.js'; import { ApiError } from '../../error.js'; @@ -171,6 +172,8 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private noteEntityService: NoteEntityService, ) { super(meta, paramDef, async (ps, me) => { let visibleUsers: User[] = []; diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts index 9fbf1bc12492..47fb051795fd 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts @@ -1,8 +1,9 @@ import { DeepPartial } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { NoteReactions } from '@/models/index.js'; +import type { NoteReactions } from '@/models/index.js'; import type { NoteReaction } from '@/models/entities/note-reaction.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { NoteReactionEntityService } from '@/services/entities/NoteReactionEntityService.js'; import { ApiError } from '../../error.js'; import type { FindOptionsWhere } from 'typeorm'; @@ -50,6 +51,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('noteReactionsRepository') + private noteReactionsRepository: typeof NoteReactions, + + private noteReactionEntityService: NoteReactionEntityService, ) { super(meta, paramDef, async (ps, me) => { const query = { @@ -64,7 +69,7 @@ export default class extends Endpoint { query.reaction = type; } - const reactions = await NoteReactions.find({ + const reactions = await this.noteReactionsRepository.find({ where: query, take: ps.limit, skip: ps.offset, @@ -74,7 +79,7 @@ export default class extends Endpoint { relations: ['user', 'user.avatar', 'user.banner', 'note'], }); - return await Promise.all(reactions.map(reaction => NoteReactions.pack(reaction, me))); + return await Promise.all(reactions.map(reaction => this.noteReactionEntityService.pack(reaction, me))); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts index 3e9bfd825cb6..d4e90b9824c2 100644 --- a/packages/backend/src/server/api/endpoints/notes/show.ts +++ b/packages/backend/src/server/api/endpoints/notes/show.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { getNote } from '../../common/getters.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { ApiError } from '../../error.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['notes'], @@ -38,9 +39,12 @@ export default class extends Endpoint { constructor( @Inject('notesRepository') private notesRepository: typeof Notes, + + private noteEntityService: NoteEntityService, + private getterService: GetterService, ) { super(meta, paramDef, async (ps, me) => { - const note = await getNote(ps.noteId).catch(err => { + const note = await this.getterService.getNote(ps.noteId).catch(err => { if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw err; }); From d9b45d631170ed36f57b71767383816d26961b1e Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 17:00:26 +0900 Subject: [PATCH 109/180] wip --- .../src/server/api/endpoints/pages/create.ts | 18 +++++++---- .../src/server/api/endpoints/pages/show.ts | 15 ++++++--- .../users/get-frequently-replied-users.ts | 10 ++++-- .../api/endpoints/users/groups/create.ts | 20 ++++++++---- .../api/endpoints/users/groups/joined.ts | 19 ++++++----- .../api/endpoints/users/groups/owned.ts | 14 ++++---- .../server/api/endpoints/users/groups/show.ts | 19 ++++++----- .../api/endpoints/users/groups/transfer.ts | 30 +++++++++-------- .../api/endpoints/users/groups/update.ts | 16 +++++----- .../api/endpoints/users/lists/create.ts | 15 ++++++--- .../api/endpoints/users/lists/delete.ts | 8 +++-- .../server/api/endpoints/users/lists/list.ts | 14 ++++---- .../server/api/endpoints/users/lists/pull.ts | 32 +++++++++++-------- .../server/api/endpoints/users/lists/push.ts | 32 +++++++++++-------- .../server/api/endpoints/users/lists/show.ts | 14 ++++---- .../api/endpoints/users/lists/update.ts | 13 +++++--- .../server/api/endpoints/users/reactions.ts | 18 ++++++----- .../src/server/api/endpoints/users/show.ts | 14 ++++---- 18 files changed, 191 insertions(+), 130 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/pages/create.ts b/packages/backend/src/server/api/endpoints/pages/create.ts index 24e7b562f540..8064c9e53fda 100644 --- a/packages/backend/src/server/api/endpoints/pages/create.ts +++ b/packages/backend/src/server/api/endpoints/pages/create.ts @@ -1,9 +1,11 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import { Pages, DriveFiles } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import type { Pages } from '@/models/index.js'; +import { DriveFiles } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; import { Page } from '@/models/entities/page.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { PageEntityService } from '@/services/entities/PageEntityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -63,6 +65,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('pagesRepository') + private pagesRepository: typeof Pages, + + private pageEntityService: PageEntityService, private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { @@ -78,7 +84,7 @@ export default class extends Endpoint { } } - await Pages.findBy({ + await this.pagesRepository.findBy({ userId: me.id, name: ps.name, }).then(result => { @@ -87,7 +93,7 @@ export default class extends Endpoint { } }); - const page = await Pages.insert(new Page({ + const page = await this.pagesRepository.insert(new Page({ id: this.idService.genId(), createdAt: new Date(), updatedAt: new Date(), @@ -103,9 +109,9 @@ export default class extends Endpoint { alignCenter: ps.alignCenter, hideTitleWhenPinned: ps.hideTitleWhenPinned, font: ps.font, - })).then(x => Pages.findOneByOrFail(x.identifiers[0])); + })).then(x => this.pagesRepository.findOneByOrFail(x.identifiers[0])); - return await Pages.pack(page); + return await this.pageEntityService.pack(page); }); } } diff --git a/packages/backend/src/server/api/endpoints/pages/show.ts b/packages/backend/src/server/api/endpoints/pages/show.ts index d8bf733f2a31..cd52aae8d599 100644 --- a/packages/backend/src/server/api/endpoints/pages/show.ts +++ b/packages/backend/src/server/api/endpoints/pages/show.ts @@ -1,9 +1,9 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { Users } from '@/models/index.js'; -import { Pages } from '@/models/index.js'; +import type { Users , Pages } from '@/models/index.js'; import type { Page } from '@/models/entities/page.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { PageEntityService } from '@/services/entities/PageEntityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -51,19 +51,24 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + @Inject('pagesRepository') + private pagesRepository: typeof Pages, + + private pageEntityService: PageEntityService, ) { super(meta, paramDef, async (ps, me) => { let page: Page | null = null; if (ps.pageId) { - page = await Pages.findOneBy({ id: ps.pageId }); + page = await this.pagesRepository.findOneBy({ id: ps.pageId }); } else if (ps.name && ps.username) { const author = await this.usersRepository.findOneBy({ host: IsNull(), usernameLower: ps.username.toLowerCase(), }); if (author) { - page = await Pages.findOneBy({ + page = await this.pagesRepository.findOneBy({ name: ps.name, userId: author.id, }); @@ -74,7 +79,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchPage); } - return await Pages.pack(page, me); + return await this.pageEntityService.pack(page, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts index 015f24839d65..36af0af05d0b 100644 --- a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts +++ b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts @@ -3,8 +3,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { maximum } from '@/prelude/array.js'; import type { Notes, Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { ApiError } from '../../error.js'; -import { getUser } from '../../common/getters.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['users'], @@ -60,10 +61,13 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private userEntityService: UserEntityService, + private getterService: GetterService, ) { super(meta, paramDef, async (ps, me) => { // Lookup user - const user = await getUser(ps.userId).catch(err => { + const user = await this.getterService.getUser(ps.userId).catch(err => { if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); throw err; }); @@ -115,7 +119,7 @@ export default class extends Endpoint { const topRepliedUsers = repliedUsersSorted.slice(0, ps.limit); // Make replies object (includes weights) - const repliesObj = await Promise.all(topRepliedthis.usersRepository.map(async (user) => ({ + const repliesObj = await Promise.all(topRepliedUsers.map(async (user) => ({ user: await this.userEntityService.pack(user, me, { detail: true }), weight: repliedUsers[user] / peak, }))); diff --git a/packages/backend/src/server/api/endpoints/users/groups/create.ts b/packages/backend/src/server/api/endpoints/users/groups/create.ts index 51d0338a95bd..fac828597a05 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/create.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/create.ts @@ -1,9 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserGroups, UserGroupJoinings } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import type { UserGroups, UserGroupJoinings } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; import type { UserGroup } from '@/models/entities/user-group.js'; import type { UserGroupJoining } from '@/models/entities/user-group-joining.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserGroupEntityService } from '@/services/entities/UserGroupEntityService'; export const meta = { tags: ['groups'], @@ -33,25 +34,32 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('userGroupsRepository') + private userGroupsRepository: typeof UserGroups, + + @Inject('userGroupJoiningsRepository') + private userGroupJoiningsRepository: typeof UserGroupJoinings, + + private userGroupEntityService: UserGroupEntityService, private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { - const userGroup = await UserGroups.insert({ + const userGroup = await this.userGroupsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), userId: me.id, name: ps.name, - } as UserGroup).then(x => UserGroups.findOneByOrFail(x.identifiers[0])); + } as UserGroup).then(x => this.userGroupsRepository.findOneByOrFail(x.identifiers[0])); // Push the owner - await UserGroupJoinings.insert({ + await this.userGroupJoiningsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), userId: me.id, userGroupId: userGroup.id, } as UserGroupJoining); - return await UserGroups.pack(userGroup); + return await this.userGroupEntityService.pack(userGroup); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/groups/joined.ts b/packages/backend/src/server/api/endpoints/users/groups/joined.ts index 37011c221c96..3904558a00f1 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/joined.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/joined.ts @@ -1,7 +1,8 @@ import { Not, In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { UserGroups, UserGroupJoinings } from '@/models/index.js'; +import type { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserGroupEntityService } from '@/services/entities/UserGroupEntityService'; export const meta = { tags: ['groups', 'account'], @@ -33,25 +34,27 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('userGroupsRepository') + private userGroupsRepository: typeof UserGroups, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('userGroupJoiningsRepository') + private userGroupJoiningsRepository: typeof UserGroupJoinings, + + private userGroupEntityService: UserGroupEntityService, ) { super(meta, paramDef, async (ps, me) => { - const ownedGroups = await UserGroups.findBy({ + const ownedGroups = await this.userGroupsRepository.findBy({ userId: me.id, }); - const joinings = await UserGroupJoinings.findBy({ + const joinings = await this.userGroupJoiningsRepository.findBy({ userId: me.id, ...(ownedGroups.length > 0 ? { userGroupId: Not(In(ownedGroups.map(x => x.id))), } : {}), }); - return await Promise.all(joinings.map(x => UserGroups.pack(x.userGroupId))); + return await Promise.all(joinings.map(x => this.userGroupEntityService.pack(x.userGroupId))); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/groups/owned.ts b/packages/backend/src/server/api/endpoints/users/groups/owned.ts index 00ad9eb025a9..c101736eeb1d 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/owned.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/owned.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserGroups } from '@/models/index.js'; +import type { UserGroups } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserGroupEntityService } from '@/services/entities/UserGroupEntityService'; export const meta = { tags: ['groups', 'account'], @@ -32,18 +33,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('userGroupsRepository') + private userGroupsRepository: typeof UserGroups, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private userGroupEntityService: UserGroupEntityService, ) { super(meta, paramDef, async (ps, me) => { - const userGroups = await UserGroups.findBy({ + const userGroups = await this.userGroupsRepository.findBy({ userId: me.id, }); - return await Promise.all(userGroups.map(x => UserGroups.pack(x))); + return await Promise.all(userGroups.map(x => this.userGroupEntityService.pack(x))); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/groups/show.ts b/packages/backend/src/server/api/endpoints/users/groups/show.ts index 525e520219f5..49c7c1b89687 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/show.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/show.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserGroups, UserGroupJoinings } from '@/models/index.js'; +import type { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserGroupEntityService } from '@/services/entities/UserGroupEntityService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -39,15 +40,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('userGroupsRepository') + private userGroupsRepository: typeof UserGroups, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('userGroupJoiningsRepository') + private userGroupJoiningsRepository: typeof UserGroupJoinings, + + private userGroupEntityService: UserGroupEntityService, ) { super(meta, paramDef, async (ps, me) => { // Fetch the group - const userGroup = await UserGroups.findOneBy({ + const userGroup = await this.userGroupsRepository.findOneBy({ id: ps.groupId, }); @@ -55,7 +58,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchGroup); } - const joining = await UserGroupJoinings.findOneBy({ + const joining = await this.userGroupJoiningsRepository.findOneBy({ userId: me.id, userGroupId: userGroup.id, }); @@ -64,7 +67,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchGroup); } - return await UserGroups.pack(userGroup); + return await this.userGroupEntityService.pack(userGroup); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/groups/transfer.ts b/packages/backend/src/server/api/endpoints/users/groups/transfer.ts index 5bc72a438777..dfad41de8eaa 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/transfer.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/transfer.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserGroups, UserGroupJoinings } from '@/models/index.js'; +import type { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserGroupEntityService } from '@/services/entities/UserGroupEntityService.js'; +import { GetterService } from '@/server/api/common/GetterService.js'; import { ApiError } from '../../../error.js'; -import { getUser } from '../../../common/getters.js'; export const meta = { tags: ['groups', 'users'], @@ -53,15 +54,18 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('userGroupsRepository') + private userGroupsRepository: typeof UserGroups, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('userGroupJoiningsRepository') + private userGroupJoiningsRepository: typeof UserGroupJoinings, + + private userGroupEntityService: UserGroupEntityService, + private getterService: GetterService, ) { super(meta, paramDef, async (ps, me) => { // Fetch the group - const userGroup = await UserGroups.findOneBy({ + const userGroup = await this.userGroupsRepository.findOneBy({ id: ps.groupId, userId: me.id, }); @@ -71,12 +75,12 @@ export default class extends Endpoint { } // Fetch the user - const user = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; + const user = await this.getterService.getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; }); - const joining = await UserGroupJoinings.findOneBy({ + const joining = await this.userGroupJoiningsRepository.findOneBy({ userGroupId: userGroup.id, userId: user.id, }); @@ -85,11 +89,11 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchGroupMember); } - await UserGroups.update(userGroup.id, { + await this.userGroupsRepository.update(userGroup.id, { userId: ps.userId, }); - return await UserGroups.pack(userGroup.id); + return await this.userGroupEntityService.pack(userGroup.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/groups/update.ts b/packages/backend/src/server/api/endpoints/users/groups/update.ts index 0795570d2b6a..deef462fd007 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/update.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/update.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserGroups } from '@/models/index.js'; +import type { UserGroups } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserGroupEntityService } from '@/services/entities/UserGroupEntityService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -40,15 +41,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('userGroupsRepository') + private userGroupsRepository: typeof UserGroups, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private userGroupEntityService: UserGroupEntityService, ) { super(meta, paramDef, async (ps, me) => { // Fetch the group - const userGroup = await UserGroups.findOneBy({ + const userGroup = await this.userGroupsRepository.findOneBy({ id: ps.groupId, userId: me.id, }); @@ -57,11 +57,11 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchGroup); } - await UserGroups.update(userGroup.id, { + await this.userGroupsRepository.update(userGroup.id, { name: ps.name, }); - return await UserGroups.pack(userGroup.id); + return await this.userGroupEntityService.pack(userGroup.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/lists/create.ts b/packages/backend/src/server/api/endpoints/users/lists/create.ts index 029e7f2c697f..1ac81f0a9494 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserLists } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import type { UserLists } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; import type { UserList } from '@/models/entities/user-list.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserListEntityService } from '@/services/entities/UserListEntityService'; export const meta = { tags: ['lists'], @@ -32,17 +33,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('userListsRepository') + private userListsRepository: typeof UserLists, + + private userListEntityService: UserListEntityService, private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { - const userList = await UserLists.insert({ + const userList = await this.userListsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), userId: me.id, name: ps.name, - } as UserList).then(x => UserLists.findOneByOrFail(x.identifiers[0])); + } as UserList).then(x => this.userListsRepository.findOneByOrFail(x.identifiers[0])); - return await UserLists.pack(userList); + return await this.userListEntityService.pack(userList); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/lists/delete.ts b/packages/backend/src/server/api/endpoints/users/lists/delete.ts index a4578516724f..9053965aa2d0 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/delete.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/delete.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserLists } from '@/models/index.js'; +import type { UserLists } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; @@ -33,9 +33,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('userListsRepository') + private userListsRepository: typeof UserLists, ) { super(meta, paramDef, async (ps, me) => { - const userList = await UserLists.findOneBy({ + const userList = await this.userListsRepository.findOneBy({ id: ps.listId, userId: me.id, }); @@ -44,7 +46,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchList); } - await UserLists.delete(userList.id); + await this.userListsRepository.delete(userList.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/lists/list.ts b/packages/backend/src/server/api/endpoints/users/lists/list.ts index 8977a3033d3b..814c6b84615f 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/list.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/list.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserLists } from '@/models/index.js'; +import type { UserLists } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserListEntityService } from '@/services/entities/UserListEntityService'; export const meta = { tags: ['lists', 'account'], @@ -32,18 +33,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('userListsRepository') + private userListsRepository: typeof UserLists, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private userListEntityService: UserListEntityService, ) { super(meta, paramDef, async (ps, me) => { - const userLists = await UserLists.findBy({ + const userLists = await this.userListsRepository.findBy({ userId: me.id, }); - return await Promise.all(userLists.map(x => UserLists.pack(x))); + return await Promise.all(userLists.map(x => this.userListEntityService.pack(x))); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/lists/pull.ts b/packages/backend/src/server/api/endpoints/users/lists/pull.ts index 8e57f6f881d3..5665af37aaa8 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/pull.ts @@ -1,9 +1,11 @@ import { Inject, Injectable } from '@nestjs/common'; -import { publishUserListStream } from '@/services/stream.js'; -import { UserLists, UserListJoinings, Users } from '@/models/index.js'; +import type { UserLists, UserListJoinings } from '@/models/index.js'; +import { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { GetterService } from '@/server/api/common/GetterService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../../../error.js'; -import { getUser } from '../../../common/getters.js'; export const meta = { tags: ['lists', 'users'], @@ -42,15 +44,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('userListsRepository') + private userListsRepository: typeof UserLists, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('userListJoiningsRepository') + private userListJoiningsRepository: typeof UserListJoinings, + + private userEntityService: UserEntityService, + private getterService: GetterService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { // Fetch the list - const userList = await UserLists.findOneBy({ + const userList = await this.userListsRepository.findOneBy({ id: ps.listId, userId: me.id, }); @@ -60,15 +66,15 @@ export default class extends Endpoint { } // Fetch the user - const user = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; + const user = await this.getterService.getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; }); // Pull the user - await UserListJoinings.delete({ userListId: userList.id, userId: user.id }); + await this.userListJoiningsRepository.delete({ userListId: userList.id, userId: user.id }); - publishUserListStream(userList.id, 'userRemoved', await this.userEntityService.pack(user)); + this.globalEventService.publishUserListStream(userList.id, 'userRemoved', await this.userEntityService.pack(user)); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/lists/push.ts b/packages/backend/src/server/api/endpoints/users/lists/push.ts index a9e6a2d1af36..99248c411269 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/push.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/push.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; -import { pushUserToUserList } from '@/services/user-list/push.js'; -import { UserLists, UserListJoinings, Blockings } from '@/models/index.js'; +import type { UserLists, UserListJoinings , Blockings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { GetterService } from '@/server/api/common/GetterService.js'; +import { UserListService } from '@/services/UserListService.js'; import { ApiError } from '../../../error.js'; -import { getUser } from '../../../common/getters.js'; export const meta = { tags: ['lists', 'users'], @@ -54,15 +54,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('userListsRepository') + private userListsRepository: typeof UserLists, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('userListJoiningsRepository') + private userListJoiningsRepository: typeof UserListJoinings, + + @Inject('blockingsRepository') + private blockingsRepository: typeof Blockings, + + private getterService: GetterService, + private userListService: UserListService, ) { super(meta, paramDef, async (ps, me) => { // Fetch the list - const userList = await UserLists.findOneBy({ + const userList = await this.userListsRepository.findOneBy({ id: ps.listId, userId: me.id, }); @@ -72,9 +78,9 @@ export default class extends Endpoint { } // Fetch the user - const user = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; + const user = await this.getterService.getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; }); // Check blocking @@ -88,7 +94,7 @@ export default class extends Endpoint { } } - const exist = await UserListJoinings.findOneBy({ + const exist = await this.userListJoiningsRepository.findOneBy({ userListId: userList.id, userId: user.id, }); @@ -98,7 +104,7 @@ export default class extends Endpoint { } // Push the user - await pushUserToUserList(user, userList); + await this.userListService.push(user, userList); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/lists/show.ts b/packages/backend/src/server/api/endpoints/users/lists/show.ts index 0a573a809fab..eba72cc41d89 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/show.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/show.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserLists } from '@/models/index.js'; +import type { UserLists } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserListEntityService } from '@/services/entities/UserListEntityService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -39,15 +40,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('userListsRepository') + private userListsRepository: typeof UserLists, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private userListEntityService: UserListEntityService, ) { super(meta, paramDef, async (ps, me) => { // Fetch the list - const userList = await UserLists.findOneBy({ + const userList = await this.userListsRepository.findOneBy({ id: ps.listId, userId: me.id, }); @@ -56,7 +56,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchList); } - return await UserLists.pack(userList); + return await this.userListEntityService.pack(userList); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/lists/update.ts b/packages/backend/src/server/api/endpoints/users/lists/update.ts index 473132b1b688..28d751277619 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/update.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/update.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserLists } from '@/models/index.js'; +import type { UserLists } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserListEntityService } from '@/services/entities/UserListEntityService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -40,10 +41,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('userListsRepository') + private userListsRepository: typeof UserLists, + + private userListEntityService: UserListEntityService, ) { super(meta, paramDef, async (ps, me) => { // Fetch the list - const userList = await UserLists.findOneBy({ + const userList = await this.userListsRepository.findOneBy({ id: ps.listId, userId: me.id, }); @@ -52,11 +57,11 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchList); } - await UserLists.update(userList.id, { + await this.userListsRepository.update(userList.id, { name: ps.name, }); - return await UserLists.pack(userList.id); + return await this.userListEntityService.pack(userList.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts index bf76413fefef..6c7d83a8d715 100644 --- a/packages/backend/src/server/api/endpoints/users/reactions.ts +++ b/packages/backend/src/server/api/endpoints/users/reactions.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; -import { NoteReactions, UserProfiles } from '@/models/index.js'; +import type { UserProfiles , NoteReactions } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import { NoteReactionEntityService } from '@/services/entities/NoteReactionEntityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -47,22 +48,23 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('noteReactionsRepository') + private noteReactionsRepository: typeof NoteReactions, + private noteReactionEntityService: NoteReactionEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const profile = await UserProfiles.findOneByOrFail({ userId: ps.userId }); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: ps.userId }); if (me == null || (me.id !== ps.userId && !profile.publicReactions)) { throw new ApiError(meta.errors.reactionsNotPublic); } - const query = this.queryService.makePaginationQuery(NoteReactions.createQueryBuilder('reaction'), + const query = this.queryService.makePaginationQuery(this.noteReactionsRepository.createQueryBuilder('reaction'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('reaction.userId = :userId', { userId: ps.userId }) .leftJoinAndSelect('reaction.note', 'note'); @@ -73,7 +75,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return await Promise.all(reactions.map(reaction => NoteReactions.pack(reaction, me, { withNote: true }))); + return await Promise.all(reactions.map(reaction => this.noteReactionEntityService.pack(reaction, me, { withNote: true }))); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts index d53b45f58953..25d546dde7bf 100644 --- a/packages/backend/src/server/api/endpoints/users/show.ts +++ b/packages/backend/src/server/api/endpoints/users/show.ts @@ -1,11 +1,12 @@ import { In, IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { resolveUser } from '@/services/remote/resolve-user.js'; import type { Users } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { apiLogger } from '../../logger.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { ResolveUserService } from '@/services/remote/ResolveUserService.js'; import { ApiError } from '../../error.js'; +import { ApiLoggerService } from '../../ApiLoggerService.js'; import type { FindOptionsWhere } from 'typeorm'; export const meta = { @@ -86,8 +87,9 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private userEntityService: UserEntityService, + private resolveUserService: ResolveUserService, + private apiLoggerService: ApiLoggerService, ) { super(meta, paramDef, async (ps, me) => { let user; @@ -118,8 +120,8 @@ export default class extends Endpoint { } else { // Lookup user if (typeof ps.host === 'string' && typeof ps.username === 'string') { - user = await resolveUser(ps.username, ps.host).catch(e => { - apiLogger.warn(`failed to resolve remote user: ${e}`); + user = await this.resolveUserService.resolveUser(ps.username, ps.host).catch(err => { + this.apiLoggerService.logger.warn(`failed to resolve remote user: ${err}`); throw new ApiError(meta.errors.failedToResolveRemoteUser); }); } else { From 169552be97dbede1dd9fd04c8dcb83375d8663e0 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 17:18:16 +0900 Subject: [PATCH 110/180] wip --- .../src/server/api/stream/channels/antenna.ts | 8 +-- .../src/server/api/stream/channels/channel.ts | 17 +++-- .../api/stream/channels/global-timeline.ts | 9 ++- .../src/server/api/stream/channels/hashtag.ts | 8 +-- .../api/stream/channels/home-timeline.ts | 8 +-- .../api/stream/channels/hybrid-timeline.ts | 9 ++- .../api/stream/channels/local-timeline.ts | 9 ++- .../src/server/api/stream/channels/main.ts | 8 +-- .../server/api/stream/channels/user-list.ts | 12 ++-- .../src/server/web/ClientServerService.ts | 26 +++++-- .../src/services/CreateNotificationService.ts | 6 +- packages/backend/src/services/DriveService.ts | 63 ++++++++++------- .../backend/src/services/NoteCreateService.ts | 67 ++++++++++--------- .../src/services/UserBlockingService.ts | 29 ++++---- .../src/services/UserFollowingService.ts | 25 +++---- .../backend/src/services/UserListService.ts | 14 ++-- 16 files changed, 170 insertions(+), 148 deletions(-) diff --git a/packages/backend/src/server/api/stream/channels/antenna.ts b/packages/backend/src/server/api/stream/channels/antenna.ts index 891480705fed..65cd3b5253df 100644 --- a/packages/backend/src/server/api/stream/channels/antenna.ts +++ b/packages/backend/src/server/api/stream/channels/antenna.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notes } from '@/models/index.js'; import { isUserRelated } from '@/misc/is-user-related.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import Channel from '../channel.js'; import type { StreamMessages } from '../types.js'; @@ -11,7 +12,7 @@ class AntennaChannel extends Channel { private antennaId: string; constructor( - private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, id: string, connection: Channel['connection'], @@ -56,14 +57,13 @@ export class AntennaChannelService { public readonly requireCredential = AntennaChannel.requireCredential; constructor( - @Inject('notesRepository') - private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, ) { } public create(id: string, connection: Channel['connection']): AntennaChannel { return new AntennaChannel( - this.notesRepository, + this.noteEntityService, id, connection, ); diff --git a/packages/backend/src/server/api/stream/channels/channel.ts b/packages/backend/src/server/api/stream/channels/channel.ts index d46464669b64..42c5577b8990 100644 --- a/packages/backend/src/server/api/stream/channels/channel.ts +++ b/packages/backend/src/server/api/stream/channels/channel.ts @@ -3,6 +3,8 @@ import type { Notes, Users } from '@/models/index.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import type { User } from '@/models/entities/user.js'; import type { Packed } from '@/misc/schema.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; import Channel from '../channel.js'; import type { StreamMessages } from '../types.js'; @@ -15,8 +17,8 @@ class ChannelChannel extends Channel { private emitTypersIntervalId: ReturnType; constructor( - private usersRepository: typeof Users, - private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, + private userEntityService: UserEntityService, id: string, connection: Channel['connection'], @@ -103,18 +105,15 @@ export class ChannelChannelService { public readonly requireCredential = ChannelChannel.requireCredential; constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, + private userEntityService: UserEntityService, ) { } public create(id: string, connection: Channel['connection']): ChannelChannel { return new ChannelChannel( - this.usersRepository, - this.notesRepository, + this.noteEntityService, + this.userEntityService, id, connection, ); diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index 161d9342be2e..0abe2d1e5862 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -5,6 +5,7 @@ import { isInstanceMuted } from '@/misc/is-instance-muted.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import type { Packed } from '@/misc/schema.js'; import { MetaService } from '@/services/MetaService.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import Channel from '../channel.js'; class GlobalTimelineChannel extends Channel { @@ -14,7 +15,7 @@ class GlobalTimelineChannel extends Channel { constructor( private metaService: MetaService, - private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, id: string, connection: Channel['connection'], @@ -89,17 +90,15 @@ export class GlobalTimelineChannelService { public readonly requireCredential = GlobalTimelineChannel.requireCredential; constructor( - @Inject('notesRepository') - private notesRepository: typeof Notes, - private metaService: MetaService, + private noteEntityService: NoteEntityService, ) { } public create(id: string, connection: Channel['connection']): GlobalTimelineChannel { return new GlobalTimelineChannel( this.metaService, - this.notesRepository, + this.noteEntityService, id, connection, ); diff --git a/packages/backend/src/server/api/stream/channels/hashtag.ts b/packages/backend/src/server/api/stream/channels/hashtag.ts index 898af4dec818..9c1fda875a51 100644 --- a/packages/backend/src/server/api/stream/channels/hashtag.ts +++ b/packages/backend/src/server/api/stream/channels/hashtag.ts @@ -3,6 +3,7 @@ import type { Notes } from '@/models/index.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import type { Packed } from '@/misc/schema.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import Channel from '../channel.js'; class HashtagChannel extends Channel { @@ -12,7 +13,7 @@ class HashtagChannel extends Channel { private q: string[][]; constructor( - private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, id: string, connection: Channel['connection'], @@ -64,14 +65,13 @@ export class HashtagChannelService { public readonly requireCredential = HashtagChannel.requireCredential; constructor( - @Inject('notesRepository') - private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, ) { } public create(id: string, connection: Channel['connection']): HashtagChannel { return new HashtagChannel( - this.notesRepository, + this.noteEntityService, id, connection, ); diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index 8cc7ff20aba6..cf1cafba41a9 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -4,6 +4,7 @@ import { checkWordMute } from '@/misc/check-word-mute.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { isInstanceMuted } from '@/misc/is-instance-muted.js'; import type { Packed } from '@/misc/schema.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import Channel from '../channel.js'; class HomeTimelineChannel extends Channel { @@ -12,7 +13,7 @@ class HomeTimelineChannel extends Channel { public static requireCredential = true; constructor( - private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, id: string, connection: Channel['connection'], @@ -96,14 +97,13 @@ export class HomeTimelineChannelService { public readonly requireCredential = HomeTimelineChannel.requireCredential; constructor( - @Inject('notesRepository') - private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, ) { } public create(id: string, connection: Channel['connection']): HomeTimelineChannel { return new HomeTimelineChannel( - this.notesRepository, + this.noteEntityService, id, connection, ); diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index c0d671c322fa..3ba88f041303 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -6,6 +6,7 @@ import { isInstanceMuted } from '@/misc/is-instance-muted.js'; import type { Packed } from '@/misc/schema.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import { MetaService } from '@/services/MetaService.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import Channel from '../channel.js'; class HybridTimelineChannel extends Channel { @@ -15,7 +16,7 @@ class HybridTimelineChannel extends Channel { constructor( private metaService: MetaService, - private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, id: string, connection: Channel['connection'], @@ -106,17 +107,15 @@ export class HybridTimelineChannelService { public readonly requireCredential = HybridTimelineChannel.requireCredential; constructor( - @Inject('notesRepository') - private notesRepository: typeof Notes, - private metaService: MetaService, + private noteEntityService: NoteEntityService, ) { } public create(id: string, connection: Channel['connection']): HybridTimelineChannel { return new HybridTimelineChannel( this.metaService, - this.notesRepository, + this.noteEntityService, id, connection, ); diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index 86066a43d386..f17b35d45cfa 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -4,6 +4,7 @@ import { checkWordMute } from '@/misc/check-word-mute.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import type { Packed } from '@/misc/schema.js'; import { MetaService } from '@/services/MetaService.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import Channel from '../channel.js'; class LocalTimelineChannel extends Channel { @@ -13,7 +14,7 @@ class LocalTimelineChannel extends Channel { constructor( private metaService: MetaService, - private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, id: string, connection: Channel['connection'], @@ -86,17 +87,15 @@ export class LocalTimelineChannelService { public readonly requireCredential = LocalTimelineChannel.requireCredential; constructor( - @Inject('notesRepository') - private notesRepository: typeof Notes, - private metaService: MetaService, + private noteEntityService: NoteEntityService, ) { } public create(id: string, connection: Channel['connection']): LocalTimelineChannel { return new LocalTimelineChannel( this.metaService, - this.notesRepository, + this.noteEntityService, id, connection, ); diff --git a/packages/backend/src/server/api/stream/channels/main.ts b/packages/backend/src/server/api/stream/channels/main.ts index 73794d2e8479..3a610278a763 100644 --- a/packages/backend/src/server/api/stream/channels/main.ts +++ b/packages/backend/src/server/api/stream/channels/main.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notes } from '@/models/index.js'; import { isInstanceMuted, isUserFromMutedInstance } from '@/misc/is-instance-muted.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import Channel from '../channel.js'; class MainChannel extends Channel { @@ -9,7 +10,7 @@ class MainChannel extends Channel { public static requireCredential = true; constructor( - private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, id: string, connection: Channel['connection'], @@ -61,14 +62,13 @@ export class MainChannelService { public readonly requireCredential = MainChannel.requireCredential; constructor( - @Inject('notesRepository') - private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, ) { } public create(id: string, connection: Channel['connection']): MainChannel { return new MainChannel( - this.notesRepository, + this.noteEntityService, id, connection, ); diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts index 91a5b3e85269..bf6d97538cff 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -3,6 +3,7 @@ import type { Notes, UserListJoinings, UserLists } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import type { Packed } from '@/misc/schema.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import Channel from '../channel.js'; class UserListChannel extends Channel { @@ -14,10 +15,10 @@ class UserListChannel extends Channel { private listUsersClock: NodeJS.Timer; constructor( - private notesRepository: typeof Notes, private userListsRepository: typeof UserLists, private userListJoiningsRepository: typeof UserListJoinings, - + private noteEntityService: NoteEntityService, + id: string, connection: Channel['connection'], ) { @@ -105,22 +106,21 @@ export class UserListChannelService { public readonly requireCredential = UserListChannel.requireCredential; constructor( - @Inject('notesRepository') - private notesRepository: typeof Notes, - @Inject('userListsRepository') private userListsRepository: typeof UserLists, @Inject('userListJoiningsRepository') private userListJoiningsRepository: typeof UserListJoinings, + + private noteEntityService: NoteEntityService, ) { } public create(id: string, connection: Channel['connection']): UserListChannel { return new UserListChannel( - this.notesRepository, this.userListsRepository, this.userListJoiningsRepository, + this.noteEntityService, id, connection, ); diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 2a4ffac574ce..61aa9290140f 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -20,9 +20,15 @@ import { DI_SYMBOLS } from '@/di-symbols.js'; import * as Acct from '@/misc/acct.js'; import { MetaService } from '@/services/MetaService.js'; import { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from '@/queue/queue.module.js'; -import { UrlPreviewService } from './UrlPreviewService.js'; -import { FeedService } from './FeedService.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { PageEntityService } from '@/services/entities/PageEntityService.js'; +import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService.js'; +import { ClipEntityService } from '@/services/entities/ClipEntityService.js'; +import { ChannelEntityService } from '@/services/entities/ChannelEntityService.js'; import manifest from './manifest.json' assert { type: 'json' }; +import { FeedService } from './FeedService.js'; +import { UrlPreviewService } from './UrlPreviewService.js'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); @@ -59,6 +65,12 @@ export class ClientServerService { @Inject('pagesRepository') private pagesRepository: typeof Pages, + private userEntityService: UserEntityService, + private noteEntityService: NoteEntityService, + private pageEntityService: PageEntityService, + private galleryPostEntityService: GalleryPostEntityService, + private clipEntityService: ClipEntityService, + private channelEntityService: ChannelEntityService, private metaService: MetaService, private urlPreviewService: UrlPreviewService, private feedService: FeedService, @@ -345,7 +357,7 @@ export class ClientServerService { await ctx.render('user', { user, profile, me, - avatarUrl: await this.usersRepository.getAvatarUrl(user), + avatarUrl: await this.userEntityService.getAvatarUrl(user), sub: ctx.params.sub, instanceName: meta.name || 'Misskey', icon: meta.iconUrl, @@ -388,7 +400,7 @@ export class ClientServerService { await ctx.render('note', { note: _note, profile, - avatarUrl: await this.usersRepository.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: note.userId })), + avatarUrl: await this.userEntityService.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: note.userId })), // TODO: Let locale changeable by instance setting summary: getNoteSummary(_note), instanceName: meta.name || 'Misskey', @@ -426,7 +438,7 @@ export class ClientServerService { await ctx.render('page', { page: _page, profile, - avatarUrl: await this.usersRepository.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: page.userId })), + avatarUrl: await this.userEntityService.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: page.userId })), instanceName: meta.name || 'Misskey', icon: meta.iconUrl, themeColor: meta.themeColor, @@ -458,7 +470,7 @@ export class ClientServerService { await ctx.render('clip', { clip: _clip, profile, - avatarUrl: await this.usersRepository.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: clip.userId })), + avatarUrl: await this.userEntityService.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: clip.userId })), instanceName: meta.name || 'Misskey', icon: meta.iconUrl, themeColor: meta.themeColor, @@ -483,7 +495,7 @@ export class ClientServerService { await ctx.render('gallery-post', { post: _post, profile, - avatarUrl: await this.usersRepository.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: post.userId })), + avatarUrl: await this.userEntityService.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: post.userId })), instanceName: meta.name || 'Misskey', icon: meta.iconUrl, themeColor: meta.themeColor, diff --git a/packages/backend/src/services/CreateNotificationService.ts b/packages/backend/src/services/CreateNotificationService.ts index 3c5dd532c8e7..18b6b41175e7 100644 --- a/packages/backend/src/services/CreateNotificationService.ts +++ b/packages/backend/src/services/CreateNotificationService.ts @@ -2,8 +2,9 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Mutings, Notifications, UserProfiles , Users } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; import type { Notification } from '@/models/entities/notification.js'; -import type { GlobalEventService } from '@/services/GlobalEventService.js'; -import type { IdService } from '@/services/IdService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { IdService } from '@/services/IdService.js'; +import { NotificationEntityService } from './entities/NotificationEntityService'; @Injectable() export class CreateNotificationService { @@ -20,6 +21,7 @@ export class CreateNotificationService { @Inject('mutingsRepository') private mutingsRepository: typeof Mutings, + private notificationEntityService: NotificationEntityService, private idService: IdService, private globalEventServie: GlobalEventService, ) { diff --git a/packages/backend/src/services/DriveService.ts b/packages/backend/src/services/DriveService.ts index 3a13b6c80dee..fa0fc2f3fd3b 100644 --- a/packages/backend/src/services/DriveService.ts +++ b/packages/backend/src/services/DriveService.ts @@ -4,31 +4,34 @@ import { v4 as uuid } from 'uuid'; import sharp from 'sharp'; import { IsNull } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { DriveFiles } from '@/models/index.js'; -import { DriveFolders, Users } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { DriveFiles , Users , DriveFolders , UserProfiles } from '@/models/index.js'; + +import { Config } from '@/config/types.js'; import Logger from '@/Logger.js'; import type { IRemoteUser, User } from '@/models/entities/user.js'; -import type { MetaService } from '@/services/MetaService.js'; +import { MetaService } from '@/services/MetaService.js'; import { DriveFile } from '@/models/entities/drive-file.js'; -import type { IdService } from '@/services/IdService.js'; +import { IdService } from '@/services/IdService.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { contentDisposition } from '@/misc/content-disposition.js'; import { getFileInfo } from '@/misc/get-file-info.js'; -import type { GlobalEventService } from '@/services/GlobalEventService.js'; -import type { VideoProcessingService } from '@/services/VideoProcessingService.js'; -import type { IImage, ImageProcessingService } from '@/services/ImageProcessingService.js'; -import type { QueueService } from '@/queue/queue.service.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { VideoProcessingService } from '@/services/VideoProcessingService.js'; +import { ImageProcessingService } from '@/services/ImageProcessingService.js'; +import type { IImage } from '@/services/ImageProcessingService.js'; +import { QueueService } from '@/queue/queue.service.js'; import type { DriveFolder } from '@/models/entities/drive-folder.js'; import { createTemp } from '@/misc/create-temp.js'; -import type DriveChart from '@/services/chart/charts/drive.js'; -import type PerUserDriveChart from '@/services/chart/charts/per-user-drive.js'; -import type InstanceChart from '@/services/chart/charts/instance.js'; -import type { DownloadService } from '@/services/DownloadService.js'; -import type { S3Service } from '@/services/drive/S3Service.js'; -import type { InternalStorageService } from '@/services/drive/InternalStorageService.js'; +import DriveChart from '@/services/chart/charts/drive.js'; +import PerUserDriveChart from '@/services/chart/charts/per-user-drive.js'; +import InstanceChart from '@/services/chart/charts/instance.js'; +import { DownloadService } from '@/services/DownloadService.js'; +import { S3Service } from '@/services/drive/S3Service.js'; +import { InternalStorageService } from '@/services/drive/InternalStorageService.js'; +import { DriveFileEntityService } from './entities/DriveFileEntityService'; +import { UserEntityService } from './entities/UserEntityService'; import type S3 from 'aws-sdk/clients/s3.js'; type AddFileArgs = { @@ -82,9 +85,17 @@ export class DriveService { @Inject('usersRepository') private usersRepository: typeof Users, + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + @Inject('driveFilesRepository') private driveFilesRepository: typeof DriveFiles, + @Inject('driveFoldersRepository') + private driveFoldersRepository: typeof DriveFolders, + + private userEntityService: UserEntityService, + private driveFileEntityService: DriveFileEntityService, private idService: IdService, private metaService: MetaService, private downloadService: DownloadService, @@ -405,8 +416,8 @@ export class DriveService { const instance = await this.metaService.fetch(); if (user == null) skipNsfwCheck = true; if (instance.sensitiveMediaDetection === 'none') skipNsfwCheck = true; - if (user && instance.sensitiveMediaDetection === 'local' && Users.isRemoteUser(user)) skipNsfwCheck = true; - if (user && instance.sensitiveMediaDetection === 'remote' && Users.isLocalUser(user)) skipNsfwCheck = true; + if (user && instance.sensitiveMediaDetection === 'local' && this.userEntityService.isRemoteUser(user)) skipNsfwCheck = true; + if (user && instance.sensitiveMediaDetection === 'remote' && this.userEntityService.isLocalUser(user)) skipNsfwCheck = true; const info = await getFileInfo(path, { skipSensitiveDetection: skipNsfwCheck, @@ -444,13 +455,13 @@ export class DriveService { //#region Check drive usage if (user && !isLink) { - const usage = await this.driveFilesRepository.calcDriveUsageOf(user); - const u = await Users.findOneBy({ id: user.id }); + const usage = await this.driveFileEntityService.calcDriveUsageOf(user); + const u = await this.usersRepository.findOneBy({ id: user.id }); const instance = await this.metaService.fetch(); - let driveCapacity = 1024 * 1024 * (Users.isLocalUser(user) ? instance.localDriveCapacityMb : instance.remoteDriveCapacityMb); + let driveCapacity = 1024 * 1024 * (this.userEntityService.isLocalUser(user) ? instance.localDriveCapacityMb : instance.remoteDriveCapacityMb); - if (Users.isLocalUser(user) && u?.driveCapacityOverrideMb != null) { + if (this.userEntityService.isLocalUser(user) && u?.driveCapacityOverrideMb != null) { driveCapacity = 1024 * 1024 * u.driveCapacityOverrideMb; this.#registerLogger.debug('drive capacity override applied'); this.#registerLogger.debug(`overrideCap: ${driveCapacity}bytes, usage: ${usage}bytes, u+s: ${usage + info.size}bytes`); @@ -460,11 +471,11 @@ export class DriveService { // If usage limit exceeded if (usage + info.size > driveCapacity) { - if (Users.isLocalUser(user)) { + if (this.userEntityService.isLocalUser(user)) { throw new IdentifiableError('c6244ed2-a39a-4e1c-bf93-f0fbd7764fa6', 'No free space.'); } else { // (アバターまたはバナーを含まず)最も古いファイルを削除する - this.#deleteOldFile(await Users.findOneByOrFail({ id: user.id }) as IRemoteUser); + this.#deleteOldFile(await this.usersRepository.findOneByOrFail({ id: user.id }) as IRemoteUser); } } } @@ -475,7 +486,7 @@ export class DriveService { return null; } - const driveFolder = await DriveFolders.findOneBy({ + const driveFolder = await this.driveFoldersRepository.findOneBy({ id: folderId, userId: user ? user.id : IsNull(), }); @@ -499,7 +510,7 @@ export class DriveService { properties['orientation'] = info.orientation; } - const profile = user ? await UserProfiles.findOneBy({ userId: user.id }) : null; + const profile = user ? await this.userProfilesRepository.findOneBy({ userId: user.id }) : null; const folder = await fetchFolder(); @@ -518,7 +529,7 @@ export class DriveService { file.maybeSensitive = info.sensitive; file.maybePorn = info.porn; file.isSensitive = user - ? Users.isLocalUser(user) && profile!.alwaysMarkNsfw ? true : + ? this.userEntityService.isLocalUser(user) && profile!.alwaysMarkNsfw ? true : (sensitive !== null && sensitive !== undefined) ? sensitive : false diff --git a/packages/backend/src/services/NoteCreateService.ts b/packages/backend/src/services/NoteCreateService.ts index edb5f806017c..7e011afa37c4 100644 --- a/packages/backend/src/services/NoteCreateService.ts +++ b/packages/backend/src/services/NoteCreateService.ts @@ -6,19 +6,12 @@ import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mf import { extractHashtags } from '@/misc/extract-hashtags.js'; import type { IMentionedRemoteUsers } from '@/models/entities/note.js'; import { Note } from '@/models/entities/note.js'; -import type { Notes } from '@/models/index.js'; -import { Mutings, Users, NoteWatchings, Instances, UserProfiles, MutedNotes, Channels, ChannelFollowings, NoteThreadMutings } from '@/models/index.js'; +import type { Notes , Users } from '@/models/index.js'; +import { Mutings, NoteWatchings, Instances, UserProfiles, MutedNotes, Channels, ChannelFollowings, NoteThreadMutings } from '@/models/index.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import type { App } from '@/models/entities/app.js'; -import { insertNoteUnread } from '@/services/note/unread.js'; import { concat } from '@/prelude/array.js'; -import { resolveUser } from '@/services/remote/resolve-user.js'; -import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; -import renderAnnounce from '@/services/remote/activitypub/renderer/announce.js'; -import renderCreate from '@/services/remote/activitypub/renderer/create.js'; -import renderNote from '@/services/remote/activitypub/renderer/note.js'; -import DeliverManager from '@/services/remote/activitypub/deliver-manager.js'; -import type { IdService } from '@/services/IdService.js'; +import { IdService } from '@/services/IdService.js'; import type { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; import type { IPoll } from '@/models/entities/poll.js'; import { Poll } from '@/models/entities/poll.js'; @@ -32,21 +25,26 @@ import { getAntennas } from '@/misc/antenna-cache.js'; import { Cache } from '@/misc/cache.js'; import type { UserProfile } from '@/models/entities/user-profile.js'; import { db } from '@/db/postgre.js'; -import type { RelayService } from '@/services/RelayService.js'; -import type { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; +import { RelayService } from '@/services/RelayService.js'; +import { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; -import type NotesChart from '@/services/chart/charts/notes.js'; -import type PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; -import type InstanceChart from '@/services/chart/charts/instance.js'; -import type ActiveUsersChart from '@/services/chart/charts/active-users.js'; -import type { GlobalEventService } from '@/services/GlobalEventService.js'; -import type { CreateNotificationService } from '@/services/CreateNotificationService.js'; -import type { WebhookService } from '@/services/WebhookService.js'; -import type { HashtagService } from '@/services/HashtagService.js'; -import type { AntennaService } from '@/services/AntennaService.js'; -import type { QueueService } from '@/queue/queue.service.js'; +import { Config } from '@/config/types.js'; +import NotesChart from '@/services/chart/charts/notes.js'; +import PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; +import InstanceChart from '@/services/chart/charts/instance.js'; +import ActiveUsersChart from '@/services/chart/charts/active-users.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { CreateNotificationService } from '@/services/CreateNotificationService.js'; +import { WebhookService } from '@/services/WebhookService.js'; +import { HashtagService } from '@/services/HashtagService.js'; +import { AntennaService } from '@/services/AntennaService.js'; +import { QueueService } from '@/queue/queue.service.js'; import es from '../db/elasticsearch.js'; +import { NoteEntityService } from './entities/NoteEntityService.js'; +import { UserEntityService } from './entities/UserEntityService.js'; +import { NoteReadService } from './NoteReadService.js'; +import { ApRendererService } from './remote/activitypub/ApRendererService.js'; +import { ResolveUserService } from './remote/ResolveUserService.js'; const mutedWordsCache = new Cache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5); @@ -145,15 +143,20 @@ export class NoteCreateService { @Inject('notesRepository') private notesRepository: typeof Notes, + private userEntityService: UserEntityService, + private noteEntityService: NoteEntityService, private idService: IdService, private globalEventServie: GlobalEventService, private queueService: QueueService, + private noteReadService: NoteReadService, private createNotificationService: CreateNotificationService, private relayService: RelayService, private federatedInstanceService: FederatedInstanceService, private hashtagService: HashtagService, private antennaService: AntennaService, private webhookService: WebhookService, + private resolveUserService: ResolveUserService, + private apRendererService: ApRendererService, private notesChart: NotesChart, private perUserNotesChart: PerUserNotesChart, private activeUsersChart: ActiveUsersChart, @@ -281,7 +284,7 @@ export class NoteCreateService { async #insertNote(user: { id: User['id']; host: User['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]) { const insert = new Note({ - id: genId(data.createdAt!), + id: this.idService.genId(data.createdAt!), createdAt: data.createdAt!, fileIds: data.files ? data.files.map(file => file.id) : [], replyId: data.reply ? data.reply.id : null, @@ -324,7 +327,7 @@ export class NoteCreateService { if (mentionedUsers.length > 0) { insert.mentions = mentionedUsers.map(u => u.id); const profiles = await UserProfiles.findBy({ userId: In(insert.mentions) }); - insert.mentionedRemoteUsers = JSON.stringify(mentionedUsers.filter(u => Users.isRemoteUser(u)).map(u => { + insert.mentionedRemoteUsers = JSON.stringify(mentionedUsers.filter(u => this.userEntityService.isRemoteUser(u)).map(u => { const profile = profiles.find(p => p.userId === u.id); const url = profile != null ? profile.url : null; return { @@ -436,7 +439,7 @@ export class NoteCreateService { if (note.channelId) { ChannelFollowings.findBy({ followeeId: note.channelId }).then(followings => { for (const following of followings) { - insertNoteUnread(following.followerId, note, { + this.noteReadService.insertNoteUnread(following.followerId, note, { isSpecified: false, isMentioned: false, }); @@ -474,7 +477,7 @@ export class NoteCreateService { // ローカルユーザーのみ if (!this.userEntityService.isLocalUser(u)) continue; - insertNoteUnread(u.id, note, { + this.noteReadService.insertNoteUnread(u.id, note, { isSpecified: true, isMentioned: false, }); @@ -484,7 +487,7 @@ export class NoteCreateService { // ローカルユーザーのみ if (!this.userEntityService.isLocalUser(u)) continue; - insertNoteUnread(u.id, note, { + this.noteReadService.insertNoteUnread(u.id, note, { isSpecified: false, isMentioned: true, }); @@ -672,10 +675,10 @@ export class NoteCreateService { if (data.localOnly) return null; const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length === 0) - ? renderAnnounce(data.renote.uri ? data.renote.uri : `${this.config.url}/notes/${data.renote.id}`, note) - : renderCreate(await renderNote(note, false), note); + ? this.apRendererService.renderAnnounce(data.renote.uri ? data.renote.uri : `${this.config.url}/notes/${data.renote.id}`, note) + : this.apRendererService.renderCreate(await this.apRendererService.renderNote(note, false), note); - return renderActivity(content); + return this.apRendererService.renderActivity(content); } #index(note: Note) { @@ -729,7 +732,7 @@ export class NoteCreateService { const mentions = extractMentions(tokens); let mentionedUsers = (await Promise.all(mentions.map(m => - resolveUser(m.username, m.host || user.host).catch(() => null), + this.resolveUserService.resolveUser(m.username, m.host || user.host).catch(() => null), ))).filter(x => x != null) as User[]; // Drop duplicate users diff --git a/packages/backend/src/services/UserBlockingService.ts b/packages/backend/src/services/UserBlockingService.ts index f4835d43f72a..c0b6ade20348 100644 --- a/packages/backend/src/services/UserBlockingService.ts +++ b/packages/backend/src/services/UserBlockingService.ts @@ -2,18 +2,15 @@ import { Inject, Injectable } from '@nestjs/common'; import type { FollowRequests , Followings , UserLists , UserListJoinings , Users , Blockings } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import { IdService } from '@/services/IdService.js'; import type { CacheableUser, User } from '@/models/entities/user.js'; import type { Blocking } from '@/models/entities/blocking.js'; -import type { QueueService } from '@/queue/queue.service.js'; -import type { GlobalEventService } from '@/services/GlobalEventService.js'; -import { renderActivity } from '@/services/remote/activitypub/renderer'; -import { renderBlock } from '@/services/remote/activitypub/renderer/block'; -import renderFollow from '@/services/remote/activitypub/renderer/follow.js'; -import renderUndo from '@/services/remote/activitypub/renderer/undo.js'; -import renderReject from '@/services/remote/activitypub/renderer/reject.js'; -import type PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; -import type { WebhookService } from '@/services/webhookService.js'; +import { QueueService } from '@/queue/queue.service.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; +import { UserEntityService } from './entities/UserEntityService'; +import { WebhookService } from './WebhookService'; +import { ApRendererService } from './remote/activitypub/ApRendererService'; @Injectable() export class UserBlockingService { @@ -36,10 +33,12 @@ export class UserBlockingService { @Inject('userListJoiningsRepository') private userListJoiningsRepository: typeof UserListJoinings, + private userEntityService: UserEntityService, private idService: IdService, private queueService: QueueService, private globalEventServie: GlobalEventService, private webhookService: WebhookService, + private apRendererService: ApRendererService, private perUserFollowingChart: PerUserFollowingChart, ) { } @@ -65,7 +64,7 @@ export class UserBlockingService { await this.blockingsRepository.insert(blocking); if (this.userEntityService.isLocalUser(blocker) && this.userEntityService.isRemoteUser(blockee)) { - const content = renderActivity(renderBlock(blocking)); + const content = this.apRendererService.renderActivity(this.apRendererService.renderBlock(blocking)); this.queueService.deliver(blocker, content, blockee.inbox); } } @@ -109,13 +108,13 @@ export class UserBlockingService { // リモートにフォローリクエストをしていたらUndoFollow送信 if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { - const content = renderActivity(renderUndo(renderFollow(follower, followee), follower)); + const content = this.apRendererService.renderActivity(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower)); this.queueService.deliver(follower, content, followee.inbox); } // リモートからフォローリクエストを受けていたらReject送信 if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { - const content = renderActivity(renderReject(renderFollow(follower, followee, request.requestId!), followee)); + const content = this.apRendererService.renderActivity(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee, request.requestId!), followee)); this.queueService.deliver(followee, content, follower.inbox); } } @@ -156,7 +155,7 @@ export class UserBlockingService { // リモートにフォローをしていたらUndoFollow送信 if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { - const content = renderActivity(renderUndo(renderFollow(follower, followee), follower)); + const content = this.apRendererService.renderActivity(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower)); this.queueService.deliver(follower, content, followee.inbox); } } @@ -194,7 +193,7 @@ export class UserBlockingService { // deliver if remote bloking if (this.userEntityService.isLocalUser(blocker) && this.userEntityService.isRemoteUser(blockee)) { - const content = renderActivity(renderUndo(renderBlock(blocking), blocker)); + const content = this.apRendererService.renderActivity(this.apRendererService.renderUndo(this.apRendererService.renderBlock(blocking), blocker)); this.queueService.deliver(blocker, content, blockee.inbox); } } diff --git a/packages/backend/src/services/UserFollowingService.ts b/packages/backend/src/services/UserFollowingService.ts index bf18549200a0..91f9009c6403 100644 --- a/packages/backend/src/services/UserFollowingService.ts +++ b/packages/backend/src/services/UserFollowingService.ts @@ -1,24 +1,20 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users , Followings, FollowRequests , UserProfiles , Instances , Blockings } from '@/models/index.js'; import type { CacheableUser, ILocalUser, IRemoteUser, User } from '@/models/entities/user.js'; -import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; -import renderFollow from '@/services/remote/activitypub/renderer/follow.js'; -import renderAccept from '@/services/remote/activitypub/renderer/accept.js'; -import renderReject from '@/services/remote/activitypub/renderer/reject.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { QueueService } from '@/queue/queue.service.js'; import PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { IdService } from '@/services/IdService.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; -import renderUndo from '@/services/remote/activitypub/renderer/undo.js'; import type { Packed } from '@/misc/schema.js'; import InstanceChart from '@/services/chart/charts/instance.js'; import { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; -import { WebhookService } from '@/services/webhookService.js'; +import { WebhookService } from '@/services/WebhookService.js'; import { CreateNotificationService } from '@/services/CreateNotificationService.js'; import Logger from './logger.js'; import { UserEntityService } from './entities/UserEntityService.js'; +import { ApRendererService } from './remote/activitypub/ApRendererService.js'; const logger = new Logger('following/create'); @@ -63,6 +59,7 @@ export class UserFollowingService { private createNotificationService: CreateNotificationService, private federatedInstanceService: FederatedInstanceService, private webhookService: WebhookService, + private apRendererService: ApRendererService, private perUserFollowingChart: PerUserFollowingChart, private instanceChart: InstanceChart, ) { @@ -88,7 +85,7 @@ export class UserFollowingService { if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee) && blocked) { // リモートフォローを受けてブロックしていた場合は、エラーにするのではなくRejectを送り返しておしまい。 - const content = renderActivity(renderReject(renderFollow(follower, followee, requestId), followee)); + const content = this.apRendererService.renderActivity(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee, requestId), followee)); this.queueService.deliver(followee , content, follower.inbox); return; } else if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee) && blocking) { @@ -137,7 +134,7 @@ export class UserFollowingService { await this.insertFollowingDoc(followee, follower); if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { - const content = renderActivity(renderAccept(renderFollow(follower, followee, requestId), followee)); + const content = this.apRendererService.renderActivity(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, requestId), followee)); this.queueService.deliver(followee, content, follower.inbox); } } @@ -296,13 +293,13 @@ export class UserFollowingService { } if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { - const content = renderActivity(renderUndo(renderFollow(follower, followee), follower)); + const content = this.apRendererService.renderActivity(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower)); this.queueService.deliver(follower, content, followee.inbox); } if (this.userEntityService.isLocalUser(followee) && this.userEntityService.isRemoteUser(follower)) { // local user has null host - const content = renderActivity(renderReject(renderFollow(follower, followee), followee)); + const content = this.apRendererService.renderActivity(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee), followee)); this.queueService.deliver(followee, content, follower.inbox); } } @@ -393,7 +390,7 @@ export class UserFollowingService { } if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { - const content = renderActivity(renderFollow(follower, followee)); + const content = this.apRendererService.renderActivity(this.apRendererService.renderFollow(follower, followee)); this.queueService.deliver(follower, content, followee.inbox); } } @@ -407,7 +404,7 @@ export class UserFollowingService { }, ): Promise { if (this.userEntityService.isRemoteUser(followee)) { - const content = renderActivity(renderUndo(renderFollow(follower, followee), follower)); + const content = this.apRendererService.renderActivity(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower, followee), follower)); if (this.userEntityService.isLocalUser(follower)) { // 本来このチェックは不要だけどTSに怒られるので this.queueService.deliver(follower, content, followee.inbox); @@ -451,7 +448,7 @@ export class UserFollowingService { await this.insertFollowingDoc(followee, follower); if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { - const content = renderActivity(renderAccept(renderFollow(follower, followee, request.requestId!), followee)); + const content = this.apRendererService.renderActivity(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, request.requestId!), followee)); this.queueService.deliver(followee, content, follower.inbox); } @@ -552,7 +549,7 @@ export class UserFollowingService { followerId: follower.id, }); - const content = renderActivity(renderReject(renderFollow(follower, followee, request?.requestId || undefined), followee)); + const content = this.apRendererService.renderActivity(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee, request?.requestId || undefined), followee)); this.queueService.deliver(followee, content, follower.inbox); } diff --git a/packages/backend/src/services/UserListService.ts b/packages/backend/src/services/UserListService.ts index c320c073866f..d4bfe5417ed7 100644 --- a/packages/backend/src/services/UserListService.ts +++ b/packages/backend/src/services/UserListService.ts @@ -1,12 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { UserListJoinings } from '@/models/index.js'; -import { Users } from '@/models/index.js'; +import type { UserListJoinings , Users } from '@/models/index.js'; + import type { User } from '@/models/entities/user.js'; import type { UserList } from '@/models/entities/user-list.js'; import type { UserListJoining } from '@/models/entities/user-list-joining.js'; -import type { IdService } from '@/services/IdService.js'; -import type { UserFollowingService } from '@/services/UserFollowingService.js'; -import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import { IdService } from '@/services/IdService.js'; +import { UserFollowingService } from '@/services/UserFollowingService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { UserEntityService } from './entities/UserEntityService'; @Injectable() export class UserListService { @@ -17,6 +18,7 @@ export class UserListService { @Inject('userListJoiningsRepository') private userListJoiningsRepository: typeof UserListJoinings, + private userEntityService: UserEntityService, private idService: IdService, private userFollowingService: UserFollowingService, private globalEventServie: GlobalEventService, @@ -34,7 +36,7 @@ export class UserListService { this.globalEventServie.publishUserListStream(list.id, 'userAdded', await this.userEntityService.pack(target)); // このインスタンス内にこのリモートユーザーをフォローしているユーザーがいなくても投稿を受け取るためにダミーのユーザーがフォローしたということにする - if (Users.isRemoteUser(target)) { + if (this.userEntityService.isRemoteUser(target)) { const proxy = await fetchProxyAccount(); if (proxy) { this.userFollowingService.follow(proxy, target); From 7890b3ba84e6b2fa78a9b1c3c224d16297dd1995 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 18:25:07 +0900 Subject: [PATCH 111/180] wip --- .../endpoints/drive/files/attached-notes.ts | 11 ++++++++--- .../endpoints/drive/files/check-existence.ts | 6 ++++-- .../server/api/endpoints/drive/files/delete.ts | 18 ++++++++++++------ .../api/endpoints/drive/files/find-by-hash.ts | 11 ++++++++--- .../server/api/endpoints/drive/files/find.ts | 7 +++++-- 5 files changed, 37 insertions(+), 16 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts index c3f9a6df2f17..7f75668ae359 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Notes } from '@/models/index.js'; -import { DriveFiles } from '@/models/index.js'; +import type { Notes , DriveFiles } from '@/models/index.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -44,12 +44,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + @Inject('notesRepository') private notesRepository: typeof Notes, + + private noteEntityService: NoteEntityService, ) { super(meta, paramDef, async (ps, me) => { // Fetch file - const file = await DriveFiles.findOneBy({ + const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId, userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts index ede78131ad29..8bb5dd21cb38 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFiles } from '@/models/index.js'; +import type { DriveFiles } from '@/models/index.js'; export const meta = { tags: ['drive'], @@ -29,9 +29,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, ) { super(meta, paramDef, async (ps, me) => { - const file = await DriveFiles.findOneBy({ + const file = await this.driveFilesRepository.findOneBy({ md5: ps.md5, userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/drive/files/delete.ts b/packages/backend/src/server/api/endpoints/drive/files/delete.ts index 6db4ec5b0e0e..309a4820fbf0 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/delete.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/delete.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; -import { deleteFile } from '@/services/drive/delete-file.js'; -import { publishDriveStream } from '@/services/stream.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFiles, Users } from '@/models/index.js'; +import type { DriveFiles } from '@/models/index.js'; +import { Users } from '@/models/index.js'; +import { DriveService } from '@/services/DriveService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -41,9 +42,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private driveService: DriveService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { - const file = await DriveFiles.findOneBy({ id: ps.fileId }); + const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (file == null) { throw new ApiError(meta.errors.noSuchFile); @@ -54,10 +60,10 @@ export default class extends Endpoint { } // Delete - await deleteFile(file); + await this.driveService.deleteFile(file); // Publish fileDeleted event - publishDriveStream(me.id, 'fileDeleted', file.id); + this.globalEventService.publishDriveStream(me.id, 'fileDeleted', file.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts index f0e0a60a9644..0a379bce78ff 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DriveFiles } from '@/models/index.js'; +import type { DriveFiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService'; export const meta = { tags: ['drive'], @@ -34,14 +35,18 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private driveFileEntityService: DriveFileEntityService, ) { super(meta, paramDef, async (ps, me) => { - const files = await DriveFiles.findBy({ + const files = await this.driveFilesRepository.findBy({ md5: ps.md5, userId: me.id, }); - return await DriveFiles.packMany(files, { self: true }); + return await this.driveFileEntityService.packMany(files, { self: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/drive/files/find.ts b/packages/backend/src/server/api/endpoints/drive/files/find.ts index 3d9ef5e9fe80..5b31bb348b91 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/find.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/find.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFiles } from '@/models/index.js'; +import type { DriveFiles } from '@/models/index.js'; import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; export const meta = { @@ -37,10 +37,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + private driveFileEntityService: DriveFileEntityService, ) { super(meta, paramDef, async (ps, me) => { - const files = await DriveFiles.findBy({ + const files = await this.driveFilesRepository.findBy({ name: ps.name, userId: me.id, folderId: ps.folderId ?? IsNull(), From c4bc058c4b6243e59e87345a409931c8aa1dc6c4 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 18:35:26 +0900 Subject: [PATCH 112/180] wip --- .../src/server/api/endpoints/blocking/list.ts | 11 +++++----- .../server/api/endpoints/channels/follow.ts | 13 ++++++++---- .../server/api/endpoints/channels/timeline.ts | 2 ++ .../server/api/endpoints/channels/unfollow.ts | 11 +++++++--- .../server/api/endpoints/clips/add-note.ts | 7 ++++--- .../src/server/api/endpoints/clips/delete.ts | 8 ++++--- .../src/server/api/endpoints/clips/notes.ts | 11 +++++++--- .../server/api/endpoints/clips/remove-note.ts | 21 ++++++++++++------- .../src/server/api/endpoints/drive/files.ts | 11 +++++++--- .../api/endpoints/drive/folders/create.ts | 5 +++-- .../api/endpoints/drive/folders/delete.ts | 20 ++++++++++++------ .../src/server/api/endpoints/drive/stream.ts | 11 +++++++--- 12 files changed, 88 insertions(+), 43 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/blocking/list.ts b/packages/backend/src/server/api/endpoints/blocking/list.ts index 45bb1975371d..c71a130853f5 100644 --- a/packages/backend/src/server/api/endpoints/blocking/list.ts +++ b/packages/backend/src/server/api/endpoints/blocking/list.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Blockings } from '@/models/index.js'; +import type { Blockings } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { BlockingEntityService } from '@/services/entities/BlockingEntityService'; export const meta = { tags: ['account'], @@ -35,12 +36,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('blockingsRepository') + private blockingsRepository: typeof Blockings, + private blockingEntityService: BlockingEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/channels/follow.ts b/packages/backend/src/server/api/endpoints/channels/follow.ts index 074e14e0eeb9..799cf0b2ed80 100644 --- a/packages/backend/src/server/api/endpoints/channels/follow.ts +++ b/packages/backend/src/server/api/endpoints/channels/follow.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Channels, ChannelFollowings } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; -import { publishUserEvent } from '@/services/stream.js'; +import type { ChannelFollowings } from '@/models/index.js'; +import { Channels } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -33,7 +34,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('channelFollowingsRepository') + private channelFollowingsRepository: typeof ChannelFollowings, + private idService: IdService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { const channel = await Channels.findOneBy({ @@ -51,7 +56,7 @@ export default class extends Endpoint { followeeId: channel.id, }); - publishUserEvent(me.id, 'followChannel', channel); + this.globalEventService.publishUserEvent(me.id, 'followChannel', channel); }); } } diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index 57d2b2c55d5c..31f2b01779ea 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -4,6 +4,7 @@ import type { Notes } from '@/models/index.js'; import { Channels } from '@/models/index.js'; import { activeUsersChart } from '@/services/chart/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -50,6 +51,7 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/channels/unfollow.ts b/packages/backend/src/server/api/endpoints/channels/unfollow.ts index a1fe58b53c73..81e5d7635314 100644 --- a/packages/backend/src/server/api/endpoints/channels/unfollow.ts +++ b/packages/backend/src/server/api/endpoints/channels/unfollow.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Channels, ChannelFollowings } from '@/models/index.js'; -import { publishUserEvent } from '@/services/stream.js'; +import type { ChannelFollowings } from '@/models/index.js'; +import { Channels } from '@/models/index.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -32,6 +33,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('channelFollowingsRepository') + private channelFollowingsRepository: typeof ChannelFollowings, + + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { const channel = await Channels.findOneBy({ @@ -47,7 +52,7 @@ export default class extends Endpoint { followeeId: channel.id, }); - publishUserEvent(me.id, 'unfollowChannel', channel); + this.globalEventService.publishUserEvent(me.id, 'unfollowChannel', channel); }); } } diff --git a/packages/backend/src/server/api/endpoints/clips/add-note.ts b/packages/backend/src/server/api/endpoints/clips/add-note.ts index 1239d22c78f1..4434b1a26f2c 100644 --- a/packages/backend/src/server/api/endpoints/clips/add-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/add-note.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ClipNotes, Clips } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import { IdService } from '@/services/IdService.js'; import { ApiError } from '../../error.js'; -import { getNote } from '../../common/getters.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['account', 'notes', 'clips'], @@ -47,6 +47,7 @@ export const paramDef = { export default class extends Endpoint { constructor( private idService: IdService, + private getterService: GetterService, ) { super(meta, paramDef, async (ps, me) => { const clip = await Clips.findOneBy({ @@ -58,7 +59,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchClip); } - const note = await getNote(ps.noteId).catch(e => { + const note = await this.getterService.getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; }); diff --git a/packages/backend/src/server/api/endpoints/clips/delete.ts b/packages/backend/src/server/api/endpoints/clips/delete.ts index a434f903f506..cf70fcdaf977 100644 --- a/packages/backend/src/server/api/endpoints/clips/delete.ts +++ b/packages/backend/src/server/api/endpoints/clips/delete.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Clips } from '@/models/index.js'; +import type { Clips } from '@/models/index.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -31,9 +31,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('clipsRepository') + private clipsRepository: typeof Clips, ) { super(meta, paramDef, async (ps, me) => { - const clip = await Clips.findOneBy({ + const clip = await this.clipsRepository.findOneBy({ id: ps.clipId, userId: me.id, }); @@ -42,7 +44,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchClip); } - await Clips.delete(clip.id); + await this.clipsRepository.delete(clip.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts index 5063a185a125..421dde429dd7 100644 --- a/packages/backend/src/server/api/endpoints/clips/notes.ts +++ b/packages/backend/src/server/api/endpoints/clips/notes.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Notes } from '@/models/index.js'; -import { ClipNotes, Clips } from '@/models/index.js'; +import type { Notes , Clips } from '@/models/index.js'; +import { ClipNotes } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -46,13 +47,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('clipsRepository') + private clipsRepository: typeof Clips, + @Inject('notesRepository') private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const clip = await Clips.findOneBy({ + const clip = await this.clipsRepository.findOneBy({ id: ps.clipId, }); diff --git a/packages/backend/src/server/api/endpoints/clips/remove-note.ts b/packages/backend/src/server/api/endpoints/clips/remove-note.ts index 118d000d319c..8d56da7f41b9 100644 --- a/packages/backend/src/server/api/endpoints/clips/remove-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/remove-note.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ClipNotes, Clips } from '@/models/index.js'; +import type { ClipNotes, Clips } from '@/models/index.js'; import { ApiError } from '../../error.js'; -import { getNote } from '../../common/getters.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['account', 'notes', 'clips'], @@ -39,9 +39,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('clipsRepository') + private clipsRepository: typeof Clips, + + @Inject('clipNotesRepository') + private clipNotesRepository: typeof ClipNotes, + + private getterService: GetterService, ) { super(meta, paramDef, async (ps, me) => { - const clip = await Clips.findOneBy({ + const clip = await this.clipsRepository.findOneBy({ id: ps.clipId, userId: me.id, }); @@ -50,12 +57,12 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchClip); } - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; + const note = await this.getterService.getNote(ps.noteId).catch(err => { + if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw err; }); - await ClipNotes.delete({ + await this.clipNotesRepository.delete({ noteId: note.id, clipId: clip.id, }); diff --git a/packages/backend/src/server/api/endpoints/drive/files.ts b/packages/backend/src/server/api/endpoints/drive/files.ts index 57ce52202acc..a48a55b817c3 100644 --- a/packages/backend/src/server/api/endpoints/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/drive/files.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFiles } from '@/models/index.js'; +import type { DriveFiles } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService'; export const meta = { tags: ['drive'], @@ -37,10 +38,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private driveFileEntityService: DriveFileEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.driveFilesRepository.createQueryBuilder('file'), ps.sinceId, ps.untilId) .andWhere('file.userId = :userId', { userId: me.id }); if (ps.folderId) { @@ -59,7 +64,7 @@ export default class extends Endpoint { const files = await query.take(ps.limit).getMany(); - return await DriveFiles.packMany(files, { detail: false, self: true }); + return await this.driveFileEntityService.packMany(files, { detail: false, self: true }); }); } } diff --git a/packages/backend/src/server/api/endpoints/drive/folders/create.ts b/packages/backend/src/server/api/endpoints/drive/folders/create.ts index c019ba66cdff..d511835cda29 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/create.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; -import { publishDriveStream } from '@/services/stream.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFolders } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; import { DriveFolderEntityService } from '@/services/entities/DriveFolderEntityService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -46,6 +46,7 @@ export default class extends Endpoint { private driveFolderEntityService: DriveFolderEntityService, private idService: IdService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { // If the parent folder is specified @@ -74,7 +75,7 @@ export default class extends Endpoint { const folderObj = await this.driveFolderEntityService.pack(folder); // Publish folderCreated event - publishDriveStream(me.id, 'folderCreated', folderObj); + this.globalEventService.publishDriveStream(me.id, 'folderCreated', folderObj); return folderObj; }); diff --git a/packages/backend/src/server/api/endpoints/drive/folders/delete.ts b/packages/backend/src/server/api/endpoints/drive/folders/delete.ts index 9d775445ec80..d61d48be7684 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/delete.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/delete.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { publishDriveStream } from '@/services/stream.js'; -import { DriveFolders, DriveFiles } from '@/models/index.js'; +import type { DriveFolders, DriveFiles } from '@/models/index.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -38,10 +39,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + @Inject('driveFoldersRepository') + private driveFoldersRepository: typeof DriveFolders, + + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { // Get folder - const folder = await DriveFolders.findOneBy({ + const folder = await this.driveFoldersRepository.findOneBy({ id: ps.folderId, userId: me.id, }); @@ -51,18 +59,18 @@ export default class extends Endpoint { } const [childFoldersCount, childFilesCount] = await Promise.all([ - DriveFolders.countBy({ parentId: folder.id }), - DriveFiles.countBy({ folderId: folder.id }), + this.driveFoldersRepository.countBy({ parentId: folder.id }), + this.driveFilesRepository.countBy({ folderId: folder.id }), ]); if (childFoldersCount !== 0 || childFilesCount !== 0) { throw new ApiError(meta.errors.hasChildFilesOrFolders); } - await DriveFolders.delete(folder.id); + await this.driveFoldersRepository.delete(folder.id); // Publish folderCreated event - publishDriveStream(me.id, 'folderDeleted', folder.id); + this.globalEventService.publishDriveStream(me.id, 'folderDeleted', folder.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/drive/stream.ts b/packages/backend/src/server/api/endpoints/drive/stream.ts index 4176d7d84e17..40fd49c498e4 100644 --- a/packages/backend/src/server/api/endpoints/drive/stream.ts +++ b/packages/backend/src/server/api/endpoints/drive/stream.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFiles } from '@/models/index.js'; +import type { DriveFiles } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService'; export const meta = { tags: ['drive'], @@ -36,10 +37,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private driveFileEntityService: DriveFileEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.driveFilesRepository.createQueryBuilder('file'), ps.sinceId, ps.untilId) .andWhere('file.userId = :userId', { userId: me.id }); if (ps.type) { @@ -52,7 +57,7 @@ export default class extends Endpoint { const files = await query.take(ps.limit).getMany(); - return await DriveFiles.packMany(files, { detail: false, self: true }); + return await this.driveFileEntityService.packMany(files, { detail: false, self: true }); }); } } From a7156bc35c40f77a029677eade1e36c333871b9b Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 18:48:20 +0900 Subject: [PATCH 113/180] wip --- .../api/endpoints/federation/followers.ts | 11 +++++----- .../api/endpoints/federation/following.ts | 11 +++++----- .../api/endpoints/federation/instances.ts | 20 +++++++++---------- .../server/api/endpoints/federation/stats.ts | 18 ++++++++++++----- .../federation/update-remote-user.ts | 10 ++++++---- .../server/api/endpoints/federation/users.ts | 2 ++ .../endpoints/following/requests/accept.ts | 18 +++++++++-------- .../endpoints/following/requests/reject.ts | 14 +++++++------ .../server/api/endpoints/gallery/featured.ts | 14 ++++++------- .../server/api/endpoints/gallery/popular.ts | 18 ++++++++--------- .../src/server/api/endpoints/gallery/posts.ts | 15 +++++++------- .../api/endpoints/gallery/posts/like.ts | 18 +++++++++++------ .../api/endpoints/gallery/posts/unlike.ts | 15 +++++++++----- .../src/server/api/endpoints/hashtags/list.ts | 14 ++++++------- .../server/api/endpoints/hashtags/search.ts | 6 ++++-- .../server/api/endpoints/hashtags/trend.ts | 9 ++++----- .../server/api/endpoints/hashtags/users.ts | 10 +++++----- 17 files changed, 124 insertions(+), 99 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/federation/followers.ts b/packages/backend/src/server/api/endpoints/federation/followers.ts index fcb24b4550a1..90a4143f0706 100644 --- a/packages/backend/src/server/api/endpoints/federation/followers.ts +++ b/packages/backend/src/server/api/endpoints/federation/followers.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Followings } from '@/models/index.js'; +import type { Followings } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { FollowingEntityService } from '@/services/entities/FollowingEntityService'; export const meta = { tags: ['federation'], @@ -34,12 +35,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + private followingEntityService: FollowingEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/federation/following.ts b/packages/backend/src/server/api/endpoints/federation/following.ts index 53ac1269983c..1fa2c90fe7e6 100644 --- a/packages/backend/src/server/api/endpoints/federation/following.ts +++ b/packages/backend/src/server/api/endpoints/federation/following.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Followings } from '@/models/index.js'; +import type { Followings } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { FollowingEntityService } from '@/services/entities/FollowingEntityService'; export const meta = { tags: ['federation'], @@ -34,12 +35,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + private followingEntityService: FollowingEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts index 283246eafef9..46c751447ff4 100644 --- a/packages/backend/src/server/api/endpoints/federation/instances.ts +++ b/packages/backend/src/server/api/endpoints/federation/instances.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; -import config from '@/config/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Instances } from '@/models/index.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; +import type { Instances } from '@/models/index.js'; +import { InstanceEntityService } from '@/services/entities/InstanceEntityService'; +import { MetaService } from '@/services/MetaService'; export const meta = { tags: ['federation'], @@ -41,14 +41,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('instancesRepository') + private instancesRepository: typeof Instances, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private instanceEntityService: InstanceEntityService, + private metaService: MetaService, ) { super(meta, paramDef, async (ps, me) => { - const query = Instances.createQueryBuilder('instance'); + const query = this.instancesRepository.createQueryBuilder('instance'); switch (ps.sort) { case '+pubSub': query.orderBy('instance.followingCount', 'DESC').orderBy('instance.followersCount', 'DESC'); break; @@ -70,7 +70,7 @@ export default class extends Endpoint { } if (typeof ps.blocked === 'boolean') { - const meta = await fetchMeta(true); + const meta = await this.metaService.fetch(true); if (ps.blocked) { query.andWhere('instance.host IN (:...blocks)', { blocks: meta.blockedHosts }); } else { @@ -124,7 +124,7 @@ export default class extends Endpoint { const instances = await query.take(ps.limit).skip(ps.offset).getMany(); - return await Instances.packMany(instances); + return await this.instanceEntityService.packMany(instances); }); } } diff --git a/packages/backend/src/server/api/endpoints/federation/stats.ts b/packages/backend/src/server/api/endpoints/federation/stats.ts index c5aa4384e15a..971e949a5baf 100644 --- a/packages/backend/src/server/api/endpoints/federation/stats.ts +++ b/packages/backend/src/server/api/endpoints/federation/stats.ts @@ -1,8 +1,9 @@ import { IsNull, MoreThan, Not } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { Followings, Instances } from '@/models/index.js'; +import type { Followings, Instances } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { InstanceEntityService } from '@/services/entities/InstanceEntityService'; export const meta = { tags: ['federation'], @@ -25,10 +26,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('instancesRepository') + private instancesRepository: typeof Instances, + + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + private instanceEntityService: InstanceEntityService, ) { super(meta, paramDef, async (ps, me) => { const [topSubInstances, topPubInstances, allSubCount, allPubCount] = await Promise.all([ - Instances.find({ + this.instancesRepository.find({ where: { followersCount: MoreThan(0), }, @@ -37,7 +45,7 @@ export default class extends Endpoint { }, take: ps.limit, }), - Instances.find({ + this.instancesRepository.find({ where: { followingCount: MoreThan(0), }, @@ -62,9 +70,9 @@ export default class extends Endpoint { const gotPubCount = topPubInstances.map(x => x.followingCount).reduce((a, b) => a + b, 0); return await awaitAll({ - topSubInstances: Instances.packMany(topSubInstances), + topSubInstances: this.instanceEntityService.packMany(topSubInstances), otherFollowersCount: Math.max(0, allSubCount - gotSubCount), - topPubInstances: Instances.packMany(topPubInstances), + topPubInstances: this.instanceEntityService.packMany(topPubInstances), otherFollowingCount: Math.max(0, allPubCount - gotPubCount), }); }); diff --git a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts index a19e4fa83440..3b4400a35596 100644 --- a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts +++ b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { updatePerson } from '@/services/remote/activitypub/models/person.js'; -import { getRemoteUser } from '../../common/getters.js'; +import { ApPersonService } from '@/services/remote/activitypub/models/ApPersonService'; +import { GetterService } from '../../common/GetterService'; export const meta = { tags: ['federation'], @@ -21,10 +21,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private getterService: GetterService, + private apPersonService: ApPersonService, ) { super(meta, paramDef, async (ps) => { - const user = await getRemoteUser(ps.userId); - await updatePerson(user.uri!); + const user = await this.getterService.getRemoteUser(ps.userId); + await this.apPersonService.updatePerson(user.uri!); }); } } diff --git a/packages/backend/src/server/api/endpoints/federation/users.ts b/packages/backend/src/server/api/endpoints/federation/users.ts index 46976039e2de..0ba1dc9f899a 100644 --- a/packages/backend/src/server/api/endpoints/federation/users.ts +++ b/packages/backend/src/server/api/endpoints/federation/users.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { UserEntityService } from '@/services/entities/UserEntityService'; export const meta = { tags: ['federation'], @@ -37,6 +38,7 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, + private userEntityService: UserEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/following/requests/accept.ts b/packages/backend/src/server/api/endpoints/following/requests/accept.ts index f2f47d8806c2..f2d6b439073f 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/accept.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/accept.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; -import acceptFollowRequest from '@/services/following/requests/accept.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { GetterService } from '@/server/api/common/GetterService.js'; +import { UserFollowingService } from '@/services/UserFollowingService.js'; import { ApiError } from '../../../error.js'; -import { getUser } from '../../../common/getters.js'; export const meta = { tags: ['following', 'account'], @@ -37,17 +37,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private getterService: GetterService, + private userFollowingService: UserFollowingService, ) { super(meta, paramDef, async (ps, me) => { // Fetch follower - const follower = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; + const follower = await this.getterService.getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; }); - await acceptFollowRequest(me, follower).catch(e => { - if (e.id === '8884c2dd-5795-4ac9-b27e-6a01d38190f9') throw new ApiError(meta.errors.noFollowRequest); - throw e; + await this.userFollowingService.acceptFollowRequest(me, follower).catch(err => { + if (err.id === '8884c2dd-5795-4ac9-b27e-6a01d38190f9') throw new ApiError(meta.errors.noFollowRequest); + throw err; }); return; diff --git a/packages/backend/src/server/api/endpoints/following/requests/reject.ts b/packages/backend/src/server/api/endpoints/following/requests/reject.ts index ff9d055f8683..8914ff17b66d 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/reject.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/reject.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; -import { rejectFollowRequest } from '@/services/following/reject.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { GetterService } from '@/server/api/common/GetterService.js'; +import { UserFollowingService } from '@/services/UserFollowingService.js'; import { ApiError } from '../../../error.js'; -import { getUser } from '../../../common/getters.js'; export const meta = { tags: ['following', 'account'], @@ -32,15 +32,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private getterService: GetterService, + private userFollowingService: UserFollowingService, ) { super(meta, paramDef, async (ps, me) => { // Fetch follower - const follower = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; + const follower = await this.getterService.getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; }); - await rejectFollowRequest(me, follower); + await this.userFollowingService.rejectFollowRequest(me, follower); return; }); diff --git a/packages/backend/src/server/api/endpoints/gallery/featured.ts b/packages/backend/src/server/api/endpoints/gallery/featured.ts index 07265ac2ff83..be7def4b0c0b 100644 --- a/packages/backend/src/server/api/endpoints/gallery/featured.ts +++ b/packages/backend/src/server/api/endpoints/gallery/featured.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GalleryPosts } from '@/models/index.js'; +import type { GalleryPosts } from '@/models/index.js'; +import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService'; export const meta = { tags: ['gallery'], @@ -28,21 +29,20 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('galleryPostsRepository') + private galleryPostsRepository: typeof GalleryPosts, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private galleryPostEntityService: GalleryPostEntityService, ) { super(meta, paramDef, async (ps, me) => { - const query = GalleryPosts.createQueryBuilder('post') + const query = this.galleryPostsRepository.createQueryBuilder('post') .andWhere('post.createdAt > :date', { date: new Date(Date.now() - (1000 * 60 * 60 * 24 * 3)) }) .andWhere('post.likedCount > 0') .orderBy('post.likedCount', 'DESC'); const posts = await query.take(10).getMany(); - return await GalleryPosts.packMany(posts, me); + return await this.galleryPostEntityService.packMany(posts, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/gallery/popular.ts b/packages/backend/src/server/api/endpoints/gallery/popular.ts index 69c3500a3fda..648ccc50788e 100644 --- a/packages/backend/src/server/api/endpoints/gallery/popular.ts +++ b/packages/backend/src/server/api/endpoints/gallery/popular.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GalleryPosts } from '@/models/index.js'; +import type { GalleryPosts } from '@/models/index.js'; +import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService'; export const meta = { tags: ['gallery'], @@ -28,20 +29,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('galleryPostsRepository') + private galleryPostsRepository: typeof GalleryPosts, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private galleryPostEntityService: GalleryPostEntityService, ) { super(meta, paramDef, async (ps, me) => { - const query = GalleryPosts.createQueryBuilder('post') - .andWhere('post.likedCount > 0') - .orderBy('post.likedCount', 'DESC'); + const query = this.galleryPostsRepository.createQueryBuilder('post') + .andWhere('post.likedCount > 0') + .orderBy('post.likedCount', 'DESC'); const posts = await query.take(10).getMany(); - return await GalleryPosts.packMany(posts, me); + return await this.galleryPostEntityService.packMany(posts, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/gallery/posts.ts b/packages/backend/src/server/api/endpoints/gallery/posts.ts index d6d6e103516c..5d67acc7ab3d 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GalleryPosts } from '@/models/index.js'; +import type { GalleryPosts } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService'; export const meta = { tags: ['gallery'], @@ -31,21 +32,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('galleryPostsRepository') + private galleryPostsRepository: typeof GalleryPosts, + private galleryPostEntityService: GalleryPostEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.galleryPostsRepository.createQueryBuilder('post'), ps.sinceId, ps.untilId) .innerJoinAndSelect('post.user', 'user'); const posts = await query.take(ps.limit).getMany(); - return await GalleryPosts.packMany(posts, me); + return await this.galleryPostEntityService.packMany(posts, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts index 93ff709e79bb..7ad453eef3f7 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GalleryPosts, GalleryLikes } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import type { GalleryLikes , GalleryPosts } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -44,10 +44,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('galleryPostsRepository') + private galleryPostsRepository: typeof GalleryPosts, + + @Inject('galleryLikesRepository') + private galleryLikesRepository: typeof GalleryLikes, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { - const post = await GalleryPosts.findOneBy({ id: ps.postId }); + const post = await this.galleryPostsRepository.findOneBy({ id: ps.postId }); if (post == null) { throw new ApiError(meta.errors.noSuchPost); } @@ -57,7 +63,7 @@ export default class extends Endpoint { } // if already liked - const exist = await GalleryLikes.findOneBy({ + const exist = await this.galleryLikesRepository.findOneBy({ postId: post.id, userId: me.id, }); @@ -67,14 +73,14 @@ export default class extends Endpoint { } // Create like - await GalleryLikes.insert({ + await this.galleryLikesRepository.insert({ id: this.idService.genId(), createdAt: new Date(), postId: post.id, userId: me.id, }); - GalleryPosts.increment({ id: post.id }, 'likedCount', 1); + this.galleryPostsRepository.increment({ id: post.id }, 'likedCount', 1); }); } } diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts index 1df20f9ac33b..e04c9610ecd3 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GalleryPosts, GalleryLikes } from '@/models/index.js'; +import type { GalleryPosts, GalleryLikes } from '@/models/index.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -37,14 +37,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('galleryPostsRepository') + private galleryPostsRepository: typeof GalleryPosts, + + @Inject('galleryLikesRepository') + private galleryLikesRepository: typeof GalleryLikes, ) { super(meta, paramDef, async (ps, me) => { - const post = await GalleryPosts.findOneBy({ id: ps.postId }); + const post = await this.galleryPostsRepository.findOneBy({ id: ps.postId }); if (post == null) { throw new ApiError(meta.errors.noSuchPost); } - const exist = await GalleryLikes.findOneBy({ + const exist = await this.galleryLikesRepository.findOneBy({ postId: post.id, userId: me.id, }); @@ -54,9 +59,9 @@ export default class extends Endpoint { } // Delete like - await GalleryLikes.delete(exist.id); + await this.galleryLikesRepository.delete(exist.id); - GalleryPosts.decrement({ id: post.id }, 'likedCount', 1); + this.galleryPostsRepository.decrement({ id: post.id }, 'likedCount', 1); }); } } diff --git a/packages/backend/src/server/api/endpoints/hashtags/list.ts b/packages/backend/src/server/api/endpoints/hashtags/list.ts index eb496f33da61..7587fb1d5b47 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/list.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/list.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Hashtags } from '@/models/index.js'; +import type { Hashtags } from '@/models/index.js'; +import { HashtagEntityService } from '@/services/entities/HashtagEntityService'; export const meta = { tags: ['hashtags'], @@ -34,14 +35,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('hashtagsRepository') + private hashtagsRepository: typeof Hashtags, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private hashtagEntityService: HashtagEntityService, ) { super(meta, paramDef, async (ps, me) => { - const query = Hashtags.createQueryBuilder('tag'); + const query = this.hashtagsRepository.createQueryBuilder('tag'); if (ps.attachedToUserOnly) query.andWhere('tag.attachedUsersCount != 0'); if (ps.attachedToLocalUserOnly) query.andWhere('tag.attachedLocalUsersCount != 0'); @@ -74,7 +74,7 @@ export default class extends Endpoint { const tags = await query.take(ps.limit).getMany(); - return Hashtags.packMany(tags); + return this.hashtagEntityService.packMany(tags); }); } } diff --git a/packages/backend/src/server/api/endpoints/hashtags/search.ts b/packages/backend/src/server/api/endpoints/hashtags/search.ts index 447d4306703b..0cacffe81200 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/search.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/search.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Hashtags } from '@/models/index.js'; +import type { Hashtags } from '@/models/index.js'; export const meta = { tags: ['hashtags'], @@ -31,9 +31,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('hashtagsRepository') + private hashtagsRepository: typeof Hashtags, ) { super(meta, paramDef, async (ps, me) => { - const hashtags = await Hashtags.createQueryBuilder('tag') + const hashtags = await this.hashtagsRepository.createQueryBuilder('tag') .where('tag.name like :q', { q: ps.query.toLowerCase() + '%' }) .orderBy('tag.count', 'DESC') .groupBy('tag.id') diff --git a/packages/backend/src/server/api/endpoints/hashtags/trend.ts b/packages/backend/src/server/api/endpoints/hashtags/trend.ts index 919b7dd62edb..4424f0a09be2 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/trend.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/trend.ts @@ -1,11 +1,11 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; import type { Notes } from '@/models/index.js'; import type { Note } from '@/models/entities/note.js'; import { safeForSql } from '@/misc/safe-for-sql.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; +import { MetaService } from '@/services/MetaService'; /* トレンドに載るためには「『直近a分間のユニーク投稿数が今からa分前~今からb分前の間のユニーク投稿数のn倍以上』のハッシュタグの上位5位以内に入る」ことが必要 @@ -64,14 +64,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - @Inject('notesRepository') private notesRepository: typeof Notes, + + private metaService: MetaService, ) { super(meta, paramDef, async () => { - const instance = await fetchMeta(true); + const instance = await this.metaService.fetch(true); const hiddenTags = instance.hiddenTags.map(t => normalizeForSearch(t)); const now = new Date(); // 5分単位で丸めた現在日時 diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts index 70982fc63537..925009c16f63 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/users.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; +import { UserEntityService } from '@/services/entities/UserEntityService'; export const meta = { requireCredential: false, @@ -37,13 +38,12 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + + private userEntityService: UserEntityService, ) { super(meta, paramDef, async (ps, me) => { const query = this.usersRepository.createQueryBuilder('user') - .where(':tag = ANY(user.tags)', { tag: normalizeForSearch(ps.tag) }); + .where(':tag = ANY(user.tags)', { tag: normalizeForSearch(ps.tag) }); const recent = new Date(Date.now() - (1000 * 60 * 60 * 24 * 5)); From fd14ecba9c43ec627eade5fa032657afc883c5fe Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 18:55:44 +0900 Subject: [PATCH 114/180] wip --- .../src/server/api/endpoints/i/2fa/done.ts | 8 +++++--- .../server/api/endpoints/i/2fa/key-done.ts | 3 --- .../api/endpoints/i/2fa/password-less.ts | 6 ++++-- .../api/endpoints/i/2fa/register-key.ts | 19 +++++++++++++------ .../server/api/endpoints/i/2fa/register.ts | 8 +++++--- .../server/api/endpoints/i/2fa/unregister.ts | 8 +++++--- .../backend/src/services/NoteCreateService.ts | 6 ++++-- .../activitypub/ApDeliverManagerService.ts | 19 ++++++++++++++++--- 8 files changed, 52 insertions(+), 25 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/i/2fa/done.ts b/packages/backend/src/server/api/endpoints/i/2fa/done.ts index b6e43846d73d..4621448a95bc 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/done.ts @@ -1,7 +1,7 @@ import * as speakeasy from 'speakeasy'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserProfiles } from '@/models/index.js'; +import type { UserProfiles } from '@/models/index.js'; export const meta = { requireCredential: true, @@ -21,11 +21,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, ) { super(meta, paramDef, async (ps, me) => { const token = ps.token.replace(/\s/g, ''); - const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); if (profile.twoFactorTempSecret == null) { throw new Error('二段階認証の設定が開始されていません'); @@ -41,7 +43,7 @@ export default class extends Endpoint { throw new Error('not verified'); } - await UserProfiles.update(me.id, { + await this.userProfilesRepository.update(me.id, { twoFactorSecret: profile.twoFactorTempSecret, twoFactorEnabled: true, }); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts index b88c4c74fb8f..389cf04f4c77 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts @@ -43,9 +43,6 @@ export default class extends Endpoint { @Inject(DI_SYMBOLS.config) private config: Config, - @Inject('usersRepository') - private usersRepository: typeof Users, - @Inject('userProfilesRepository') private userProfilesRepository: typeof UserProfiles, diff --git a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts index feaf88f0f15d..da6a49d6934f 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserProfiles } from '@/models/index.js'; +import type { UserProfiles } from '@/models/index.js'; export const meta = { requireCredential: true, @@ -20,9 +20,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, ) { super(meta, paramDef, async (ps, me) => { - await UserProfiles.update(me.id, { + await this.userProfilesRepository.update(me.id, { usePasswordLessLogin: ps.value, }); }); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts index c513d7126063..8cf2aedf629a 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts @@ -3,9 +3,9 @@ import * as crypto from 'node:crypto'; import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserProfiles, AttestationChallenges } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; -import { hash } from '../../../2fa.js'; +import type { UserProfiles , AttestationChallenges } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; +import { TwoFactorAuthenticationService } from '@/services/TwoFactorAuthenticationService.js'; const randomBytes = promisify(crypto.randomBytes); @@ -27,10 +27,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + @Inject('attestationChallengesRepository') + private attestationChallengesRepository: typeof AttestationChallenges, + private idService: IdService, + private twoFactorAuthenticationService: TwoFactorAuthenticationService, ) { super(meta, paramDef, async (ps, me) => { - const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); @@ -52,10 +59,10 @@ export default class extends Endpoint { const challengeId = this.idService.genId(); - await AttestationChallenges.insert({ + await this.attestationChallengesRepository.insert({ userId: me.id, id: challengeId, - challenge: hash(Buffer.from(challenge, 'utf-8')).toString('hex'), + challenge: this.twoFactorAuthenticationService.hash(Buffer.from(challenge, 'utf-8')).toString('hex'), createdAt: new Date(), registrationChallenge: true, }); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register.ts b/packages/backend/src/server/api/endpoints/i/2fa/register.ts index d6eb6bd17fac..76844c03c5f8 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts @@ -3,7 +3,7 @@ import * as speakeasy from 'speakeasy'; import * as QRCode from 'qrcode'; import { Inject, Injectable } from '@nestjs/common'; import config from '@/config/index.js'; -import { UserProfiles } from '@/models/index.js'; +import type { UserProfiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -24,9 +24,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, ) { super(meta, paramDef, async (ps, me) => { - const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); @@ -40,7 +42,7 @@ export default class extends Endpoint { length: 32, }); - await UserProfiles.update(me.id, { + await this.userProfilesRepository.update(me.id, { twoFactorTempSecret: secret.base32, }); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts index ef107ac9649f..25232065c4c4 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts @@ -1,7 +1,7 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserProfiles } from '@/models/index.js'; +import type { UserProfiles } from '@/models/index.js'; export const meta = { requireCredential: true, @@ -21,9 +21,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, ) { super(meta, paramDef, async (ps, me) => { - const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); @@ -32,7 +34,7 @@ export default class extends Endpoint { throw new Error('incorrect password'); } - await UserProfiles.update(me.id, { + await this.userProfilesRepository.update(me.id, { twoFactorSecret: null, twoFactorEnabled: false, }); diff --git a/packages/backend/src/services/NoteCreateService.ts b/packages/backend/src/services/NoteCreateService.ts index 7e011afa37c4..dd11121ab6b8 100644 --- a/packages/backend/src/services/NoteCreateService.ts +++ b/packages/backend/src/services/NoteCreateService.ts @@ -45,6 +45,7 @@ import { UserEntityService } from './entities/UserEntityService.js'; import { NoteReadService } from './NoteReadService.js'; import { ApRendererService } from './remote/activitypub/ApRendererService.js'; import { ResolveUserService } from './remote/ResolveUserService.js'; +import { ApDeliverManagerService } from './remote/activitypub/ApDeliverManagerService.js'; const mutedWordsCache = new Cache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5); @@ -156,6 +157,7 @@ export class NoteCreateService { private antennaService: AntennaService, private webhookService: WebhookService, private resolveUserService: ResolveUserService, + private apDeliverManagerService: ApDeliverManagerService, private apRendererService: ApRendererService, private notesChart: NotesChart, private perUserNotesChart: PerUserNotesChart, @@ -458,7 +460,7 @@ export class NoteCreateService { if (data.poll && data.poll.expiresAt) { const delay = data.poll.expiresAt.getTime() - Date.now(); - endedPollNotificationQueue.add({ + this.queueService.endedPollNotificationQueue.add({ noteId: note.id, }, { delay, @@ -572,7 +574,7 @@ export class NoteCreateService { if (this.userEntityService.isLocalUser(user)) { (async () => { const noteActivity = await this.#renderNoteOrRenoteActivity(data, note); - const dm = new DeliverManager(user, noteActivity); + const dm = this.apDeliverManagerService.createDeliverManager(user, noteActivity); // メンションされたリモートユーザーに配送 for (const u of mentionedUsers.filter(u => this.userEntityService.isRemoteUser(u))) { diff --git a/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts b/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts index e503d98e47e9..5f4fc18967fa 100644 --- a/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts +++ b/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts @@ -2,9 +2,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, Not } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Followings , Users } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import { Config } from '@/config/types.js'; import type { ILocalUser, IRemoteUser, User } from '@/models/entities/user.js'; -import type { QueueService } from '@/queue/queue.service'; +import { QueueService } from '@/queue/queue.service'; +import { UserEntityService } from '@/services/entities/UserEntityService'; interface IRecipe { type: string; @@ -37,6 +38,7 @@ export class ApDeliverManagerService { @Inject('followingsRepository') private followingsRepository: typeof Followings, + private userEntityService: UserEntityService, private queueService: QueueService, ) { } @@ -74,6 +76,17 @@ export class ApDeliverManagerService { manager.addDirectRecipe(to); await manager.execute(); } + + public createDeliverManager(actor: { id: User['id']; host: null; }, activity: any) { + return new DeliverManager( + this.userEntityService, + this.followingsRepository, + this.queueService, + + actor, + activity, + ); + } } class DeliverManager { @@ -87,7 +100,7 @@ class DeliverManager { * @param activity Activity to deliver */ constructor( - private usersRepository: typeof Users, + private userEntityService: UserEntityService, private followingsRepository: typeof Followings, private queueService: QueueService, From adccfdaaa138a949a1e9722b6093979580ad6479 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 20:12:58 +0900 Subject: [PATCH 115/180] wip --- .../server/api/endpoints/i/gallery/likes.ts | 11 ++++++++--- .../server/api/endpoints/i/gallery/posts.ts | 11 ++++++++--- .../server/api/endpoints/i/registry/get-all.ts | 6 ++++-- .../api/endpoints/i/registry/get-detail.ts | 6 ++++-- .../src/server/api/endpoints/i/registry/get.ts | 6 ++++-- .../api/endpoints/i/registry/keys-with-type.ts | 6 ++++-- .../server/api/endpoints/i/registry/keys.ts | 6 ++++-- .../server/api/endpoints/i/registry/remove.ts | 8 +++++--- .../server/api/endpoints/i/registry/scopes.ts | 6 ++++-- .../src/server/api/endpoints/i/registry/set.ts | 17 +++++++++++------ .../server/api/endpoints/i/webhooks/create.ts | 18 ++++++++++++------ .../server/api/endpoints/i/webhooks/delete.ts | 16 +++++++++++----- .../server/api/endpoints/i/webhooks/list.ts | 11 ++++------- .../server/api/endpoints/i/webhooks/show.ts | 6 ++++-- .../server/api/endpoints/i/webhooks/update.ts | 15 +++++++++++---- 15 files changed, 98 insertions(+), 51 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts index 22924c7a4f5a..a017ace18c28 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GalleryLikes } from '@/models/index.js'; +import type { GalleryLikes } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { GalleryLikeEntityService } from '@/services/entities/GalleryLikeEntityService'; export const meta = { tags: ['account', 'gallery'], @@ -46,10 +47,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('galleryLikesRepository') + private galleryLikesRepository: typeof GalleryLikes, + + private galleryLikeEntityService: GalleryLikeEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(GalleryLikes.createQueryBuilder('like'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.galleryLikesRepository.createQueryBuilder('like'), ps.sinceId, ps.untilId) .andWhere('like.userId = :meId', { meId: me.id }) .leftJoinAndSelect('like.post', 'post'); @@ -57,7 +62,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return await GalleryLikes.packMany(likes, me); + return await this.galleryLikeEntityService.packMany(likes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts index b21e604f7597..8f3320be8415 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GalleryPosts } from '@/models/index.js'; +import type { GalleryPosts } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService'; export const meta = { tags: ['account', 'gallery'], @@ -35,17 +36,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('galleryPostsRepository') + private galleryPostsRepository: typeof GalleryPosts, + + private galleryPostEntityService: GalleryPostEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.galleryPostsRepository.createQueryBuilder('post'), ps.sinceId, ps.untilId) .andWhere('post.userId = :meId', { meId: me.id }); const posts = await query .take(ps.limit) .getMany(); - return await GalleryPosts.packMany(posts, me); + return await this.galleryPostEntityService.packMany(posts, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts index edc8e7de16fb..fc66cabc1d9b 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { RegistryItems } from '@/models/index.js'; +import type { RegistryItems } from '@/models/index.js'; export const meta = { requireCredential: true, @@ -22,9 +22,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('registryItemsRepository') + private registryItemsRepository: typeof RegistryItems, ) { super(meta, paramDef, async (ps, me) => { - const query = RegistryItems.createQueryBuilder('item') + const query = this.registryItemsRepository.createQueryBuilder('item') .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: me.id }) .andWhere('item.scope = :scope', { scope: ps.scope }); diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts index cb776dc09c55..64f8397355de 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { RegistryItems } from '@/models/index.js'; +import type { RegistryItems } from '@/models/index.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -32,9 +32,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('registryItemsRepository') + private registryItemsRepository: typeof RegistryItems, ) { super(meta, paramDef, async (ps, me) => { - const query = RegistryItems.createQueryBuilder('item') + const query = this.registryItemsRepository.createQueryBuilder('item') .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: me.id }) .andWhere('item.key = :key', { key: ps.key }) diff --git a/packages/backend/src/server/api/endpoints/i/registry/get.ts b/packages/backend/src/server/api/endpoints/i/registry/get.ts index ba8ccfbdb40f..9c625d6aba2d 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { RegistryItems } from '@/models/index.js'; +import type { RegistryItems } from '@/models/index.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -32,9 +32,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('registryItemsRepository') + private registryItemsRepository: typeof RegistryItems, ) { super(meta, paramDef, async (ps, me) => { - const query = RegistryItems.createQueryBuilder('item') + const query = this.registryItemsRepository.createQueryBuilder('item') .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: me.id }) .andWhere('item.key = :key', { key: ps.key }) diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts index 16d575f8cb31..4ce992a63efc 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { RegistryItems } from '@/models/index.js'; +import type { RegistryItems } from '@/models/index.js'; export const meta = { requireCredential: true, @@ -22,9 +22,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('registryItemsRepository') + private registryItemsRepository: typeof RegistryItems, ) { super(meta, paramDef, async (ps, me) => { - const query = RegistryItems.createQueryBuilder('item') + const query = this.registryItemsRepository.createQueryBuilder('item') .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: me.id }) .andWhere('item.scope = :scope', { scope: ps.scope }); diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys.ts b/packages/backend/src/server/api/endpoints/i/registry/keys.ts index aad49a9b4674..fa28c40ffc5f 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/keys.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/keys.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { RegistryItems } from '@/models/index.js'; +import type { RegistryItems } from '@/models/index.js'; export const meta = { requireCredential: true, @@ -22,9 +22,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('registryItemsRepository') + private registryItemsRepository: typeof RegistryItems, ) { super(meta, paramDef, async (ps, me) => { - const query = RegistryItems.createQueryBuilder('item') + const query = this.registryItemsRepository.createQueryBuilder('item') .select('item.key') .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: me.id }) diff --git a/packages/backend/src/server/api/endpoints/i/registry/remove.ts b/packages/backend/src/server/api/endpoints/i/registry/remove.ts index f39a7e60cb69..b1177495f296 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/remove.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/remove.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { RegistryItems } from '@/models/index.js'; +import type { RegistryItems } from '@/models/index.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -32,9 +32,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('registryItemsRepository') + private registryItemsRepository: typeof RegistryItems, ) { super(meta, paramDef, async (ps, me) => { - const query = RegistryItems.createQueryBuilder('item') + const query = this.registryItemsRepository.createQueryBuilder('item') .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: me.id }) .andWhere('item.key = :key', { key: ps.key }) @@ -46,7 +48,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchKey); } - await RegistryItems.remove(item); + await this.registryItemsRepository.remove(item); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/registry/scopes.ts b/packages/backend/src/server/api/endpoints/i/registry/scopes.ts index e15591df3607..99c7ec24c4ea 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/scopes.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/scopes.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { RegistryItems } from '@/models/index.js'; +import type { RegistryItems } from '@/models/index.js'; export const meta = { requireCredential: true, @@ -18,9 +18,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('registryItemsRepository') + private registryItemsRepository: typeof RegistryItems, ) { super(meta, paramDef, async (ps, me) => { - const query = RegistryItems.createQueryBuilder('item') + const query = this.registryItemsRepository.createQueryBuilder('item') .select('item.scope') .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: me.id }); diff --git a/packages/backend/src/server/api/endpoints/i/registry/set.ts b/packages/backend/src/server/api/endpoints/i/registry/set.ts index 8052389dff53..ec7cfa39d316 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/set.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/set.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { publishMainStream } from '@/services/stream.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { RegistryItems } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import type { RegistryItems } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; +import { GlobalEventService } from '@/services/GlobalEventService'; export const meta = { requireCredential: true, @@ -26,10 +27,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('registryItemsRepository') + private registryItemsRepository: typeof RegistryItems, + private idService: IdService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { - const query = RegistryItems.createQueryBuilder('item') + const query = this.registryItemsRepository.createQueryBuilder('item') .where('item.domain IS NULL') .andWhere('item.userId = :userId', { userId: me.id }) .andWhere('item.key = :key', { key: ps.key }) @@ -38,12 +43,12 @@ export default class extends Endpoint { const existingItem = await query.getOne(); if (existingItem) { - await RegistryItems.update(existingItem.id, { + await this.registryItemsRepository.update(existingItem.id, { updatedAt: new Date(), value: ps.value, }); } else { - await RegistryItems.insert({ + await this.registryItemsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), updatedAt: new Date(), @@ -56,7 +61,7 @@ export default class extends Endpoint { } // TODO: サードパーティアプリが傍受出来てしまうのでどうにかする - publishMainStream(me.id, 'registryUpdated', { + this.globalEventService.publishMainStream(me.id, 'registryUpdated', { scope: ps.scope, key: ps.key, value: ps.value, diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts index 9375244f23aa..71ae7c20ad71 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { IdService } from '@/services/IdService.js'; -import { Webhooks } from '@/models/index.js'; -import { publishInternalEvent } from '@/services/stream.js'; +import { IdService } from '@/services/IdService.js'; +import type { Webhooks } from '@/models/index.js'; import { webhookEventTypes } from '@/models/entities/webhook.js'; +import { GlobalEventService } from '@/services/GlobalEventService'; export const meta = { tags: ['webhooks'], @@ -26,14 +26,20 @@ export const paramDef = { required: ['name', 'url', 'secret', 'on'], } as const; +// TODO: ロジックをサービスに切り出す + // eslint-disable-next-line import/no-default-export @Injectable() export default class extends Endpoint { constructor( + @Inject('webhooksRepository') + private webhooksRepository: typeof Webhooks, + private idService: IdService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { - const webhook = await Webhooks.insert({ + const webhook = await this.webhooksRepository.insert({ id: this.idService.genId(), createdAt: new Date(), userId: me.id, @@ -41,9 +47,9 @@ export default class extends Endpoint { url: ps.url, secret: ps.secret, on: ps.on, - }).then(x => Webhooks.findOneByOrFail(x.identifiers[0])); + }).then(x => this.webhooksRepository.findOneByOrFail(x.identifiers[0])); - publishInternalEvent('webhookCreated', webhook); + this.globalEventService.publishInternalEvent('webhookCreated', webhook); return webhook; }); diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts b/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts index 88350af066f5..900f74d5932f 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Webhooks } from '@/models/index.js'; -import { publishInternalEvent } from '@/services/stream.js'; +import type { Webhooks } from '@/models/index.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -28,13 +28,19 @@ export const paramDef = { required: ['webhookId'], } as const; +// TODO: ロジックをサービスに切り出す + // eslint-disable-next-line import/no-default-export @Injectable() export default class extends Endpoint { constructor( + @Inject('webhooksRepository') + private webhooksRepository: typeof Webhooks, + + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { - const webhook = await Webhooks.findOneBy({ + const webhook = await this.webhooksRepository.findOneBy({ id: ps.webhookId, userId: me.id, }); @@ -43,9 +49,9 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchWebhook); } - await Webhooks.delete(webhook.id); + await this.webhooksRepository.delete(webhook.id); - publishInternalEvent('webhookDeleted', webhook); + this.globalEventService.publishInternalEvent('webhookDeleted', webhook); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/list.ts b/packages/backend/src/server/api/endpoints/i/webhooks/list.ts index bfbc52d48f59..5a1e2213505c 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/list.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/list.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Webhooks } from '@/models/index.js'; +import type { Webhooks } from '@/models/index.js'; export const meta = { tags: ['webhooks', 'account'], @@ -20,14 +20,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('webhooksRepository') + private webhooksRepository: typeof Webhooks, ) { super(meta, paramDef, async (ps, me) => { - const webhooks = await Webhooks.findBy({ + const webhooks = await this.webhooksRepository.findBy({ userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/show.ts b/packages/backend/src/server/api/endpoints/i/webhooks/show.ts index 73d0ed08b602..54d8c0a37ddd 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/show.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/show.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Webhooks } from '@/models/index.js'; +import type { Webhooks } from '@/models/index.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -31,9 +31,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('webhooksRepository') + private webhooksRepository: typeof Webhooks, ) { super(meta, paramDef, async (ps, me) => { - const webhook = await Webhooks.findOneBy({ + const webhook = await this.webhooksRepository.findOneBy({ id: ps.webhookId, userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/update.ts b/packages/backend/src/server/api/endpoints/i/webhooks/update.ts index fdd86ca36626..0fb14c655f0b 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/update.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/update.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Webhooks } from '@/models/index.js'; +import type { Webhooks } from '@/models/index.js'; import { publishInternalEvent } from '@/services/stream.js'; import { webhookEventTypes } from '@/models/entities/webhook.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -37,13 +38,19 @@ export const paramDef = { required: ['webhookId', 'name', 'url', 'secret', 'on', 'active'], } as const; +// TODO: ロジックをサービスに切り出す + // eslint-disable-next-line import/no-default-export @Injectable() export default class extends Endpoint { constructor( + @Inject('webhooksRepository') + private webhooksRepository: typeof Webhooks, + + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { - const webhook = await Webhooks.findOneBy({ + const webhook = await this.webhooksRepository.findOneBy({ id: ps.webhookId, userId: me.id, }); @@ -52,7 +59,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchWebhook); } - await Webhooks.update(webhook.id, { + await this.webhooksRepository.update(webhook.id, { name: ps.name, url: ps.url, secret: ps.secret, @@ -60,7 +67,7 @@ export default class extends Endpoint { active: ps.active, }); - publishInternalEvent('webhookUpdated', webhook); + this.globalEventService.publishInternalEvent('webhookUpdated', webhook); }); } } From f1b3007e67ee5b4617ce4dd8c8d6b788980e6515 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 20:32:04 +0900 Subject: [PATCH 116/180] wip --- packages/backend/src/app.module.ts | 26 +++--- packages/backend/src/boot/master.ts | 11 ++- .../backend/src/{config/load.ts => config.ts} | 89 ++++++++++++++++++- packages/backend/src/config/types.ts | 87 ------------------ .../src/queue/DbQueueProcessorsService.ts | 2 +- .../ObjectStorageQueueProcessorsService.ts | 2 +- .../src/queue/SystemQueueProcessorsService.ts | 2 +- .../CheckExpiredMutingsProcessorService.ts | 2 +- .../processors/CleanChartsProcessorService.ts | 2 +- .../queue/processors/CleanProcessorService.ts | 2 +- .../CleanRemoteFilesProcessorService.ts | 2 +- .../DeleteAccountProcessorService.ts | 2 +- .../DeleteDriveFilesProcessorService.ts | 2 +- .../processors/DeleteFileProcessorService.ts | 2 +- .../processors/DeliverProcessorService.ts | 2 +- .../EndedPollNotificationProcessorService.ts | 2 +- .../ExportBlockingProcessorService.ts | 2 +- .../ExportCustomEmojisProcessorService.ts | 2 +- .../ExportFollowingProcessorService.ts | 2 +- .../ExportMutingProcessorService.ts | 2 +- .../processors/ExportNotesProcessorService.ts | 2 +- .../ExportUserListsProcessorService.ts | 2 +- .../ImportBlockingProcessorService.ts | 2 +- .../ImportCustomEmojisProcessorService.ts | 2 +- .../ImportFollowingProcessorService.ts | 2 +- .../ImportMutingProcessorService.ts | 2 +- .../ImportUserListsProcessorService.ts | 2 +- .../queue/processors/InboxProcessorService.ts | 2 +- .../ResyncChartsProcessorService.ts | 2 +- .../processors/TickChartsProcessorService.ts | 2 +- .../WebhookDeliverProcessorService.ts | 2 +- packages/backend/src/queue/queue.module.ts | 2 +- packages/backend/src/queue/queue.service.ts | 2 +- .../src/server/ActivityPubServerService.ts | 2 +- .../backend/src/server/FileServerService.ts | 2 +- .../src/server/MediaProxyServerService.ts | 2 +- .../src/server/NodeinfoServerService.ts | 2 +- packages/backend/src/server/ServerService.ts | 2 +- .../src/server/WellKnownServerService.ts | 2 +- .../src/server/api/ApiServerService.ts | 2 +- .../src/server/api/SigninApiService.ts | 2 +- .../backend/src/server/api/SigninService.ts | 2 +- .../src/server/api/SignupApiService.ts | 2 +- .../server/api/StreamingApiServerService.ts | 2 +- .../src/server/api/endpoints/admin/meta.ts | 2 +- .../api/endpoints/auth/session/generate.ts | 2 +- .../server/api/endpoints/i/2fa/key-done.ts | 2 +- .../src/server/api/endpoints/i/apps.ts | 6 +- .../server/api/endpoints/i/change-password.ts | 8 +- .../server/api/endpoints/i/delete-account.ts | 14 +-- .../src/server/api/endpoints/i/favorites.ts | 11 ++- .../endpoints/i/get-word-muted-notes-count.ts | 6 +- .../server/api/endpoints/i/notifications.ts | 24 +++-- .../src/server/api/endpoints/i/page-likes.ts | 11 ++- .../src/server/api/endpoints/i/pages.ts | 11 ++- .../backend/src/server/api/endpoints/i/pin.ts | 17 ++-- .../i/read-all-messaging-messages.ts | 19 ++-- .../api/endpoints/i/read-all-unread-notes.ts | 14 +-- .../api/endpoints/i/read-announcement.ts | 27 +++--- .../api/endpoints/i/regenerate-token.ts | 18 ++-- .../server/api/endpoints/i/revoke-token.ts | 14 +-- .../src/server/api/endpoints/i/unpin.ts | 12 +-- .../server/api/endpoints/i/update-email.ts | 2 +- .../backend/src/server/api/endpoints/meta.ts | 2 +- .../src/server/web/ClientServerService.ts | 2 +- .../backend/src/server/web/FeedService.ts | 2 +- .../src/server/web/UrlPreviewService.ts | 2 +- .../src/services/AccountUpdateService.ts | 2 +- packages/backend/src/services/AiService.ts | 2 +- .../backend/src/services/CaptchaService.ts | 2 +- .../src/services/CustomEmojiService.ts | 2 +- .../backend/src/services/DownloadService.ts | 2 +- packages/backend/src/services/DriveService.ts | 2 +- packages/backend/src/services/EmailService.ts | 2 +- .../src/services/GlobalEventService.ts | 2 +- packages/backend/src/services/IdService.ts | 2 +- .../src/services/ImageProcessingService.ts | 2 +- .../src/services/InternalStorageService.ts | 2 +- packages/backend/src/services/MfmService.ts | 2 +- .../backend/src/services/NoteCreateService.ts | 2 +- .../backend/src/services/NoteDeleteService.ts | 2 +- .../backend/src/services/NotePiningService.ts | 2 +- packages/backend/src/services/S3Service.ts | 2 +- .../backend/src/services/SignupService.ts | 2 +- .../TwoFactorAuthenticationService.ts | 2 +- .../src/services/UserSuspendService.ts | 2 +- .../src/services/VideoProcessingService.ts | 2 +- .../entities/DriveFileEntityService.ts | 2 +- .../services/entities/NoteEntityService.ts | 2 +- .../services/entities/UserEntityService.ts | 2 +- .../src/services/remote/ResolveUserService.ts | 2 +- .../src/services/remote/WebfingerService.ts | 2 +- .../remote/activitypub/ApDbResolverService.ts | 2 +- .../activitypub/ApDeliverManagerService.ts | 2 +- .../remote/activitypub/ApInboxService.ts | 2 +- .../remote/activitypub/ApMfmService.ts | 2 +- .../remote/activitypub/ApRendererService.ts | 2 +- .../remote/activitypub/ApRequestService.ts | 2 +- .../remote/activitypub/ApResolverService.ts | 2 +- .../activitypub/models/ApImageService.ts | 2 +- .../activitypub/models/ApMentionService.ts | 2 +- .../activitypub/models/ApNoteService.ts | 2 +- .../activitypub/models/ApPersonService.ts | 2 +- .../activitypub/models/ApQuestionService.ts | 2 +- 104 files changed, 329 insertions(+), 266 deletions(-) rename packages/backend/src/{config/load.ts => config.ts} (53%) delete mode 100644 packages/backend/src/config/types.ts diff --git a/packages/backend/src/app.module.ts b/packages/backend/src/app.module.ts index b42955dd3352..cf88e9721301 100644 --- a/packages/backend/src/app.module.ts +++ b/packages/backend/src/app.module.ts @@ -4,27 +4,27 @@ import { EndpointsModule } from '@/server/api/endpoints.module.js'; import { QueueModule } from '@/queue/queue.module.js'; import { ChartsModule } from './services/chart/charts.module'; import { DI_SYMBOLS } from './di-symbols'; -import { loadConfig } from './config/load'; +import { loadConfig } from './config'; import { db } from './db/postgre'; @Module({ imports: [ - ChartsModule, - EndpointsModule, - QueueModule, + ChartsModule, + EndpointsModule, + QueueModule, ], providers: [{ - provide: DI_SYMBOLS.config, - useValue: loadConfig(), + provide: DI_SYMBOLS.config, + useValue: loadConfig(), }, { - provide: DI_SYMBOLS.db, - useValue: db, + provide: DI_SYMBOLS.db, + useValue: db, }, { - provide: 'usersRepository', - useValue: Users, + provide: 'usersRepository', + useValue: Users, }, { - provide: 'notesRepository', - useValue: Notes, + provide: 'notesRepository', + useValue: Notes, }], - }) +}) export class AppModule {} diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts index bf51960482ad..48f2a6263029 100644 --- a/packages/backend/src/boot/master.ts +++ b/packages/backend/src/boot/master.ts @@ -6,13 +6,12 @@ import cluster from 'node:cluster'; import chalk from 'chalk'; import chalkTemplate from 'chalk-template'; import semver from 'semver'; - import Logger from '@/services/logger.js'; -import loadConfig from '@/config/load.js'; -import { Config } from '@/config/types.js'; +import loadConfig from '@/config.js'; +import type { Config } from '@/config.js'; import { lessThan } from '@/prelude/array.js'; -import { envOption } from '../env.js'; import { showMachineInfo } from '@/misc/show-machine-info.js'; +import { envOption } from '../env.js'; import { db, initDb } from '../db/postgre.js'; const _filename = fileURLToPath(import.meta.url); @@ -143,7 +142,7 @@ async function connectDb(): Promise { } } -async function spawnWorkers(limit: number = 1) { +async function spawnWorkers(limit = 1) { const workers = Math.min(limit, os.cpus().length); bootLogger.info(`Starting ${workers} worker${workers === 1 ? '' : 's'}...`); await Promise.all([...Array(workers)].map(spawnWorker)); @@ -155,7 +154,7 @@ function spawnWorker(): Promise { const worker = cluster.fork(); worker.on('message', message => { if (message === 'listenFailed') { - bootLogger.error(`The server Listen failed due to the previous error.`); + bootLogger.error('The server Listen failed due to the previous error.'); process.exit(1); } if (message !== 'ready') return; diff --git a/packages/backend/src/config/load.ts b/packages/backend/src/config.ts similarity index 53% rename from packages/backend/src/config/load.ts rename to packages/backend/src/config.ts index d547c5af712c..c3b8ae084a37 100644 --- a/packages/backend/src/config/load.ts +++ b/packages/backend/src/config.ts @@ -6,7 +6,94 @@ import * as fs from 'node:fs'; import { fileURLToPath } from 'node:url'; import { dirname } from 'node:path'; import * as yaml from 'js-yaml'; -import type { Source, Mixin } from './types.js'; + +/** + * ユーザーが設定する必要のある情報 + */ +export type Source = { + repository_url?: string; + feedback_url?: string; + url: string; + port: number; + disableHsts?: boolean; + db: { + host: string; + port: number; + db: string; + user: string; + pass: string; + disableCache?: boolean; + extra?: { [x: string]: string }; + }; + redis: { + host: string; + port: number; + family?: number; + pass: string; + db?: number; + prefix?: string; + }; + elasticsearch: { + host: string; + port: number; + ssl?: boolean; + user?: string; + pass?: string; + index?: string; + }; + + proxy?: string; + proxySmtp?: string; + proxyBypassHosts?: string[]; + + allowedPrivateNetworks?: string[]; + + maxFileSize?: number; + + accesslog?: string; + + clusterLimit?: number; + + id: string; + + outgoingAddressFamily?: 'ipv4' | 'ipv6' | 'dual'; + + deliverJobConcurrency?: number; + inboxJobConcurrency?: number; + deliverJobPerSec?: number; + inboxJobPerSec?: number; + deliverJobMaxAttempts?: number; + inboxJobMaxAttempts?: number; + + syslog: { + host: string; + port: number; + }; + + mediaProxy?: string; + proxyRemoteFiles?: boolean; + + signToActivityPubGet?: boolean; +}; + +/** + * Misskeyが自動的に(ユーザーが設定した情報から推論して)設定する情報 + */ +export type Mixin = { + version: string; + host: string; + hostname: string; + scheme: string; + wsScheme: string; + apiUrl: string; + wsUrl: string; + authUrl: string; + driveUrl: string; + userAgent: string; + clientEntry: string; +}; + +export type Config = Source & Mixin; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); diff --git a/packages/backend/src/config/types.ts b/packages/backend/src/config/types.ts deleted file mode 100644 index 78510c83773b..000000000000 --- a/packages/backend/src/config/types.ts +++ /dev/null @@ -1,87 +0,0 @@ -/** - * ユーザーが設定する必要のある情報 - */ -export type Source = { - repository_url?: string; - feedback_url?: string; - url: string; - port: number; - disableHsts?: boolean; - db: { - host: string; - port: number; - db: string; - user: string; - pass: string; - disableCache?: boolean; - extra?: { [x: string]: string }; - }; - redis: { - host: string; - port: number; - family?: number; - pass: string; - db?: number; - prefix?: string; - }; - elasticsearch: { - host: string; - port: number; - ssl?: boolean; - user?: string; - pass?: string; - index?: string; - }; - - proxy?: string; - proxySmtp?: string; - proxyBypassHosts?: string[]; - - allowedPrivateNetworks?: string[]; - - maxFileSize?: number; - - accesslog?: string; - - clusterLimit?: number; - - id: string; - - outgoingAddressFamily?: 'ipv4' | 'ipv6' | 'dual'; - - deliverJobConcurrency?: number; - inboxJobConcurrency?: number; - deliverJobPerSec?: number; - inboxJobPerSec?: number; - deliverJobMaxAttempts?: number; - inboxJobMaxAttempts?: number; - - syslog: { - host: string; - port: number; - }; - - mediaProxy?: string; - proxyRemoteFiles?: boolean; - - signToActivityPubGet?: boolean; -}; - -/** - * Misskeyが自動的に(ユーザーが設定した情報から推論して)設定する情報 - */ -export type Mixin = { - version: string; - host: string; - hostname: string; - scheme: string; - wsScheme: string; - apiUrl: string; - wsUrl: string; - authUrl: string; - driveUrl: string; - userAgent: string; - clientEntry: string; -}; - -export type Config = Source & Mixin; diff --git a/packages/backend/src/queue/DbQueueProcessorsService.ts b/packages/backend/src/queue/DbQueueProcessorsService.ts index 707895b89349..52cd483261b1 100644 --- a/packages/backend/src/queue/DbQueueProcessorsService.ts +++ b/packages/backend/src/queue/DbQueueProcessorsService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { DbJobData } from '@/queue/types.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type { DeleteDriveFilesProcessorService } from './processors/DeleteDriveFilesProcessorService.js'; import type { ExportCustomEmojisProcessorService } from './processors/ExportCustomEmojisProcessorService.js'; import type { ExportNotesProcessorService } from './processors/ExportNotesProcessorService.js'; diff --git a/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts b/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts index f37f92235fbe..b2deb4d72a32 100644 --- a/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts +++ b/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { ObjectStorageJobData } from '@/queue/types.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type { CleanRemoteFilesProcessorService } from './processors/CleanRemoteFilesProcessorService.js'; import type { DeleteFileProcessorService } from './processors/DeleteFileProcessorService.js'; import type Bull from 'bull'; diff --git a/packages/backend/src/queue/SystemQueueProcessorsService.ts b/packages/backend/src/queue/SystemQueueProcessorsService.ts index 2370b61b5814..63e5083d9d71 100644 --- a/packages/backend/src/queue/SystemQueueProcessorsService.ts +++ b/packages/backend/src/queue/SystemQueueProcessorsService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type { TickChartsProcessorService } from './processors/TickChartsProcessorService.js'; import type { ResyncChartsProcessorService } from './processors/ResyncChartsProcessorService.js'; import type { CleanChartsProcessorService } from './processors/CleanChartsProcessorService.js'; diff --git a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts index 075385fac4ed..80753cc357eb 100644 --- a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts +++ b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, MoreThan } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Mutings } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; import type Bull from 'bull'; diff --git a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts index 03a8f6ad3ddb..f0eaf64974c4 100644 --- a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, MoreThan } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type FederationChart from '@/services/chart/charts/federation.js'; import type NotesChart from '@/services/chart/charts/notes.js'; diff --git a/packages/backend/src/queue/processors/CleanProcessorService.ts b/packages/backend/src/queue/processors/CleanProcessorService.ts index d08289219e49..08aebdf22add 100644 --- a/packages/backend/src/queue/processors/CleanProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanProcessorService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, LessThan, MoreThan } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { UserIps } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type Bull from 'bull'; import type { QueueLoggerService } from '../QueueLoggerService.js'; diff --git a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts index f55b27d4cf93..42fd69757141 100644 --- a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan, Not } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { DriveFiles } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type { DriveService } from '@/services/DriveService.js'; import type Bull from 'bull'; diff --git a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts index 62a086a3d2b4..dc49693b7bb5 100644 --- a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts @@ -3,7 +3,7 @@ import { MoreThan } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { DriveFiles, UserProfiles } from '@/models/index.js'; import { Notes, Users } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type { DriveService } from '@/services/DriveService.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; diff --git a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts index 87f422036542..f47861c1deb9 100644 --- a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Users, DriveFiles } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type { DriveService } from '@/services/DriveService.js'; import type Bull from 'bull'; diff --git a/packages/backend/src/queue/processors/DeleteFileProcessorService.ts b/packages/backend/src/queue/processors/DeleteFileProcessorService.ts index 60bf39e8c775..d15f128b927d 100644 --- a/packages/backend/src/queue/processors/DeleteFileProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteFileProcessorService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type { DriveService } from '@/services/DriveService.js'; import type Bull from 'bull'; diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts index 99f24fb02ad9..6a74e8b7ab29 100644 --- a/packages/backend/src/queue/processors/DeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { DriveFiles , Instances } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { MetaService } from '@/services/MetaService.js'; import { toPuny } from '@/misc/convert-host.js'; diff --git a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts index 13972f8f4463..e430b3ca03bc 100644 --- a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts +++ b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { PollVotes , Notes } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type { CreateNotificationService } from '@/services/CreateNotificationService.js'; import type Bull from 'bull'; diff --git a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts index 2534fae00877..19c7522a7928 100644 --- a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts @@ -4,7 +4,7 @@ import { MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { DriveFiles, UserProfiles , Notes , Users , Blockings } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type { DriveService } from '@/services/DriveService.js'; import { getFullApAccount } from '@/misc/convert-host.js'; diff --git a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts index 0b82f78ceca4..9935e5a01628 100644 --- a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts @@ -7,7 +7,7 @@ import mime from 'mime-types'; import archiver from 'archiver'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Emojis, Users } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type { DriveService } from '@/services/DriveService.js'; import { createTemp, createTempDir } from '@/misc/create-temp.js'; diff --git a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts index 6e1a63de35bc..f98d7d2911ea 100644 --- a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts @@ -5,7 +5,7 @@ import { format as dateFormat } from 'date-fns'; import { DI_SYMBOLS } from '@/di-symbols.js'; import { Users } from '@/models/index.js'; import type { Followings, Mutings } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type { DriveService } from '@/services/DriveService.js'; import { getFullApAccount } from '@/misc/convert-host.js'; diff --git a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts index 5645c9c9a785..9fd61054f0ad 100644 --- a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts @@ -4,7 +4,7 @@ import { IsNull, MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Mutings, Users , Blockings } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type { DriveService } from '@/services/DriveService.js'; import { getFullApAccount } from '@/misc/convert-host.js'; diff --git a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts index c481f222c15e..bb9523677565 100644 --- a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts @@ -4,7 +4,7 @@ import { IsNull, MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Notes, Polls , Users } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type { DriveService } from '@/services/DriveService.js'; import { createTemp } from '@/misc/create-temp.js'; diff --git a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts index f461ee40d8e4..241e7479a46e 100644 --- a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts @@ -4,7 +4,7 @@ import { In, IsNull, MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { UserListJoinings, UserLists, Users } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type { DriveService } from '@/services/DriveService.js'; import { createTemp } from '@/misc/create-temp.js'; diff --git a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts index 8eb1af8b2125..95e686bb8e3a 100644 --- a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts @@ -3,7 +3,7 @@ import { IsNull, MoreThan } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import { Users } from '@/models/index.js'; import type { Blockings , DriveFiles } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { isSelfHost, toPuny } from '@/misc/convert-host.js'; import * as Acct from '@/misc/acct.js'; diff --git a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts index dcb19bc8fc9d..12375d5005da 100644 --- a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts @@ -4,7 +4,7 @@ import { IsNull, MoreThan } from 'typeorm'; import unzipper from 'unzipper'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Emojis , DriveFiles , Users } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type { CustomEmojiService } from '@/services/CustomEmojiService.js'; import { createTempDir } from '@/misc/create-temp.js'; diff --git a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts index 7b4a36f4b80c..10fe8674e639 100644 --- a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts @@ -3,7 +3,7 @@ import { IsNull, MoreThan } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import { Users } from '@/models/index.js'; import type { DriveFiles } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { isSelfHost, toPuny } from '@/misc/convert-host.js'; import * as Acct from '@/misc/acct.js'; diff --git a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts index 4359a435c9a3..1763eb1bf628 100644 --- a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts @@ -4,7 +4,7 @@ import { DI_SYMBOLS } from '@/di-symbols.js'; import type { DriveFiles } from '@/models/index.js'; import { Users } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { isSelfHost, toPuny } from '@/misc/convert-host.js'; import * as Acct from '@/misc/acct.js'; diff --git a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts index a65ccc769d69..ce55a1d0efe9 100644 --- a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts @@ -3,7 +3,7 @@ import { IsNull, MoreThan } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { DriveFiles , UserListJoinings , UserLists } from '@/models/index.js'; import { Users } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { isSelfHost, toPuny } from '@/misc/convert-host.js'; import * as Acct from '@/misc/acct.js'; diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index 58fdf0a23ab4..bf1118dc02ca 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -5,7 +5,7 @@ import httpSignature from '@peertube/http-signature'; import { DI_SYMBOLS } from '@/di-symbols.js'; import { Instances } from '@/models/index.js'; import type { DriveFiles } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { MetaService } from '@/services/MetaService.js'; import { extractDbHost, toPuny } from '@/misc/convert-host.js'; diff --git a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts index f97f251c1686..28ed62b3e86d 100644 --- a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, MoreThan } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type FederationChart from '@/services/chart/charts/federation.js'; import type NotesChart from '@/services/chart/charts/notes.js'; diff --git a/packages/backend/src/queue/processors/TickChartsProcessorService.ts b/packages/backend/src/queue/processors/TickChartsProcessorService.ts index 10beee62096d..8eb61fbe56f3 100644 --- a/packages/backend/src/queue/processors/TickChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/TickChartsProcessorService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, MoreThan } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type FederationChart from '@/services/chart/charts/federation.js'; import type NotesChart from '@/services/chart/charts/notes.js'; diff --git a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts index 3ed6ecf12da1..da02acafb77c 100644 --- a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Webhooks } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { HttpRequestService } from '@/services/HttpRequestService.js'; import { StatusError } from '@/misc/status-error.js'; diff --git a/packages/backend/src/queue/queue.module.ts b/packages/backend/src/queue/queue.module.ts index 2827c15c722e..bda128d128ef 100644 --- a/packages/backend/src/queue/queue.module.ts +++ b/packages/backend/src/queue/queue.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import { initialize as initializeQueue } from './initialize.js'; import type Bull from 'bull'; import type { DeliverJobData, InboxJobData, DbJobData, ObjectStorageJobData, EndedPollNotificationJobData, WebhookDeliverJobData } from './types.js'; diff --git a/packages/backend/src/queue/queue.service.ts b/packages/backend/src/queue/queue.service.ts index 9f642bdd35bf..7d20bda9e0a2 100644 --- a/packages/backend/src/queue/queue.service.ts +++ b/packages/backend/src/queue/queue.service.ts @@ -3,7 +3,7 @@ import { v4 as uuid } from 'uuid'; import type { IActivity } from '@/services/remote/activitypub/type.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import type { Webhook, webhookEventTypes } from '@/models/entities/webhook.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from './queue.module.js'; import type { ThinUser } from './types.js'; diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 5d8986fb04e5..46136de6da6a 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -7,7 +7,7 @@ import { DI_SYMBOLS } from '@/di-symbols.js'; import { Followings , Notes } from '@/models/index.js'; import type { Emojis, NoteReactions , UserProfiles , UserNotePinings , Users } from '@/models/index.js'; import * as url from '@/prelude/url.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import { isSelfHost } from '@/misc/convert-host.js'; import { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; import { QueueService } from '@/queue/queue.service.js'; diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index b4e18942d0c3..437806e47810 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -7,7 +7,7 @@ import cors from '@koa/cors'; import Router from '@koa/router'; import send from 'koa-send'; import rename from 'rename'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import type { DriveFiles } from '@/models/index.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import { createTemp } from '@/misc/create-temp.js'; diff --git a/packages/backend/src/server/MediaProxyServerService.ts b/packages/backend/src/server/MediaProxyServerService.ts index d9ff85f801aa..1597c49cd7f5 100644 --- a/packages/backend/src/server/MediaProxyServerService.ts +++ b/packages/backend/src/server/MediaProxyServerService.ts @@ -5,7 +5,7 @@ import cors from '@koa/cors'; import Router from '@koa/router'; import sharp from 'sharp'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import { isMimeImage } from '@/misc/is-mime-image.js'; import { detectType } from '@/misc/get-file-info.js'; import { createTemp } from '@/misc/create-temp.js'; diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index b80e12dc7509..20df97270acf 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -4,7 +4,7 @@ import { IsNull, MoreThan } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Notes , Users } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import { MetaService } from '@/services/MetaService.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const'; import { Cache } from '@/misc/cache.js'; diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 2848a95c1af3..55d10ca1218b 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -9,7 +9,7 @@ import koaLogger from 'koa-logger'; import * as slow from 'koa-slow'; import { IsNull } from 'typeorm'; import { GlobalEventService } from '@/services/GlobalEventService.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import type { UserProfiles , Users } from '@/models/index.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import Logger from '@/logger.js'; diff --git a/packages/backend/src/server/WellKnownServerService.ts b/packages/backend/src/server/WellKnownServerService.ts index b309916d4f7f..a9068f84916b 100644 --- a/packages/backend/src/server/WellKnownServerService.ts +++ b/packages/backend/src/server/WellKnownServerService.ts @@ -3,7 +3,7 @@ import Router from '@koa/router'; import { IsNull, MoreThan } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import { escapeAttribute, escapeValue } from '@/prelude/xml'; import type { User } from '@/models/entities/user'; import * as Acct from '@/misc/acct.js'; diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts index 6312b6832b7e..031d44055916 100644 --- a/packages/backend/src/server/api/ApiServerService.ts +++ b/packages/backend/src/server/api/ApiServerService.ts @@ -5,7 +5,7 @@ import multer from '@koa/multer'; import bodyParser from 'koa-bodyparser'; import cors from '@koa/cors'; import { ModuleRef } from '@nestjs/core'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import type { Users } from '@/models/index.js'; import { Instances, AccessTokens } from '@/models/index.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index bf4b67f2b7c3..e266cd9dfac9 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -6,7 +6,7 @@ import { IsNull } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { UserSecurityKeys , Signins, UserProfiles } from '@/models/index.js'; import { AttestationChallenges, Users } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import { getIpHash } from '@/misc/get-ip-hash.js'; import type { ILocalUser } from '@/models/entities/user.js'; import { IdService } from '@/services/IdService.js'; diff --git a/packages/backend/src/server/api/SigninService.ts b/packages/backend/src/server/api/SigninService.ts index 0e5f3b29246c..02bd73ad5057 100644 --- a/packages/backend/src/server/api/SigninService.ts +++ b/packages/backend/src/server/api/SigninService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Signins , Users } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import { IdService } from '@/services/IdService.js'; import type { ILocalUser } from '@/models/entities/user.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts index 790b12ae6b95..428aa9e8ad4c 100644 --- a/packages/backend/src/server/api/SignupApiService.ts +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -4,7 +4,7 @@ import bcrypt from 'bcryptjs'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { RegistrationTickets , UserPendings, UserProfiles , Users } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import { MetaService } from '@/services/MetaService.js'; import { CaptchaService } from '@/services/CaptchaService.js'; import { IdService } from '@/services/IdService.js'; diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts index 753dd1a9e397..3db119879880 100644 --- a/packages/backend/src/server/api/StreamingApiServerService.ts +++ b/packages/backend/src/server/api/StreamingApiServerService.ts @@ -5,7 +5,7 @@ import * as websocket from 'websocket'; import { DI_SYMBOLS } from '@/di-symbols.js'; import { Users } from '@/models/index.js'; import type { Blockings, ChannelFollowings, Followings, Mutings, UserProfiles } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import { NoteReadService } from '@/services/NoteReadService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { AuthenticateService } from './AuthenticateService.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 22a484f0911b..6ae5548939b3 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { MetaService } from '@/services/MetaService.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/auth/session/generate.ts b/packages/backend/src/server/api/endpoints/auth/session/generate.ts index 86c479f2ef18..aa4a332c3343 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/generate.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/generate.ts @@ -3,7 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Apps, AuthSessions } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts index 389cf04f4c77..650a90592dd0 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts @@ -11,7 +11,7 @@ import { AttestationChallenges, } from '@/models/index.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import { GlobalEventService } from '@/services/GlobalEventService'; import { TwoFactorAuthenticationService } from '@/services/TwoFactorAuthenticationService'; diff --git a/packages/backend/src/server/api/endpoints/i/apps.ts b/packages/backend/src/server/api/endpoints/i/apps.ts index f1487d673614..b8bcbbcf29cb 100644 --- a/packages/backend/src/server/api/endpoints/i/apps.ts +++ b/packages/backend/src/server/api/endpoints/i/apps.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { AccessTokens } from '@/models/index.js'; +import type { AccessTokens } from '@/models/index.js'; export const meta = { requireCredential: true, @@ -20,9 +20,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('accessTokensRepository') + private accessTokensRepository: typeof AccessTokens, ) { super(meta, paramDef, async (ps, me) => { - const query = AccessTokens.createQueryBuilder('token') + const query = this.accessTokensRepository.createQueryBuilder('token') .where('token.userId = :userId', { userId: me.id }); switch (ps.sort) { diff --git a/packages/backend/src/server/api/endpoints/i/change-password.ts b/packages/backend/src/server/api/endpoints/i/change-password.ts index 1e9482d81e82..beadcb626a6e 100644 --- a/packages/backend/src/server/api/endpoints/i/change-password.ts +++ b/packages/backend/src/server/api/endpoints/i/change-password.ts @@ -1,7 +1,7 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserProfiles } from '@/models/index.js'; +import type { UserProfiles } from '@/models/index.js'; export const meta = { requireCredential: true, @@ -22,9 +22,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, ) { super(meta, paramDef, async (ps, me) => { - const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); // Compare password const same = await bcrypt.compare(ps.currentPassword, profile.password!); @@ -37,7 +39,7 @@ export default class extends Endpoint { const salt = await bcrypt.genSalt(8); const hash = await bcrypt.hash(ps.newPassword, salt); - await UserProfiles.update(me.id, { + await this.userProfilesRepository.update(me.id, { password: hash, }); }); diff --git a/packages/backend/src/server/api/endpoints/i/delete-account.ts b/packages/backend/src/server/api/endpoints/i/delete-account.ts index b13a996d99fb..acdcca469ba3 100644 --- a/packages/backend/src/server/api/endpoints/i/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts @@ -1,9 +1,8 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; -import type { Users } from '@/models/index.js'; -import { UserProfiles } from '@/models/index.js'; -import { deleteAccount } from '@/services/delete-account.js'; +import type { Users , UserProfiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DeleteAccountService } from '@/services/DeleteAccountService'; export const meta = { requireCredential: true, @@ -25,9 +24,14 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + private deleteAccountService: DeleteAccountService, ) { super(meta, paramDef, async (ps, me) => { - const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); const userDetailed = await this.usersRepository.findOneByOrFail({ id: me.id }); if (userDetailed.isDeleted) { return; @@ -40,7 +44,7 @@ export default class extends Endpoint { throw new Error('incorrect password'); } - await deleteAccount(me); + await this.deleteAccountService.deleteAccount(me); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/favorites.ts b/packages/backend/src/server/api/endpoints/i/favorites.ts index 411eb1f84422..3c4c1e9feb79 100644 --- a/packages/backend/src/server/api/endpoints/i/favorites.ts +++ b/packages/backend/src/server/api/endpoints/i/favorites.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { NoteFavorites } from '@/models/index.js'; +import type { NoteFavorites } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { NoteFavoriteEntityService } from '@/services/entities/NoteFavoriteEntityService'; export const meta = { tags: ['account', 'notes', 'favorites'], @@ -35,10 +36,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('noteFavoritesRepository') + private noteFavoritesRepository: typeof NoteFavorites, + + private noteFavoriteEntityService: NoteFavoriteEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(NoteFavorites.createQueryBuilder('favorite'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.noteFavoritesRepository.createQueryBuilder('favorite'), ps.sinceId, ps.untilId) .andWhere('favorite.userId = :meId', { meId: me.id }) .leftJoinAndSelect('favorite.note', 'note'); @@ -46,7 +51,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return await NoteFavorites.packMany(favorites, me); + return await this.noteFavoriteEntityService.packMany(favorites, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts b/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts index 4fc62eaea296..85b107f1fdd6 100644 --- a/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts +++ b/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { MutedNotes } from '@/models/index.js'; +import type { MutedNotes } from '@/models/index.js'; export const meta = { tags: ['account'], @@ -31,10 +31,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('mutedNotesRepository') + private mutedNotesRepository: typeof MutedNotes, ) { super(meta, paramDef, async (ps, me) => { return { - count: await MutedNotes.countBy({ + count: await this.mutedNotesRepository.countBy({ userId: me.id, reason: 'word', }), diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index 29ef0ae455ab..aa39434b4479 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -1,11 +1,12 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { Users } from '@/models/index.js'; -import { Notifications, Followings, Mutings, UserProfiles } from '@/models/index.js'; +import type { Users , Followings, Mutings, UserProfiles } from '@/models/index.js'; +import { Notifications } from '@/models/index.js'; import { notificationTypes } from '@/types.js'; -import read from '@/services/note/read.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import { NoteReadService } from '@/services/NoteReadService.js'; +import { NotificationEntityService } from '@/services/entities/NotificationEntityService.js'; import { readNotification } from '../../common/read-notification.js'; export const meta = { @@ -57,7 +58,18 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + @Inject('mutingsRepository') + private mutingsRepository: typeof Mutings, + + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + private notificationEntityService: NotificationEntityService, private queryService: QueryService, + private noteReadService: NoteReadService, ) { super(meta, paramDef, async (ps, me) => { // includeTypes が空の場合はクエリしない @@ -76,7 +88,7 @@ export default class extends Endpoint { .select('muting.muteeId') .where('muting.muterId = :muterId', { muterId: me.id }); - const mutingInstanceQuery = UserProfiles.createQueryBuilder('user_profile') + const mutingInstanceQuery = this.userProfilesRepository.createQueryBuilder('user_profile') .select('user_profile.mutedInstances') .where('user_profile.userId = :muterId', { muterId: me.id }); @@ -147,10 +159,10 @@ export default class extends Endpoint { const notes = notifications.filter(notification => ['mention', 'reply', 'quote'].includes(notification.type)).map(notification => notification.note!); if (notes.length > 0) { - read(me.id, notes); + this.noteReadService.read(me.id, notes); } - return await Notifications.packMany(notifications, me.id); + return await this.notificationEntityService.packMany(notifications, me.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/page-likes.ts b/packages/backend/src/server/api/endpoints/i/page-likes.ts index 7c1d4b0a235c..771c9b74cc4f 100644 --- a/packages/backend/src/server/api/endpoints/i/page-likes.ts +++ b/packages/backend/src/server/api/endpoints/i/page-likes.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { PageLikes } from '@/models/index.js'; +import type { PageLikes } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { PageLikeEntityService } from '@/services/entities/PageLikeEntityService'; export const meta = { tags: ['account', 'pages'], @@ -45,10 +46,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('pageLikesRepository') + private pageLikesRepository: typeof PageLikes, + + private pageLikeEntityService: PageLikeEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(PageLikes.createQueryBuilder('like'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.pageLikesRepository.createQueryBuilder('like'), ps.sinceId, ps.untilId) .andWhere('like.userId = :meId', { meId: me.id }) .leftJoinAndSelect('like.page', 'page'); @@ -56,7 +61,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return PageLikes.packMany(likes, me); + return this.pageLikeEntityService.packMany(likes, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/pages.ts b/packages/backend/src/server/api/endpoints/i/pages.ts index 8d65d32e5c0c..e23d9fcfac27 100644 --- a/packages/backend/src/server/api/endpoints/i/pages.ts +++ b/packages/backend/src/server/api/endpoints/i/pages.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Pages } from '@/models/index.js'; +import type { Pages } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { PageEntityService } from '@/services/entities/PageEntityService'; export const meta = { tags: ['account', 'pages'], @@ -35,17 +36,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('pagesRepository') + private pagesRepository: typeof Pages, + + private pageEntityService: PageEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(Pages.createQueryBuilder('page'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.pagesRepository.createQueryBuilder('page'), ps.sinceId, ps.untilId) .andWhere('page.userId = :meId', { meId: me.id }); const pages = await query .take(ps.limit) .getMany(); - return await Pages.packMany(pages); + return await this.pageEntityService.packMany(pages); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/pin.ts b/packages/backend/src/server/api/endpoints/i/pin.ts index 47db5c1b31a3..c5fc4d2f3aea 100644 --- a/packages/backend/src/server/api/endpoints/i/pin.ts +++ b/packages/backend/src/server/api/endpoints/i/pin.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; -import { addPinned } from '@/services/i/pin.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { NotePiningService } from '@/services/NotePiningService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -50,15 +51,15 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + private userEntityService: UserEntityService, + private notePiningService: NotePiningService, ) { super(meta, paramDef, async (ps, me) => { - await addPinned(me, ps.noteId).catch(e => { - if (e.id === '70c4e51f-5bea-449c-a030-53bee3cce202') throw new ApiError(meta.errors.noSuchNote); - if (e.id === '15a018eb-58e5-4da1-93be-330fcc5e4e1a') throw new ApiError(meta.errors.pinLimitExceeded); - if (e.id === '23f0cf4e-59a3-4276-a91d-61a5891c1514') throw new ApiError(meta.errors.alreadyPinned); - throw e; + await this.notePiningService.addPinned(me, ps.noteId).catch(err => { + if (err.id === '70c4e51f-5bea-449c-a030-53bee3cce202') throw new ApiError(meta.errors.noSuchNote); + if (err.id === '15a018eb-58e5-4da1-93be-330fcc5e4e1a') throw new ApiError(meta.errors.pinLimitExceeded); + if (err.id === '23f0cf4e-59a3-4276-a91d-61a5891c1514') throw new ApiError(meta.errors.alreadyPinned); + throw err; }); return await this.userEntityService.pack(me.id, me, { diff --git a/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts b/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts index fb2fa4e9091e..b8b4fe1db8c4 100644 --- a/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts +++ b/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; -import { publishMainStream } from '@/services/stream.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { MessagingMessages, UserGroupJoinings } from '@/models/index.js'; +import type { MessagingMessages, UserGroupJoinings } from '@/models/index.js'; +import { GlobalEventService } from '@/services/GlobalEventService'; export const meta = { tags: ['account', 'messaging'], @@ -21,19 +21,26 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('messagingMessagesRepository') + private messagingMessagesRepository: typeof MessagingMessages, + + @Inject('userGroupJoinings') + private userGroupJoinings: typeof UserGroupJoinings, + + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { // Update documents - await MessagingMessages.update({ + await this.messagingMessagesRepository.update({ recipientId: me.id, isRead: false, }, { isRead: true, }); - const joinings = await UserGroupJoinings.findBy({ userId: me.id }); + const joinings = await this.userGroupJoinings.findBy({ userId: me.id }); - await Promise.all(joinings.map(j => MessagingMessages.createQueryBuilder().update() + await Promise.all(joinings.map(j => this.messagingMessagesRepository.createQueryBuilder().update() .set({ reads: (() => `array_append("reads", '${me.id}')`) as any, }) @@ -42,7 +49,7 @@ export default class extends Endpoint { .andWhere('NOT (:userId = ANY(reads))', { userId: me.id }) .execute())); - publishMainStream(me.id, 'readAllMessagingMessages'); + this.globalEventService.publishMainStream(me.id, 'readAllMessagingMessages'); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts b/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts index 8fb9df4617d4..877f8987c128 100644 --- a/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts +++ b/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; -import { publishMainStream } from '@/services/stream.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { NoteUnreads } from '@/models/index.js'; +import type { NoteUnreads } from '@/models/index.js'; +import { GlobalEventService } from '@/services/GlobalEventService'; export const meta = { tags: ['account'], @@ -21,16 +21,20 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('noteUnreadsJoinings') + private noteUnreadsJoinings: typeof NoteUnreads, + + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { // Remove documents - await NoteUnreads.delete({ + await this.noteUnreadsJoinings.delete({ userId: me.id, }); // 全て既読になったイベントを発行 - publishMainStream(me.id, 'readAllUnreadMentions'); - publishMainStream(me.id, 'readAllUnreadSpecifiedNotes'); + this.globalEventService.publishMainStream(me.id, 'readAllUnreadMentions'); + this.globalEventService.publishMainStream(me.id, 'readAllUnreadSpecifiedNotes'); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/read-announcement.ts b/packages/backend/src/server/api/endpoints/i/read-announcement.ts index de38ae4ffa70..0607a41fdb86 100644 --- a/packages/backend/src/server/api/endpoints/i/read-announcement.ts +++ b/packages/backend/src/server/api/endpoints/i/read-announcement.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { IdService } from '@/services/IdService.js'; -import type { Users } from '@/models/index.js'; -import { AnnouncementReads, Announcements } from '@/models/index.js'; -import { publishMainStream } from '@/services/stream.js'; +import { IdService } from '@/services/IdService.js'; +import type { Users , AnnouncementReads, Announcements } from '@/models/index.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -34,21 +34,26 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('announcementsRepository') + private announcementsRepository: typeof Announcements, + @Inject('announcementReadsRepository') + private announcementReadsRepository: typeof AnnouncementReads, + + private userEntityService: UserEntityService, private idService: IdService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { // Check if announcement exists - const announcement = await Announcements.findOneBy({ id: ps.announcementId }); + const announcement = await this.announcementsRepository.findOneBy({ id: ps.announcementId }); if (announcement == null) { throw new ApiError(meta.errors.noSuchAnnouncement); } // Check if already read - const read = await AnnouncementReads.findOneBy({ + const read = await this.announcementReadsRepository.findOneBy({ announcementId: ps.announcementId, userId: me.id, }); @@ -58,15 +63,15 @@ export default class extends Endpoint { } // Create read - await AnnouncementReads.insert({ + await this.announcementReadsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), announcementId: ps.announcementId, userId: me.id, }); - if (!await this.usersRepository.getHasUnreadAnnouncement(me.id)) { - publishMainStream(me.id, 'readAllAnnouncements'); + if (!await this.userEntityService.getHasUnreadAnnouncement(me.id)) { + this.globalEventService.publishMainStream(me.id, 'readAllAnnouncements'); } }); } diff --git a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts index 6c350db7f7b2..a68a12b4aec5 100644 --- a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts +++ b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts @@ -1,10 +1,9 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; -import { publishInternalEvent, publishMainStream, publishUserEvent } from '@/services/stream.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users } from '@/models/index.js'; -import { UserProfiles } from '@/models/index.js'; +import type { Users , UserProfiles } from '@/models/index.js'; import generateUserToken from '@/misc/generate-native-user-token.js'; +import { GlobalEventService } from '@/services/GlobalEventService'; export const meta = { requireCredential: true, @@ -26,12 +25,17 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { const freshUser = await this.usersRepository.findOneByOrFail({ id: me.id }); const oldToken = freshUser.token; - const profile = await UserProfiles.findOneByOrFail({ userId: me.id }); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: me.id }); // Compare password const same = await bcrypt.compare(ps.password, profile.password!); @@ -47,12 +51,12 @@ export default class extends Endpoint { }); // Publish event - publishInternalEvent('userTokenRegenerated', { id: me.id, oldToken, newToken }); - publishMainStream(me.id, 'myTokenRegenerated'); + this.globalEventService.publishInternalEvent('userTokenRegenerated', { id: me.id, oldToken, newToken }); + this.globalEventService.publishMainStream(me.id, 'myTokenRegenerated'); // Terminate streaming setTimeout(() => { - publishUserEvent(me.id, 'terminate', {}); + this.globalEventService.publishUserEvent(me.id, 'terminate', {}); }, 5000); }); } diff --git a/packages/backend/src/server/api/endpoints/i/revoke-token.ts b/packages/backend/src/server/api/endpoints/i/revoke-token.ts index 9680eacdf1b6..83986cdf188c 100644 --- a/packages/backend/src/server/api/endpoints/i/revoke-token.ts +++ b/packages/backend/src/server/api/endpoints/i/revoke-token.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { AccessTokens } from '@/models/index.js'; -import { publishUserEvent } from '@/services/stream.js'; +import type { AccessTokens } from '@/models/index.js'; +import { GlobalEventService } from '@/services/GlobalEventService'; export const meta = { requireCredential: true, @@ -21,18 +21,22 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('accessTokensRepository') + private accessTokensRepository: typeof AccessTokens, + + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { - const token = await AccessTokens.findOneBy({ id: ps.tokenId }); + const token = await this.accessTokensRepository.findOneBy({ id: ps.tokenId }); if (token) { - await AccessTokens.delete({ + await this.accessTokensRepository.delete({ id: ps.tokenId, userId: me.id, }); // Terminate streaming - publishUserEvent(me.id, 'terminate'); + this.globalEventService.publishUserEvent(me.id, 'terminate'); } }); } diff --git a/packages/backend/src/server/api/endpoints/i/unpin.ts b/packages/backend/src/server/api/endpoints/i/unpin.ts index 46b0bb8c8a2f..3b940af78708 100644 --- a/packages/backend/src/server/api/endpoints/i/unpin.ts +++ b/packages/backend/src/server/api/endpoints/i/unpin.ts @@ -2,6 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { removePinned } from '@/services/i/pin.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { NotePiningService } from '@/services/NotePiningService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -38,13 +40,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + private userEntityService: UserEntityService, + private notePiningService: NotePiningService, ) { super(meta, paramDef, async (ps, me) => { - await removePinned(me, ps.noteId).catch(e => { - if (e.id === 'b302d4cf-c050-400a-bbb3-be208681f40c') throw new ApiError(meta.errors.noSuchNote); - throw e; + await this.notePiningService.removePinned(me, ps.noteId).catch(err => { + if (err.id === 'b302d4cf-c050-400a-bbb3-be208681f40c') throw new ApiError(meta.errors.noSuchNote); + throw err; }); return await this.userEntityService.pack(me.id, me, { diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts index f00cf53bc35b..ba002404207d 100644 --- a/packages/backend/src/server/api/endpoints/i/update-email.ts +++ b/packages/backend/src/server/api/endpoints/i/update-email.ts @@ -8,7 +8,7 @@ import { UserProfiles } from '@/models/index.js'; import { validateEmailForAccount } from '@/services/validate-email-for-account.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { EmailService } from '@/services/EmailService.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 0aacc9b6c90f..e51697039a68 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -8,7 +8,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { EmojiEntityService } from '@/services/entities/EmojiEntityService.js'; import { MetaService } from '@/services/MetaService.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; export const meta = { diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 61aa9290140f..488fa47c79d0 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -13,7 +13,7 @@ import { createBullBoard } from '@bull-board/api'; import { BullAdapter } from '@bull-board/api/bullAdapter.js'; import { KoaAdapter } from '@bull-board/koa'; import { In, IsNull } from 'typeorm'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import type { Pages , Channels, Clips, GalleryPosts , Notes, UserProfiles, Users } from '@/models/index.js'; import { getNoteSummary } from '@/misc/get-note-summary.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts index 5f5c01a5c40d..d0f6777c2eb4 100644 --- a/packages/backend/src/server/web/FeedService.ts +++ b/packages/backend/src/server/web/FeedService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, IsNull } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { DriveFiles, Notes, UserProfiles , Users } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import type { User } from '@/models/entities/user'; @Injectable() diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts index e646c301370f..1913bc495e74 100644 --- a/packages/backend/src/server/web/UrlPreviewService.ts +++ b/packages/backend/src/server/web/UrlPreviewService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import summaly from 'summaly'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import { MetaService } from '@/services/MetaService.js'; import { HttpRequestService } from '@/services/HttpRequestService.js'; import Logger from '@/logger.js'; diff --git a/packages/backend/src/services/AccountUpdateService.ts b/packages/backend/src/services/AccountUpdateService.ts index 2f5c58ea0859..fa6b48af23e6 100644 --- a/packages/backend/src/services/AccountUpdateService.ts +++ b/packages/backend/src/services/AccountUpdateService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI_SYMBOLS } from '@/di-symbols.js'; import { Users } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type { User } from '@/models/entities/user.js'; import type { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; import type { RelayService } from '@/services/RelayService.js'; diff --git a/packages/backend/src/services/AiService.ts b/packages/backend/src/services/AiService.ts index 286f1c2e28a5..91d7825539b7 100644 --- a/packages/backend/src/services/AiService.ts +++ b/packages/backend/src/services/AiService.ts @@ -4,7 +4,7 @@ import { dirname } from 'node:path'; import { Inject, Injectable } from '@nestjs/common'; import * as nsfw from 'nsfwjs'; import si from 'systeminformation'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; const _filename = fileURLToPath(import.meta.url); diff --git a/packages/backend/src/services/CaptchaService.ts b/packages/backend/src/services/CaptchaService.ts index 4809925c1460..e35435676da8 100644 --- a/packages/backend/src/services/CaptchaService.ts +++ b/packages/backend/src/services/CaptchaService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import { HttpRequestService } from './HttpRequestService.js'; type CaptchaResponse = { diff --git a/packages/backend/src/services/CustomEmojiService.ts b/packages/backend/src/services/CustomEmojiService.ts index 620a9d84d8f2..abfad3915570 100644 --- a/packages/backend/src/services/CustomEmojiService.ts +++ b/packages/backend/src/services/CustomEmojiService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Emojis } from '@/models/index.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type { IdService } from '@/services/IdService.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import type { Emoji } from '@/models/entities/emoji.js'; diff --git a/packages/backend/src/services/DownloadService.ts b/packages/backend/src/services/DownloadService.ts index da7e38056fba..41e44e4f258a 100644 --- a/packages/backend/src/services/DownloadService.ts +++ b/packages/backend/src/services/DownloadService.ts @@ -7,7 +7,7 @@ import PrivateIp from 'private-ip'; import got, * as Got from 'got'; import chalk from 'chalk'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import Logger from '@/logger.js'; import { HttpRequestService } from '@/services/HttpRequestService.js'; import { createTemp } from '@/misc/create-temp'; diff --git a/packages/backend/src/services/DriveService.ts b/packages/backend/src/services/DriveService.ts index fa0fc2f3fd3b..e50d5b84e2a9 100644 --- a/packages/backend/src/services/DriveService.ts +++ b/packages/backend/src/services/DriveService.ts @@ -6,7 +6,7 @@ import { IsNull } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { DriveFiles , Users , DriveFolders , UserProfiles } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import Logger from '@/Logger.js'; import type { IRemoteUser, User } from '@/models/entities/user.js'; import { MetaService } from '@/services/MetaService.js'; diff --git a/packages/backend/src/services/EmailService.ts b/packages/backend/src/services/EmailService.ts index 19385d4ef5ac..639dc8aa7e5a 100644 --- a/packages/backend/src/services/EmailService.ts +++ b/packages/backend/src/services/EmailService.ts @@ -2,7 +2,7 @@ import * as nodemailer from 'nodemailer'; import { Inject, Injectable } from '@nestjs/common'; import type { MetaService } from '@/services/MetaService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import Logger from '@/logger.js'; @Injectable() diff --git a/packages/backend/src/services/GlobalEventService.ts b/packages/backend/src/services/GlobalEventService.ts index 327eca26a6e0..0dcb0ddc41f1 100644 --- a/packages/backend/src/services/GlobalEventService.ts +++ b/packages/backend/src/services/GlobalEventService.ts @@ -24,7 +24,7 @@ import type { } from '@/server/api/stream/types.js'; import type { Packed } from '@/misc/schema.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; @Injectable() export class GlobalEventService { diff --git a/packages/backend/src/services/IdService.ts b/packages/backend/src/services/IdService.ts index ac76fd4afd74..8e1b61e8fee8 100644 --- a/packages/backend/src/services/IdService.ts +++ b/packages/backend/src/services/IdService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { ulid } from 'ulid'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import { genAid } from '@/misc/id/aid.js'; import { genMeid } from '@/misc/id/meid.js'; import { genMeidg } from '@/misc/id/meidg.js'; diff --git a/packages/backend/src/services/ImageProcessingService.ts b/packages/backend/src/services/ImageProcessingService.ts index 6e8cdd865b25..07b83106548e 100644 --- a/packages/backend/src/services/ImageProcessingService.ts +++ b/packages/backend/src/services/ImageProcessingService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import sharp from 'sharp'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; export type IImage = { data: Buffer; diff --git a/packages/backend/src/services/InternalStorageService.ts b/packages/backend/src/services/InternalStorageService.ts index 7b916ed60b2e..41c72d83573c 100644 --- a/packages/backend/src/services/InternalStorageService.ts +++ b/packages/backend/src/services/InternalStorageService.ts @@ -4,7 +4,7 @@ import { fileURLToPath } from 'node:url'; import { dirname } from 'node:path'; import { Inject, Injectable } from '@nestjs/common'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); diff --git a/packages/backend/src/services/MfmService.ts b/packages/backend/src/services/MfmService.ts index 49686705929d..9be544fae101 100644 --- a/packages/backend/src/services/MfmService.ts +++ b/packages/backend/src/services/MfmService.ts @@ -4,7 +4,7 @@ import * as parse5 from 'parse5'; import { JSDOM } from 'jsdom'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import { intersperse } from '@/prelude/array.js'; import type { IMentionedRemoteUsers } from '@/models/entities/note.js'; import * as TreeAdapter from '../../node_modules/parse5/dist/tree-adapters/default.js'; diff --git a/packages/backend/src/services/NoteCreateService.ts b/packages/backend/src/services/NoteCreateService.ts index dd11121ab6b8..c9b0428734ba 100644 --- a/packages/backend/src/services/NoteCreateService.ts +++ b/packages/backend/src/services/NoteCreateService.ts @@ -28,7 +28,7 @@ import { db } from '@/db/postgre.js'; import { RelayService } from '@/services/RelayService.js'; import { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import NotesChart from '@/services/chart/charts/notes.js'; import PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; import InstanceChart from '@/services/chart/charts/instance.js'; diff --git a/packages/backend/src/services/NoteDeleteService.ts b/packages/backend/src/services/NoteDeleteService.ts index 8e388c1deb59..988524e8c545 100644 --- a/packages/backend/src/services/NoteDeleteService.ts +++ b/packages/backend/src/services/NoteDeleteService.ts @@ -14,7 +14,7 @@ import { countSameRenotes } from '@/misc/count-same-renotes.js'; import type { RelayService } from '@/services/RelayService.js'; import type { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type NotesChart from '@/services/chart/charts/notes.js'; import type PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; import type InstanceChart from '@/services/chart/charts/instance.js'; diff --git a/packages/backend/src/services/NotePiningService.ts b/packages/backend/src/services/NotePiningService.ts index 2558da10958a..46630c6fce6c 100644 --- a/packages/backend/src/services/NotePiningService.ts +++ b/packages/backend/src/services/NotePiningService.ts @@ -8,7 +8,7 @@ import type { Note } from '@/models/entities/note.js'; import type { IdService } from '@/services/IdService.js'; import type { UserNotePining } from '@/models/entities/user-note-pining.js'; import type { RelayService } from '@/services/RelayService.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; @Injectable() export class NotePiningService { diff --git a/packages/backend/src/services/S3Service.ts b/packages/backend/src/services/S3Service.ts index 8a3d1a218e2d..14e351ed36dd 100644 --- a/packages/backend/src/services/S3Service.ts +++ b/packages/backend/src/services/S3Service.ts @@ -2,7 +2,7 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import S3 from 'aws-sdk/clients/s3.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type { Meta } from '@/models/entities/meta'; import type { HttpRequestService } from '../HttpRequestService.js'; diff --git a/packages/backend/src/services/SignupService.ts b/packages/backend/src/services/SignupService.ts index 222b130fc618..deb7927e47f7 100644 --- a/packages/backend/src/services/SignupService.ts +++ b/packages/backend/src/services/SignupService.ts @@ -5,7 +5,7 @@ import { DataSource, IsNull } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { UsedUsernames } from '@/models/index.js'; import { Users } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import { User } from '@/models/entities/user.js'; import { UserProfile } from '@/models/entities/user-profile.js'; import { IdService } from '@/services/IdService.js'; diff --git a/packages/backend/src/services/TwoFactorAuthenticationService.ts b/packages/backend/src/services/TwoFactorAuthenticationService.ts index 054814f44fa0..40933c15df40 100644 --- a/packages/backend/src/services/TwoFactorAuthenticationService.ts +++ b/packages/backend/src/services/TwoFactorAuthenticationService.ts @@ -3,7 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import * as jsrsasign from 'jsrsasign'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; const ECC_PRELUDE = Buffer.from([0x04]); const NULL_BYTE = Buffer.from([0]); diff --git a/packages/backend/src/services/UserSuspendService.ts b/packages/backend/src/services/UserSuspendService.ts index 78f5b3a17d3d..5eb5c2514d4f 100644 --- a/packages/backend/src/services/UserSuspendService.ts +++ b/packages/backend/src/services/UserSuspendService.ts @@ -8,7 +8,7 @@ import renderUndo from '@/services/remote/activitypub/renderer/undo.js'; import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; @Injectable() export class UserSuspendService { diff --git a/packages/backend/src/services/VideoProcessingService.ts b/packages/backend/src/services/VideoProcessingService.ts index b2c312651b0f..c22169103ca3 100644 --- a/packages/backend/src/services/VideoProcessingService.ts +++ b/packages/backend/src/services/VideoProcessingService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import FFmpeg from 'fluent-ffmpeg'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type { IImage, ImageProcessingService } from '@/services/ImageProcessingService.js'; import { createTempDir } from '@/misc/create-temp'; diff --git a/packages/backend/src/services/entities/DriveFileEntityService.ts b/packages/backend/src/services/entities/DriveFileEntityService.ts index 9d13a429c9ac..3259cc08978c 100644 --- a/packages/backend/src/services/entities/DriveFileEntityService.ts +++ b/packages/backend/src/services/entities/DriveFileEntityService.ts @@ -3,7 +3,7 @@ import { DataSource, In } from 'typeorm'; import * as mfm from 'mfm-js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Notes , DriveFiles } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import type { Packed } from '@/misc/schema.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { User } from '@/models/entities/user'; diff --git a/packages/backend/src/services/entities/NoteEntityService.ts b/packages/backend/src/services/entities/NoteEntityService.ts index 2f950efe6ca5..ac13f2e28f6b 100644 --- a/packages/backend/src/services/entities/NoteEntityService.ts +++ b/packages/backend/src/services/entities/NoteEntityService.ts @@ -3,7 +3,7 @@ import { DataSource, In } from 'typeorm'; import * as mfm from 'mfm-js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Notes , Polls, PollVotes , DriveFiles , Channels , Followings , Users , NoteReactions } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import type { Packed } from '@/misc/schema.js'; import { nyaize } from '@/misc/nyaize.js'; import { awaitAll } from '@/prelude/await-all.js'; diff --git a/packages/backend/src/services/entities/UserEntityService.ts b/packages/backend/src/services/entities/UserEntityService.ts index 2be6e03a7708..81ce0bb20d20 100644 --- a/packages/backend/src/services/entities/UserEntityService.ts +++ b/packages/backend/src/services/entities/UserEntityService.ts @@ -4,7 +4,7 @@ import Ajv from 'ajv'; import { DI_SYMBOLS } from '@/di-symbols.js'; import { Pages } from '@/models/index.js'; import type { AntennaNotes, Instances, MessagingMessages, UserSecurityKeys , Blockings, Mutings , Followings, FollowRequests, Users, DriveFiles, NoteUnreads, ChannelFollowings, Notifications, UserNotePinings, UserProfiles , AnnouncementReads, Announcements, UserGroupJoinings } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import type { Packed } from '@/misc/schema.js'; import type { Promiseable } from '@/prelude/await-all.js'; import { awaitAll } from '@/prelude/await-all.js'; diff --git a/packages/backend/src/services/remote/ResolveUserService.ts b/packages/backend/src/services/remote/ResolveUserService.ts index f22ca69e3112..cbdb6a377d81 100644 --- a/packages/backend/src/services/remote/ResolveUserService.ts +++ b/packages/backend/src/services/remote/ResolveUserService.ts @@ -5,7 +5,7 @@ import { IsNull } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; import type { IRemoteUser, User } from '@/models/entities/user.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import { toPuny } from '@/misc/convert-host.js'; import type Logger from '@/logger.js'; import { createPerson, updatePerson } from './activitypub/models/person.js'; diff --git a/packages/backend/src/services/remote/WebfingerService.ts b/packages/backend/src/services/remote/WebfingerService.ts index 536322f25fae..137c1569815a 100644 --- a/packages/backend/src/services/remote/WebfingerService.ts +++ b/packages/backend/src/services/remote/WebfingerService.ts @@ -1,7 +1,7 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import { query as urlQuery } from '@/prelude/url.js'; import type { HttpRequestService } from '@/services/HttpRequestService.js'; diff --git a/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts b/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts index dfa7525f6711..ee4630d80858 100644 --- a/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts +++ b/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts @@ -3,7 +3,7 @@ import escapeRegexp from 'escape-regexp'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { MessagingMessages, Notes, UserPublickeys } from '@/models/index.js'; import { Users } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type { CacheableRemoteUser, CacheableUser } from '@/models/entities/user.js'; import { Cache } from '@/misc/cache.js'; import type { UserPublickey } from '@/models/entities/user-publickey.js'; diff --git a/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts b/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts index 5f4fc18967fa..35b36fdb2935 100644 --- a/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts +++ b/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, Not } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Followings , Users } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import type { ILocalUser, IRemoteUser, User } from '@/models/entities/user.js'; import { QueueService } from '@/queue/queue.service'; import { UserEntityService } from '@/services/entities/UserEntityService'; diff --git a/packages/backend/src/services/remote/activitypub/ApInboxService.ts b/packages/backend/src/services/remote/activitypub/ApInboxService.ts index 573a4115a3d7..973f71100337 100644 --- a/packages/backend/src/services/remote/activitypub/ApInboxService.ts +++ b/packages/backend/src/services/remote/activitypub/ApInboxService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Followings, Notes , Users } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import type { CacheableRemoteUser } from '@/models/entities/user.js'; import { UserFollowingService } from '@/services/UserFollowingService.js'; import { ReactionService } from '@/services/ReactionService.js'; diff --git a/packages/backend/src/services/remote/activitypub/ApMfmService.ts b/packages/backend/src/services/remote/activitypub/ApMfmService.ts index d8c39391d283..ff967e2b6592 100644 --- a/packages/backend/src/services/remote/activitypub/ApMfmService.ts +++ b/packages/backend/src/services/remote/activitypub/ApMfmService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type { MfmService } from '@/services/MfmService.js'; import type { IObject } from './type'; diff --git a/packages/backend/src/services/remote/activitypub/ApRendererService.ts b/packages/backend/src/services/remote/activitypub/ApRendererService.ts index 480e1676c3b7..82aee28acf26 100644 --- a/packages/backend/src/services/remote/activitypub/ApRendererService.ts +++ b/packages/backend/src/services/remote/activitypub/ApRendererService.ts @@ -4,7 +4,7 @@ import { v4 as uuid } from 'uuid'; import * as mfm from 'mfm-js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Polls , DriveFiles, Emojis, Notes, Users } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type { ILocalUser, IRemoteUser, User } from '@/models/entities/user.js'; import type { IMentionedRemoteUsers, Note } from '@/models/entities/note.js'; import type { Blocking } from '@/models/entities/blocking.js'; diff --git a/packages/backend/src/services/remote/activitypub/ApRequestService.ts b/packages/backend/src/services/remote/activitypub/ApRequestService.ts index 836948661db6..e6f9bde77454 100644 --- a/packages/backend/src/services/remote/activitypub/ApRequestService.ts +++ b/packages/backend/src/services/remote/activitypub/ApRequestService.ts @@ -2,7 +2,7 @@ import * as crypto from 'node:crypto'; import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type { User } from '@/models/entities/user.js'; import type { UserKeypairStoreService } from '@/services/UserKeypairStoreService.js'; import type { HttpRequestService } from '@/services/HttpRequestService.js'; diff --git a/packages/backend/src/services/remote/activitypub/ApResolverService.ts b/packages/backend/src/services/remote/activitypub/ApResolverService.ts index 6e69debc0081..43fc1f51907e 100644 --- a/packages/backend/src/services/remote/activitypub/ApResolverService.ts +++ b/packages/backend/src/services/remote/activitypub/ApResolverService.ts @@ -3,7 +3,7 @@ import type { ILocalUser } from '@/models/entities/user.js'; import type { InstanceActorService } from '@/services/InstanceActorService.js'; import { extractDbHost, isSelfHost } from '@/misc/convert-host.js'; import type { Notes , Polls , NoteReactions , Users } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type { MetaService } from '@/services/MetaService.js'; import type { HttpRequestService } from '@/services/HttpRequestService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; diff --git a/packages/backend/src/services/remote/activitypub/models/ApImageService.ts b/packages/backend/src/services/remote/activitypub/models/ApImageService.ts index adc674aa8a03..6a5fe62673fd 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApImageService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApImageService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { DriveFiles } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type { CacheableRemoteUser } from '@/models/entities/user.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import type { MetaService } from '@/services/MetaService.js'; diff --git a/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts b/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts index 7cea3868bdfc..e2dd1697d0ff 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import { toArray, unique } from '@/prelude/array.js'; import type { CacheableUser } from '@/models/entities/user.js'; import { isMention } from '../type.js'; diff --git a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts index a584ed1f333e..7bb446016c19 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts @@ -2,7 +2,7 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Polls , Emojis, Users } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import type { CacheableRemoteUser } from '@/models/entities/user.js'; import { extractDbHost, toPuny } from '@/misc/convert-host'; import type { Note } from '@/models/entities/note.js'; diff --git a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts index 289582fb065a..50e1487c9dc1 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts @@ -3,7 +3,7 @@ import promiseLimit from 'promise-limit'; import { DataSource } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Followings , Instances, UserProfiles, UserPublickeys, Users } from '@/models/index.js'; -import { Config } from '@/config/types.js'; +import { Config } from '@/config.js'; import type { CacheableUser, IRemoteUser } from '@/models/entities/user.js'; import { User } from '@/models/entities/user.js'; import { toPuny } from '@/misc/convert-host.js'; diff --git a/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts b/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts index e46cb040521d..ec7405f94891 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Notes , Polls } from '@/models/index.js'; -import type { Config } from '@/config/types.js'; +import type { Config } from '@/config.js'; import type { IPoll } from '@/models/entities/poll.js'; import type Logger from '@/logger.js'; import { isQuestion } from '../type.js'; From e646b9de41ecbf5eecd3965eeb055949309a16dc Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 20:43:27 +0900 Subject: [PATCH 117/180] wip --- packages/backend/src/models/entities/user.ts | 7 +++ .../src/server/api/endpoints/i/update.ts | 52 ++++++++++++------- .../api/endpoints/i/user-group-invites.ts | 11 ++-- .../src/services/UserFollowingService.ts | 14 ++--- .../services/entities/UserEntityService.ts | 8 +-- 5 files changed, 55 insertions(+), 37 deletions(-) diff --git a/packages/backend/src/models/entities/user.ts b/packages/backend/src/models/entities/user.ts index bc9446be4108..76f8e7935e35 100644 --- a/packages/backend/src/models/entities/user.ts +++ b/packages/backend/src/models/entities/user.ts @@ -246,3 +246,10 @@ export type CacheableLocalUser = ILocalUser; export type CacheableRemoteUser = IRemoteUser; export type CacheableUser = CacheableLocalUser | CacheableRemoteUser; + +export const localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const; +export const passwordSchema = { type: 'string', minLength: 1 } as const; +export const nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const; +export const descriptionSchema = { type: 'string', minLength: 1, maxLength: 500 } as const; +export const locationSchema = { type: 'string', minLength: 1, maxLength: 50 } as const; +export const birthdaySchema = { type: 'string', pattern: /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.toString().slice(1, -1) } as const; diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 9e131ae40611..770346ce6968 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -1,19 +1,22 @@ import RE2 from 're2'; import * as mfm from 'mfm-js'; import { Inject, Injectable } from '@nestjs/common'; -import { publishMainStream, publishUserEvent } from '@/services/stream.js'; -import acceptAllFollowRequests from '@/services/following/requests/accept-all.js'; -import { publishToFollowers } from '@/services/i/update.js'; import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js'; import { extractHashtags } from '@/misc/extract-hashtags.js'; -import { updateUsertags } from '@/services/update-hashtag.js'; -import { Users, DriveFiles, UserProfiles, Pages } from '@/models/index.js'; +import type { Users , DriveFiles , UserProfiles } from '@/models/index.js'; +import { Pages } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; +import { birthdaySchema, descriptionSchema, locationSchema, nameSchema } from '@/models/entities/user.js'; import type { UserProfile } from '@/models/entities/user-profile.js'; import { notificationTypes } from '@/types.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { langmap } from '@/misc/langmap.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { UserFollowingService } from '@/services/UserFollowingService.js'; +import { AccountUpdateService } from '@/services/AccountUpdateService.js'; +import { HashtagService } from '@/services/HashtagService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -71,10 +74,10 @@ export const meta = { export const paramDef = { type: 'object', properties: { - name: { ...this.usersRepository.nameSchema, nullable: true }, - description: { ...this.usersRepository.descriptionSchema, nullable: true }, - location: { ...this.usersRepository.locationSchema, nullable: true }, - birthday: { ...this.usersRepository.birthdaySchema, nullable: true }, + name: { ...nameSchema, nullable: true }, + description: { ...descriptionSchema, nullable: true }, + location: { ...locationSchema, nullable: true }, + birthday: { ...birthdaySchema, nullable: true }, lang: { type: 'string', enum: [null, ...Object.keys(langmap)], nullable: true }, avatarId: { type: 'string', format: 'misskey:id', nullable: true }, bannerId: { type: 'string', format: 'misskey:id', nullable: true }, @@ -129,8 +132,17 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private userEntityService: UserEntityService, + private globalEventService: GlobalEventService, + private userFollowingService: UserFollowingService, + private accountUpdateService: AccountUpdateService, + private hashtagService: HashtagService, ) { super(meta, paramDef, async (ps, _user, token) => { const user = await this.usersRepository.findOneByOrFail({ id: _user.id }); @@ -139,7 +151,7 @@ export default class extends Endpoint { const updates = {} as Partial; const profileUpdates = {} as Partial; - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); if (ps.name !== undefined) updates.name = ps.name; if (ps.description !== undefined) profileUpdates.description = ps.description; @@ -184,14 +196,14 @@ export default class extends Endpoint { if (ps.emailNotificationTypes !== undefined) profileUpdates.emailNotificationTypes = ps.emailNotificationTypes; if (ps.avatarId) { - const avatar = await DriveFiles.findOneBy({ id: ps.avatarId }); + const avatar = await this.driveFilesRepository.findOneBy({ id: ps.avatarId }); if (avatar == null || avatar.userId !== user.id) throw new ApiError(meta.errors.noSuchAvatar); if (!avatar.type.startsWith('image/')) throw new ApiError(meta.errors.avatarNotAnImage); } if (ps.bannerId) { - const banner = await DriveFiles.findOneBy({ id: ps.bannerId }); + const banner = await this.driveFilesRepository.findOneBy({ id: ps.bannerId }); if (banner == null || banner.userId !== user.id) throw new ApiError(meta.errors.noSuchBanner); if (!banner.type.startsWith('image/')) throw new ApiError(meta.errors.bannerNotAnImage); @@ -238,11 +250,11 @@ export default class extends Endpoint { updates.tags = tags; // ハッシュタグ更新 - updateUsertags(user, tags); + this.hashtagService.updateUsertags(user, tags); //#endregion if (Object.keys(updates).length > 0) await this.usersRepository.update(user.id, updates); - if (Object.keys(profileUpdates).length > 0) await UserProfiles.update(user.id, profileUpdates); + if (Object.keys(profileUpdates).length > 0) await this.userProfilesRepository.update(user.id, profileUpdates); const iObj = await this.userEntityService.pack(user.id, user, { detail: true, @@ -250,16 +262,16 @@ export default class extends Endpoint { }); // Publish meUpdated event - publishMainStream(user.id, 'meUpdated', iObj); - publishUserEvent(user.id, 'updateUserProfile', await UserProfiles.findOneBy({ userId: user.id })); + this.globalEventService.publishMainStream(user.id, 'meUpdated', iObj); + this.globalEventService.publishUserEvent(user.id, 'updateUserProfile', await this.userProfilesRepository.findOneBy({ userId: user.id })); // 鍵垢を解除したとき、溜まっていたフォローリクエストがあるならすべて承認 if (user.isLocked && ps.isLocked === false) { - acceptAllFollowRequests(user); + this.userFollowingService.acceptAllFollowRequests(user); } // フォロワーにUpdateを配信 - publishToFollowers(user.id); + this.accountUpdateService.publishToFollowers(user.id); return iObj; }); diff --git a/packages/backend/src/server/api/endpoints/i/user-group-invites.ts b/packages/backend/src/server/api/endpoints/i/user-group-invites.ts index ea5c731cf217..3a4ec52e86f5 100644 --- a/packages/backend/src/server/api/endpoints/i/user-group-invites.ts +++ b/packages/backend/src/server/api/endpoints/i/user-group-invites.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserGroupInvitations } from '@/models/index.js'; +import type { UserGroupInvitations } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { UserGroupInvitationEntityService } from '@/services/entities/UserGroupInvitationEntityService'; export const meta = { tags: ['account', 'groups'], @@ -46,10 +47,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('userGroupInvitationsRepository') + private userGroupInvitationsRepository: typeof UserGroupInvitations, + + private userGroupInvitationEntityService: UserGroupInvitationEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(UserGroupInvitations.createQueryBuilder('invitation'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.userGroupInvitationsRepository.createQueryBuilder('invitation'), ps.sinceId, ps.untilId) .andWhere('invitation.userId = :meId', { meId: me.id }) .leftJoinAndSelect('invitation.userGroup', 'user_group'); @@ -57,7 +62,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return await UserGroupInvitations.packMany(invitations); + return await this.userGroupInvitationEntityService.packMany(invitations); }); } } diff --git a/packages/backend/src/services/UserFollowingService.ts b/packages/backend/src/services/UserFollowingService.ts index 91f9009c6403..16b27c940554 100644 --- a/packages/backend/src/services/UserFollowingService.ts +++ b/packages/backend/src/services/UserFollowingService.ts @@ -131,7 +131,7 @@ export class UserFollowingService { } } - await this.insertFollowingDoc(followee, follower); + await this.#insertFollowingDoc(followee, follower); if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { const content = this.apRendererService.renderActivity(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, requestId), followee)); @@ -139,7 +139,7 @@ export class UserFollowingService { } } - public async insertFollowingDoc( + async #insertFollowingDoc( followee: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'] }, @@ -273,7 +273,7 @@ export class UserFollowingService { await this.followingsRepository.delete(following.id); - this.decrementFollowing(follower, followee); + this.#decrementFollowing(follower, followee); // Publish unfollow event if (!silent && this.userEntityService.isLocalUser(follower)) { @@ -304,7 +304,7 @@ export class UserFollowingService { } } - public async decrementFollowing( + async #decrementFollowing( follower: {id: User['id']; host: User['host']; }, followee: { id: User['id']; host: User['host']; }, ): Promise { @@ -445,7 +445,7 @@ export class UserFollowingService { throw new IdentifiableError('8884c2dd-5795-4ac9-b27e-6a01d38190f9', 'No follow request.'); } - await this.insertFollowingDoc(followee, follower); + await this.#insertFollowingDoc(followee, follower); if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { const content = this.apRendererService.renderActivity(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, request.requestId!), followee)); @@ -457,7 +457,7 @@ export class UserFollowingService { }).then(packed => this.globalEventServie.publishMainStream(followee.id, 'meUpdated', packed)); } - public async acceptAllFollowRequest( + public async acceptAllFollowRequests( user: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; }, @@ -537,7 +537,7 @@ export class UserFollowingService { if (!following) return; await this.followingsRepository.delete(following.id); - this.decrementFollowing(follower, followee); + this.#decrementFollowing(follower, followee); } /** diff --git a/packages/backend/src/services/entities/UserEntityService.ts b/packages/backend/src/services/entities/UserEntityService.ts index 81ce0bb20d20..84e0b39fe302 100644 --- a/packages/backend/src/services/entities/UserEntityService.ts +++ b/packages/backend/src/services/entities/UserEntityService.ts @@ -14,6 +14,7 @@ import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js'; import { Cache } from '@/misc/cache.js'; import type { Instance } from '@/models/entities/instance.js'; import type { ILocalUser, IRemoteUser, User } from '@/models/entities/user.js'; +import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/entities/user.js'; import { NoteEntityService } from './NoteEntityService.js'; const userInstanceCache = new Cache(1000 * 60 * 60 * 3); @@ -28,13 +29,6 @@ type IsMeAndIsUserDetailed(user: T): user is T & { host: null; }; function isLocalUser(user: User | { host: User['host'] }): boolean { From 0b68fc7c82e1b3bc68df37039c373d6d0a8d91e4 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 21:05:35 +0900 Subject: [PATCH 118/180] wip --- .../api/common/read-messaging-message.ts | 149 --------- .../endpoints/messaging/messages/create.ts | 33 +- .../endpoints/messaging/messages/delete.ts | 11 +- .../api/endpoints/messaging/messages/read.ts | 22 +- .../server/api/endpoints/miauth/gen-token.ts | 9 +- .../src/server/api/endpoints/mute/create.ts | 26 +- .../src/server/api/endpoints/mute/delete.ts | 19 +- .../src/server/api/endpoints/mute/list.ts | 11 +- .../api/endpoints/notes/watching/create.ts | 45 --- .../api/endpoints/notes/watching/delete.ts | 45 --- .../backend/src/services/MessagingService.ts | 294 ++++++++++++++++++ .../src/services/PushNotificationService.ts | 102 ++++++ .../backend/src/services/messages/create.ts | 108 ------- .../backend/src/services/messages/delete.ts | 30 -- .../backend/src/services/push-notification.ts | 85 ----- 15 files changed, 478 insertions(+), 511 deletions(-) delete mode 100644 packages/backend/src/server/api/common/read-messaging-message.ts delete mode 100644 packages/backend/src/server/api/endpoints/notes/watching/create.ts delete mode 100644 packages/backend/src/server/api/endpoints/notes/watching/delete.ts create mode 100644 packages/backend/src/services/MessagingService.ts create mode 100644 packages/backend/src/services/PushNotificationService.ts delete mode 100644 packages/backend/src/services/messages/create.ts delete mode 100644 packages/backend/src/services/messages/delete.ts delete mode 100644 packages/backend/src/services/push-notification.ts diff --git a/packages/backend/src/server/api/common/read-messaging-message.ts b/packages/backend/src/server/api/common/read-messaging-message.ts deleted file mode 100644 index 39d1290ad7d2..000000000000 --- a/packages/backend/src/server/api/common/read-messaging-message.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { In } from 'typeorm'; -import { publishMainStream, publishGroupMessagingStream , publishMessagingStream , publishMessagingIndexStream } from '@/services/stream.js'; -import { pushNotification } from '@/services/push-notification.js'; -import type { User, IRemoteUser } from '@/models/entities/user.js'; -import type { MessagingMessage } from '@/models/entities/messaging-message.js'; -import { MessagingMessages, UserGroupJoinings, Users } from '@/models/index.js'; -import { IdentifiableError } from '@/misc/identifiable-error.js'; -import type { UserGroup } from '@/models/entities/user-group.js'; -import { toArray } from '@/prelude/array.js'; -import { renderReadActivity } from '@/services/remote/activitypub/renderer/read.js'; -import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; -import { deliver } from '@/queue/index.js'; -import orderedCollection from '@/services/remote/activitypub/renderer/ordered-collection.js'; - -/** - * Mark messages as read - */ -export async function readUserMessagingMessage( - userId: User['id'], - otherpartyId: User['id'], - messageIds: MessagingMessage['id'][], -) { - if (messageIds.length === 0) return; - - const messages = await MessagingMessages.findBy({ - id: In(messageIds), - }); - - for (const message of messages) { - if (message.recipientId !== userId) { - throw new IdentifiableError('e140a4bf-49ce-4fb6-b67c-b78dadf6b52f', 'Access denied (user).'); - } - } - - // Update documents - await MessagingMessages.update({ - id: In(messageIds), - userId: otherpartyId, - recipientId: userId, - isRead: false, - }, { - isRead: true, - }); - - // Publish event - publishMessagingStream(otherpartyId, userId, 'read', messageIds); - publishMessagingIndexStream(userId, 'read', messageIds); - - if (!await Users.getHasUnreadMessagingMessage(userId)) { - // 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行 - publishMainStream(userId, 'readAllMessagingMessages'); - pushNotification(userId, 'readAllMessagingMessages', undefined); - } else { - // そのユーザーとのメッセージで未読がなければイベント発行 - const count = await MessagingMessages.count({ - where: { - userId: otherpartyId, - recipientId: userId, - isRead: false, - }, - take: 1, - }); - - if (!count) { - pushNotification(userId, 'readAllMessagingMessagesOfARoom', { userId: otherpartyId }); - } - } -} - -/** - * Mark messages as read - */ -export async function readGroupMessagingMessage( - userId: User['id'], - groupId: UserGroup['id'], - messageIds: MessagingMessage['id'][], -) { - if (messageIds.length === 0) return; - - // check joined - const joining = await UserGroupJoinings.findOneBy({ - userId: userId, - userGroupId: groupId, - }); - - if (joining == null) { - throw new IdentifiableError('930a270c-714a-46b2-b776-ad27276dc569', 'Access denied (group).'); - } - - const messages = await MessagingMessages.findBy({ - id: In(messageIds), - }); - - const reads: MessagingMessage['id'][] = []; - - for (const message of messages) { - if (message.userId === userId) continue; - if (message.reads.includes(userId)) continue; - - // Update document - await MessagingMessages.createQueryBuilder().update() - .set({ - reads: (() => `array_append("reads", '${joining.userId}')`) as any, - }) - .where('id = :id', { id: message.id }) - .execute(); - - reads.push(message.id); - } - - // Publish event - publishGroupMessagingStream(groupId, 'read', { - ids: reads, - userId: userId, - }); - publishMessagingIndexStream(userId, 'read', reads); - - if (!await Users.getHasUnreadMessagingMessage(userId)) { - // 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行 - publishMainStream(userId, 'readAllMessagingMessages'); - pushNotification(userId, 'readAllMessagingMessages', undefined); - } else { - // そのグループにおいて未読がなければイベント発行 - const unreadExist = await MessagingMessages.createQueryBuilder('message') - .where('message.groupId = :groupId', { groupId: groupId }) - .andWhere('message.userId != :userId', { userId: userId }) - .andWhere('NOT (:userId = ANY(message.reads))', { userId: userId }) - .andWhere('message.createdAt > :joinedAt', { joinedAt: joining.createdAt }) // 自分が加入する前の会話については、未読扱いしない - .getOne().then(x => x != null); - - if (!unreadExist) { - pushNotification(userId, 'readAllMessagingMessagesOfARoom', { groupId }); - } - } -} - -export async function deliverReadActivity(user: { id: User['id']; host: null; }, recipient: IRemoteUser, messages: MessagingMessage | MessagingMessage[]) { - messages = toArray(messages).filter(x => x.uri); - const contents = messages.map(x => renderReadActivity(user, x)); - - if (contents.length > 1) { - const collection = orderedCollection(null, contents.length, undefined, undefined, contents); - deliver(user, renderActivity(collection), recipient.inbox); - } else { - for (const content of contents) { - deliver(user, renderActivity(content), recipient.inbox); - } - } -} diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts index c05f6d90f318..d650913af744 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts @@ -1,9 +1,12 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { MessagingMessages, DriveFiles, UserGroups, UserGroupJoinings, Blockings } from '@/models/index.js'; +import type { Blockings , UserGroupJoinings , DriveFiles , UserGroups } from '@/models/index.js'; +import { MessagingMessages } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; import type { UserGroup } from '@/models/entities/user-group.js'; import { createMessage } from '@/services/messages/create.js'; +import { GetterService } from '@/server/api/common/GetterService.js'; +import { MessagingService } from '@/services/MessagingService.js'; import { getUser } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; @@ -91,6 +94,20 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('userGroupsRepository') + private userGroupsRepository: typeof UserGroups, + + @Inject('userGroupJoiningsRepository') + private userGroupJoiningsRepository: typeof UserGroupJoinings, + + @Inject('blockingsRepository') + private blockingsRepository: typeof Blockings, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private getterService: GetterService, + private messagingService: MessagingService, ) { super(meta, paramDef, async (ps, me) => { let recipientUser: User | null; @@ -103,9 +120,9 @@ export default class extends Endpoint { } // Fetch recipient (user) - recipientUser = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; + recipientUser = await this.getterService.getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; }); // Check blocking @@ -118,14 +135,14 @@ export default class extends Endpoint { } } else if (ps.groupId != null) { // Fetch recipient (group) - recipientGroup = await UserGroups.findOneBy({ id: ps.groupId! }); + recipientGroup = await this.userGroupsRepository.findOneBy({ id: ps.groupId! }); if (recipientGroup == null) { throw new ApiError(meta.errors.noSuchGroup); } // check joined - const joining = await UserGroupJoinings.findOneBy({ + const joining = await this.userGroupJoiningsRepository.findOneBy({ userId: me.id, userGroupId: recipientGroup.id, }); @@ -137,7 +154,7 @@ export default class extends Endpoint { let file = null; if (ps.fileId != null) { - file = await DriveFiles.findOneBy({ + file = await this.driveFilesRepository.findOneBy({ id: ps.fileId, userId: me.id, }); @@ -152,7 +169,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.contentRequired); } - return await createMessage(me, recipientUser, recipientGroup, ps.text, file); + return await this.messagingService.createMessage(me, recipientUser, recipientGroup, ps.text, file); }); } } diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts b/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts index 3e76d25ad09c..baeb01b79fa8 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { MessagingMessages } from '@/models/index.js'; +import type { MessagingMessages } from '@/models/index.js'; import { deleteMessage } from '@/services/messages/delete.js'; +import { MessagingService } from '@/services/MessagingService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -39,9 +40,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('messagingMessagesRepository') + private messagingMessagesRepository: typeof MessagingMessages, + + private messagingService: MessagingService, ) { super(meta, paramDef, async (ps, me) => { - const message = await MessagingMessages.findOneBy({ + const message = await this.messagingMessagesRepository.findOneBy({ id: ps.messageId, userId: me.id, }); @@ -50,7 +55,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchMessage); } - await deleteMessage(message); + await this.messagingService.deleteMessage(message); }); } } diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/read.ts b/packages/backend/src/server/api/endpoints/messaging/messages/read.ts index a7e38d084f6a..b7000ea69789 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/read.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/read.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { MessagingMessages } from '@/models/index.js'; +import type { MessagingMessages } from '@/models/index.js'; +import { MessagingService } from '@/services/MessagingService.js'; import { ApiError } from '../../../error.js'; -import { readUserMessagingMessage, readGroupMessagingMessage } from '../../../common/read-messaging-message.js'; export const meta = { tags: ['messaging'], @@ -32,23 +32,27 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('messagingMessagesRepository') + private messagingMessagesRepository: typeof MessagingMessages, + + private messagingService: MessagingService, ) { super(meta, paramDef, async (ps, me) => { - const message = await MessagingMessages.findOneBy({ id: ps.messageId }); + const message = await this.messagingMessagesRepository.findOneBy({ id: ps.messageId }); if (message == null) { throw new ApiError(meta.errors.noSuchMessage); } if (message.recipientId) { - await readUserMessagingMessage(me.id, message.userId, [message.id]).catch(e => { - if (e.id === 'e140a4bf-49ce-4fb6-b67c-b78dadf6b52f') throw new ApiError(meta.errors.noSuchMessage); - throw e; + await this.messagingService.readUserMessagingMessage(me.id, message.userId, [message.id]).catch(err => { + if (err.id === 'e140a4bf-49ce-4fb6-b67c-b78dadf6b52f') throw new ApiError(meta.errors.noSuchMessage); + throw err; }); } else if (message.groupId) { - await readGroupMessagingMessage(me.id, message.groupId, [message.id]).catch(e => { - if (e.id === '930a270c-714a-46b2-b776-ad27276dc569') throw new ApiError(meta.errors.noSuchMessage); - throw e; + await this.messagingService.readGroupMessagingMessage(me.id, message.groupId, [message.id]).catch(err => { + if (err.id === '930a270c-714a-46b2-b776-ad27276dc569') throw new ApiError(meta.errors.noSuchMessage); + throw err; }); } }); diff --git a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts index cac954dec425..06ce35a002ab 100644 --- a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts +++ b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { AccessTokens } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import type { AccessTokens } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; export const meta = { @@ -41,6 +41,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('accessTokensRepository') + private accessTokensRepository: typeof AccessTokens, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { @@ -50,7 +53,7 @@ export default class extends Endpoint { const now = new Date(); // Insert access token doc - await AccessTokens.insert({ + await this.accessTokensRepository.insert({ id: this.idService.genId(), createdAt: now, lastUsedAt: now, diff --git a/packages/backend/src/server/api/endpoints/mute/create.ts b/packages/backend/src/server/api/endpoints/mute/create.ts index 8dab6ce9f3b9..a96a7e9ff334 100644 --- a/packages/backend/src/server/api/endpoints/mute/create.ts +++ b/packages/backend/src/server/api/endpoints/mute/create.ts @@ -1,11 +1,11 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { IdService } from '@/services/IdService.js'; -import { Mutings, NoteWatchings } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; +import type { Mutings } from '@/models/index.js'; import type { Muting } from '@/models/entities/muting.js'; -import { publishUserEvent } from '@/services/stream.js'; -import { getUser } from '../../common/getters.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../../error.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['account'], @@ -52,6 +52,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('mutingsRepository') + private mutingsRepository: typeof Mutings, + + private globalEventService: GlobalEventService, + private getterService: GetterService, private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { @@ -63,9 +68,9 @@ export default class extends Endpoint { } // Get mutee - const mutee = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; + const mutee = await this.getterService.getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; }); // Check if already muting @@ -91,12 +96,7 @@ export default class extends Endpoint { muteeId: mutee.id, } as Muting); - publishUserEvent(me.id, 'mute', mutee); - - NoteWatchings.delete({ - userId: muter.id, - noteUserId: mutee.id, - }); + this.globalEventService.publishUserEvent(me.id, 'mute', mutee); }); } } diff --git a/packages/backend/src/server/api/endpoints/mute/delete.ts b/packages/backend/src/server/api/endpoints/mute/delete.ts index 1ff8e80e575c..e37fbba0296d 100644 --- a/packages/backend/src/server/api/endpoints/mute/delete.ts +++ b/packages/backend/src/server/api/endpoints/mute/delete.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Mutings } from '@/models/index.js'; -import { publishUserEvent } from '@/services/stream.js'; +import type { Mutings } from '@/models/index.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../../error.js'; -import { getUser } from '../../common/getters.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['account'], @@ -45,6 +45,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('mutingsRepository') + private mutingsRepository: typeof Mutings, + + private globalEventService: GlobalEventService, + private getterService: GetterService, ) { super(meta, paramDef, async (ps, me) => { const muter = me; @@ -55,9 +60,9 @@ export default class extends Endpoint { } // Get mutee - const mutee = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; + const mutee = await this.getterService.getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; }); // Check not muting @@ -75,7 +80,7 @@ export default class extends Endpoint { id: exist.id, }); - publishUserEvent(me.id, 'unmute', mutee); + this.globalEventService.publishUserEvent(me.id, 'unmute', mutee); }); } } diff --git a/packages/backend/src/server/api/endpoints/mute/list.ts b/packages/backend/src/server/api/endpoints/mute/list.ts index 80a356e00957..a3b52dbbcdbd 100644 --- a/packages/backend/src/server/api/endpoints/mute/list.ts +++ b/packages/backend/src/server/api/endpoints/mute/list.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Mutings } from '@/models/index.js'; +import type { Mutings } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { MutingEntityService } from '@/services/entities/MutingEntityService'; export const meta = { tags: ['account'], @@ -35,12 +36,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('mutingsRepository') + private mutingsRepository: typeof Mutings, + private mutingEntityService: MutingEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/notes/watching/create.ts b/packages/backend/src/server/api/endpoints/notes/watching/create.ts deleted file mode 100644 index a9bdbd9fb62e..000000000000 --- a/packages/backend/src/server/api/endpoints/notes/watching/create.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import watch from '@/services/note/watch.js'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import { getNote } from '../../../common/getters.js'; -import { ApiError } from '../../../error.js'; - -export const meta = { - tags: ['notes'], - - requireCredential: true, - - kind: 'write:account', - - errors: { - noSuchNote: { - message: 'No such note.', - code: 'NO_SUCH_NOTE', - id: 'ea0e37a6-90a3-4f58-ba6b-c328ca206fc7', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - noteId: { type: 'string', format: 'misskey:id' }, - }, - required: ['noteId'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint { - constructor( - ) { - super(meta, paramDef, async (ps, me) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); - - await watch(me.id, note); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/notes/watching/delete.ts b/packages/backend/src/server/api/endpoints/notes/watching/delete.ts deleted file mode 100644 index 64da18e990dc..000000000000 --- a/packages/backend/src/server/api/endpoints/notes/watching/delete.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import unwatch from '@/services/note/unwatch.js'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import { getNote } from '../../../common/getters.js'; -import { ApiError } from '../../../error.js'; - -export const meta = { - tags: ['notes'], - - requireCredential: true, - - kind: 'write:account', - - errors: { - noSuchNote: { - message: 'No such note.', - code: 'NO_SUCH_NOTE', - id: '09b3695c-f72c-4731-a428-7cff825fc82e', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - noteId: { type: 'string', format: 'misskey:id' }, - }, - required: ['noteId'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint { - constructor( - ) { - super(meta, paramDef, async (ps, me) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; - }); - - await unwatch(me.id, note); - }); - } -} diff --git a/packages/backend/src/services/MessagingService.ts b/packages/backend/src/services/MessagingService.ts new file mode 100644 index 000000000000..4fd28c4876af --- /dev/null +++ b/packages/backend/src/services/MessagingService.ts @@ -0,0 +1,294 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { In, Not } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { MessagingMessages, Mutings, UserGroupJoinings, Users } from '@/models/index.js'; +import { Config } from '@/config.js'; +import type { DriveFile } from '@/models/entities/drive-file'; +import type { MessagingMessage } from '@/models/entities/messaging-message'; +import type { Note } from '@/models/entities/note'; +import type { User, CacheableUser } from '@/models/entities/user'; +import type { UserGroup } from '@/models/entities/user-group'; +import { QueueService } from '@/queue/queue.service'; +import { toArray } from '@/prelude/array'; +import { IdentifiableError } from '@/misc/identifiable-error'; +import { IdService } from './IdService'; +import { GlobalEventService } from './GlobalEventService'; +import { UserEntityService } from './entities/UserEntityService'; +import { ApRendererService } from './remote/activitypub/ApRendererService'; +import { MessagingMessageEntityService } from './entities/MessagingMessageEntityService'; +import { PushNotificationService } from './PushNotificationService'; + +@Injectable() +export class MessagingService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('messagingMessagesRepository') + private messagingMessagesRepository: typeof MessagingMessages, + + private userEntityService: UserEntityService, + private messagingMessageEntityService: MessagingMessageEntityService, + private idService: IdService, + private globalEventService: GlobalEventService, + private apRendererService: ApRendererService, + private queueService: QueueService, + private pushNotificationService: PushNotificationService, + ) { + } + + public async createMessage(user: { id: User['id']; host: User['host']; }, recipientUser: CacheableUser | undefined, recipientGroup: UserGroup | undefined, text: string | null | undefined, file: DriveFile | null, uri?: string) { + const message = { + id: this.idService.genId(), + createdAt: new Date(), + fileId: file ? file.id : null, + recipientId: recipientUser ? recipientUser.id : null, + groupId: recipientGroup ? recipientGroup.id : null, + text: text ? text.trim() : null, + userId: user.id, + isRead: false, + reads: [] as any[], + uri, + } as MessagingMessage; + + await this.messagingMessagesRepository.insert(message); + + const messageObj = await this.messagingMessageEntityService.pack(message); + + if (recipientUser) { + if (this.userEntityService.isLocalUser(user)) { + // 自分のストリーム + this.globalEventService.publishMessagingStream(message.userId, recipientUser.id, 'message', messageObj); + this.globalEventService.publishMessagingIndexStream(message.userId, 'message', messageObj); + this.globalEventService.publishMainStream(message.userId, 'messagingMessage', messageObj); + } + + if (this.userEntityService.isLocalUser(recipientUser)) { + // 相手のストリーム + this.globalEventService.publishMessagingStream(recipientUser.id, message.userId, 'message', messageObj); + this.globalEventService.publishMessagingIndexStream(recipientUser.id, 'message', messageObj); + this.globalEventService.publishMainStream(recipientUser.id, 'messagingMessage', messageObj); + } + } else if (recipientGroup) { + // グループのストリーム + this.globalEventService.publishGroupMessagingStream(recipientGroup.id, 'message', messageObj); + + // メンバーのストリーム + const joinings = await UserGroupJoinings.findBy({ userGroupId: recipientGroup.id }); + for (const joining of joinings) { + this.globalEventService.publishMessagingIndexStream(joining.userId, 'message', messageObj); + this.globalEventService.publishMainStream(joining.userId, 'messagingMessage', messageObj); + } + } + + // 2秒経っても(今回作成した)メッセージが既読にならなかったら「未読のメッセージがありますよ」イベントを発行する + setTimeout(async () => { + const freshMessage = await MessagingMessages.findOneBy({ id: message.id }); + if (freshMessage == null) return; // メッセージが削除されている場合もある + + if (recipientUser && this.userEntityService.isLocalUser(recipientUser)) { + if (freshMessage.isRead) return; // 既読 + + //#region ただしミュートされているなら発行しない + const mute = await Mutings.findBy({ + muterId: recipientUser.id, + }); + if (mute.map(m => m.muteeId).includes(user.id)) return; + //#endregion + + this.globalEventService.publishMainStream(recipientUser.id, 'unreadMessagingMessage', messageObj); + this.pushNotificationService.pushNotification(recipientUser.id, 'unreadMessagingMessage', messageObj); + } else if (recipientGroup) { + const joinings = await UserGroupJoinings.findBy({ userGroupId: recipientGroup.id, userId: Not(user.id) }); + for (const joining of joinings) { + if (freshMessage.reads.includes(joining.userId)) return; // 既読 + this.globalEventService.publishMainStream(joining.userId, 'unreadMessagingMessage', messageObj); + this.pushNotificationService.pushNotification(joining.userId, 'unreadMessagingMessage', messageObj); + } + } + }, 2000); + + if (recipientUser && this.userEntityService.isLocalUser(user) && this.userEntityService.isRemoteUser(recipientUser)) { + const note = { + id: message.id, + createdAt: message.createdAt, + fileIds: message.fileId ? [ message.fileId ] : [], + text: message.text, + userId: message.userId, + visibility: 'specified', + mentions: [ recipientUser ].map(u => u.id), + mentionedRemoteUsers: JSON.stringify([ recipientUser ].map(u => ({ + uri: u.uri, + username: u.username, + host: u.host, + }))), + } as Note; + + const activity = this.apRendererService.renderActivity(this.apRendererService.renderCreate(await this.apRendererService.renderNote(note, false, true), note)); + + this.queueService.deliver(user, activity, recipientUser.inbox); + } + return messageObj; + } + + public async deleteMessage(message: MessagingMessage) { + await MessagingMessages.delete(message.id); + this.#postDeleteMessage(message); + } + + async #postDeleteMessage(message: MessagingMessage) { + if (message.recipientId) { + const user = await Users.findOneByOrFail({ id: message.userId }); + const recipient = await Users.findOneByOrFail({ id: message.recipientId }); + + if (this.userEntityService.isLocalUser(user)) this.globalEventService.publishMessagingStream(message.userId, message.recipientId, 'deleted', message.id); + if (this.userEntityService.isLocalUser(recipient)) this.globalEventService.publishMessagingStream(message.recipientId, message.userId, 'deleted', message.id); + + if (this.userEntityService.isLocalUser(user) && this.userEntityService.isRemoteUser(recipient)) { + const activity = this.apRendererService.renderActivity(this.apRendererService.renderDelete(this.apRendererService.renderTombstone(`${this.config.url}/notes/${message.id}`), user)); + this.queueService.deliver(user, activity, recipient.inbox); + } + } else if (message.groupId) { + this.globalEventService.publishGroupMessagingStream(message.groupId, 'deleted', message.id); + } + } + + /** + * Mark messages as read + */ + public async readUserMessagingMessage( + userId: User['id'], + otherpartyId: User['id'], + messageIds: MessagingMessage['id'][], + ) { + if (messageIds.length === 0) return; + + const messages = await MessagingMessages.findBy({ + id: In(messageIds), + }); + + for (const message of messages) { + if (message.recipientId !== userId) { + throw new IdentifiableError('e140a4bf-49ce-4fb6-b67c-b78dadf6b52f', 'Access denied (user).'); + } + } + + // Update documents + await MessagingMessages.update({ + id: In(messageIds), + userId: otherpartyId, + recipientId: userId, + isRead: false, + }, { + isRead: true, + }); + + // Publish event + this.globalEventService.publishMessagingStream(otherpartyId, userId, 'read', messageIds); + this.globalEventService.publishMessagingIndexStream(userId, 'read', messageIds); + + if (!await this.userEntityService.getHasUnreadMessagingMessage(userId)) { + // 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行 + this.globalEventService.publishMainStream(userId, 'readAllMessagingMessages'); + this.pushNotificationService.pushNotification(userId, 'readAllMessagingMessages', undefined); + } else { + // そのユーザーとのメッセージで未読がなければイベント発行 + const count = await MessagingMessages.count({ + where: { + userId: otherpartyId, + recipientId: userId, + isRead: false, + }, + take: 1, + }); + + if (!count) { + this.pushNotificationService.pushNotification(userId, 'readAllMessagingMessagesOfARoom', { userId: otherpartyId }); + } + } + } + + /** + * Mark messages as read + */ + public async readGroupMessagingMessage( + userId: User['id'], + groupId: UserGroup['id'], + messageIds: MessagingMessage['id'][], + ) { + if (messageIds.length === 0) return; + + // check joined + const joining = await UserGroupJoinings.findOneBy({ + userId: userId, + userGroupId: groupId, + }); + + if (joining == null) { + throw new IdentifiableError('930a270c-714a-46b2-b776-ad27276dc569', 'Access denied (group).'); + } + + const messages = await MessagingMessages.findBy({ + id: In(messageIds), + }); + + const reads: MessagingMessage['id'][] = []; + + for (const message of messages) { + if (message.userId === userId) continue; + if (message.reads.includes(userId)) continue; + + // Update document + await MessagingMessages.createQueryBuilder().update() + .set({ + reads: (() => `array_append("reads", '${joining.userId}')`) as any, + }) + .where('id = :id', { id: message.id }) + .execute(); + + reads.push(message.id); + } + + // Publish event + this.globalEventService.publishGroupMessagingStream(groupId, 'read', { + ids: reads, + userId: userId, + }); + this.globalEventService.publishMessagingIndexStream(userId, 'read', reads); + + if (!await this.userEntityService.getHasUnreadMessagingMessage(userId)) { + // 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行 + this.globalEventService.publishMainStream(userId, 'readAllMessagingMessages'); + this.pushNotificationService.pushNotification(userId, 'readAllMessagingMessages', undefined); + } else { + // そのグループにおいて未読がなければイベント発行 + const unreadExist = await MessagingMessages.createQueryBuilder('message') + .where('message.groupId = :groupId', { groupId: groupId }) + .andWhere('message.userId != :userId', { userId: userId }) + .andWhere('NOT (:userId = ANY(message.reads))', { userId: userId }) + .andWhere('message.createdAt > :joinedAt', { joinedAt: joining.createdAt }) // 自分が加入する前の会話については、未読扱いしない + .getOne().then(x => x != null); + + if (!unreadExist) { + this.pushNotificationService.pushNotification(userId, 'readAllMessagingMessagesOfARoom', { groupId }); + } + } + } + + public async deliverReadActivity(user: { id: User['id']; host: null; }, recipient: IRemoteUser, messages: MessagingMessage | MessagingMessage[]) { + messages = toArray(messages).filter(x => x.uri); + const contents = messages.map(x => this.apRendererService.renderRead(user, x)); + + if (contents.length > 1) { + const collection = this.apRendererService.renderOrderedCollection(null, contents.length, undefined, undefined, contents); + this.queueService.deliver(user, this.apRendererService.renderActivity(collection), recipient.inbox); + } else { + for (const content of contents) { + this.queueService.deliver(user, this.apRendererService.renderActivity(content), recipient.inbox); + } + } + } +} diff --git a/packages/backend/src/services/PushNotificationService.ts b/packages/backend/src/services/PushNotificationService.ts new file mode 100644 index 000000000000..a4c55ab87d00 --- /dev/null +++ b/packages/backend/src/services/PushNotificationService.ts @@ -0,0 +1,102 @@ +import { Inject, Injectable } from '@nestjs/common'; +import push from 'web-push'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { SwSubscriptions } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; +import { Config } from '@/config.js'; +import type { Packed } from '@/misc/schema'; +import { getNoteSummary } from '@/misc/get-note-summary.js'; +import { MetaService } from './MetaService'; + +// Defined also packages/sw/types.ts#L14-L21 +type pushNotificationsTypes = { + 'notification': Packed<'Notification'>; + 'unreadMessagingMessage': Packed<'MessagingMessage'>; + 'readNotifications': { notificationIds: string[] }; + 'readAllNotifications': undefined; + 'readAllMessagingMessages': undefined; + 'readAllMessagingMessagesOfARoom': { userId: string } | { groupId: string }; +}; + +// プッシュメッセージサーバーには文字数制限があるため、内容を削減します +function truncateNotification(notification: Packed<'Notification'>): any { + if (notification.note) { + return { + ...notification, + note: { + ...notification.note, + // textをgetNoteSummaryしたものに置き換える + text: getNoteSummary(notification.type === 'renote' ? notification.note.renote as Packed<'Note'> : notification.note), + + cw: undefined, + reply: undefined, + renote: undefined, + user: undefined as any, // 通知を受け取ったユーザーである場合が多いのでこれも捨てる + }, + }; + } + + return notification; +} + +@Injectable() +export class PushNotificationService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject('swSubscriptionsRepository') + private swSubscriptionsRepository: typeof SwSubscriptions, + + private metaService: MetaService, + ) { + } + + public async pushNotification(userId: string, type: T, body: pushNotificationsTypes[T]) { + const meta = await this.metaService.fetch(); + + if (!meta.enableServiceWorker || meta.swPublicKey == null || meta.swPrivateKey == null) return; + + // アプリケーションの連絡先と、サーバーサイドの鍵ペアの情報を登録 + push.setVapidDetails(this.config.url, + meta.swPublicKey, + meta.swPrivateKey); + + // Fetch + const subscriptions = await this.swSubscriptionsRepository.findBy({ + userId: userId, + }); + + for (const subscription of subscriptions) { + const pushSubscription = { + endpoint: subscription.endpoint, + keys: { + auth: subscription.auth, + p256dh: subscription.publickey, + }, + }; + + push.sendNotification(pushSubscription, JSON.stringify({ + type, + body: type === 'notification' ? truncateNotification(body as Packed<'Notification'>) : body, + userId, + dateTime: (new Date()).getTime(), + }), { + proxy: this.config.proxy, + }).catch((err: any) => { + //swLogger.info(err.statusCode); + //swLogger.info(err.headers); + //swLogger.info(err.body); + + if (err.statusCode === 410) { + SwSubscriptions.delete({ + userId: userId, + endpoint: subscription.endpoint, + auth: subscription.auth, + publickey: subscription.publickey, + }); + } + }); + } + } +} diff --git a/packages/backend/src/services/messages/create.ts b/packages/backend/src/services/messages/create.ts deleted file mode 100644 index 5231f153e378..000000000000 --- a/packages/backend/src/services/messages/create.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { Not } from 'typeorm'; -import type { CacheableUser, User } from '@/models/entities/user.js'; -import type { UserGroup } from '@/models/entities/user-group.js'; -import type { DriveFile } from '@/models/entities/drive-file.js'; -import { MessagingMessages, UserGroupJoinings, Mutings, Users } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; -import type { MessagingMessage } from '@/models/entities/messaging-message.js'; -import { publishMessagingStream, publishMessagingIndexStream, publishMainStream, publishGroupMessagingStream } from '@/services/stream.js'; -import { pushNotification } from '@/services/push-notification.js'; -import type { Note } from '@/models/entities/note.js'; -import renderNote from '@/services/remote/activitypub/renderer/note.js'; -import renderCreate from '@/services/remote/activitypub/renderer/create.js'; -import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; -import { deliver } from '@/queue/index.js'; - -export async function createMessage(user: { id: User['id']; host: User['host']; }, recipientUser: CacheableUser | undefined, recipientGroup: UserGroup | undefined, text: string | null | undefined, file: DriveFile | null, uri?: string) { - const message = { - id: this.idService.genId(), - createdAt: new Date(), - fileId: file ? file.id : null, - recipientId: recipientUser ? recipientUser.id : null, - groupId: recipientGroup ? recipientGroup.id : null, - text: text ? text.trim() : null, - userId: user.id, - isRead: false, - reads: [] as any[], - uri, - } as MessagingMessage; - - await MessagingMessages.insert(message); - - const messageObj = await MessagingMessages.pack(message); - - if (recipientUser) { - if (Users.isLocalUser(user)) { - // 自分のストリーム - publishMessagingStream(message.userId, recipientUser.id, 'message', messageObj); - publishMessagingIndexStream(message.userId, 'message', messageObj); - publishMainStream(message.userId, 'messagingMessage', messageObj); - } - - if (Users.isLocalUser(recipientUser)) { - // 相手のストリーム - publishMessagingStream(recipientUser.id, message.userId, 'message', messageObj); - publishMessagingIndexStream(recipientUser.id, 'message', messageObj); - publishMainStream(recipientUser.id, 'messagingMessage', messageObj); - } - } else if (recipientGroup) { - // グループのストリーム - publishGroupMessagingStream(recipientGroup.id, 'message', messageObj); - - // メンバーのストリーム - const joinings = await UserGroupJoinings.findBy({ userGroupId: recipientGroup.id }); - for (const joining of joinings) { - publishMessagingIndexStream(joining.userId, 'message', messageObj); - publishMainStream(joining.userId, 'messagingMessage', messageObj); - } - } - - // 2秒経っても(今回作成した)メッセージが既読にならなかったら「未読のメッセージがありますよ」イベントを発行する - setTimeout(async () => { - const freshMessage = await MessagingMessages.findOneBy({ id: message.id }); - if (freshMessage == null) return; // メッセージが削除されている場合もある - - if (recipientUser && Users.isLocalUser(recipientUser)) { - if (freshMessage.isRead) return; // 既読 - - //#region ただしミュートされているなら発行しない - const mute = await Mutings.findBy({ - muterId: recipientUser.id, - }); - if (mute.map(m => m.muteeId).includes(user.id)) return; - //#endregion - - publishMainStream(recipientUser.id, 'unreadMessagingMessage', messageObj); - pushNotification(recipientUser.id, 'unreadMessagingMessage', messageObj); - } else if (recipientGroup) { - const joinings = await UserGroupJoinings.findBy({ userGroupId: recipientGroup.id, userId: Not(user.id) }); - for (const joining of joinings) { - if (freshMessage.reads.includes(joining.userId)) return; // 既読 - publishMainStream(joining.userId, 'unreadMessagingMessage', messageObj); - pushNotification(joining.userId, 'unreadMessagingMessage', messageObj); - } - } - }, 2000); - - if (recipientUser && Users.isLocalUser(user) && Users.isRemoteUser(recipientUser)) { - const note = { - id: message.id, - createdAt: message.createdAt, - fileIds: message.fileId ? [ message.fileId ] : [], - text: message.text, - userId: message.userId, - visibility: 'specified', - mentions: [ recipientUser ].map(u => u.id), - mentionedRemoteUsers: JSON.stringify([ recipientUser ].map(u => ({ - uri: u.uri, - username: u.username, - host: u.host, - }))), - } as Note; - - const activity = renderActivity(renderCreate(await renderNote(note, false, true), note)); - - deliver(user, activity, recipientUser.inbox); - } - return messageObj; -} diff --git a/packages/backend/src/services/messages/delete.ts b/packages/backend/src/services/messages/delete.ts deleted file mode 100644 index 6c3a407be873..000000000000 --- a/packages/backend/src/services/messages/delete.ts +++ /dev/null @@ -1,30 +0,0 @@ -import config from '@/config/index.js'; -import { MessagingMessages, Users } from '@/models/index.js'; -import type { MessagingMessage } from '@/models/entities/messaging-message.js'; -import { publishGroupMessagingStream, publishMessagingStream } from '@/services/stream.js'; -import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; -import renderDelete from '@/services/remote/activitypub/renderer/delete.js'; -import renderTombstone from '@/services/remote/activitypub/renderer/tombstone.js'; -import { deliver } from '@/queue/index.js'; - -export async function deleteMessage(message: MessagingMessage) { - await MessagingMessages.delete(message.id); - postDeleteMessage(message); -} - -async function postDeleteMessage(message: MessagingMessage) { - if (message.recipientId) { - const user = await Users.findOneByOrFail({ id: message.userId }); - const recipient = await Users.findOneByOrFail({ id: message.recipientId }); - - if (Users.isLocalUser(user)) publishMessagingStream(message.userId, message.recipientId, 'deleted', message.id); - if (Users.isLocalUser(recipient)) publishMessagingStream(message.recipientId, message.userId, 'deleted', message.id); - - if (Users.isLocalUser(user) && Users.isRemoteUser(recipient)) { - const activity = renderActivity(renderDelete(renderTombstone(`${config.url}/notes/${message.id}`), user)); - deliver(user, activity, recipient.inbox); - } - } else if (message.groupId) { - publishGroupMessagingStream(message.groupId, 'deleted', message.id); - } -} diff --git a/packages/backend/src/services/push-notification.ts b/packages/backend/src/services/push-notification.ts deleted file mode 100644 index 393a23d0507b..000000000000 --- a/packages/backend/src/services/push-notification.ts +++ /dev/null @@ -1,85 +0,0 @@ -import push from 'web-push'; -import config from '@/config/index.js'; -import { SwSubscriptions } from '@/models/index.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import { Packed } from '@/misc/schema.js'; -import { getNoteSummary } from '@/misc/get-note-summary.js'; - -// Defined also packages/sw/types.ts#L14-L21 -type pushNotificationsTypes = { - 'notification': Packed<'Notification'>; - 'unreadMessagingMessage': Packed<'MessagingMessage'>; - 'readNotifications': { notificationIds: string[] }; - 'readAllNotifications': undefined; - 'readAllMessagingMessages': undefined; - 'readAllMessagingMessagesOfARoom': { userId: string } | { groupId: string }; -}; - -// プッシュメッセージサーバーには文字数制限があるため、内容を削減します -function truncateNotification(notification: Packed<'Notification'>): any { - if (notification.note) { - return { - ...notification, - note: { - ...notification.note, - // textをgetNoteSummaryしたものに置き換える - text: getNoteSummary(notification.type === 'renote' ? notification.note.renote as Packed<'Note'> : notification.note), - - cw: undefined, - reply: undefined, - renote: undefined, - user: undefined as any, // 通知を受け取ったユーザーである場合が多いのでこれも捨てる - } - }; - } - - return notification; -} - -export async function pushNotification(userId: string, type: T, body: pushNotificationsTypes[T]) { - const meta = await fetchMeta(); - - if (!meta.enableServiceWorker || meta.swPublicKey == null || meta.swPrivateKey == null) return; - - // アプリケーションの連絡先と、サーバーサイドの鍵ペアの情報を登録 - push.setVapidDetails(config.url, - meta.swPublicKey, - meta.swPrivateKey); - - // Fetch - const subscriptions = await SwSubscriptions.findBy({ - userId: userId, - }); - - for (const subscription of subscriptions) { - const pushSubscription = { - endpoint: subscription.endpoint, - keys: { - auth: subscription.auth, - p256dh: subscription.publickey, - }, - }; - - push.sendNotification(pushSubscription, JSON.stringify({ - type, - body: type === 'notification' ? truncateNotification(body as Packed<'Notification'>) : body, - userId, - dateTime: (new Date()).getTime(), - }), { - proxy: config.proxy, - }).catch((err: any) => { - //swLogger.info(err.statusCode); - //swLogger.info(err.headers); - //swLogger.info(err.body); - - if (err.statusCode === 410) { - SwSubscriptions.delete({ - userId: userId, - endpoint: subscription.endpoint, - auth: subscription.auth, - publickey: subscription.publickey, - }); - } - }); - } -} From b92da09c6a6b0620505306cebb385d6d0a9b952d Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 22:39:16 +0900 Subject: [PATCH 119/180] wip --- .../api/endpoints/notes/favorites/create.ts | 20 +++-- .../api/endpoints/notes/favorites/delete.ts | 13 ++-- .../endpoints/notes/polls/recommendation.ts | 19 ++++- .../server/api/endpoints/notes/polls/vote.ts | 73 ++++++++++--------- .../api/endpoints/notes/reactions/create.ts | 20 ++--- .../api/endpoints/notes/reactions/delete.ts | 18 +++-- 6 files changed, 93 insertions(+), 70 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts index 3c16374706eb..51ef02ee6619 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; -import { NoteFavorites } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import type { NoteFavorites } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { GetterService } from '@/server/api/common/GetterService.js'; import { ApiError } from '../../../error.js'; -import { getNote } from '../../../common/getters.js'; export const meta = { tags: ['notes', 'favorites'], @@ -39,17 +39,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('noteFavoritesRepository') + private noteFavoritesRepository: typeof NoteFavorites, + private idService: IdService, + private getterService: GetterService, ) { super(meta, paramDef, async (ps, me) => { // Get favoritee - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; + const note = await this.getterService.getNote(ps.noteId).catch(err => { + if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw err; }); // if already favorited - const exist = await NoteFavorites.findOneBy({ + const exist = await this.noteFavoritesRepository.findOneBy({ noteId: note.id, userId: me.id, }); @@ -59,7 +63,7 @@ export default class extends Endpoint { } // Create favorite - await NoteFavorites.insert({ + await this.noteFavoritesRepository.insert({ id: this.idService.genId(), createdAt: new Date(), noteId: note.id, diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts index a3097f957050..08650e6e0959 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { NoteFavorites } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { GetterService } from '@/server/api/common/GetterService.js'; import { ApiError } from '../../../error.js'; -import { getNote } from '../../../common/getters.js'; export const meta = { tags: ['notes', 'favorites'], @@ -38,17 +38,20 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('noteFavoritesRepository') + private noteFavoritesRepository: typeof NoteFavorites, + private getterService: GetterService, ) { super(meta, paramDef, async (ps, me) => { // Get favoritee - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; + const note = await this.getterService.getNote(ps.noteId).catch(err => { + if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw err; }); // if already favorited - const exist = await NoteFavorites.findOneBy({ + const exist = await this.noteFavoritesRepository.findOneBy({ noteId: note.id, userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts index 2150fa0fa380..a8b31cdf689a 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts @@ -1,8 +1,8 @@ import { Brackets, In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { Notes } from '@/models/index.js'; -import { Polls, Mutings, PollVotes } from '@/models/index.js'; +import type { Notes , Mutings , Polls, PollVotes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService'; export const meta = { tags: ['notes'], @@ -35,9 +35,20 @@ export default class extends Endpoint { constructor( @Inject('notesRepository') private notesRepository: typeof Notes, + + @Inject('pollsRepository') + private pollsRepository: typeof Polls, + + @Inject('pollVotesRepository') + private pollVotesRepository: typeof PollVotes, + + @Inject('mutingsRepository') + private mutingsRepository: typeof Mutings, + + private noteEntityService: NoteEntityService, ) { super(meta, paramDef, async (ps, me) => { - const query = Polls.createQueryBuilder('poll') + const query = this.pollsRepository.createQueryBuilder('poll') .where('poll.userHost IS NULL') .andWhere('poll.userId != :meId', { meId: me.id }) .andWhere('poll.noteVisibility = \'public\'') @@ -47,7 +58,7 @@ export default class extends Endpoint { })); //#region exclude arleady voted polls - const votedQuery = PollVotes.createQueryBuilder('vote') + const votedQuery = this.pollVotesRepository.createQueryBuilder('vote') .select('vote.noteId') .where('vote.userId = :meId', { meId: me.id }); 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 15dfc52fb249..480a0e9b945e 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -1,17 +1,15 @@ import { Not } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { publishNoteStream } from '@/services/stream.js'; -import { createNotification } from '@/services/create-notification.js'; -import { deliver } from '@/queue/index.js'; -import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; -import renderVote from '@/services/remote/activitypub/renderer/vote.js'; -import { deliverQuestionUpdate } from '@/services/note/polls/update.js'; -import type { Users } from '@/models/index.js'; -import { PollVotes, NoteWatchings, Polls, Blockings } from '@/models/index.js'; +import type { Users , Blockings , Polls , PollVotes } from '@/models/index.js'; import type { IRemoteUser } from '@/models/entities/user.js'; -import type { IdService } from '@/services/IdService.js'; +import { IdService } from '@/services/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { getNote } from '../../../common/getters.js'; +import { GetterService } from '@/server/api/common/GetterService.js'; +import { QueueService } from '@/queue/queue.service.js'; +import { PollService } from '@/services/PollService.js'; +import { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { CreateNotificationService } from '@/services/CreateNotificationService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -69,6 +67,8 @@ export const paramDef = { required: ['noteId', 'choice'], } as const; +// TODO: ロジックをサービスに切り出す + // eslint-disable-next-line import/no-default-export @Injectable() export default class extends Endpoint { @@ -76,15 +76,30 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, + @Inject('blockingsRepository') + private blockingsRepository: typeof Blockings, + + @Inject('pollsRepository') + private pollsRepository: typeof Polls, + + @Inject('pollVotesRepository') + private pollVotesRepository: typeof PollVotes, + private idService: IdService, + private getterService: GetterService, + private queueService: QueueService, + private pollService: PollService, + private apRendererService: ApRendererService, + private globalEventService: GlobalEventService, + private createNotificationService: CreateNotificationService, ) { super(meta, paramDef, async (ps, me) => { const createdAt = new Date(); // Get votee - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; + const note = await this.getterService.getNote(ps.noteId).catch(err => { + if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw err; }); if (!note.hasPoll) { @@ -102,7 +117,7 @@ export default class extends Endpoint { } } - const poll = await Polls.findOneByOrFail({ noteId: note.id }); + const poll = await this.pollsRepository.findOneByOrFail({ noteId: note.id }); if (poll.expiresAt && poll.expiresAt < createdAt) { throw new ApiError(meta.errors.alreadyExpired); @@ -113,7 +128,7 @@ export default class extends Endpoint { } // if already voted - const exist = await PollVotes.findBy({ + const exist = await this.pollVotesRepository.findBy({ noteId: note.id, userId: me.id, }); @@ -129,53 +144,39 @@ export default class extends Endpoint { } // Create vote - const vote = await PollVotes.insert({ + const vote = await this.pollVotesRepository.insert({ id: this.idService.genId(), createdAt, noteId: note.id, userId: me.id, choice: ps.choice, - }).then(x => PollVotes.findOneByOrFail(x.identifiers[0])); + }).then(x => this.pollVotesRepository.findOneByOrFail(x.identifiers[0])); // Increment votes count const index = ps.choice + 1; // In SQL, array index is 1 based - await Polls.query(`UPDATE poll SET votes[${index}] = votes[${index}] + 1 WHERE "noteId" = '${poll.noteId}'`); + await this.pollsRepository.query(`UPDATE poll SET votes[${index}] = votes[${index}] + 1 WHERE "noteId" = '${poll.noteId}'`); - publishNoteStream(note.id, 'pollVoted', { + this.globalEventService.publishNoteStream(note.id, 'pollVoted', { choice: ps.choice, userId: me.id, }); // Notify - createNotification(note.userId, 'pollVote', { + this.createNotificationService.createNotification(note.userId, 'pollVote', { notifierId: me.id, noteId: note.id, choice: ps.choice, }); - // Fetch watchers - NoteWatchings.findBy({ - noteId: note.id, - userId: Not(me.id), - }).then(watchers => { - for (const watcher of watchers) { - createNotification(watcher.userId, 'pollVote', { - notifierId: me.id, - noteId: note.id, - choice: ps.choice, - }); - } - }); - // リモート投票の場合リプライ送信 if (note.userHost != null) { const pollOwner = await this.usersRepository.findOneByOrFail({ id: note.userId }) as IRemoteUser; - deliver(me, renderActivity(await renderVote(me, vote, note, poll, pollOwner)), pollOwner.inbox); + this.queueService.deliver(me, this.apRendererService.renderActivity(await this.apRendererService.renderVote(me, vote, note, poll, pollOwner)), pollOwner.inbox); } // リモートフォロワーにUpdate配信 - deliverQuestionUpdate(note.id); + this.pollService.deliverQuestionUpdate(note.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts index 3bd4ffd05cc7..37af499de84e 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; -import createReaction from '@/services/note/reaction/create.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { getNote } from '../../../common/getters.js'; +import { GetterService } from '@/server/api/common/GetterService.js'; +import { ReactionService } from '@/services/ReactionService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -45,16 +45,18 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private getterService: GetterService, + private reactionService: ReactionService, ) { super(meta, paramDef, async (ps, me) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; + const note = await this.getterService.getNote(ps.noteId).catch(err => { + if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw err; }); - await createReaction(me, note, ps.reaction).catch(e => { - if (e.id === '51c42bb4-931a-456b-bff7-e5a8a70dd298') throw new ApiError(meta.errors.alreadyReacted); - if (e.id === 'e70412a4-7197-4726-8e74-f3e0deb92aa7') throw new ApiError(meta.errors.youHaveBeenBlocked); - throw e; + await this.reactionService.create(me, note, ps.reaction).catch(err => { + if (err.id === '51c42bb4-931a-456b-bff7-e5a8a70dd298') throw new ApiError(meta.errors.alreadyReacted); + if (err.id === 'e70412a4-7197-4726-8e74-f3e0deb92aa7') throw new ApiError(meta.errors.youHaveBeenBlocked); + throw err; }); return; }); diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts index 71b128212ce2..b98c32d58ae5 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts @@ -1,8 +1,8 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import deleteReaction from '@/services/note/reaction/delete.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { getNote } from '../../../common/getters.js'; +import { GetterService } from '@/server/api/common/GetterService.js'; +import { ReactionService } from '@/services/ReactionService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -45,15 +45,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private getterService: GetterService, + private reactionService: ReactionService, ) { super(meta, paramDef, async (ps, me) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; + const note = await this.getterService.getNote(ps.noteId).catch(err => { + if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw err; }); - await deleteReaction(me, note).catch(e => { - if (e.id === '60527ec9-b4cb-4a88-a6bd-32d3ad26817d') throw new ApiError(meta.errors.notReacted); - throw e; + await this.reactionService.delete(me, note).catch(err => { + if (err.id === '60527ec9-b4cb-4a88-a6bd-32d3ad26817d') throw new ApiError(meta.errors.notReacted); + throw err; }); }); } From 3daebca5da5f45437e0d3f5cbd0d7bba2d504c08 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 22:55:13 +0900 Subject: [PATCH 120/180] wip --- .../server/api/endpoints/notes/children.ts | 2 ++ .../api/endpoints/notes/conversation.ts | 12 ++++++--- .../src/server/api/endpoints/notes/create.ts | 12 ++++++--- .../src/server/api/endpoints/notes/delete.ts | 15 ++++++----- .../server/api/endpoints/notes/featured.ts | 2 ++ .../api/endpoints/notes/global-timeline.ts | 12 ++++++--- .../api/endpoints/notes/hybrid-timeline.ts | 18 ++++++++----- .../api/endpoints/notes/local-timeline.ts | 12 ++++++--- .../server/api/endpoints/notes/mentions.ts | 14 +++++++--- .../src/server/api/endpoints/notes/renotes.ts | 11 +++++--- .../src/server/api/endpoints/notes/replies.ts | 2 ++ .../api/endpoints/notes/search-by-tag.ts | 5 ++-- .../src/server/api/endpoints/notes/search.ts | 15 +++++++---- .../src/server/api/endpoints/notes/state.ts | 7 +++-- .../endpoints/notes/thread-muting/create.ts | 16 +++++++----- .../endpoints/notes/thread-muting/delete.ts | 14 ++++++---- .../server/api/endpoints/notes/timeline.ts | 14 +++++++--- .../server/api/endpoints/notes/translate.ts | 26 +++++++++++++------ .../server/api/endpoints/notes/unrenote.ts | 11 +++++--- .../api/endpoints/notes/user-list-timeline.ts | 20 +++++++++----- 20 files changed, 161 insertions(+), 79 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index 9ce24a542fbc..75e126b802c3 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -3,6 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService'; export const meta = { tags: ['notes'], @@ -38,6 +39,7 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/notes/conversation.ts b/packages/backend/src/server/api/endpoints/notes/conversation.ts index ac75dacd5052..c3848013c8ab 100644 --- a/packages/backend/src/server/api/endpoints/notes/conversation.ts +++ b/packages/backend/src/server/api/endpoints/notes/conversation.ts @@ -2,8 +2,9 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Note } from '@/models/entities/note.js'; import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { ApiError } from '../../error.js'; -import { getNote } from '../../common/getters.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['notes'], @@ -45,9 +46,12 @@ export default class extends Endpoint { constructor( @Inject('notesRepository') private notesRepository: typeof Notes, + + private noteEntityService: NoteEntityService, + private getterService: GetterService, ) { super(meta, paramDef, async (ps, me) => { - const note = await getNote(ps.noteId).catch(err => { + const note = await this.getterService.getNote(ps.noteId).catch(err => { if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw err; }); @@ -55,7 +59,7 @@ export default class extends Endpoint { const conversation: Note[] = []; let i = 0; - async function get(id: any) { + const get = async (id: any) => { i++; const p = await this.notesRepository.findOneBy({ id }); if (p == null) return; @@ -71,7 +75,7 @@ export default class extends Endpoint { if (p.replyId) { await get(p.replyId); } - } + }; if (note.replyId) { await get(note.replyId); diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 300962939c00..a62047ceb772 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -2,15 +2,15 @@ import ms from 'ms'; import { In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { User } from '@/models/entities/user.js'; -import type { Users , Notes } from '@/models/index.js'; -import { DriveFiles, Channels, Blockings } from '@/models/index.js'; +import type { Users , Notes , Blockings } from '@/models/index.js'; +import { DriveFiles, Channels } from '@/models/index.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import type { Note } from '@/models/entities/note.js'; import type { Channel } from '@/models/entities/channel.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; -import { noteService } from '@/services/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { NoteCreateService } from '@/services/NoteCreateService.js'; import { noteVisibilities } from '../../../../types.js'; import { ApiError } from '../../error.js'; @@ -173,7 +173,11 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + @Inject('blockingsRepository') + private blockingsRepository: typeof Blockings, + private noteEntityService: NoteEntityService, + private noteCreateService: NoteCreateService, ) { super(meta, paramDef, async (ps, me) => { let visibleUsers: User[] = []; @@ -262,7 +266,7 @@ export default class extends Endpoint { } // 投稿を作成 - const note = await noteService.create(me, { + const note = await this.noteCreateService.create(me, { createdAt: new Date(), files: files, poll: ps.poll ? { diff --git a/packages/backend/src/server/api/endpoints/notes/delete.ts b/packages/backend/src/server/api/endpoints/notes/delete.ts index 89f5a9f2b2e1..15520231f60f 100644 --- a/packages/backend/src/server/api/endpoints/notes/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/delete.ts @@ -1,10 +1,10 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import deleteNote from '@/services/note/delete.js'; import type { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { getNote } from '../../common/getters.js'; +import { NoteDeleteService } from '@/services/NoteDeleteService.js'; import { ApiError } from '../../error.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['notes'], @@ -48,11 +48,14 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + private getterService: GetterService, + private noteDeleteService: NoteDeleteService, ) { super(meta, paramDef, async (ps, me) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; + const note = await this.getterService.getNote(ps.noteId).catch(err => { + if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw err; }); if ((!me.isAdmin && !me.isModerator) && (note.userId !== me.id)) { @@ -60,7 +63,7 @@ export default class extends Endpoint { } // この操作を行うのが投稿者とは限らない(例えばモデレーター)ため - await deleteNote(await this.usersRepository.findOneByOrFail({ id: note.userId }), note); + await this.noteDeleteService.delete(await this.usersRepository.findOneByOrFail({ id: note.userId }), note); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index 06cb679f7864..5fc5525d88a7 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService'; export const meta = { tags: ['notes'], @@ -35,6 +36,7 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index ce119a3e1554..8f5baa93e114 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -1,9 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; -import { fetchMeta } from '@/misc/fetch-meta.js'; import type { Notes } from '@/models/index.js'; -import { activeUsersChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { MetaService } from '@/services/MetaService.js'; +import ActiveUsersChart from '@/services/chart/charts/active-users.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -52,10 +53,13 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, private queryService: QueryService, + private metaService: MetaService, + private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { - const m = await fetchMeta(); + const m = await this.metaService.fetch(); if (m.disableGlobalTimeline) { if (me == null || (!me.isAdmin && !me.isModerator)) { throw new ApiError(meta.errors.gtlDisabled); @@ -95,7 +99,7 @@ export default class extends Endpoint { process.nextTick(() => { if (me) { - activeUsersChart.read(me); + this.activeUsersChart.read(me); } }); diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 36ec6f56168b..1a3d7b77d53d 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -1,11 +1,11 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import type { Notes } from '@/models/index.js'; -import { Followings } from '@/models/index.js'; -import { activeUsersChart } from '@/services/chart/index.js'; +import type { Notes , Followings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import ActiveUsersChart from '@/services/chart/charts/active-users.js'; +import { MetaService } from '@/services/MetaService.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -59,10 +59,16 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + private noteEntityService: NoteEntityService, private queryService: QueryService, + private metaService: MetaService, + private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { - const m = await fetchMeta(); + const m = await this.metaService.fetch(); if (m.disableLocalTimeline && (!me.isAdmin && !me.isModerator)) { throw new ApiError(meta.errors.stlDisabled); } @@ -136,7 +142,7 @@ export default class extends Endpoint { const timeline = await query.take(ps.limit).getMany(); process.nextTick(() => { - activeUsersChart.read(me); + this.activeUsersChart.read(me); }); return await this.noteEntityService.packMany(timeline, me); diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index aec538419c31..324b9d2c8bc9 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -1,10 +1,11 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { fetchMeta } from '@/misc/fetch-meta.js'; import type { Users , Notes } from '@/models/index.js'; -import { activeUsersChart } from '@/services/chart/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { MetaService } from '@/services/MetaService.js'; +import ActiveUsersChart from '@/services/chart/charts/active-users.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -57,10 +58,13 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, private queryService: QueryService, + private metaService: MetaService, + private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { - const m = await fetchMeta(); + const m = await this.metaService.fetch(); if (m.disableLocalTimeline) { if (me == null || (!me.isAdmin && !me.isModerator)) { throw new ApiError(meta.errors.ltlDisabled); @@ -114,7 +118,7 @@ export default class extends Endpoint { process.nextTick(() => { if (me) { - activeUsersChart.read(me); + this.activeUsersChart.read(me); } }); diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index be3794c39534..0666e13dc3c6 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -1,10 +1,11 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import read from '@/services/note/read.js'; -import type { Notes } from '@/models/index.js'; -import { Followings } from '@/models/index.js'; +import type { Notes , Followings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService'; +import { MetaService } from '@/services/MetaService'; +import { NoteReadService } from '@/services/NoteReadService'; export const meta = { tags: ['notes'], @@ -41,7 +42,12 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + private noteEntityService: NoteEntityService, private queryService: QueryService, + private noteReadService: NoteReadService, ) { super(meta, paramDef, async (ps, me) => { const followingQuery = this.followingsRepository.createQueryBuilder('following') @@ -81,7 +87,7 @@ export default class extends Endpoint { const mentions = await query.take(ps.limit).getMany(); - read(me.id, mentions); + this.noteReadService.read(me.id, mentions); return await this.noteEntityService.packMany(mentions, me); }); diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index 2166c6c0328a..c2c4c4ab0cf2 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -2,8 +2,9 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; -import { getNote } from '../../common/getters.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { ApiError } from '../../error.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['notes'], @@ -47,12 +48,14 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, private queryService: QueryService, + private getterService: GetterService, ) { super(meta, paramDef, async (ps, me) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; + const note = await this.getterService.getNote(ps.noteId).catch(err => { + if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw err; }); const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index f9e2d0688ec9..622b255dba4b 100644 --- a/packages/backend/src/server/api/endpoints/notes/replies.ts +++ b/packages/backend/src/server/api/endpoints/notes/replies.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService'; export const meta = { tags: ['notes'], @@ -37,6 +38,7 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts index d225bf3f0da9..821c73e6ffb6 100644 --- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts +++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts @@ -5,6 +5,7 @@ import { safeForSql } from '@/misc/safe-for-sql.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService'; export const meta = { tags: ['notes', 'hashtags'], @@ -67,12 +68,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - @Inject('notesRepository') private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index e70963f4948a..d63666d5c95c 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -1,9 +1,11 @@ import { In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { Notes } from '@/models/index.js'; -import config from '@/config/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { Config } from '@/config.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; import es from '../../../../db/elasticsearch.js'; export const meta = { @@ -44,16 +46,19 @@ export const paramDef = { required: ['query'], } as const; +// TODO: ロジックをサービスに切り出す + // eslint-disable-next-line import/no-default-export @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - + @Inject(DI_SYMBOLS.config) + private config: Config, + @Inject('notesRepository') private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { @@ -111,7 +116,7 @@ export default class extends Endpoint { : []; const result = await es.search({ - index: config.elasticsearch.index || 'misskey_note', + index: this.config.elasticsearch.index || 'misskey_note', body: { size: ps.limit, from: ps.offset, diff --git a/packages/backend/src/server/api/endpoints/notes/state.ts b/packages/backend/src/server/api/endpoints/notes/state.ts index db1bfa7db06c..ab9333142f3c 100644 --- a/packages/backend/src/server/api/endpoints/notes/state.ts +++ b/packages/backend/src/server/api/endpoints/notes/state.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { Notes } from '@/models/index.js'; -import { NoteFavorites, NoteThreadMutings, NoteWatchings } from '@/models/index.js'; +import type { Notes , NoteThreadMutings } from '@/models/index.js'; +import { NoteFavorites, NoteWatchings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -42,6 +42,9 @@ export default class extends Endpoint { constructor( @Inject('notesRepository') private notesRepository: typeof Notes, + + @Inject('noteThreadMutingsRepository') + private noteThreadMutingsRepository: typeof NoteThreadMutings, ) { super(meta, paramDef, async (ps, me) => { const note = await this.notesRepository.findOneByOrFail({ id: ps.noteId }); diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts index cbb2b917092b..b7e9316bc832 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts @@ -1,10 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { Notes } from '@/models/index.js'; -import { NoteThreadMutings } from '@/models/index.js'; +import type { Notes , NoteThreadMutings } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; -import readNote from '@/services/note/read.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { getNote } from '../../../common/getters.js'; +import { GetterService } from '@/server/api/common/GetterService.js'; +import { NoteReadService } from '@/services/NoteReadService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -38,10 +37,15 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + @Inject('noteThreadMutingsRepository') + private noteThreadMutingsRepository: typeof NoteThreadMutings, + + private getterService: GetterService, + private noteReadService: NoteReadService, private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { - const note = await getNote(ps.noteId).catch(err => { + const note = await this.getterService.getNote(ps.noteId).catch(err => { if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw err; }); @@ -54,7 +58,7 @@ export default class extends Endpoint { }], }); - await readNote(me.id, mutedNotes); + await this.noteReadService.read(me.id, mutedNotes); await this.noteThreadMutingsRepository.insert({ id: this.idService.genId(), diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts index 0ce8d4ae392f..d05376dacc2a 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; -import { NoteThreadMutings } from '@/models/index.js'; +import type { NoteThreadMutings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { getNote } from '../../../common/getters.js'; +import { GetterService } from '@/server/api/common/GetterService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -32,11 +32,15 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('noteThreadMutingsRepository') + private noteThreadMutingsRepository: typeof NoteThreadMutings, + + private getterService: GetterService, ) { super(meta, paramDef, async (ps, me) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; + const note = await this.getterService.getNote(ps.noteId).catch(err => { + if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw err; }); await this.noteThreadMutingsRepository.delete({ diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 48bd6043d2bf..4c58026f45e4 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -1,10 +1,11 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { Notes } from '@/models/index.js'; -import { Followings } from '@/models/index.js'; -import { activeUsersChart } from '@/services/chart/index.js'; +import type { Notes , Followings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import ActiveUsersChart from '@/services/chart/charts/active-users'; +import { NoteEntityService } from '@/services/entities/NoteEntityService'; +import { MetaService } from '@/services/MetaService'; export const meta = { tags: ['notes'], @@ -49,7 +50,12 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + private noteEntityService: NoteEntityService, private queryService: QueryService, + private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { const hasFollowing = (await this.followingsRepository.count({ @@ -128,7 +134,7 @@ export default class extends Endpoint { const timeline = await query.take(ps.limit).getMany(); process.nextTick(() => { - activeUsersChart.read(me); + this.activeUsersChart.read(me); }); return await this.noteEntityService.packMany(timeline, me); diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts index 107f0c6bb291..c6f537303c75 100644 --- a/packages/backend/src/server/api/endpoints/notes/translate.ts +++ b/packages/backend/src/server/api/endpoints/notes/translate.ts @@ -1,13 +1,15 @@ import { URLSearchParams } from 'node:url'; import fetch from 'node-fetch'; import { Inject, Injectable } from '@nestjs/common'; -import config from '@/config/index.js'; -import { getAgentByUrl } from '@/misc/fetch.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { Config } from '@/config.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { MetaService } from '@/services/MetaService.js'; +import { HttpRequestService } from '@/services/HttpRequestService.js'; import { ApiError } from '../../error.js'; -import { getNote } from '../../common/getters.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['notes'], @@ -41,16 +43,24 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + @Inject('notesRepository') private notesRepository: typeof Notes, + + private noteEntityService: NoteEntityService, + private getterService: GetterService, + private metaService: MetaService, + private httpRequestService: HttpRequestService, ) { super(meta, paramDef, async (ps, me) => { - const note = await getNote(ps.noteId).catch(err => { + const note = await this.getterService.getNote(ps.noteId).catch(err => { if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw err; }); - if (!(await this.notesRepository.isVisibleForMe(note, me ? me.id : null))) { + if (!(await this.noteEntityService.isVisibleForMe(note, me ? me.id : null))) { return 204; // TODO: 良い感じのエラー返す } @@ -58,7 +68,7 @@ export default class extends Endpoint { return 204; } - const instance = await fetchMeta(); + const instance = await this.metaService.fetch(); if (instance.deeplAuthKey == null) { return 204; // TODO: 良い感じのエラー返す @@ -84,7 +94,7 @@ export default class extends Endpoint { body: params, // TODO //timeout: 10000, - agent: getAgentByUrl, + agent: (url) => this.httpRequestService.getAgentByUrl(url), }); const json = (await res.json()) as { diff --git a/packages/backend/src/server/api/endpoints/notes/unrenote.ts b/packages/backend/src/server/api/endpoints/notes/unrenote.ts index d22d2480ede1..63339f1efc5c 100644 --- a/packages/backend/src/server/api/endpoints/notes/unrenote.ts +++ b/packages/backend/src/server/api/endpoints/notes/unrenote.ts @@ -1,10 +1,10 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import deleteNote from '@/services/note/delete.js'; import type { Users , Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { getNote } from '../../common/getters.js'; +import { NoteDeleteService } from '@/services/NoteDeleteService.js'; import { ApiError } from '../../error.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['notes'], @@ -45,9 +45,12 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + private getterService: GetterService, + private noteDeleteService: NoteDeleteService, ) { super(meta, paramDef, async (ps, me) => { - const note = await getNote(ps.noteId).catch(err => { + const note = await this.getterService.getNote(ps.noteId).catch(err => { if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw err; }); @@ -58,7 +61,7 @@ export default class extends Endpoint { }); for (const note of renotes) { - deleteNote(await this.usersRepository.findOneByOrFail({ id: me.id }), note); + this.noteDeleteService.delete(await this.usersRepository.findOneByOrFail({ id: me.id }), note); } }); } diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index b8e4df6d59f1..100da776c3bd 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -1,10 +1,10 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { Notes } from '@/models/index.js'; -import { UserLists, UserListJoinings } from '@/models/index.js'; -import { activeUsersChart } from '@/services/chart/index.js'; +import type { Notes , UserLists, UserListJoinings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import ActiveUsersChart from '@/services/chart/charts/active-users.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -59,10 +59,18 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + @Inject('userListsRepository') + private userListsRepository: typeof UserLists, + + @Inject('userListJoiningsRepository') + private userListJoiningsRepository: typeof UserListJoinings, + + private noteEntityService: NoteEntityService, private queryService: QueryService, + private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { - const list = await UserLists.findOneBy({ + const list = await this.userListsRepository.findOneBy({ id: ps.listId, userId: me.id, }); @@ -73,7 +81,7 @@ export default class extends Endpoint { //#region Construct query const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .innerJoin(UserListJoinings.metadata.targetName, 'userListJoining', 'userListJoining.userId = note.userId') + .innerJoin(this.userListJoiningsRepository.metadata.targetName, 'userListJoining', 'userListJoining.userId = note.userId') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') .leftJoinAndSelect('user.banner', 'banner') @@ -126,7 +134,7 @@ export default class extends Endpoint { const timeline = await query.take(ps.limit).getMany(); - activeUsersChart.read(me); + this.activeUsersChart.read(me); return await this.noteEntityService.packMany(timeline, me); }); From 20a7be19ee6530ae1c3909fa2186be35fa9dde0f Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 23:02:44 +0900 Subject: [PATCH 121/180] wip --- .../server/api/common/read-notification.ts | 50 -------------- .../api/endpoints/notifications/create.ts | 10 +-- .../notifications/mark-all-as-read.ts | 17 +++-- .../api/endpoints/notifications/read.ts | 7 +- .../src/services/NotificationService.ts | 65 +++++++++++++++++++ 5 files changed, 83 insertions(+), 66 deletions(-) delete mode 100644 packages/backend/src/server/api/common/read-notification.ts create mode 100644 packages/backend/src/services/NotificationService.ts diff --git a/packages/backend/src/server/api/common/read-notification.ts b/packages/backend/src/server/api/common/read-notification.ts deleted file mode 100644 index b0d38a9e39bc..000000000000 --- a/packages/backend/src/server/api/common/read-notification.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { In } from 'typeorm'; -import { publishMainStream } from '@/services/stream.js'; -import { pushNotification } from '@/services/push-notification.js'; -import { User } from '@/models/entities/user.js'; -import { Notification } from '@/models/entities/notification.js'; -import { Notifications, Users } from '@/models/index.js'; - -export async function readNotification( - userId: User['id'], - notificationIds: Notification['id'][], -) { - if (notificationIds.length === 0) return; - - // Update documents - const result = await Notifications.update({ - notifieeId: userId, - id: In(notificationIds), - isRead: false, - }, { - isRead: true, - }); - - if (result.affected === 0) return; - - if (!await Users.getHasUnreadNotification(userId)) return postReadAllNotifications(userId); - else return postReadNotifications(userId, notificationIds); -} - -export async function readNotificationByQuery( - userId: User['id'], - query: Record, -) { - const notificationIds = await Notifications.findBy({ - ...query, - notifieeId: userId, - isRead: false, - }).then(notifications => notifications.map(notification => notification.id)); - - return readNotification(userId, notificationIds); -} - -function postReadAllNotifications(userId: User['id']) { - publishMainStream(userId, 'readAllNotifications'); - return pushNotification(userId, 'readAllNotifications', undefined); -} - -function postReadNotifications(userId: User['id'], notificationIds: Notification['id'][]) { - publishMainStream(userId, 'readNotifications', notificationIds); - return pushNotification(userId, 'readNotifications', { notificationIds }); -} diff --git a/packages/backend/src/server/api/endpoints/notifications/create.ts b/packages/backend/src/server/api/endpoints/notifications/create.ts index 811b637bec6b..eaa8b282ef42 100644 --- a/packages/backend/src/server/api/endpoints/notifications/create.ts +++ b/packages/backend/src/server/api/endpoints/notifications/create.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; -import { createNotification } from '@/services/create-notification.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { CreateNotificationService } from '@/services/CreateNotificationService'; export const meta = { tags: ['notifications'], @@ -27,14 +27,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, + private createNotificationService: CreateNotificationService, ) { super(meta, paramDef, async (ps, user, token) => { - createNotification(user.id, 'app', { + this.createNotificationService.createNotification(user.id, 'app', { appAccessTokenId: token ? token.id : null, customBody: ps.body, customHeader: ps.header, diff --git a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts index 43ba486d29ad..3df6368749bd 100644 --- a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; -import { publishMainStream } from '@/services/stream.js'; -import { pushNotification } from '@/services/push-notification.js'; -import { Notifications } from '@/models/index.js'; +import type { Notifications } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { GlobalEventService } from '@/services/GlobalEventService'; +import { PushNotificationService } from '@/services/PushNotificationService'; export const meta = { tags: ['notifications', 'account'], @@ -22,10 +22,15 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('notificationsRepository') + private notificationsRepository: typeof Notifications, + + private globalEventService: GlobalEventService, + private pushNotificationService: PushNotificationService, ) { super(meta, paramDef, async (ps, me) => { // Update documents - await Notifications.update({ + await this.notificationsRepository.update({ notifieeId: me.id, isRead: false, }, { @@ -33,8 +38,8 @@ export default class extends Endpoint { }); // 全ての通知を読みましたよというイベントを発行 - publishMainStream(me.id, 'readAllNotifications'); - pushNotification(me.id, 'readAllNotifications', undefined); + this.globalEventService.publishMainStream(me.id, 'readAllNotifications'); + this.pushNotificationService.pushNotification(me.id, 'readAllNotifications', undefined); }); } } diff --git a/packages/backend/src/server/api/endpoints/notifications/read.ts b/packages/backend/src/server/api/endpoints/notifications/read.ts index acf75be869b4..f519e1d37859 100644 --- a/packages/backend/src/server/api/endpoints/notifications/read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/read.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { readNotification } from '../../common/read-notification.js'; +import { NotificationService } from '@/services/NotificationService'; export const meta = { tags: ['notifications', 'account'], @@ -47,10 +47,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private notificationService: NotificationService, ) { super(meta, paramDef, async (ps, me) => { - if ('notificationId' in ps) return readNotification(me.id, [ps.notificationId]); - return readNotification(me.id, ps.notificationIds); + if ('notificationId' in ps) return this.notificationService.readNotification(me.id, [ps.notificationId]); + return this.notificationService.readNotification(me.id, ps.notificationIds); }); } } diff --git a/packages/backend/src/services/NotificationService.ts b/packages/backend/src/services/NotificationService.ts new file mode 100644 index 000000000000..7638a520a573 --- /dev/null +++ b/packages/backend/src/services/NotificationService.ts @@ -0,0 +1,65 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { In } from 'typeorm'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import type { Notifications , Users } from '@/models/index.js'; +import type { User } from '@/models/entities/user'; +import { UserEntityService } from './entities/UserEntityService'; +import { GlobalEventService } from './GlobalEventService'; +import { PushNotificationService } from './PushNotificationService'; + +@Injectable() +export class NotificationService { + constructor( + @Inject('notificationsRepository') + private notificationsRepository: typeof Notifications, + + private userEntityService: UserEntityService, + private globalEventService: GlobalEventService, + private pushNotificationService: PushNotificationService, + ) { + } + + public async readNotification( + userId: User['id'], + notificationIds: Notification['id'][], + ) { + if (notificationIds.length === 0) return; + + // Update documents + const result = await this.notificationsRepository.update({ + notifieeId: userId, + id: In(notificationIds), + isRead: false, + }, { + isRead: true, + }); + + if (result.affected === 0) return; + + if (!await this.userEntityService.getHasUnreadNotification(userId)) return this.#postReadAllNotifications(userId); + else return this.#postReadNotifications(userId, notificationIds); + } + + public async readNotificationByQuery( + userId: User['id'], + query: Record, + ) { + const notificationIds = await this.notificationsRepository.findBy({ + ...query, + notifieeId: userId, + isRead: false, + }).then(notifications => notifications.map(notification => notification.id)); + + return this.readNotification(userId, notificationIds); + } + + #postReadAllNotifications(userId: User['id']) { + this.globalEventService.publishMainStream(userId, 'readAllNotifications'); + return this.pushNotificationService.pushNotification(userId, 'readAllNotifications', undefined); + } + + #postReadNotifications(userId: User['id'], notificationIds: Notification['id'][]) { + this.globalEventService.publishMainStream(userId, 'readNotifications', notificationIds); + return this.pushNotificationService.pushNotification(userId, 'readNotifications', { notificationIds }); + } +} From 5bc95eede89b5f35409cb7faa6e9824a2622b5ec Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 15 Sep 2022 23:08:09 +0900 Subject: [PATCH 122/180] wip --- .../src/server/api/endpoints/pages/delete.ts | 8 +++++--- .../server/api/endpoints/pages/featured.ts | 14 ++++++------- .../src/server/api/endpoints/pages/like.ts | 18 +++++++++++------ .../src/server/api/endpoints/pages/unlike.ts | 15 +++++++++----- .../src/server/api/endpoints/pages/update.ts | 15 +++++++++----- .../src/server/api/endpoints/promo/read.ts | 20 +++++++++++-------- .../src/server/api/endpoints/sw/register.ts | 15 +++++++++----- .../src/server/api/endpoints/sw/unregister.ts | 6 ++++-- .../api/endpoints/username/available.ts | 10 +++++++--- 9 files changed, 77 insertions(+), 44 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/pages/delete.ts b/packages/backend/src/server/api/endpoints/pages/delete.ts index cd248f7ae961..fc8f3deb206a 100644 --- a/packages/backend/src/server/api/endpoints/pages/delete.ts +++ b/packages/backend/src/server/api/endpoints/pages/delete.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Pages } from '@/models/index.js'; +import type { Pages } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; @@ -37,9 +37,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('pagesRepository') + private pagesRepository: typeof Pages, ) { super(meta, paramDef, async (ps, me) => { - const page = await Pages.findOneBy({ id: ps.pageId }); + const page = await this.pagesRepository.findOneBy({ id: ps.pageId }); if (page == null) { throw new ApiError(meta.errors.noSuchPage); } @@ -47,7 +49,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.accessDenied); } - await Pages.delete(page.id); + await this.pagesRepository.delete(page.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/pages/featured.ts b/packages/backend/src/server/api/endpoints/pages/featured.ts index c34b7ddff4f3..3a6582b31967 100644 --- a/packages/backend/src/server/api/endpoints/pages/featured.ts +++ b/packages/backend/src/server/api/endpoints/pages/featured.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Pages } from '@/models/index.js'; +import type { Pages } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { PageEntityService } from '@/services/entities/PageEntityService'; export const meta = { tags: ['pages'], @@ -28,21 +29,20 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('pagesRepository') + private pagesRepository: typeof Pages, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private pageEntityService: PageEntityService, ) { super(meta, paramDef, async (ps, me) => { - const query = Pages.createQueryBuilder('page') + const query = this.pagesRepository.createQueryBuilder('page') .where('page.visibility = \'public\'') .andWhere('page.likedCount > 0') .orderBy('page.likedCount', 'DESC'); const pages = await query.take(10).getMany(); - return await Pages.packMany(pages, me); + return await this.pageEntityService.packMany(pages, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/pages/like.ts b/packages/backend/src/server/api/endpoints/pages/like.ts index b6f62839e7cf..63137ff2c93e 100644 --- a/packages/backend/src/server/api/endpoints/pages/like.ts +++ b/packages/backend/src/server/api/endpoints/pages/like.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Pages, PageLikes } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import type { Pages , PageLikes } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; @@ -44,10 +44,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('pagesRepository') + private pagesRepository: typeof Pages, + + @Inject('pageLikesRepository') + private pageLikesRepository: typeof PageLikes, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { - const page = await Pages.findOneBy({ id: ps.pageId }); + const page = await this.pagesRepository.findOneBy({ id: ps.pageId }); if (page == null) { throw new ApiError(meta.errors.noSuchPage); } @@ -57,7 +63,7 @@ export default class extends Endpoint { } // if already liked - const exist = await PageLikes.findOneBy({ + const exist = await this.pageLikesRepository.findOneBy({ pageId: page.id, userId: me.id, }); @@ -67,14 +73,14 @@ export default class extends Endpoint { } // Create like - await PageLikes.insert({ + await this.pageLikesRepository.insert({ id: this.idService.genId(), createdAt: new Date(), pageId: page.id, userId: me.id, }); - Pages.increment({ id: page.id }, 'likedCount', 1); + this.pagesRepository.increment({ id: page.id }, 'likedCount', 1); }); } } diff --git a/packages/backend/src/server/api/endpoints/pages/unlike.ts b/packages/backend/src/server/api/endpoints/pages/unlike.ts index 7622641da2e1..fd4e77c94ec6 100644 --- a/packages/backend/src/server/api/endpoints/pages/unlike.ts +++ b/packages/backend/src/server/api/endpoints/pages/unlike.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Pages, PageLikes } from '@/models/index.js'; +import type { Pages, PageLikes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; @@ -37,14 +37,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('pagesRepository') + private pagesRepository: typeof Pages, + + @Inject('pageLikesRepository') + private pageLikesRepository: typeof PageLikes, ) { super(meta, paramDef, async (ps, me) => { - const page = await Pages.findOneBy({ id: ps.pageId }); + const page = await this.pagesRepository.findOneBy({ id: ps.pageId }); if (page == null) { throw new ApiError(meta.errors.noSuchPage); } - const exist = await PageLikes.findOneBy({ + const exist = await this.pageLikesRepository.findOneBy({ pageId: page.id, userId: me.id, }); @@ -54,9 +59,9 @@ export default class extends Endpoint { } // Delete like - await PageLikes.delete(exist.id); + await this.pageLikesRepository.delete(exist.id); - Pages.decrement({ id: page.id }, 'likedCount', 1); + this.pagesRepository.decrement({ id: page.id }, 'likedCount', 1); }); } } diff --git a/packages/backend/src/server/api/endpoints/pages/update.ts b/packages/backend/src/server/api/endpoints/pages/update.ts index d1d8cebf89c6..4e446b129e24 100644 --- a/packages/backend/src/server/api/endpoints/pages/update.ts +++ b/packages/backend/src/server/api/endpoints/pages/update.ts @@ -1,7 +1,7 @@ import ms from 'ms'; import { Not } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { Pages, DriveFiles } from '@/models/index.js'; +import type { Pages , DriveFiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; @@ -69,9 +69,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('pagesRepository') + private pagesRepository: typeof Pages, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, ) { super(meta, paramDef, async (ps, me) => { - const page = await Pages.findOneBy({ id: ps.pageId }); + const page = await this.pagesRepository.findOneBy({ id: ps.pageId }); if (page == null) { throw new ApiError(meta.errors.noSuchPage); } @@ -81,7 +86,7 @@ export default class extends Endpoint { let eyeCatchingImage = null; if (ps.eyeCatchingImageId != null) { - eyeCatchingImage = await DriveFiles.findOneBy({ + eyeCatchingImage = await this.driveFilesRepository.findOneBy({ id: ps.eyeCatchingImageId, userId: me.id, }); @@ -91,7 +96,7 @@ export default class extends Endpoint { } } - await Pages.findBy({ + await this.pagesRepository.findBy({ id: Not(ps.pageId), userId: me.id, name: ps.name, @@ -101,7 +106,7 @@ export default class extends Endpoint { } }); - await Pages.update(page.id, { + await this.pagesRepository.update(page.id, { updatedAt: new Date(), title: ps.title, name: ps.name === undefined ? page.name : ps.name, diff --git a/packages/backend/src/server/api/endpoints/promo/read.ts b/packages/backend/src/server/api/endpoints/promo/read.ts index 2c8015abb0d5..741bd2b7a92c 100644 --- a/packages/backend/src/server/api/endpoints/promo/read.ts +++ b/packages/backend/src/server/api/endpoints/promo/read.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; -import { PromoReads } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import type { PromoReads } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../error.js'; -import { getNote } from '../../common/getters.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['notes'], @@ -31,15 +31,19 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('promoReadsRepository') + private promoReadsRepository: typeof PromoReads, + private idService: IdService, + private getterService: GetterService, ) { super(meta, paramDef, async (ps, me) => { - const note = await getNote(ps.noteId).catch(e => { - if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); - throw e; + const note = await this.getterService.getNote(ps.noteId).catch(err => { + if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw err; }); - const exist = await PromoReads.findOneBy({ + const exist = await this.promoReadsRepository.findOneBy({ noteId: note.id, userId: me.id, }); @@ -48,7 +52,7 @@ export default class extends Endpoint { return; } - await PromoReads.insert({ + await this.promoReadsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), noteId: note.id, diff --git a/packages/backend/src/server/api/endpoints/sw/register.ts b/packages/backend/src/server/api/endpoints/sw/register.ts index ae326ca7a6de..1266df6e51e9 100644 --- a/packages/backend/src/server/api/endpoints/sw/register.ts +++ b/packages/backend/src/server/api/endpoints/sw/register.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { fetchMeta } from '@/misc/fetch-meta.js'; -import type { IdService } from '@/services/IdService.js'; -import { SwSubscriptions } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; +import type { SwSubscriptions } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { MetaService } from '@/services/MetaService'; export const meta = { tags: ['account'], @@ -42,18 +43,22 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('swSubscriptionsRepository') + private swSubscriptionsRepository: typeof SwSubscriptions, + private idService: IdService, + private metaService: MetaService, ) { super(meta, paramDef, async (ps, me) => { // if already subscribed - const exist = await SwSubscriptions.findOneBy({ + const exist = await this.swSubscriptionsRepository.findOneBy({ userId: me.id, endpoint: ps.endpoint, auth: ps.auth, publickey: ps.publickey, }); - const instance = await fetchMeta(true); + const instance = await this.metaService.fetch(true); if (exist != null) { return { @@ -62,7 +67,7 @@ export default class extends Endpoint { }; } - await SwSubscriptions.insert({ + await this.swSubscriptionsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/sw/unregister.ts b/packages/backend/src/server/api/endpoints/sw/unregister.ts index da0b9c044064..d2cb75681799 100644 --- a/packages/backend/src/server/api/endpoints/sw/unregister.ts +++ b/packages/backend/src/server/api/endpoints/sw/unregister.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { SwSubscriptions } from '@/models/index.js'; +import type { SwSubscriptions } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -22,9 +22,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('swSubscriptionsRepository') + private swSubscriptionsRepository: typeof SwSubscriptions, ) { super(meta, paramDef, async (ps, me) => { - await SwSubscriptions.delete({ + await this.swSubscriptionsRepository.delete({ userId: me.id, endpoint: ps.endpoint, }); diff --git a/packages/backend/src/server/api/endpoints/username/available.ts b/packages/backend/src/server/api/endpoints/username/available.ts index 127d553d7746..756faac6c825 100644 --- a/packages/backend/src/server/api/endpoints/username/available.ts +++ b/packages/backend/src/server/api/endpoints/username/available.ts @@ -1,7 +1,8 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { Users , UsedUsernames } from '@/models/index.js'; +import type { UsedUsernames , Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { localUsernameSchema } from '@/models/entities/user'; export const meta = { tags: ['users'], @@ -23,7 +24,7 @@ export const meta = { export const paramDef = { type: 'object', properties: { - username: Users.localUsernameSchema, + username: localUsernameSchema, }, required: ['username'], } as const; @@ -34,6 +35,9 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, + + @Inject('usedUsernamesRepository') + private usedUsernamesRepository: typeof UsedUsernames, ) { super(meta, paramDef, async (ps, me) => { // Get exist @@ -42,7 +46,7 @@ export default class extends Endpoint { usernameLower: ps.username.toLowerCase(), }); - const exist2 = await UsedUsernames.countBy({ username: ps.username.toLowerCase() }); + const exist2 = await this.usedUsernamesRepository.countBy({ username: ps.username.toLowerCase() }); return { available: exist === 0 && exist2 === 0, From 7f88cbeaa0e9933b1eb5d51f45cf0e6cda230405 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 00:11:15 +0900 Subject: [PATCH 123/180] wip --- .../backend/src/server/api/endpoints/drive.ts | 9 ++-- .../src/server/api/endpoints/users/clips.ts | 11 +++-- .../server/api/endpoints/users/followers.ts | 14 ++++--- .../server/api/endpoints/users/following.ts | 14 ++++--- .../api/endpoints/users/gallery/posts.ts | 11 +++-- .../api/endpoints/users/groups/delete.ts | 8 ++-- .../users/groups/invitations/accept.ts | 16 ++++--- .../users/groups/invitations/reject.ts | 8 ++-- .../api/endpoints/users/groups/invite.ts | 39 +++++++++-------- .../api/endpoints/users/groups/leave.ts | 14 +++---- .../server/api/endpoints/users/groups/pull.ts | 23 +++++----- .../src/server/api/endpoints/users/notes.ts | 10 ++--- .../src/server/api/endpoints/users/pages.ts | 4 +- .../api/endpoints/users/recommendation.ts | 9 ++-- .../server/api/endpoints/users/relation.ts | 8 ++-- .../api/endpoints/users/report-abuse.ts | 37 ++++++++-------- .../users/search-by-username-and-host.ts | 10 +++-- .../src/server/api/endpoints/users/search.ts | 6 +-- .../src/server/api/endpoints/users/stats.ts | 42 ++++++++++++++----- 19 files changed, 180 insertions(+), 113 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/drive.ts b/packages/backend/src/server/api/endpoints/drive.ts index ea4cef042654..bafc79490b77 100644 --- a/packages/backend/src/server/api/endpoints/drive.ts +++ b/packages/backend/src/server/api/endpoints/drive.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; -import { fetchMeta } from '@/misc/fetch-meta.js'; import { DriveFiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { MetaService } from '@/services/MetaService'; +import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService'; export const meta = { tags: ['drive', 'account'], @@ -36,12 +37,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private metaService: MetaService, + private driveFileEntityService: DriveFileEntityService, ) { super(meta, paramDef, async (ps, me) => { - const instance = await fetchMeta(true); + const instance = await this.metaService.fetch(true); // Calculate drive usage - const usage = await DriveFiles.calcDriveUsageOf(me.id); + const usage = await this.driveFileEntityService.calcDriveUsageOf(me.id); return { capacity: 1024 * 1024 * (me.driveCapacityOverrideMb || instance.localDriveCapacityMb), diff --git a/packages/backend/src/server/api/endpoints/users/clips.ts b/packages/backend/src/server/api/endpoints/users/clips.ts index a3e71e96cc94..1dbb126cdec7 100644 --- a/packages/backend/src/server/api/endpoints/users/clips.ts +++ b/packages/backend/src/server/api/endpoints/users/clips.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Clips } from '@/models/index.js'; +import type { Clips } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import { ClipEntityService } from '@/services/entities/ClipEntityService'; export const meta = { tags: ['users', 'clips'], @@ -34,10 +35,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('clipsRepository') + private clipsRepository: typeof Clips, + + private clipEntityService: ClipEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(Clips.createQueryBuilder('clip'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.clipsRepository.createQueryBuilder('clip'), ps.sinceId, ps.untilId) .andWhere('clip.userId = :userId', { userId: ps.userId }) .andWhere('clip.isPublic = true'); @@ -45,7 +50,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return await Clips.packMany(clips); + return await this.clipEntityService.packMany(clips); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index 4915ef11d97e..b36e13d26831 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -1,10 +1,10 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { Users } from '@/models/index.js'; -import { Followings, UserProfiles } from '@/models/index.js'; +import type { Users , Followings, UserProfiles } from '@/models/index.js'; import { toPunyNullable } from '@/misc/convert-host.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import { FollowingEntityService } from '@/services/entities/FollowingEntityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -74,9 +74,13 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + private followingEntityService: FollowingEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { @@ -88,7 +92,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchUser); } - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); if (profile.ffVisibility === 'private') { if (me == null || (me.id !== user.id)) { diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index 61e18c09ab27..968c195a2abd 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -1,10 +1,10 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { Users } from '@/models/index.js'; -import { Followings, UserProfiles } from '@/models/index.js'; +import type { Users , Followings , UserProfiles } from '@/models/index.js'; import { toPunyNullable } from '@/misc/convert-host.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import { FollowingEntityService } from '@/services/entities/FollowingEntityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -74,9 +74,13 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + private followingEntityService: FollowingEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { @@ -88,7 +92,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchUser); } - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); if (profile.ffVisibility === 'private') { if (me == null || (me.id !== user.id)) { diff --git a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts index 824398b1bb59..763ee7036deb 100644 --- a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GalleryPosts } from '@/models/index.js'; +import type { GalleryPosts } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService'; export const meta = { tags: ['users', 'gallery'], @@ -34,17 +35,21 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('galleryPostsRepository') + private galleryPostsRepository: typeof GalleryPosts, + + private galleryPostEntityService: GalleryPostEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const query = this.queryService.makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) + const query = this.queryService.makePaginationQuery(this.galleryPostsRepository.createQueryBuilder('post'), ps.sinceId, ps.untilId) .andWhere('post.userId = :userId', { userId: ps.userId }); const posts = await query .take(ps.limit) .getMany(); - return await GalleryPosts.packMany(posts, me); + return await this.galleryPostEntityService.packMany(posts, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/groups/delete.ts b/packages/backend/src/server/api/endpoints/users/groups/delete.ts index 00e2639443bb..6ce2c8534edb 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/delete.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/delete.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserGroups } from '@/models/index.js'; +import type { UserGroups } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; @@ -33,9 +33,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('userGroupsRepository') + private userGroupsRepository: typeof UserGroups, ) { super(meta, paramDef, async (ps, me) => { - const userGroup = await UserGroups.findOneBy({ + const userGroup = await this.userGroupsRepository.findOneBy({ id: ps.groupId, userId: me.id, }); @@ -44,7 +46,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchGroup); } - await UserGroups.delete(userGroup.id); + await this.userGroupsRepository.delete(userGroup.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts b/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts index 7ba0e1b697c3..aff52446d692 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserGroupJoinings, UserGroupInvitations } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import type { UserGroupInvitations , UserGroupJoinings } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; import type { UserGroupJoining } from '@/models/entities/user-group-joining.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../../error.js'; @@ -35,11 +35,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('userGroupInvitationsRepository') + private userGroupInvitationsRepository: typeof UserGroupInvitations, + + @Inject('userGroupJoiningRepository') + private userGroupJoiningRepository: typeof UserGroupJoinings, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { // Fetch the invitation - const invitation = await UserGroupInvitations.findOneBy({ + const invitation = await this.userGroupInvitationsRepository.findOneBy({ id: ps.invitationId, }); @@ -52,14 +58,14 @@ export default class extends Endpoint { } // Push the user - await UserGroupJoinings.insert({ + await this.userGroupJoiningRepository.insert({ id: this.idService.genId(), createdAt: new Date(), userId: me.id, userGroupId: invitation.userGroupId, } as UserGroupJoining); - UserGroupInvitations.delete(invitation.id); + this.userGroupInvitationsRepository.delete(invitation.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts b/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts index dd714191a782..2c8e4b732564 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserGroupInvitations } from '@/models/index.js'; +import type { UserGroupInvitations } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../../error.js'; @@ -33,10 +33,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('userGroupInvitationsRepository') + private userGroupInvitationsRepository: typeof UserGroupInvitations, ) { super(meta, paramDef, async (ps, me) => { // Fetch the invitation - const invitation = await UserGroupInvitations.findOneBy({ + const invitation = await this.userGroupInvitationsRepository.findOneBy({ id: ps.invitationId, }); @@ -48,7 +50,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchInvitation); } - await UserGroupInvitations.delete(invitation.id); + await this.userGroupInvitationsRepository.delete(invitation.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/groups/invite.ts b/packages/backend/src/server/api/endpoints/users/groups/invite.ts index 663a1fbb04f9..63fa2bf26f70 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invite.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invite.ts @@ -1,10 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserGroups, UserGroupJoinings, UserGroupInvitations } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import type { UserGroups, UserGroupJoinings, UserGroupInvitations } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; import type { UserGroupInvitation } from '@/models/entities/user-group-invitation.js'; -import { createNotification } from '@/services/create-notification.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { getUser } from '../../../common/getters.js'; +import { GetterService } from '@/server/api/common/GetterService.js'; +import { CreateNotificationService } from '@/services/CreateNotificationService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -56,17 +56,22 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('userGroupsRepository') + private userGroupsRepository: typeof UserGroups, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('userGroupInvitationsRepository') + private userGroupInvitationsRepository: typeof UserGroupInvitations, + + @Inject('userGroupJoiningRepository') + private userGroupJoiningRepository: typeof UserGroupJoinings, private idService: IdService, + private getterService: GetterService, + private createNotificationService: CreateNotificationService, ) { super(meta, paramDef, async (ps, me) => { // Fetch the group - const userGroup = await UserGroups.findOneBy({ + const userGroup = await this.userGroupsRepository.findOneBy({ id: ps.groupId, userId: me.id, }); @@ -76,12 +81,12 @@ export default class extends Endpoint { } // Fetch the user - const user = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; + const user = await this.getterService.getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; }); - const joining = await UserGroupJoinings.findOneBy({ + const joining = await this.userGroupJoiningRepository.findOneBy({ userGroupId: userGroup.id, userId: user.id, }); @@ -90,7 +95,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.alreadyAdded); } - const existInvitation = await UserGroupInvitations.findOneBy({ + const existInvitation = await this.userGroupInvitationsRepository.findOneBy({ userGroupId: userGroup.id, userId: user.id, }); @@ -99,15 +104,15 @@ export default class extends Endpoint { throw new ApiError(meta.errors.alreadyInvited); } - const invitation = await UserGroupInvitations.insert({ + const invitation = await this.userGroupInvitationsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), userId: user.id, userGroupId: userGroup.id, - } as UserGroupInvitation).then(x => UserGroupInvitations.findOneByOrFail(x.identifiers[0])); + } as UserGroupInvitation).then(x => this.userGroupInvitationsRepository.findOneByOrFail(x.identifiers[0])); // 通知を作成 - createNotification(user.id, 'groupInvited', { + this.createNotificationService.createNotification(user.id, 'groupInvited', { notifierId: me.id, userGroupInvitationId: invitation.id, }); diff --git a/packages/backend/src/server/api/endpoints/users/groups/leave.ts b/packages/backend/src/server/api/endpoints/users/groups/leave.ts index 945e4c132fcf..9d2fa738a9b2 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/leave.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/leave.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserGroups, UserGroupJoinings } from '@/models/index.js'; +import type { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../error.js'; @@ -39,15 +39,15 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('userGroupsRepository') + private userGroupsRepository: typeof UserGroups, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('userGroupJoiningRepository') + private userGroupJoiningRepository: typeof UserGroupJoinings, ) { super(meta, paramDef, async (ps, me) => { // Fetch the group - const userGroup = await UserGroups.findOneBy({ + const userGroup = await this.userGroupsRepository.findOneBy({ id: ps.groupId, }); @@ -59,7 +59,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.youAreOwner); } - await UserGroupJoinings.delete({ userGroupId: userGroup.id, userId: me.id }); + await this.userGroupJoiningRepository.delete({ userGroupId: userGroup.id, userId: me.id }); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/groups/pull.ts b/packages/backend/src/server/api/endpoints/users/groups/pull.ts index 48e16c7d8613..9365fee7fe63 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/pull.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserGroups, UserGroupJoinings } from '@/models/index.js'; +import type { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { GetterService } from '@/server/api/common/GetterService.js'; import { ApiError } from '../../../error.js'; import { getUser } from '../../../common/getters.js'; @@ -47,15 +48,17 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, + @Inject('userGroupsRepository') + private userGroupsRepository: typeof UserGroups, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('userGroupJoiningRepository') + private userGroupJoiningRepository: typeof UserGroupJoinings, + + private getterService: GetterService, ) { super(meta, paramDef, async (ps, me) => { // Fetch the group - const userGroup = await UserGroups.findOneBy({ + const userGroup = await this.userGroupsRepository.findOneBy({ id: ps.groupId, userId: me.id, }); @@ -65,9 +68,9 @@ export default class extends Endpoint { } // Fetch the user - const user = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; + const user = await this.getterService.getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; }); if (user.id === userGroup.userId) { @@ -75,7 +78,7 @@ export default class extends Endpoint { } // Pull the user - await UserGroupJoinings.delete({ userGroupId: userGroup.id, userId: user.id }); + await this.userGroupJoiningRepository.delete({ userGroupId: userGroup.id, userId: user.id }); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 8917cbd8a27d..e987d8387cc0 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -3,8 +3,9 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { ApiError } from '../../error.js'; -import { getUser } from '../../common/getters.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['users', 'notes'], @@ -54,17 +55,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - @Inject('notesRepository') private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, private queryService: QueryService, + private getterService: GetterService, ) { super(meta, paramDef, async (ps, me) => { // Lookup user - const user = await getUser(ps.userId).catch(err => { + const user = await this.getterService.getUser(ps.userId).catch(err => { if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); throw err; }); diff --git a/packages/backend/src/server/api/endpoints/users/pages.ts b/packages/backend/src/server/api/endpoints/users/pages.ts index bfbe57a5aa95..ccc46e38e147 100644 --- a/packages/backend/src/server/api/endpoints/users/pages.ts +++ b/packages/backend/src/server/api/endpoints/users/pages.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Pages } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import { PageEntityService } from '@/services/entities/PageEntityService'; export const meta = { tags: ['users', 'pages'], @@ -34,6 +35,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private pageEntityService: PageEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { @@ -45,7 +47,7 @@ export default class extends Endpoint { .take(ps.limit) .getMany(); - return await Pages.packMany(pages); + return await this.pageEntityService.packMany(pages); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/recommendation.ts b/packages/backend/src/server/api/endpoints/users/recommendation.ts index cc8918ddeece..49d542440eaa 100644 --- a/packages/backend/src/server/api/endpoints/users/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/users/recommendation.ts @@ -1,9 +1,9 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import type { Users } from '@/models/index.js'; -import { Followings } from '@/models/index.js'; +import type { Users , Followings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import { UserEntityService } from '@/services/entities/UserEntityService'; export const meta = { tags: ['users'], @@ -41,9 +41,10 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + private userEntityService: UserEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/users/relation.ts b/packages/backend/src/server/api/endpoints/users/relation.ts index b506a3a56ae6..03adda6bbba8 100644 --- a/packages/backend/src/server/api/endpoints/users/relation.ts +++ b/packages/backend/src/server/api/endpoints/users/relation.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserEntityService } from '@/services/entities/UserEntityService'; export const meta = { tags: ['users'], @@ -119,13 +120,12 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private userEntityService: UserEntityService, ) { super(meta, paramDef, async (ps, me) => { const ids = Array.isArray(ps.userId) ? ps.userId : [ps.userId]; - const relations = await Promise.all(ids.map(id => this.usersRepository.getRelation(me.id, id))); + const relations = await Promise.all(ids.map(id => this.userEntityService.getRelation(me.id, id))); return Array.isArray(ps.userId) ? relations : relations[0]; }); diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts index 11399ff4153e..a5b017dced0b 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -1,14 +1,13 @@ import * as sanitizeHtml from 'sanitize-html'; import { Inject, Injectable } from '@nestjs/common'; -import { publishAdminStream } from '@/services/stream.js'; -import type { Users } from '@/models/index.js'; -import { AbuseUserReports } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; -import { sendEmail } from '@/services/send-email.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; +import type { Users , AbuseUserReports } from '@/models/index.js'; +import { IdService } from '@/services/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { getUser } from '../../common/getters.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { MetaService } from '@/services/MetaService.js'; +import { EmailService } from '@/services/EmailService.js'; import { ApiError } from '../../error.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['users'], @@ -54,16 +53,20 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('abuseUserReportsRepository') + private abuseUserReportsRepository: typeof AbuseUserReports, private idService: IdService, + private metaService: MetaService, + private emailService: EmailService, + private getterService: GetterService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { // Lookup user - const user = await getUser(ps.userId).catch(e => { - if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw e; + const user = await this.getterService.getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; }); if (user.id === me.id) { @@ -74,7 +77,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.cannotReportAdmin); } - const report = await AbuseUserReports.insert({ + const report = await this.abuseUserReportsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), targetUserId: user.id, @@ -82,7 +85,7 @@ export default class extends Endpoint { reporterId: me.id, reporterHost: null, comment: ps.comment, - }).then(x => AbuseUserReports.findOneByOrFail(x.identifiers[0])); + }).then(x => this.abuseUserReportsRepository.findOneByOrFail(x.identifiers[0])); // Publish event to moderators setImmediate(async () => { @@ -95,7 +98,7 @@ export default class extends Endpoint { }); for (const moderator of moderators) { - publishAdminStream(moderator.id, 'newAbuseUserReport', { + this.globalEventService.publishAdminStream(moderator.id, 'newAbuseUserReport', { id: report.id, targetUserId: report.targetUserId, reporterId: report.reporterId, @@ -103,9 +106,9 @@ export default class extends Endpoint { }); } - const meta = await fetchMeta(); + const meta = await this.metaService.fetch(); if (meta.email) { - sendEmail(meta.email, 'New abuse report', + this.emailService.sendEmail(meta.email, 'New abuse report', sanitizeHtml(ps.comment), sanitizeHtml(ps.comment)); } diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index 39da94d790aa..9f923d26978c 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -1,10 +1,10 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { Users } from '@/models/index.js'; -import { Followings } from '@/models/index.js'; +import type { Users , Followings } from '@/models/index.js'; import { USER_ACTIVE_THRESHOLD } from '@/const.js'; import type { User } from '@/models/entities/user.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserEntityService } from '@/services/entities/UserEntityService'; export const meta = { tags: ['users'], @@ -47,8 +47,10 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + private userEntityService: UserEntityService, ) { super(meta, paramDef, async (ps, me) => { const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日 diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index 32b1e94d292a..ee6cbc007747 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -4,6 +4,7 @@ import type { Users } from '@/models/index.js'; import { UserProfiles } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserEntityService } from '@/services/entities/UserEntityService'; export const meta = { tags: ['users'], @@ -42,8 +43,7 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private userEntityService: UserEntityService, ) { super(meta, paramDef, async (ps, me) => { const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日 @@ -78,7 +78,7 @@ export default class extends Endpoint { qb.where('user.name ILIKE :query', { query: '%' + ps.query + '%' }); // Also search username if it qualifies as username - if (this.usersRepository.validateLocalUsername(ps.query)) { + if (this.userEntityService.validateLocalUsername(ps.query)) { qb.orWhere('user.usernameLower LIKE :username', { username: '%' + ps.query.toLowerCase() + '%' }); } })) diff --git a/packages/backend/src/server/api/endpoints/users/stats.ts b/packages/backend/src/server/api/endpoints/users/stats.ts index 90c7de7e38b8..82d01354a102 100644 --- a/packages/backend/src/server/api/endpoints/users/stats.ts +++ b/packages/backend/src/server/api/endpoints/users/stats.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { Users , Notes } from '@/models/index.js'; -import { DriveFiles, Followings, NoteFavorites, NoteReactions, PageLikes, PollVotes } from '@/models/index.js'; +import type { Users , Notes , Followings , DriveFiles, NoteFavorites, NoteReactions, PageLikes, PollVotes } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -126,6 +126,26 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + @Inject('noteReactionsRepository') + private noteReactionsRepository: typeof NoteReactions, + + @Inject('pageLikesRepository') + private pageLikesRepository: typeof PageLikes, + + @Inject('noteFavoritesRepository') + private noteFavoritesRepository: typeof NoteFavorites, + + @Inject('pollVotesRepository') + private pollVotesRepository: typeof PollVotes, + + private driveFileEntityService: DriveFileEntityService, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); @@ -151,10 +171,10 @@ export default class extends Endpoint { renotedCount: this.notesRepository.createQueryBuilder('note') .where('note.renoteUserId = :userId', { userId: user.id }) .getCount(), - pollVotesCount: PollVotes.createQueryBuilder('vote') + pollVotesCount: this.pollVotesRepository.createQueryBuilder('vote') .where('vote.userId = :userId', { userId: user.id }) .getCount(), - pollVotedCount: PollVotes.createQueryBuilder('vote') + pollVotedCount: this.pollVotesRepository.createQueryBuilder('vote') .innerJoin('vote.note', 'note') .where('note.userId = :userId', { userId: user.id }) .getCount(), @@ -174,27 +194,27 @@ export default class extends Endpoint { .where('following.followeeId = :userId', { userId: user.id }) .andWhere('following.followerHost IS NOT NULL') .getCount(), - sentReactionsCount: NoteReactions.createQueryBuilder('reaction') + sentReactionsCount: this.noteReactionsRepository.createQueryBuilder('reaction') .where('reaction.userId = :userId', { userId: user.id }) .getCount(), - receivedReactionsCount: NoteReactions.createQueryBuilder('reaction') + receivedReactionsCount: this.noteReactionsRepository.createQueryBuilder('reaction') .innerJoin('reaction.note', 'note') .where('note.userId = :userId', { userId: user.id }) .getCount(), - noteFavoritesCount: NoteFavorites.createQueryBuilder('favorite') + noteFavoritesCount: this.noteFavoritesRepository.createQueryBuilder('favorite') .where('favorite.userId = :userId', { userId: user.id }) .getCount(), - pageLikesCount: PageLikes.createQueryBuilder('like') + pageLikesCount: this.pageLikesRepository.createQueryBuilder('like') .where('like.userId = :userId', { userId: user.id }) .getCount(), - pageLikedCount: PageLikes.createQueryBuilder('like') + pageLikedCount: this.pageLikesRepository.createQueryBuilder('like') .innerJoin('like.page', 'page') .where('page.userId = :userId', { userId: user.id }) .getCount(), - driveFilesCount: DriveFiles.createQueryBuilder('file') + driveFilesCount: this.driveFilesRepository.createQueryBuilder('file') .where('file.userId = :userId', { userId: user.id }) .getCount(), - driveUsage: DriveFiles.calcDriveUsageOf(user), + driveUsage: this.driveFileEntityService.calcDriveUsageOf(user), }); result.followingCount = result.localFollowingCount + result.remoteFollowingCount; From 55f393f816851a3e72a7fb856611c43109aaab66 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 00:23:42 +0900 Subject: [PATCH 124/180] wip --- packages/backend/src/app.module.ts | 4 ++-- packages/backend/src/server/api/endpoints.ts | 6 ------ .../server/api/endpoints/admin/delete-account.ts | 2 +- .../admin/delete-all-files-of-a-user.ts | 2 +- .../admin/federation/delete-all-files.ts | 2 +- .../server/api/endpoints/admin/queue/clear.ts | 2 +- .../src/server/api/endpoints/blocking/list.ts | 2 +- .../backend/src/server/api/endpoints/drive.ts | 4 ++-- .../src/server/api/endpoints/drive/files.ts | 2 +- .../api/endpoints/drive/files/find-by-hash.ts | 2 +- .../server/api/endpoints/drive/folders/find.ts | 2 +- .../src/server/api/endpoints/drive/stream.ts | 2 +- .../backend/src/server/api/endpoints/endpoint.ts | 1 - .../src/server/api/endpoints/endpoints.ts | 5 ----- .../server/api/endpoints/export-custom-emojis.ts | 6 +++--- .../server/api/endpoints/federation/followers.ts | 2 +- .../server/api/endpoints/federation/following.ts | 2 +- .../server/api/endpoints/federation/instances.ts | 4 ++-- .../api/endpoints/federation/show-instance.ts | 2 +- .../src/server/api/endpoints/federation/stats.ts | 2 +- .../endpoints/federation/update-remote-user.ts | 4 ++-- .../src/server/api/endpoints/federation/users.ts | 2 +- .../src/server/api/endpoints/fetch-rss.ts | 11 ++++++++--- .../api/endpoints/following/requests/list.ts | 2 +- .../src/server/api/endpoints/gallery/featured.ts | 2 +- .../src/server/api/endpoints/gallery/popular.ts | 2 +- .../src/server/api/endpoints/gallery/posts.ts | 2 +- .../api/endpoints/get-online-users-count.ts | 5 +---- .../src/server/api/endpoints/hashtags/list.ts | 2 +- .../src/server/api/endpoints/hashtags/trend.ts | 2 +- .../src/server/api/endpoints/hashtags/users.ts | 2 +- packages/backend/src/server/api/endpoints/i.ts | 6 +++--- .../src/server/api/endpoints/i/2fa/key-done.ts | 4 ++-- .../src/server/api/endpoints/i/2fa/remove-key.ts | 4 ++-- .../server/api/endpoints/i/authorized-apps.ts | 2 +- .../src/server/api/endpoints/i/delete-account.ts | 2 +- .../src/server/api/endpoints/i/favorites.ts | 2 +- .../src/server/api/endpoints/i/gallery/likes.ts | 2 +- .../src/server/api/endpoints/i/gallery/posts.ts | 2 +- .../src/server/api/endpoints/i/page-likes.ts | 2 +- .../backend/src/server/api/endpoints/i/pages.ts | 2 +- .../endpoints/i/read-all-messaging-messages.ts | 2 +- .../api/endpoints/i/read-all-unread-notes.ts | 2 +- .../server/api/endpoints/i/regenerate-token.ts | 2 +- .../src/server/api/endpoints/i/registry/set.ts | 2 +- .../src/server/api/endpoints/i/revoke-token.ts | 2 +- .../src/server/api/endpoints/i/signin-history.ts | 2 +- .../server/api/endpoints/i/user-group-invites.ts | 2 +- .../server/api/endpoints/i/webhooks/create.ts | 2 +- .../server/api/endpoints/messaging/history.ts | 2 +- .../src/server/api/endpoints/mute/list.ts | 2 +- .../backend/src/server/api/endpoints/my/apps.ts | 2 +- .../backend/src/server/api/endpoints/notes.ts | 2 ++ .../src/server/api/endpoints/notes/children.ts | 2 +- .../src/server/api/endpoints/notes/featured.ts | 2 +- .../src/server/api/endpoints/notes/mentions.ts | 6 +++--- .../api/endpoints/notes/polls/recommendation.ts | 2 +- .../src/server/api/endpoints/notes/replies.ts | 2 +- .../server/api/endpoints/notes/search-by-tag.ts | 2 +- .../src/server/api/endpoints/notes/timeline.ts | 4 ++-- .../server/api/endpoints/notifications/create.ts | 2 +- .../endpoints/notifications/mark-all-as-read.ts | 4 ++-- .../server/api/endpoints/notifications/read.ts | 2 +- .../src/server/api/endpoints/pages/featured.ts | 2 +- .../backend/src/server/api/endpoints/ping.ts | 5 ----- .../src/server/api/endpoints/pinned-users.ts | 11 ++++++----- .../api/endpoints/request-reset-password.ts | 16 ++++++++++------ .../src/server/api/endpoints/reset-password.ts | 14 +++++++++----- .../src/server/api/endpoints/server-info.ts | 5 ----- .../backend/src/server/api/endpoints/stats.ts | 3 +-- .../src/server/api/endpoints/sw/register.ts | 2 +- .../backend/src/server/api/endpoints/test.ts | 5 ----- .../backend/src/server/api/endpoints/users.ts | 5 ++--- .../src/server/api/endpoints/users/clips.ts | 2 +- .../server/api/endpoints/users/gallery/posts.ts | 2 +- .../server/api/endpoints/users/groups/create.ts | 2 +- .../server/api/endpoints/users/groups/joined.ts | 2 +- .../server/api/endpoints/users/groups/owned.ts | 2 +- .../server/api/endpoints/users/lists/create.ts | 2 +- .../src/server/api/endpoints/users/lists/list.ts | 2 +- .../src/server/api/endpoints/users/pages.ts | 2 +- .../server/api/endpoints/users/recommendation.ts | 2 +- .../src/server/api/endpoints/users/relation.ts | 2 +- .../users/search-by-username-and-host.ts | 2 +- .../src/server/api/endpoints/users/search.ts | 2 +- .../src/services/CreateNotificationService.ts | 2 +- packages/backend/src/services/DriveService.ts | 4 ++-- .../backend/src/services/MessagingService.ts | 14 +++++++------- .../backend/src/services/NotificationService.ts | 6 +++--- .../src/services/PushNotificationService.ts | 2 +- .../backend/src/services/UserBlockingService.ts | 6 +++--- packages/backend/src/services/UserListService.ts | 2 +- .../activitypub/ApDeliverManagerService.ts | 4 ++-- 93 files changed, 144 insertions(+), 160 deletions(-) diff --git a/packages/backend/src/app.module.ts b/packages/backend/src/app.module.ts index cf88e9721301..ccad3a186c5b 100644 --- a/packages/backend/src/app.module.ts +++ b/packages/backend/src/app.module.ts @@ -3,8 +3,8 @@ import { Notes, Users } from '@/models/index.js'; import { EndpointsModule } from '@/server/api/endpoints.module.js'; import { QueueModule } from '@/queue/queue.module.js'; import { ChartsModule } from './services/chart/charts.module'; -import { DI_SYMBOLS } from './di-symbols'; -import { loadConfig } from './config'; +import { DI_SYMBOLS } from './di-symbols.js'; +import { loadConfig } from './config.js'; import { db } from './db/postgre'; @Module({ diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index cd6eaab0f839..a05bb5a7e02a 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -59,7 +59,6 @@ import * as ep___admin_suspendUser from './endpoints/admin/suspend-user.js'; import * as ep___admin_unsilenceUser from './endpoints/admin/unsilence-user.js'; import * as ep___admin_unsuspendUser from './endpoints/admin/unsuspend-user.js'; import * as ep___admin_updateMeta from './endpoints/admin/update-meta.js'; -import * as ep___admin_vacuum from './endpoints/admin/vacuum.js'; import * as ep___admin_deleteAccount from './endpoints/admin/delete-account.js'; import * as ep___admin_updateUserNote from './endpoints/admin/update-user-note.js'; import * as ep___announcements from './endpoints/announcements.js'; @@ -253,8 +252,6 @@ import * as ep___notes_timeline from './endpoints/notes/timeline.js'; import * as ep___notes_translate from './endpoints/notes/translate.js'; import * as ep___notes_unrenote from './endpoints/notes/unrenote.js'; import * as ep___notes_userListTimeline from './endpoints/notes/user-list-timeline.js'; -import * as ep___notes_watching_create from './endpoints/notes/watching/create.js'; -import * as ep___notes_watching_delete from './endpoints/notes/watching/delete.js'; import * as ep___notifications_create from './endpoints/notifications/create.js'; import * as ep___notifications_markAllAsRead from './endpoints/notifications/mark-all-as-read.js'; import * as ep___notifications_read from './endpoints/notifications/read.js'; @@ -376,7 +373,6 @@ const eps = [ ['admin/unsilence-user', ep___admin_unsilenceUser], ['admin/unsuspend-user', ep___admin_unsuspendUser], ['admin/update-meta', ep___admin_updateMeta], - ['admin/vacuum', ep___admin_vacuum], ['admin/delete-account', ep___admin_deleteAccount], ['admin/update-user-note', ep___admin_updateUserNote], ['announcements', ep___announcements], @@ -570,8 +566,6 @@ const eps = [ ['notes/translate', ep___notes_translate], ['notes/unrenote', ep___notes_unrenote], ['notes/user-list-timeline', ep___notes_userListTimeline], - ['notes/watching/create', ep___notes_watching_create], - ['notes/watching/delete', ep___notes_watching_delete], ['notifications/create', ep___notifications_create], ['notifications/mark-all-as-read', ep___notifications_markAllAsRead], ['notifications/read', ep___notifications_read], diff --git a/packages/backend/src/server/api/endpoints/admin/delete-account.ts b/packages/backend/src/server/api/endpoints/admin/delete-account.ts index 3d6777d44dd7..fadf0025f079 100644 --- a/packages/backend/src/server/api/endpoints/admin/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/admin/delete-account.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DeleteAccountService } from '@/services/DeleteAccountService'; +import { DeleteAccountService } from '@/services/DeleteAccountService.js'; export const meta = { tags: ['admin'], diff --git a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts index 16cfcde75349..1d72af85c673 100644 --- a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFiles } from '@/models/index.js'; -import { DriveService } from '@/services/DriveService'; +import { DriveService } from '@/services/DriveService.js'; export const meta = { tags: ['admin'], diff --git a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts index 4939144ce437..e5cc46ed69a6 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFiles } from '@/models/index.js'; -import { DriveService } from '@/services/DriveService'; +import { DriveService } from '@/services/DriveService.js'; export const meta = { tags: ['admin'], diff --git a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts index ceaa4c273704..b8de046f0958 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ModerationLogService } from '@/services/ModerationLogService.js'; -import { QueueService } from '@/queue/queue.service'; +import { QueueService } from '@/queue/queue.service.js'; export const meta = { tags: ['admin'], diff --git a/packages/backend/src/server/api/endpoints/blocking/list.ts b/packages/backend/src/server/api/endpoints/blocking/list.ts index c71a130853f5..a09d2d9d7a1a 100644 --- a/packages/backend/src/server/api/endpoints/blocking/list.ts +++ b/packages/backend/src/server/api/endpoints/blocking/list.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Blockings } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; -import { BlockingEntityService } from '@/services/entities/BlockingEntityService'; +import { BlockingEntityService } from '@/services/entities/BlockingEntityService.js'; export const meta = { tags: ['account'], diff --git a/packages/backend/src/server/api/endpoints/drive.ts b/packages/backend/src/server/api/endpoints/drive.ts index bafc79490b77..0e43bf462e18 100644 --- a/packages/backend/src/server/api/endpoints/drive.ts +++ b/packages/backend/src/server/api/endpoints/drive.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { DriveFiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { MetaService } from '@/services/MetaService'; -import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService'; +import { MetaService } from '@/services/MetaService.js'; +import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; export const meta = { tags: ['drive', 'account'], diff --git a/packages/backend/src/server/api/endpoints/drive/files.ts b/packages/backend/src/server/api/endpoints/drive/files.ts index a48a55b817c3..11e4a7f3359a 100644 --- a/packages/backend/src/server/api/endpoints/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/drive/files.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFiles } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; -import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService'; +import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; export const meta = { tags: ['drive'], diff --git a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts index 0a379bce78ff..86ef4372eb6c 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { DriveFiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService'; +import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; export const meta = { tags: ['drive'], diff --git a/packages/backend/src/server/api/endpoints/drive/folders/find.ts b/packages/backend/src/server/api/endpoints/drive/folders/find.ts index cd21c940d464..6737f835e7cb 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/find.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/find.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFolders } from '@/models/index.js'; -import { DriveFolderEntityService } from '@/services/entities/DriveFolderEntityService'; +import { DriveFolderEntityService } from '@/services/entities/DriveFolderEntityService.js'; export const meta = { tags: ['drive'], diff --git a/packages/backend/src/server/api/endpoints/drive/stream.ts b/packages/backend/src/server/api/endpoints/drive/stream.ts index 40fd49c498e4..673cf1d4699b 100644 --- a/packages/backend/src/server/api/endpoints/drive/stream.ts +++ b/packages/backend/src/server/api/endpoints/drive/stream.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFiles } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; -import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService'; +import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; export const meta = { tags: ['drive'], diff --git a/packages/backend/src/server/api/endpoints/endpoint.ts b/packages/backend/src/server/api/endpoints/endpoint.ts index d12856cc6c25..2141dfbeb00b 100644 --- a/packages/backend/src/server/api/endpoints/endpoint.ts +++ b/packages/backend/src/server/api/endpoints/endpoint.ts @@ -20,7 +20,6 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - ) { super(meta, paramDef, async (ps) => { const ep = endpoints.find(x => x.name === ps.endpoint); diff --git a/packages/backend/src/server/api/endpoints/endpoints.ts b/packages/backend/src/server/api/endpoints/endpoints.ts index 7dbfb7262a70..91fc3ec98d3f 100644 --- a/packages/backend/src/server/api/endpoints/endpoints.ts +++ b/packages/backend/src/server/api/endpoints/endpoints.ts @@ -33,11 +33,6 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { super(meta, paramDef, async () => { return endpoints.map(x => x.name); diff --git a/packages/backend/src/server/api/endpoints/export-custom-emojis.ts b/packages/backend/src/server/api/endpoints/export-custom-emojis.ts index a33d1576b5fe..eb7c038c3341 100644 --- a/packages/backend/src/server/api/endpoints/export-custom-emojis.ts +++ b/packages/backend/src/server/api/endpoints/export-custom-emojis.ts @@ -1,7 +1,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import { createExportCustomEmojisJob } from '@/queue/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { QueueService } from '@/queue/queue.service.js'; export const meta = { secure: true, @@ -22,10 +22,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - + private queueService: QueueService, ) { super(meta, paramDef, async (ps, me) => { - createExportCustomEmojisJob(me); + this.queueService.createExportCustomEmojisJob(me); }); } } diff --git a/packages/backend/src/server/api/endpoints/federation/followers.ts b/packages/backend/src/server/api/endpoints/federation/followers.ts index 90a4143f0706..10c81a0c2a8b 100644 --- a/packages/backend/src/server/api/endpoints/federation/followers.ts +++ b/packages/backend/src/server/api/endpoints/federation/followers.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Followings } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; -import { FollowingEntityService } from '@/services/entities/FollowingEntityService'; +import { FollowingEntityService } from '@/services/entities/FollowingEntityService.js'; export const meta = { tags: ['federation'], diff --git a/packages/backend/src/server/api/endpoints/federation/following.ts b/packages/backend/src/server/api/endpoints/federation/following.ts index 1fa2c90fe7e6..6f29dc65f4fe 100644 --- a/packages/backend/src/server/api/endpoints/federation/following.ts +++ b/packages/backend/src/server/api/endpoints/federation/following.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Followings } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; -import { FollowingEntityService } from '@/services/entities/FollowingEntityService'; +import { FollowingEntityService } from '@/services/entities/FollowingEntityService.js'; export const meta = { tags: ['federation'], diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts index 46c751447ff4..a109e6210ff4 100644 --- a/packages/backend/src/server/api/endpoints/federation/instances.ts +++ b/packages/backend/src/server/api/endpoints/federation/instances.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Instances } from '@/models/index.js'; -import { InstanceEntityService } from '@/services/entities/InstanceEntityService'; -import { MetaService } from '@/services/MetaService'; +import { InstanceEntityService } from '@/services/entities/InstanceEntityService.js'; +import { MetaService } from '@/services/MetaService.js'; export const meta = { tags: ['federation'], diff --git a/packages/backend/src/server/api/endpoints/federation/show-instance.ts b/packages/backend/src/server/api/endpoints/federation/show-instance.ts index 9cdde7d0da5c..2a4099da3e18 100644 --- a/packages/backend/src/server/api/endpoints/federation/show-instance.ts +++ b/packages/backend/src/server/api/endpoints/federation/show-instance.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Instances } from '@/models/index.js'; import { toPuny } from '@/misc/convert-host.js'; -import { InstanceEntityService } from '@/services/entities/InstanceEntityService'; +import { InstanceEntityService } from '@/services/entities/InstanceEntityService.js'; export const meta = { tags: ['federation'], diff --git a/packages/backend/src/server/api/endpoints/federation/stats.ts b/packages/backend/src/server/api/endpoints/federation/stats.ts index 971e949a5baf..437e244d6706 100644 --- a/packages/backend/src/server/api/endpoints/federation/stats.ts +++ b/packages/backend/src/server/api/endpoints/federation/stats.ts @@ -3,7 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Followings, Instances } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { InstanceEntityService } from '@/services/entities/InstanceEntityService'; +import { InstanceEntityService } from '@/services/entities/InstanceEntityService.js'; export const meta = { tags: ['federation'], diff --git a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts index 3b4400a35596..078399094d37 100644 --- a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts +++ b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ApPersonService } from '@/services/remote/activitypub/models/ApPersonService'; -import { GetterService } from '../../common/GetterService'; +import { ApPersonService } from '@/services/remote/activitypub/models/ApPersonService.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['federation'], diff --git a/packages/backend/src/server/api/endpoints/federation/users.ts b/packages/backend/src/server/api/endpoints/federation/users.ts index 0ba1dc9f899a..4979008108ff 100644 --- a/packages/backend/src/server/api/endpoints/federation/users.ts +++ b/packages/backend/src/server/api/endpoints/federation/users.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; -import { UserEntityService } from '@/services/entities/UserEntityService'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; export const meta = { tags: ['federation'], diff --git a/packages/backend/src/server/api/endpoints/fetch-rss.ts b/packages/backend/src/server/api/endpoints/fetch-rss.ts index cefbb36bb6aa..65c2f597b941 100644 --- a/packages/backend/src/server/api/endpoints/fetch-rss.ts +++ b/packages/backend/src/server/api/endpoints/fetch-rss.ts @@ -1,8 +1,9 @@ import Parser from 'rss-parser'; import { Inject, Injectable } from '@nestjs/common'; -import { getResponse } from '@/misc/fetch.js'; -import config from '@/config/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { Config } from '@/config.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { HttpRequestService } from '@/services/HttpRequestService.js'; const rssParser = new Parser(); @@ -26,9 +27,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + private httpRequestService: HttpRequestService, ) { super(meta, paramDef, async (ps, me) => { - const res = await getResponse({ + const res = await this.httpRequestService.getResponse({ url: ps.url, method: 'GET', headers: Object.assign({ diff --git a/packages/backend/src/server/api/endpoints/following/requests/list.ts b/packages/backend/src/server/api/endpoints/following/requests/list.ts index 195c6da63131..644dcd3e1d70 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/list.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/list.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { FollowRequests } from '@/models/index.js'; -import { FollowRequestEntityService } from '@/services/entities/FollowRequestEntityService'; +import { FollowRequestEntityService } from '@/services/entities/FollowRequestEntityService.js'; export const meta = { tags: ['following', 'account'], diff --git a/packages/backend/src/server/api/endpoints/gallery/featured.ts b/packages/backend/src/server/api/endpoints/gallery/featured.ts index be7def4b0c0b..435c6ea52c07 100644 --- a/packages/backend/src/server/api/endpoints/gallery/featured.ts +++ b/packages/backend/src/server/api/endpoints/gallery/featured.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { GalleryPosts } from '@/models/index.js'; -import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService'; +import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService.js'; export const meta = { tags: ['gallery'], diff --git a/packages/backend/src/server/api/endpoints/gallery/popular.ts b/packages/backend/src/server/api/endpoints/gallery/popular.ts index 648ccc50788e..b1345c00b207 100644 --- a/packages/backend/src/server/api/endpoints/gallery/popular.ts +++ b/packages/backend/src/server/api/endpoints/gallery/popular.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { GalleryPosts } from '@/models/index.js'; -import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService'; +import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService.js'; export const meta = { tags: ['gallery'], diff --git a/packages/backend/src/server/api/endpoints/gallery/posts.ts b/packages/backend/src/server/api/endpoints/gallery/posts.ts index 5d67acc7ab3d..bbd5b83625ef 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { GalleryPosts } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; -import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService'; +import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService.js'; export const meta = { tags: ['gallery'], diff --git a/packages/backend/src/server/api/endpoints/get-online-users-count.ts b/packages/backend/src/server/api/endpoints/get-online-users-count.ts index 36336ca6e910..dde957c71b2c 100644 --- a/packages/backend/src/server/api/endpoints/get-online-users-count.ts +++ b/packages/backend/src/server/api/endpoints/get-online-users-count.ts @@ -1,7 +1,7 @@ import { MoreThan } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { USER_ONLINE_THRESHOLD } from '@/const.js'; -import { Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -22,9 +22,6 @@ export default class extends Endpoint { constructor( @Inject('usersRepository') private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { super(meta, paramDef, async () => { const count = await this.usersRepository.countBy({ diff --git a/packages/backend/src/server/api/endpoints/hashtags/list.ts b/packages/backend/src/server/api/endpoints/hashtags/list.ts index 7587fb1d5b47..12b3ee1c358e 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/list.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/list.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Hashtags } from '@/models/index.js'; -import { HashtagEntityService } from '@/services/entities/HashtagEntityService'; +import { HashtagEntityService } from '@/services/entities/HashtagEntityService.js'; export const meta = { tags: ['hashtags'], diff --git a/packages/backend/src/server/api/endpoints/hashtags/trend.ts b/packages/backend/src/server/api/endpoints/hashtags/trend.ts index 4424f0a09be2..e26b815a8fa1 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/trend.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/trend.ts @@ -5,7 +5,7 @@ import type { Notes } from '@/models/index.js'; import type { Note } from '@/models/entities/note.js'; import { safeForSql } from '@/misc/safe-for-sql.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; -import { MetaService } from '@/services/MetaService'; +import { MetaService } from '@/services/MetaService.js'; /* トレンドに載るためには「『直近a分間のユニーク投稿数が今からa分前~今からb分前の間のユニーク投稿数のn倍以上』のハッシュタグの上位5位以内に入る」ことが必要 diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts index 925009c16f63..f4aa093620b1 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/users.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; -import { UserEntityService } from '@/services/entities/UserEntityService'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; export const meta = { requireCredential: false, diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index b6bfbd13c460..ece030e9f173 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; export const meta = { tags: ['account'], @@ -27,8 +28,7 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private userEntityService: UserEntityService, ) { super(meta, paramDef, async (ps, user, token) => { const isSecure = token == null; diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts index 650a90592dd0..6776ff76ebc5 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts @@ -13,8 +13,8 @@ import { import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { Config } from '@/config.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import { GlobalEventService } from '@/services/GlobalEventService'; -import { TwoFactorAuthenticationService } from '@/services/TwoFactorAuthenticationService'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { TwoFactorAuthenticationService } from '@/services/TwoFactorAuthenticationService.js'; const cborDecodeFirst = promisify(cbor.decodeFirst) as any; diff --git a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts index 1e8487bd2ccb..8ad1e0cd96cd 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts @@ -3,8 +3,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users , UserProfiles , UserSecurityKeys } from '@/models/index.js'; import { publishMainStream } from '@/services/stream.js'; -import { UserEntityService } from '@/services/entities/UserEntityService'; -import { GlobalEventService } from '@/services/GlobalEventService'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; export const meta = { requireCredential: true, diff --git a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts index 1a187158d857..f5579d741d55 100644 --- a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts +++ b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { AccessTokens } from '@/models/index.js'; import { Apps } from '@/models/index.js'; -import { AppEntityService } from '@/services/entities/AppEntityService'; +import { AppEntityService } from '@/services/entities/AppEntityService.js'; export const meta = { requireCredential: true, diff --git a/packages/backend/src/server/api/endpoints/i/delete-account.ts b/packages/backend/src/server/api/endpoints/i/delete-account.ts index acdcca469ba3..4b555376ba2c 100644 --- a/packages/backend/src/server/api/endpoints/i/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts @@ -2,7 +2,7 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import type { Users , UserProfiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DeleteAccountService } from '@/services/DeleteAccountService'; +import { DeleteAccountService } from '@/services/DeleteAccountService.js'; export const meta = { requireCredential: true, diff --git a/packages/backend/src/server/api/endpoints/i/favorites.ts b/packages/backend/src/server/api/endpoints/i/favorites.ts index 3c4c1e9feb79..30fb5b1bc2ba 100644 --- a/packages/backend/src/server/api/endpoints/i/favorites.ts +++ b/packages/backend/src/server/api/endpoints/i/favorites.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { NoteFavorites } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; -import { NoteFavoriteEntityService } from '@/services/entities/NoteFavoriteEntityService'; +import { NoteFavoriteEntityService } from '@/services/entities/NoteFavoriteEntityService.js'; export const meta = { tags: ['account', 'notes', 'favorites'], diff --git a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts index a017ace18c28..e905e5449282 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { GalleryLikes } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; -import { GalleryLikeEntityService } from '@/services/entities/GalleryLikeEntityService'; +import { GalleryLikeEntityService } from '@/services/entities/GalleryLikeEntityService.js'; export const meta = { tags: ['account', 'gallery'], diff --git a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts index 8f3320be8415..d4eb46a9f31d 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { GalleryPosts } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; -import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService'; +import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService.js'; export const meta = { tags: ['account', 'gallery'], diff --git a/packages/backend/src/server/api/endpoints/i/page-likes.ts b/packages/backend/src/server/api/endpoints/i/page-likes.ts index 771c9b74cc4f..03a55e9723f9 100644 --- a/packages/backend/src/server/api/endpoints/i/page-likes.ts +++ b/packages/backend/src/server/api/endpoints/i/page-likes.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { PageLikes } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; -import { PageLikeEntityService } from '@/services/entities/PageLikeEntityService'; +import { PageLikeEntityService } from '@/services/entities/PageLikeEntityService.js'; export const meta = { tags: ['account', 'pages'], diff --git a/packages/backend/src/server/api/endpoints/i/pages.ts b/packages/backend/src/server/api/endpoints/i/pages.ts index e23d9fcfac27..13761b2f8553 100644 --- a/packages/backend/src/server/api/endpoints/i/pages.ts +++ b/packages/backend/src/server/api/endpoints/i/pages.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Pages } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; -import { PageEntityService } from '@/services/entities/PageEntityService'; +import { PageEntityService } from '@/services/entities/PageEntityService.js'; export const meta = { tags: ['account', 'pages'], diff --git a/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts b/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts index b8b4fe1db8c4..fa2b9c4f789e 100644 --- a/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts +++ b/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { MessagingMessages, UserGroupJoinings } from '@/models/index.js'; -import { GlobalEventService } from '@/services/GlobalEventService'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; export const meta = { tags: ['account', 'messaging'], diff --git a/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts b/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts index 877f8987c128..b5b0c750af4f 100644 --- a/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts +++ b/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { NoteUnreads } from '@/models/index.js'; -import { GlobalEventService } from '@/services/GlobalEventService'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; export const meta = { tags: ['account'], diff --git a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts index a68a12b4aec5..ff7f6599f51c 100644 --- a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts +++ b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts @@ -3,7 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users , UserProfiles } from '@/models/index.js'; import generateUserToken from '@/misc/generate-native-user-token.js'; -import { GlobalEventService } from '@/services/GlobalEventService'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; export const meta = { requireCredential: true, diff --git a/packages/backend/src/server/api/endpoints/i/registry/set.ts b/packages/backend/src/server/api/endpoints/i/registry/set.ts index ec7cfa39d316..a151c90a8407 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/set.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/set.ts @@ -3,7 +3,7 @@ import { publishMainStream } from '@/services/stream.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { RegistryItems } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; -import { GlobalEventService } from '@/services/GlobalEventService'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; export const meta = { requireCredential: true, diff --git a/packages/backend/src/server/api/endpoints/i/revoke-token.ts b/packages/backend/src/server/api/endpoints/i/revoke-token.ts index 83986cdf188c..5ffade6ad60f 100644 --- a/packages/backend/src/server/api/endpoints/i/revoke-token.ts +++ b/packages/backend/src/server/api/endpoints/i/revoke-token.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { AccessTokens } from '@/models/index.js'; -import { GlobalEventService } from '@/services/GlobalEventService'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; export const meta = { requireCredential: true, diff --git a/packages/backend/src/server/api/endpoints/i/signin-history.ts b/packages/backend/src/server/api/endpoints/i/signin-history.ts index aa9b85dcb36f..3ddc5e848a7e 100644 --- a/packages/backend/src/server/api/endpoints/i/signin-history.ts +++ b/packages/backend/src/server/api/endpoints/i/signin-history.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Signins } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; -import { SigninEntityService } from '@/services/entities/SigninEntityService'; +import { SigninEntityService } from '@/services/entities/SigninEntityService.js'; export const meta = { requireCredential: true, diff --git a/packages/backend/src/server/api/endpoints/i/user-group-invites.ts b/packages/backend/src/server/api/endpoints/i/user-group-invites.ts index 3a4ec52e86f5..b9577987ab3d 100644 --- a/packages/backend/src/server/api/endpoints/i/user-group-invites.ts +++ b/packages/backend/src/server/api/endpoints/i/user-group-invites.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UserGroupInvitations } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; -import { UserGroupInvitationEntityService } from '@/services/entities/UserGroupInvitationEntityService'; +import { UserGroupInvitationEntityService } from '@/services/entities/UserGroupInvitationEntityService.js'; export const meta = { tags: ['account', 'groups'], diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts index 71ae7c20ad71..012605d5a088 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts @@ -3,7 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/services/IdService.js'; import type { Webhooks } from '@/models/index.js'; import { webhookEventTypes } from '@/models/entities/webhook.js'; -import { GlobalEventService } from '@/services/GlobalEventService'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; export const meta = { tags: ['webhooks'], diff --git a/packages/backend/src/server/api/endpoints/messaging/history.ts b/packages/backend/src/server/api/endpoints/messaging/history.ts index 148c9f790d3c..6e2f4f4f9ef0 100644 --- a/packages/backend/src/server/api/endpoints/messaging/history.ts +++ b/packages/backend/src/server/api/endpoints/messaging/history.ts @@ -3,7 +3,7 @@ import { Brackets } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { MessagingMessage } from '@/models/entities/messaging-message.js'; import type { Mutings , UserGroupJoinings , MessagingMessages } from '@/models/index.js'; -import { MessagingMessageEntityService } from '@/services/entities/MessagingMessageEntityService'; +import { MessagingMessageEntityService } from '@/services/entities/MessagingMessageEntityService.js'; export const meta = { tags: ['messaging'], diff --git a/packages/backend/src/server/api/endpoints/mute/list.ts b/packages/backend/src/server/api/endpoints/mute/list.ts index a3b52dbbcdbd..73401365eeb2 100644 --- a/packages/backend/src/server/api/endpoints/mute/list.ts +++ b/packages/backend/src/server/api/endpoints/mute/list.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Mutings } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; -import { MutingEntityService } from '@/services/entities/MutingEntityService'; +import { MutingEntityService } from '@/services/entities/MutingEntityService.js'; export const meta = { tags: ['account'], diff --git a/packages/backend/src/server/api/endpoints/my/apps.ts b/packages/backend/src/server/api/endpoints/my/apps.ts index ccfdbfdc6c78..5aae9cb909fc 100644 --- a/packages/backend/src/server/api/endpoints/my/apps.ts +++ b/packages/backend/src/server/api/endpoints/my/apps.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Apps } from '@/models/index.js'; -import { AppEntityService } from '@/services/entities/AppEntityService'; +import { AppEntityService } from '@/services/entities/AppEntityService.js'; export const meta = { tags: ['account', 'app'], diff --git a/packages/backend/src/server/api/endpoints/notes.ts b/packages/backend/src/server/api/endpoints/notes.ts index 844b20cd1273..1a5fa4f2505d 100644 --- a/packages/backend/src/server/api/endpoints/notes.ts +++ b/packages/backend/src/server/api/endpoints/notes.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; export const meta = { tags: ['notes'], @@ -39,6 +40,7 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + private noteEntityService: NoteEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index 75e126b802c3..275a7fb89e27 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -3,7 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; -import { NoteEntityService } from '@/services/entities/NoteEntityService'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; export const meta = { tags: ['notes'], diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index 5fc5525d88a7..313e6ddabd61 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; -import { NoteEntityService } from '@/services/entities/NoteEntityService'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; export const meta = { tags: ['notes'], diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index 0666e13dc3c6..615e400577b7 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -3,9 +3,9 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notes , Followings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; -import { NoteEntityService } from '@/services/entities/NoteEntityService'; -import { MetaService } from '@/services/MetaService'; -import { NoteReadService } from '@/services/NoteReadService'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { MetaService } from '@/services/MetaService.js'; +import { NoteReadService } from '@/services/NoteReadService.js'; export const meta = { tags: ['notes'], diff --git a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts index a8b31cdf689a..189bb8a22b40 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts @@ -2,7 +2,7 @@ import { Brackets, In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { Notes , Mutings , Polls, PollVotes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { NoteEntityService } from '@/services/entities/NoteEntityService'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; export const meta = { tags: ['notes'], diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index 622b255dba4b..9703c6b79d9e 100644 --- a/packages/backend/src/server/api/endpoints/notes/replies.ts +++ b/packages/backend/src/server/api/endpoints/notes/replies.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; -import { NoteEntityService } from '@/services/entities/NoteEntityService'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; export const meta = { tags: ['notes'], diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts index 821c73e6ffb6..d59d5f38cf8a 100644 --- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts +++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts @@ -5,7 +5,7 @@ import { safeForSql } from '@/misc/safe-for-sql.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; -import { NoteEntityService } from '@/services/entities/NoteEntityService'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; export const meta = { tags: ['notes', 'hashtags'], diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 4c58026f45e4..417b58e3b2b0 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -4,8 +4,8 @@ import type { Notes , Followings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import ActiveUsersChart from '@/services/chart/charts/active-users'; -import { NoteEntityService } from '@/services/entities/NoteEntityService'; -import { MetaService } from '@/services/MetaService'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { MetaService } from '@/services/MetaService.js'; export const meta = { tags: ['notes'], diff --git a/packages/backend/src/server/api/endpoints/notifications/create.ts b/packages/backend/src/server/api/endpoints/notifications/create.ts index eaa8b282ef42..55a9ac062cc6 100644 --- a/packages/backend/src/server/api/endpoints/notifications/create.ts +++ b/packages/backend/src/server/api/endpoints/notifications/create.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { CreateNotificationService } from '@/services/CreateNotificationService'; +import { CreateNotificationService } from '@/services/CreateNotificationService.js'; export const meta = { tags: ['notifications'], diff --git a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts index 3df6368749bd..2f0aeb95ee55 100644 --- a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notifications } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GlobalEventService } from '@/services/GlobalEventService'; -import { PushNotificationService } from '@/services/PushNotificationService'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { PushNotificationService } from '@/services/PushNotificationService.js'; export const meta = { tags: ['notifications', 'account'], diff --git a/packages/backend/src/server/api/endpoints/notifications/read.ts b/packages/backend/src/server/api/endpoints/notifications/read.ts index f519e1d37859..1b6f6b79c1dc 100644 --- a/packages/backend/src/server/api/endpoints/notifications/read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/read.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { NotificationService } from '@/services/NotificationService'; +import { NotificationService } from '@/services/NotificationService.js'; export const meta = { tags: ['notifications', 'account'], diff --git a/packages/backend/src/server/api/endpoints/pages/featured.ts b/packages/backend/src/server/api/endpoints/pages/featured.ts index 3a6582b31967..e47115b168dd 100644 --- a/packages/backend/src/server/api/endpoints/pages/featured.ts +++ b/packages/backend/src/server/api/endpoints/pages/featured.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Pages } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { PageEntityService } from '@/services/entities/PageEntityService'; +import { PageEntityService } from '@/services/entities/PageEntityService.js'; export const meta = { tags: ['pages'], diff --git a/packages/backend/src/server/api/endpoints/ping.ts b/packages/backend/src/server/api/endpoints/ping.ts index 8c221c561bdb..4bb62b298efd 100644 --- a/packages/backend/src/server/api/endpoints/ping.ts +++ b/packages/backend/src/server/api/endpoints/ping.ts @@ -28,11 +28,6 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { super(meta, paramDef, async () => { return { diff --git a/packages/backend/src/server/api/endpoints/pinned-users.ts b/packages/backend/src/server/api/endpoints/pinned-users.ts index dd0b19ebbc5a..e77598332739 100644 --- a/packages/backend/src/server/api/endpoints/pinned-users.ts +++ b/packages/backend/src/server/api/endpoints/pinned-users.ts @@ -1,10 +1,11 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { Users } from '@/models/index.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; +import type { Users } from '@/models/index.js'; import * as Acct from '@/misc/acct.js'; import type { User } from '@/models/entities/user.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { MetaService } from '@/services/MetaService.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; export const meta = { tags: ['users'], @@ -35,11 +36,11 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, + private metaService: MetaService, + private userEntityService: UserEntityService, ) { super(meta, paramDef, async (ps, me) => { - const meta = await fetchMeta(); + const meta = await this.metaService.fetch(); const users = await Promise.all(meta.pinnedthis.usersRepository.map(acct => Acct.parse(acct)).map(acct => this.usersRepository.findOneBy({ usernameLower: acct.username.toLowerCase(), diff --git a/packages/backend/src/server/api/endpoints/request-reset-password.ts b/packages/backend/src/server/api/endpoints/request-reset-password.ts index 9c04d541717a..ec5c197afab9 100644 --- a/packages/backend/src/server/api/endpoints/request-reset-password.ts +++ b/packages/backend/src/server/api/endpoints/request-reset-password.ts @@ -2,13 +2,13 @@ import rndstr from 'rndstr'; import ms from 'ms'; import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { publishMainStream } from '@/services/stream.js'; -import config from '@/config/index.js'; import type { Users } from '@/models/index.js'; import { UserProfiles, PasswordResetRequests } from '@/models/index.js'; -import { sendEmail } from '@/services/send-email.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { IdService } from '@/services/IdService.js'; +import { IdService } from '@/services/IdService.js'; +import { Config } from '@/config.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { EmailService } from '@/services/EmailService.js'; import { ApiError } from '../error.js'; export const meta = { @@ -41,10 +41,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + @Inject('usersRepository') private usersRepository: typeof Users, private idService: IdService, + private emailService: EmailService, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy({ @@ -78,9 +82,9 @@ export default class extends Endpoint { token, }); - const link = `${config.url}/reset-password/${token}`; + const link = `${this.config.url}/reset-password/${token}`; - sendEmail(ps.email, 'Password reset requested', + this.emailService.sendEmail(ps.email, 'Password reset requested', `To reset password, please click this link:
${link}`, `To reset password, please click this link: ${link}`); }); diff --git a/packages/backend/src/server/api/endpoints/reset-password.ts b/packages/backend/src/server/api/endpoints/reset-password.ts index 0008efa2cadc..b297c3f7da52 100644 --- a/packages/backend/src/server/api/endpoints/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/reset-password.ts @@ -1,8 +1,7 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { publishMainStream } from '@/services/stream.js'; -import type { Users } from '@/models/index.js'; -import { UserProfiles, PasswordResetRequests } from '@/models/index.js'; +import type { Users , UserProfiles, PasswordResetRequests } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../error.js'; @@ -31,9 +30,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject('passwordResetRequestsRepository') + private passwordResetRequestsRepository: typeof PasswordResetRequests, + + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, ) { super(meta, paramDef, async (ps, me) => { - const req = await PasswordResetRequests.findOneByOrFail({ + const req = await this.passwordResetRequestsRepository.findOneByOrFail({ token: ps.token, }); @@ -46,11 +50,11 @@ export default class extends Endpoint { const salt = await bcrypt.genSalt(8); const hash = await bcrypt.hash(ps.password, salt); - await UserProfiles.update(req.userId, { + await this.userProfilesRepository.update(req.userId, { password: hash, }); - PasswordResetRequests.delete(req.id); + this.passwordResetRequestsRepository.delete(req.id); }); } } diff --git a/packages/backend/src/server/api/endpoints/server-info.ts b/packages/backend/src/server/api/endpoints/server-info.ts index 292cc37e7e51..8989a3073d9c 100644 --- a/packages/backend/src/server/api/endpoints/server-info.ts +++ b/packages/backend/src/server/api/endpoints/server-info.ts @@ -19,11 +19,6 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { super(meta, paramDef, async () => { const memStats = await si.mem(); diff --git a/packages/backend/src/server/api/endpoints/stats.ts b/packages/backend/src/server/api/endpoints/stats.ts index 1ed74e1d9817..55c39d3d2e9a 100644 --- a/packages/backend/src/server/api/endpoints/stats.ts +++ b/packages/backend/src/server/api/endpoints/stats.ts @@ -1,9 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; +import { IsNull } from 'typeorm'; import type { Notes, Users } from '@/models/index.js'; import { Instances, NoteReactions } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { } from '@/services/chart/index.js'; -import { IsNull } from 'typeorm'; export const meta = { requireCredential: false, diff --git a/packages/backend/src/server/api/endpoints/sw/register.ts b/packages/backend/src/server/api/endpoints/sw/register.ts index 1266df6e51e9..9540e6be261f 100644 --- a/packages/backend/src/server/api/endpoints/sw/register.ts +++ b/packages/backend/src/server/api/endpoints/sw/register.ts @@ -3,7 +3,7 @@ import { fetchMeta } from '@/misc/fetch-meta.js'; import { IdService } from '@/services/IdService.js'; import type { SwSubscriptions } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { MetaService } from '@/services/MetaService'; +import { MetaService } from '@/services/MetaService.js'; export const meta = { tags: ['account'], diff --git a/packages/backend/src/server/api/endpoints/test.ts b/packages/backend/src/server/api/endpoints/test.ts index 094ef90ab6d9..39ea1f21717d 100644 --- a/packages/backend/src/server/api/endpoints/test.ts +++ b/packages/backend/src/server/api/endpoints/test.ts @@ -25,11 +25,6 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') - private usersRepository: typeof Users, - - @Inject('notesRepository') - private notesRepository: typeof Notes, ) { super(meta, paramDef, async (ps, me) => { return ps; diff --git a/packages/backend/src/server/api/endpoints/users.ts b/packages/backend/src/server/api/endpoints/users.ts index 1100c231f6a8..744fb36acd16 100644 --- a/packages/backend/src/server/api/endpoints/users.ts +++ b/packages/backend/src/server/api/endpoints/users.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; export const meta = { tags: ['users'], @@ -44,9 +45,7 @@ export default class extends Endpoint { @Inject('usersRepository') private usersRepository: typeof Users, - @Inject('notesRepository') - private notesRepository: typeof Notes, - + private userEntityService: UserEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/users/clips.ts b/packages/backend/src/server/api/endpoints/users/clips.ts index 1dbb126cdec7..12a91fd7a106 100644 --- a/packages/backend/src/server/api/endpoints/users/clips.ts +++ b/packages/backend/src/server/api/endpoints/users/clips.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Clips } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; -import { ClipEntityService } from '@/services/entities/ClipEntityService'; +import { ClipEntityService } from '@/services/entities/ClipEntityService.js'; export const meta = { tags: ['users', 'clips'], diff --git a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts index 763ee7036deb..5a371642d897 100644 --- a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { GalleryPosts } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; -import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService'; +import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService.js'; export const meta = { tags: ['users', 'gallery'], diff --git a/packages/backend/src/server/api/endpoints/users/groups/create.ts b/packages/backend/src/server/api/endpoints/users/groups/create.ts index fac828597a05..f3300c24d7a3 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/create.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/create.ts @@ -4,7 +4,7 @@ import { IdService } from '@/services/IdService.js'; import type { UserGroup } from '@/models/entities/user-group.js'; import type { UserGroupJoining } from '@/models/entities/user-group-joining.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserGroupEntityService } from '@/services/entities/UserGroupEntityService'; +import { UserGroupEntityService } from '@/services/entities/UserGroupEntityService.js'; export const meta = { tags: ['groups'], diff --git a/packages/backend/src/server/api/endpoints/users/groups/joined.ts b/packages/backend/src/server/api/endpoints/users/groups/joined.ts index 3904558a00f1..763f78d5b0d3 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/joined.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/joined.ts @@ -2,7 +2,7 @@ import { Not, In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserGroupEntityService } from '@/services/entities/UserGroupEntityService'; +import { UserGroupEntityService } from '@/services/entities/UserGroupEntityService.js'; export const meta = { tags: ['groups', 'account'], diff --git a/packages/backend/src/server/api/endpoints/users/groups/owned.ts b/packages/backend/src/server/api/endpoints/users/groups/owned.ts index c101736eeb1d..e6264f595a25 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/owned.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/owned.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserGroups } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserGroupEntityService } from '@/services/entities/UserGroupEntityService'; +import { UserGroupEntityService } from '@/services/entities/UserGroupEntityService.js'; export const meta = { tags: ['groups', 'account'], diff --git a/packages/backend/src/server/api/endpoints/users/lists/create.ts b/packages/backend/src/server/api/endpoints/users/lists/create.ts index 1ac81f0a9494..7fab192c1a46 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts @@ -3,7 +3,7 @@ import type { UserLists } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; import type { UserList } from '@/models/entities/user-list.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserListEntityService } from '@/services/entities/UserListEntityService'; +import { UserListEntityService } from '@/services/entities/UserListEntityService.js'; export const meta = { tags: ['lists'], diff --git a/packages/backend/src/server/api/endpoints/users/lists/list.ts b/packages/backend/src/server/api/endpoints/users/lists/list.ts index 814c6b84615f..f36e32ae8837 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/list.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/list.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserLists } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserListEntityService } from '@/services/entities/UserListEntityService'; +import { UserListEntityService } from '@/services/entities/UserListEntityService.js'; export const meta = { tags: ['lists', 'account'], diff --git a/packages/backend/src/server/api/endpoints/users/pages.ts b/packages/backend/src/server/api/endpoints/users/pages.ts index ccc46e38e147..3927f308a7fc 100644 --- a/packages/backend/src/server/api/endpoints/users/pages.ts +++ b/packages/backend/src/server/api/endpoints/users/pages.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Pages } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; -import { PageEntityService } from '@/services/entities/PageEntityService'; +import { PageEntityService } from '@/services/entities/PageEntityService.js'; export const meta = { tags: ['users', 'pages'], diff --git a/packages/backend/src/server/api/endpoints/users/recommendation.ts b/packages/backend/src/server/api/endpoints/users/recommendation.ts index 49d542440eaa..e40e9ba3ea1a 100644 --- a/packages/backend/src/server/api/endpoints/users/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/users/recommendation.ts @@ -3,7 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users , Followings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; -import { UserEntityService } from '@/services/entities/UserEntityService'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; export const meta = { tags: ['users'], diff --git a/packages/backend/src/server/api/endpoints/users/relation.ts b/packages/backend/src/server/api/endpoints/users/relation.ts index 03adda6bbba8..87165a6fd704 100644 --- a/packages/backend/src/server/api/endpoints/users/relation.ts +++ b/packages/backend/src/server/api/endpoints/users/relation.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserEntityService } from '@/services/entities/UserEntityService'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; export const meta = { tags: ['users'], diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index 9f923d26978c..fee6a602858a 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -4,7 +4,7 @@ import type { Users , Followings } from '@/models/index.js'; import { USER_ACTIVE_THRESHOLD } from '@/const.js'; import type { User } from '@/models/entities/user.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserEntityService } from '@/services/entities/UserEntityService'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; export const meta = { tags: ['users'], diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index ee6cbc007747..afd1d44ab198 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -4,7 +4,7 @@ import type { Users } from '@/models/index.js'; import { UserProfiles } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserEntityService } from '@/services/entities/UserEntityService'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; export const meta = { tags: ['users'], diff --git a/packages/backend/src/services/CreateNotificationService.ts b/packages/backend/src/services/CreateNotificationService.ts index 18b6b41175e7..f7657a0fa6de 100644 --- a/packages/backend/src/services/CreateNotificationService.ts +++ b/packages/backend/src/services/CreateNotificationService.ts @@ -4,7 +4,7 @@ import type { User } from '@/models/entities/user.js'; import type { Notification } from '@/models/entities/notification.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { IdService } from '@/services/IdService.js'; -import { NotificationEntityService } from './entities/NotificationEntityService'; +import { NotificationEntityService } from './entities/NotificationEntityService.js'; @Injectable() export class CreateNotificationService { diff --git a/packages/backend/src/services/DriveService.ts b/packages/backend/src/services/DriveService.ts index e50d5b84e2a9..806e3eb94f70 100644 --- a/packages/backend/src/services/DriveService.ts +++ b/packages/backend/src/services/DriveService.ts @@ -30,8 +30,8 @@ import InstanceChart from '@/services/chart/charts/instance.js'; import { DownloadService } from '@/services/DownloadService.js'; import { S3Service } from '@/services/drive/S3Service.js'; import { InternalStorageService } from '@/services/drive/InternalStorageService.js'; -import { DriveFileEntityService } from './entities/DriveFileEntityService'; -import { UserEntityService } from './entities/UserEntityService'; +import { DriveFileEntityService } from './entities/DriveFileEntityService.js'; +import { UserEntityService } from './entities/UserEntityService.js'; import type S3 from 'aws-sdk/clients/s3.js'; type AddFileArgs = { diff --git a/packages/backend/src/services/MessagingService.ts b/packages/backend/src/services/MessagingService.ts index 4fd28c4876af..a819941b66b4 100644 --- a/packages/backend/src/services/MessagingService.ts +++ b/packages/backend/src/services/MessagingService.ts @@ -8,15 +8,15 @@ import type { MessagingMessage } from '@/models/entities/messaging-message'; import type { Note } from '@/models/entities/note'; import type { User, CacheableUser } from '@/models/entities/user'; import type { UserGroup } from '@/models/entities/user-group'; -import { QueueService } from '@/queue/queue.service'; +import { QueueService } from '@/queue/queue.service.js'; import { toArray } from '@/prelude/array'; import { IdentifiableError } from '@/misc/identifiable-error'; -import { IdService } from './IdService'; -import { GlobalEventService } from './GlobalEventService'; -import { UserEntityService } from './entities/UserEntityService'; -import { ApRendererService } from './remote/activitypub/ApRendererService'; -import { MessagingMessageEntityService } from './entities/MessagingMessageEntityService'; -import { PushNotificationService } from './PushNotificationService'; +import { IdService } from './IdService.js'; +import { GlobalEventService } from './GlobalEventService.js'; +import { UserEntityService } from './entities/UserEntityService.js'; +import { ApRendererService } from './remote/activitypub/ApRendererService.js'; +import { MessagingMessageEntityService } from './entities/MessagingMessageEntityService.js'; +import { PushNotificationService } from './PushNotificationService.js'; @Injectable() export class MessagingService { diff --git a/packages/backend/src/services/NotificationService.ts b/packages/backend/src/services/NotificationService.ts index 7638a520a573..a672c48b554c 100644 --- a/packages/backend/src/services/NotificationService.ts +++ b/packages/backend/src/services/NotificationService.ts @@ -3,9 +3,9 @@ import { In } from 'typeorm'; import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Notifications , Users } from '@/models/index.js'; import type { User } from '@/models/entities/user'; -import { UserEntityService } from './entities/UserEntityService'; -import { GlobalEventService } from './GlobalEventService'; -import { PushNotificationService } from './PushNotificationService'; +import { UserEntityService } from './entities/UserEntityService.js'; +import { GlobalEventService } from './GlobalEventService.js'; +import { PushNotificationService } from './PushNotificationService.js'; @Injectable() export class NotificationService { diff --git a/packages/backend/src/services/PushNotificationService.ts b/packages/backend/src/services/PushNotificationService.ts index a4c55ab87d00..e9ff6b2f840e 100644 --- a/packages/backend/src/services/PushNotificationService.ts +++ b/packages/backend/src/services/PushNotificationService.ts @@ -6,7 +6,7 @@ import type { Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type { Packed } from '@/misc/schema'; import { getNoteSummary } from '@/misc/get-note-summary.js'; -import { MetaService } from './MetaService'; +import { MetaService } from './MetaService.js'; // Defined also packages/sw/types.ts#L14-L21 type pushNotificationsTypes = { diff --git a/packages/backend/src/services/UserBlockingService.ts b/packages/backend/src/services/UserBlockingService.ts index c0b6ade20348..cd2fddd3519b 100644 --- a/packages/backend/src/services/UserBlockingService.ts +++ b/packages/backend/src/services/UserBlockingService.ts @@ -8,9 +8,9 @@ import type { Blocking } from '@/models/entities/blocking.js'; import { QueueService } from '@/queue/queue.service.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; -import { UserEntityService } from './entities/UserEntityService'; -import { WebhookService } from './WebhookService'; -import { ApRendererService } from './remote/activitypub/ApRendererService'; +import { UserEntityService } from './entities/UserEntityService.js'; +import { WebhookService } from './WebhookService.js'; +import { ApRendererService } from './remote/activitypub/ApRendererService.js'; @Injectable() export class UserBlockingService { diff --git a/packages/backend/src/services/UserListService.ts b/packages/backend/src/services/UserListService.ts index d4bfe5417ed7..d984b30d6410 100644 --- a/packages/backend/src/services/UserListService.ts +++ b/packages/backend/src/services/UserListService.ts @@ -7,7 +7,7 @@ import type { UserListJoining } from '@/models/entities/user-list-joining.js'; import { IdService } from '@/services/IdService.js'; import { UserFollowingService } from '@/services/UserFollowingService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; -import { UserEntityService } from './entities/UserEntityService'; +import { UserEntityService } from './entities/UserEntityService.js'; @Injectable() export class UserListService { diff --git a/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts b/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts index 35b36fdb2935..13bba3cb96f5 100644 --- a/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts +++ b/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts @@ -4,8 +4,8 @@ import { DI_SYMBOLS } from '@/di-symbols.js'; import type { Followings , Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type { ILocalUser, IRemoteUser, User } from '@/models/entities/user.js'; -import { QueueService } from '@/queue/queue.service'; -import { UserEntityService } from '@/services/entities/UserEntityService'; +import { QueueService } from '@/queue/queue.service.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; interface IRecipe { type: string; From 74de42b6cb5931f7d5b562d93a2fc8081640fe2b Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 00:43:48 +0900 Subject: [PATCH 125/180] wip --- .../src/server/api/ApiServerService.ts | 15 +- .../api/integration/DiscordServerService.ts | 316 ++++++++++++++++++ .../api/integration/GithubServerService.ts | 287 ++++++++++++++++ .../api/integration/TwitterServerService.ts | 231 +++++++++++++ .../backend/src/server/api/service/discord.ts | 287 ---------------- .../backend/src/server/api/service/github.ts | 259 -------------- .../backend/src/server/api/service/twitter.ts | 201 ----------- 7 files changed, 843 insertions(+), 753 deletions(-) create mode 100644 packages/backend/src/server/api/integration/DiscordServerService.ts create mode 100644 packages/backend/src/server/api/integration/GithubServerService.ts create mode 100644 packages/backend/src/server/api/integration/TwitterServerService.ts delete mode 100644 packages/backend/src/server/api/service/discord.ts delete mode 100644 packages/backend/src/server/api/service/github.ts delete mode 100644 packages/backend/src/server/api/service/twitter.ts diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts index 031d44055916..a9d3cf02091b 100644 --- a/packages/backend/src/server/api/ApiServerService.ts +++ b/packages/backend/src/server/api/ApiServerService.ts @@ -11,12 +11,12 @@ import { Instances, AccessTokens } from '@/models/index.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import endpoints from './endpoints.js'; -import discord from './service/discord.js'; -import github from './service/github.js'; -import twitter from './service/twitter.js'; import { ApiCallService } from './ApiCallService.js'; import { SignupApiService } from './SignupApiService.js'; import { SigninApiService } from './SigninApiService.js'; +import { GithubServerService } from './integration/GithubServerService.js'; +import { DiscordServerService } from './integration/DiscordServerService.js'; +import { TwitterServerService } from './integration/TwitterServerService.js'; @Injectable() export class ApiServerService { @@ -33,6 +33,9 @@ export class ApiServerService { private apiCallService: ApiCallService, private signupApiServiceService: SignupApiService, private signinApiServiceService: SigninApiService, + private githubServerService: GithubServerService, + private discordServerService: DiscordServerService, + private twitterServerService: TwitterServerService, ) { } @@ -105,9 +108,9 @@ export class ApiServerService { router.post('/signin', ctx => this.signinApiServiceService.signin(ctx)); router.post('/signup-pending', ctx => this.signupApiServiceService.signupPending(ctx)); - router.use(discord.routes()); - router.use(github.routes()); - router.use(twitter.routes()); + router.use(this.discordServerService.create().routes()); + router.use(this.githubServerService.create().routes()); + router.use(this.twitterServerService.create().routes()); router.get('/v1/instance/peers', async ctx => { const instances = await Instances.find({ diff --git a/packages/backend/src/server/api/integration/DiscordServerService.ts b/packages/backend/src/server/api/integration/DiscordServerService.ts new file mode 100644 index 000000000000..a2ee8bb41f2c --- /dev/null +++ b/packages/backend/src/server/api/integration/DiscordServerService.ts @@ -0,0 +1,316 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Redis } from 'ioredis'; +import Router from '@koa/router'; +import { OAuth2 } from 'oauth'; +import { v4 as uuid } from 'uuid'; +import { IsNull } from 'typeorm'; +import { Config } from '@/config.js'; +import type { UserProfiles , Users } from '@/models/index.js'; + +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { HttpRequestService } from '@/services/HttpRequestService'; +import type { ILocalUser } from '@/models/entities/user'; +import { GlobalEventService } from '@/services/GlobalEventService'; +import { MetaService } from '@/services/MetaService'; +import { UserEntityService } from '@/services/entities/UserEntityService'; +import { SigninService } from '../SigninService'; +import type Koa from 'koa'; + +@Injectable() +export class DiscordServerService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject(DI_SYMBOLS.redis) + private redisClient: Redis, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + private userEntityService: UserEntityService, + private httpRequestService: HttpRequestService, + private globalEventService: GlobalEventService, + private metaService: MetaService, + private signinService: SigninService, + ) { + } + + public create() { + const router = new Router(); + + router.get('/disconnect/discord', async ctx => { + if (!this.#compareOrigin(ctx)) { + ctx.throw(400, 'invalid origin'); + return; + } + + const userToken = this.#getUserToken(ctx); + if (!userToken) { + ctx.throw(400, 'signin required'); + return; + } + + const user = await this.usersRepository.findOneByOrFail({ + host: IsNull(), + token: userToken, + }); + + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); + + delete profile.integrations.discord; + + await this.userProfilesRepository.update(user.id, { + integrations: profile.integrations, + }); + + ctx.body = 'Discordの連携を解除しました :v:'; + + // Publish i updated event + this.globalEventService.publishMainStream(user.id, 'meUpdated', await this.userEntityService.pack(user, user, { + detail: true, + includeSecrets: true, + })); + }); + + const getOAuth2 = async () => { + const meta = await this.metaService.fetch(true); + + if (meta.enableDiscordIntegration) { + return new OAuth2( + meta.discordClientId!, + meta.discordClientSecret!, + 'https://discord.com/', + 'api/oauth2/authorize', + 'api/oauth2/token'); + } else { + return null; + } + }; + + router.get('/connect/discord', async ctx => { + if (!this.#compareOrigin(ctx)) { + ctx.throw(400, 'invalid origin'); + return; + } + + const userToken = this.#getUserToken(ctx); + if (!userToken) { + ctx.throw(400, 'signin required'); + return; + } + + const params = { + redirect_uri: `${this.config.url}/api/dc/cb`, + scope: ['identify'], + state: uuid(), + response_type: 'code', + }; + + this.redisClient.set(userToken, JSON.stringify(params)); + + const oauth2 = await getOAuth2(); + ctx.redirect(oauth2!.getAuthorizeUrl(params)); + }); + + router.get('/signin/discord', async ctx => { + const sessid = uuid(); + + const params = { + redirect_uri: `${this.config.url}/api/dc/cb`, + scope: ['identify'], + state: uuid(), + response_type: 'code', + }; + + ctx.cookies.set('signin_with_discord_sid', sessid, { + path: '/', + secure: this.config.url.startsWith('https'), + httpOnly: true, + }); + + this.redisClient.set(sessid, JSON.stringify(params)); + + const oauth2 = await getOAuth2(); + ctx.redirect(oauth2!.getAuthorizeUrl(params)); + }); + + router.get('/dc/cb', async ctx => { + const userToken = this.#getUserToken(ctx); + + const oauth2 = await getOAuth2(); + + if (!userToken) { + const sessid = ctx.cookies.get('signin_with_discord_sid'); + + if (!sessid) { + ctx.throw(400, 'invalid session'); + return; + } + + const code = ctx.query.code; + + if (!code || typeof code !== 'string') { + ctx.throw(400, 'invalid session'); + return; + } + + const { redirect_uri, state } = await new Promise((res, rej) => { + this.redisClient.get(sessid, async (_, state) => { + res(JSON.parse(state)); + }); + }); + + if (ctx.query.state !== state) { + ctx.throw(400, 'invalid session'); + return; + } + + const { accessToken, refreshToken, expiresDate } = await new Promise((res, rej) => + oauth2!.getOAuthAccessToken(code, { + grant_type: 'authorization_code', + redirect_uri, + }, (err, accessToken, refreshToken, result) => { + if (err) { + rej(err); + } else if (result.error) { + rej(result.error); + } else { + res({ + accessToken, + refreshToken, + expiresDate: Date.now() + Number(result.expires_in) * 1000, + }); + } + })); + + const { id, username, discriminator } = (await this.httpRequestService.getJson('https://discord.com/api/users/@me', '*/*', 10 * 1000, { + 'Authorization': `Bearer ${accessToken}`, + })) as Record; + + if (typeof id !== 'string' || typeof username !== 'string' || typeof discriminator !== 'string') { + ctx.throw(400, 'invalid session'); + return; + } + + const profile = await this.userProfilesRepository.createQueryBuilder() + .where('"integrations"->\'discord\'->>\'id\' = :id', { id: id }) + .andWhere('"userHost" IS NULL') + .getOne(); + + if (profile == null) { + ctx.throw(404, `@${username}#${discriminator}と連携しているMisskeyアカウントはありませんでした...`); + return; + } + + await this.userProfilesRepository.update(profile.userId, { + integrations: { + ...profile.integrations, + discord: { + id: id, + accessToken: accessToken, + refreshToken: refreshToken, + expiresDate: expiresDate, + username: username, + discriminator: discriminator, + }, + }, + }); + + this.signinService.signin(ctx, await this.usersRepository.findOneBy({ id: profile.userId }) as ILocalUser, true); + } else { + const code = ctx.query.code; + + if (!code || typeof code !== 'string') { + ctx.throw(400, 'invalid session'); + return; + } + + const { redirect_uri, state } = await new Promise((res, rej) => { + this.redisClient.get(userToken, async (_, state) => { + res(JSON.parse(state)); + }); + }); + + if (ctx.query.state !== state) { + ctx.throw(400, 'invalid session'); + return; + } + + const { accessToken, refreshToken, expiresDate } = await new Promise((res, rej) => + oauth2!.getOAuthAccessToken(code, { + grant_type: 'authorization_code', + redirect_uri, + }, (err, accessToken, refreshToken, result) => { + if (err) { + rej(err); + } else if (result.error) { + rej(result.error); + } else { + res({ + accessToken, + refreshToken, + expiresDate: Date.now() + Number(result.expires_in) * 1000, + }); + } + })); + + const { id, username, discriminator } = (await this.httpRequestService.getJson('https://discord.com/api/users/@me', '*/*', 10 * 1000, { + 'Authorization': `Bearer ${accessToken}`, + })) as Record; + if (typeof id !== 'string' || typeof username !== 'string' || typeof discriminator !== 'string') { + ctx.throw(400, 'invalid session'); + return; + } + + const user = await this.usersRepository.findOneByOrFail({ + host: IsNull(), + token: userToken, + }); + + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); + + await this.userProfilesRepository.update(user.id, { + integrations: { + ...profile.integrations, + discord: { + accessToken: accessToken, + refreshToken: refreshToken, + expiresDate: expiresDate, + id: id, + username: username, + discriminator: discriminator, + }, + }, + }); + + ctx.body = `Discord: @${username}#${discriminator} を、Misskey: @${user.username} に接続しました!`; + + // Publish i updated event + this.globalEventService.publishMainStream(user.id, 'meUpdated', await this.userEntityService.pack(user, user, { + detail: true, + includeSecrets: true, + })); + } + }); + + return router; + } + + #getUserToken(ctx: Koa.BaseContext): string | null { + return ((ctx.headers['cookie'] || '').match(/igi=(\w+)/) || [null, null])[1]; + } + + #compareOrigin(ctx: Koa.BaseContext): boolean { + function normalizeUrl(url?: string): string { + return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; + } + + const referer = ctx.headers['referer']; + + return (normalizeUrl(referer) === normalizeUrl(this.config.url)); + } +} diff --git a/packages/backend/src/server/api/integration/GithubServerService.ts b/packages/backend/src/server/api/integration/GithubServerService.ts new file mode 100644 index 000000000000..d50a619c367b --- /dev/null +++ b/packages/backend/src/server/api/integration/GithubServerService.ts @@ -0,0 +1,287 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Redis } from 'ioredis'; +import Router from '@koa/router'; +import { OAuth2 } from 'oauth'; +import { v4 as uuid } from 'uuid'; +import { IsNull } from 'typeorm'; +import { Config } from '@/config.js'; +import type { UserProfiles, Users } from '@/models/index.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { HttpRequestService } from '@/services/HttpRequestService'; +import type { ILocalUser } from '@/models/entities/user'; +import { GlobalEventService } from '@/services/GlobalEventService'; +import { MetaService } from '@/services/MetaService'; +import { UserEntityService } from '@/services/entities/UserEntityService'; +import { SigninService } from '../SigninService'; +import type Koa from 'koa'; + +@Injectable() +export class GithubServerService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject(DI_SYMBOLS.redis) + private redisClient: Redis, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + private userEntityService: UserEntityService, + private httpRequestService: HttpRequestService, + private globalEventService: GlobalEventService, + private metaService: MetaService, + private signinService: SigninService, + ) { + } + + public create() { + const router = new Router(); + + router.get('/disconnect/github', async ctx => { + if (!this.#compareOrigin(ctx)) { + ctx.throw(400, 'invalid origin'); + return; + } + + const userToken = this.#getUserToken(ctx); + if (!userToken) { + ctx.throw(400, 'signin required'); + return; + } + + const user = await this.usersRepository.findOneByOrFail({ + host: IsNull(), + token: userToken, + }); + + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); + + delete profile.integrations.github; + + await this.userProfilesRepository.update(user.id, { + integrations: profile.integrations, + }); + + ctx.body = 'GitHubの連携を解除しました :v:'; + + // Publish i updated event + this.globalEventService.publishMainStream(user.id, 'meUpdated', await this.userEntityService.pack(user, user, { + detail: true, + includeSecrets: true, + })); + }); + + const getOath2 = async () => { + const meta = await this.metaService.fetch(true); + + if (meta.enableGithubIntegration && meta.githubClientId && meta.githubClientSecret) { + return new OAuth2( + meta.githubClientId, + meta.githubClientSecret, + 'https://github.com/', + 'login/oauth/authorize', + 'login/oauth/access_token'); + } else { + return null; + } + }; + + router.get('/connect/github', async ctx => { + if (!this.#compareOrigin(ctx)) { + ctx.throw(400, 'invalid origin'); + return; + } + + const userToken = this.#getUserToken(ctx); + if (!userToken) { + ctx.throw(400, 'signin required'); + return; + } + + const params = { + redirect_uri: `${this.config.url}/api/gh/cb`, + scope: ['read:user'], + state: uuid(), + }; + + this.redisClient.set(userToken, JSON.stringify(params)); + + const oauth2 = await getOath2(); + ctx.redirect(oauth2!.getAuthorizeUrl(params)); + }); + + router.get('/signin/github', async ctx => { + const sessid = uuid(); + + const params = { + redirect_uri: `${this.config.url}/api/gh/cb`, + scope: ['read:user'], + state: uuid(), + }; + + ctx.cookies.set('signin_with_github_sid', sessid, { + path: '/', + secure: this.config.url.startsWith('https'), + httpOnly: true, + }); + + this.redisClient.set(sessid, JSON.stringify(params)); + + const oauth2 = await getOath2(); + ctx.redirect(oauth2!.getAuthorizeUrl(params)); + }); + + router.get('/gh/cb', async ctx => { + const userToken = this.#getUserToken(ctx); + + const oauth2 = await getOath2(); + + if (!userToken) { + const sessid = ctx.cookies.get('signin_with_github_sid'); + + if (!sessid) { + ctx.throw(400, 'invalid session'); + return; + } + + const code = ctx.query.code; + + if (!code || typeof code !== 'string') { + ctx.throw(400, 'invalid session'); + return; + } + + const { redirect_uri, state } = await new Promise((res, rej) => { + this.redisClient.get(sessid, async (_, state) => { + res(JSON.parse(state)); + }); + }); + + if (ctx.query.state !== state) { + ctx.throw(400, 'invalid session'); + return; + } + + const { accessToken } = await new Promise((res, rej) => + oauth2!.getOAuthAccessToken(code, { + redirect_uri, + }, (err, accessToken, refresh, result) => { + if (err) { + rej(err); + } else if (result.error) { + rej(result.error); + } else { + res({ accessToken }); + } + })); + + const { login, id } = (await this.httpRequestService.getJson('https://api.github.com/user', 'application/vnd.github.v3+json', 10 * 1000, { + 'Authorization': `bearer ${accessToken}`, + })) as Record; + if (typeof login !== 'string' || typeof id !== 'string') { + ctx.throw(400, 'invalid session'); + return; + } + + const link = await this.userProfilesRepository.createQueryBuilder() + .where('"integrations"->\'github\'->>\'id\' = :id', { id: id }) + .andWhere('"userHost" IS NULL') + .getOne(); + + if (link == null) { + ctx.throw(404, `@${login}と連携しているMisskeyアカウントはありませんでした...`); + return; + } + + this.signinService.signin(ctx, await this.usersRepository.findOneBy({ id: link.userId }) as ILocalUser, true); + } else { + const code = ctx.query.code; + + if (!code || typeof code !== 'string') { + ctx.throw(400, 'invalid session'); + return; + } + + const { redirect_uri, state } = await new Promise((res, rej) => { + this.redisClient.get(userToken, async (_, state) => { + res(JSON.parse(state)); + }); + }); + + if (ctx.query.state !== state) { + ctx.throw(400, 'invalid session'); + return; + } + + const { accessToken } = await new Promise((res, rej) => + oauth2!.getOAuthAccessToken( + code, + { redirect_uri }, + (err, accessToken, refresh, result) => { + if (err) { + rej(err); + } else if (result.error) { + rej(result.error); + } else { + res({ accessToken }); + } + })); + + const { login, id } = (await this.httpRequestService.getJson('https://api.github.com/user', 'application/vnd.github.v3+json', 10 * 1000, { + 'Authorization': `bearer ${accessToken}`, + })) as Record; + + if (typeof login !== 'string' || typeof id !== 'string') { + ctx.throw(400, 'invalid session'); + return; + } + + const user = await this.usersRepository.findOneByOrFail({ + host: IsNull(), + token: userToken, + }); + + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); + + await this.userProfilesRepository.update(user.id, { + integrations: { + ...profile.integrations, + github: { + accessToken: accessToken, + id: id, + login: login, + }, + }, + }); + + ctx.body = `GitHub: @${login} を、Misskey: @${user.username} に接続しました!`; + + // Publish i updated event + this.globalEventService.publishMainStream(user.id, 'meUpdated', await this.userEntityService.pack(user, user, { + detail: true, + includeSecrets: true, + })); + } + }); + + return router; + } + + #getUserToken(ctx: Koa.BaseContext): string | null { + return ((ctx.headers['cookie'] || '').match(/igi=(\w+)/) || [null, null])[1]; + } + + #compareOrigin(ctx: Koa.BaseContext): boolean { + function normalizeUrl(url?: string): string { + return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; + } + + const referer = ctx.headers['referer']; + + return (normalizeUrl(referer) === normalizeUrl(this.config.url)); + } +} diff --git a/packages/backend/src/server/api/integration/TwitterServerService.ts b/packages/backend/src/server/api/integration/TwitterServerService.ts new file mode 100644 index 000000000000..8e7b7fc48d57 --- /dev/null +++ b/packages/backend/src/server/api/integration/TwitterServerService.ts @@ -0,0 +1,231 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Redis } from 'ioredis'; +import Router from '@koa/router'; +import { v4 as uuid } from 'uuid'; +import { IsNull } from 'typeorm'; +import autwh from 'autwh'; +import { Config } from '@/config.js'; +import type { UserProfiles } from '@/models/index.js'; +import { Users } from '@/models/index.js'; +import { DI_SYMBOLS } from '@/di-symbols.js'; +import { HttpRequestService } from '@/services/HttpRequestService'; +import type { ILocalUser } from '@/models/entities/user'; +import { GlobalEventService } from '@/services/GlobalEventService'; +import { MetaService } from '@/services/MetaService'; +import { UserEntityService } from '@/services/entities/UserEntityService'; +import { SigninService } from '../SigninService'; +import type Koa from 'koa'; + +@Injectable() +export class TwitterServerService { + constructor( + @Inject(DI_SYMBOLS.config) + private config: Config, + + @Inject(DI_SYMBOLS.redis) + private redisClient: Redis, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + + private userEntityService: UserEntityService, + private httpRequestService: HttpRequestService, + private globalEventService: GlobalEventService, + private metaService: MetaService, + private signinService: SigninService, + ) { + } + + public create() { + const router = new Router(); + + router.get('/disconnect/twitter', async ctx => { + if (!this.#compareOrigin(ctx)) { + ctx.throw(400, 'invalid origin'); + return; + } + + const userToken = this.#getUserToken(ctx); + if (userToken == null) { + ctx.throw(400, 'signin required'); + return; + } + + const user = await this.usersRepository.findOneByOrFail({ + host: IsNull(), + token: userToken, + }); + + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); + + delete profile.integrations.twitter; + + await this.userProfilesRepository.update(user.id, { + integrations: profile.integrations, + }); + + ctx.body = 'Twitterの連携を解除しました :v:'; + + // Publish i updated event + this.globalEventService.publishMainStream(user.id, 'meUpdated', await this.userEntityService.pack(user, user, { + detail: true, + includeSecrets: true, + })); + }); + + const getTwAuth = async () => { + const meta = await this.metaService.fetch(true); + + if (meta.enableTwitterIntegration && meta.twitterConsumerKey && meta.twitterConsumerSecret) { + return autwh({ + consumerKey: meta.twitterConsumerKey, + consumerSecret: meta.twitterConsumerSecret, + callbackUrl: `${this.config.url}/api/tw/cb`, + }); + } else { + return null; + } + }; + + router.get('/connect/twitter', async ctx => { + if (!this.#compareOrigin(ctx)) { + ctx.throw(400, 'invalid origin'); + return; + } + + const userToken = this.#getUserToken(ctx); + if (userToken == null) { + ctx.throw(400, 'signin required'); + return; + } + + const twAuth = await getTwAuth(); + const twCtx = await twAuth!.begin(); + this.redisClient.set(userToken, JSON.stringify(twCtx)); + ctx.redirect(twCtx.url); + }); + + router.get('/signin/twitter', async ctx => { + const twAuth = await getTwAuth(); + const twCtx = await twAuth!.begin(); + + const sessid = uuid(); + + this.redisClient.set(sessid, JSON.stringify(twCtx)); + + ctx.cookies.set('signin_with_twitter_sid', sessid, { + path: '/', + secure: this.config.url.startsWith('https'), + httpOnly: true, + }); + + ctx.redirect(twCtx.url); + }); + + router.get('/tw/cb', async ctx => { + const userToken = this.#getUserToken(ctx); + + const twAuth = await getTwAuth(); + + if (userToken == null) { + const sessid = ctx.cookies.get('signin_with_twitter_sid'); + + if (sessid == null) { + ctx.throw(400, 'invalid session'); + return; + } + + const get = new Promise((res, rej) => { + this.redisClient.get(sessid, async (_, twCtx) => { + res(twCtx); + }); + }); + + const twCtx = await get; + + const verifier = ctx.query.oauth_verifier; + if (!verifier || typeof verifier !== 'string') { + ctx.throw(400, 'invalid session'); + return; + } + + const result = await twAuth!.done(JSON.parse(twCtx), verifier); + + const link = await this.userProfilesRepository.createQueryBuilder() + .where('"integrations"->\'twitter\'->>\'userId\' = :id', { id: result.userId }) + .andWhere('"userHost" IS NULL') + .getOne(); + + if (link == null) { + ctx.throw(404, `@${result.screenName}と連携しているMisskeyアカウントはありませんでした...`); + return; + } + + this.signinService.signin(ctx, await Users.findOneBy({ id: link.userId }) as ILocalUser, true); + } else { + const verifier = ctx.query.oauth_verifier; + + if (!verifier || typeof verifier !== 'string') { + ctx.throw(400, 'invalid session'); + return; + } + + const get = new Promise((res, rej) => { + this.redisClient.get(userToken, async (_, twCtx) => { + res(twCtx); + }); + }); + + const twCtx = await get; + + const result = await twAuth!.done(JSON.parse(twCtx), verifier); + + const user = await this.usersRepository.findOneByOrFail({ + host: IsNull(), + token: userToken, + }); + + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); + + await this.userProfilesRepository.update(user.id, { + integrations: { + ...profile.integrations, + twitter: { + accessToken: result.accessToken, + accessTokenSecret: result.accessTokenSecret, + userId: result.userId, + screenName: result.screenName, + }, + }, + }); + + ctx.body = `Twitter: @${result.screenName} を、Misskey: @${user.username} に接続しました!`; + + // Publish i updated event + this.globalEventService.publishMainStream(user.id, 'meUpdated', await this.userEntityService.pack(user, user, { + detail: true, + includeSecrets: true, + })); + } + }); + + return router; + } + + #getUserToken(ctx: Koa.BaseContext): string | null { + return ((ctx.headers['cookie'] || '').match(/igi=(\w+)/) || [null, null])[1]; + } + + #compareOrigin(ctx: Koa.BaseContext): boolean { + function normalizeUrl(url?: string): string { + return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; + } + + const referer = ctx.headers['referer']; + + return (normalizeUrl(referer) === normalizeUrl(this.config.url)); + } +} diff --git a/packages/backend/src/server/api/service/discord.ts b/packages/backend/src/server/api/service/discord.ts deleted file mode 100644 index 97cbcbecdb57..000000000000 --- a/packages/backend/src/server/api/service/discord.ts +++ /dev/null @@ -1,287 +0,0 @@ -import Koa from 'koa'; -import Router from '@koa/router'; -import { OAuth2 } from 'oauth'; -import { v4 as uuid } from 'uuid'; -import { IsNull } from 'typeorm'; -import { getJson } from '@/misc/fetch.js'; -import config from '@/config/index.js'; -import { publishMainStream } from '@/services/stream.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import { Users, UserProfiles } from '@/models/index.js'; -import { ILocalUser } from '@/models/entities/user.js'; -import { redisClient } from '../../../db/redis.js'; -import signin from '../common/signin.js'; - -function getUserToken(ctx: Koa.BaseContext): string | null { - return ((ctx.headers['cookie'] || '').match(/igi=(\w+)/) || [null, null])[1]; -} - -function compareOrigin(ctx: Koa.BaseContext): boolean { - function normalizeUrl(url?: string): string { - return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; - } - - const referer = ctx.headers['referer']; - - return (normalizeUrl(referer) === normalizeUrl(config.url)); -} - -// Init router -const router = new Router(); - -router.get('/disconnect/discord', async ctx => { - if (!compareOrigin(ctx)) { - ctx.throw(400, 'invalid origin'); - return; - } - - const userToken = getUserToken(ctx); - if (!userToken) { - ctx.throw(400, 'signin required'); - return; - } - - const user = await Users.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - delete profile.integrations.discord; - - await UserProfiles.update(user.id, { - integrations: profile.integrations, - }); - - ctx.body = 'Discordの連携を解除しました :v:'; - - // Publish i updated event - publishMainStream(user.id, 'meUpdated', await Users.pack(user, user, { - detail: true, - includeSecrets: true, - })); -}); - -async function getOAuth2() { - const meta = await fetchMeta(true); - - if (meta.enableDiscordIntegration) { - return new OAuth2( - meta.discordClientId!, - meta.discordClientSecret!, - 'https://discord.com/', - 'api/oauth2/authorize', - 'api/oauth2/token'); - } else { - return null; - } -} - -router.get('/connect/discord', async ctx => { - if (!compareOrigin(ctx)) { - ctx.throw(400, 'invalid origin'); - return; - } - - const userToken = getUserToken(ctx); - if (!userToken) { - ctx.throw(400, 'signin required'); - return; - } - - const params = { - redirect_uri: `${config.url}/api/dc/cb`, - scope: ['identify'], - state: uuid(), - response_type: 'code', - }; - - redisClient.set(userToken, JSON.stringify(params)); - - const oauth2 = await getOAuth2(); - ctx.redirect(oauth2!.getAuthorizeUrl(params)); -}); - -router.get('/signin/discord', async ctx => { - const sessid = uuid(); - - const params = { - redirect_uri: `${config.url}/api/dc/cb`, - scope: ['identify'], - state: uuid(), - response_type: 'code', - }; - - ctx.cookies.set('signin_with_discord_sid', sessid, { - path: '/', - secure: config.url.startsWith('https'), - httpOnly: true, - }); - - redisClient.set(sessid, JSON.stringify(params)); - - const oauth2 = await getOAuth2(); - ctx.redirect(oauth2!.getAuthorizeUrl(params)); -}); - -router.get('/dc/cb', async ctx => { - const userToken = getUserToken(ctx); - - const oauth2 = await getOAuth2(); - - if (!userToken) { - const sessid = ctx.cookies.get('signin_with_discord_sid'); - - if (!sessid) { - ctx.throw(400, 'invalid session'); - return; - } - - const code = ctx.query.code; - - if (!code || typeof code !== 'string') { - ctx.throw(400, 'invalid session'); - return; - } - - const { redirect_uri, state } = await new Promise((res, rej) => { - redisClient.get(sessid, async (_, state) => { - res(JSON.parse(state)); - }); - }); - - if (ctx.query.state !== state) { - ctx.throw(400, 'invalid session'); - return; - } - - const { accessToken, refreshToken, expiresDate } = await new Promise((res, rej) => - oauth2!.getOAuthAccessToken(code, { - grant_type: 'authorization_code', - redirect_uri, - }, (err, accessToken, refreshToken, result) => { - if (err) { - rej(err); - } else if (result.error) { - rej(result.error); - } else { - res({ - accessToken, - refreshToken, - expiresDate: Date.now() + Number(result.expires_in) * 1000, - }); - } - })); - - const { id, username, discriminator } = (await getJson('https://discord.com/api/users/@me', '*/*', 10 * 1000, { - 'Authorization': `Bearer ${accessToken}`, - })) as Record; - - if (typeof id !== 'string' || typeof username !== 'string' || typeof discriminator !== 'string') { - ctx.throw(400, 'invalid session'); - return; - } - - const profile = await UserProfiles.createQueryBuilder() - .where('"integrations"->\'discord\'->>\'id\' = :id', { id: id }) - .andWhere('"userHost" IS NULL') - .getOne(); - - if (profile == null) { - ctx.throw(404, `@${username}#${discriminator}と連携しているMisskeyアカウントはありませんでした...`); - return; - } - - await UserProfiles.update(profile.userId, { - integrations: { - ...profile.integrations, - discord: { - id: id, - accessToken: accessToken, - refreshToken: refreshToken, - expiresDate: expiresDate, - username: username, - discriminator: discriminator, - }, - }, - }); - - signin(ctx, await Users.findOneBy({ id: profile.userId }) as ILocalUser, true); - } else { - const code = ctx.query.code; - - if (!code || typeof code !== 'string') { - ctx.throw(400, 'invalid session'); - return; - } - - const { redirect_uri, state } = await new Promise((res, rej) => { - redisClient.get(userToken, async (_, state) => { - res(JSON.parse(state)); - }); - }); - - if (ctx.query.state !== state) { - ctx.throw(400, 'invalid session'); - return; - } - - const { accessToken, refreshToken, expiresDate } = await new Promise((res, rej) => - oauth2!.getOAuthAccessToken(code, { - grant_type: 'authorization_code', - redirect_uri, - }, (err, accessToken, refreshToken, result) => { - if (err) { - rej(err); - } else if (result.error) { - rej(result.error); - } else { - res({ - accessToken, - refreshToken, - expiresDate: Date.now() + Number(result.expires_in) * 1000, - }); - } - })); - - const { id, username, discriminator } = (await getJson('https://discord.com/api/users/@me', '*/*', 10 * 1000, { - 'Authorization': `Bearer ${accessToken}`, - })) as Record; - if (typeof id !== 'string' || typeof username !== 'string' || typeof discriminator !== 'string') { - ctx.throw(400, 'invalid session'); - return; - } - - const user = await Users.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - await UserProfiles.update(user.id, { - integrations: { - ...profile.integrations, - discord: { - accessToken: accessToken, - refreshToken: refreshToken, - expiresDate: expiresDate, - id: id, - username: username, - discriminator: discriminator, - }, - }, - }); - - ctx.body = `Discord: @${username}#${discriminator} を、Misskey: @${user.username} に接続しました!`; - - // Publish i updated event - publishMainStream(user.id, 'meUpdated', await Users.pack(user, user, { - detail: true, - includeSecrets: true, - })); - } -}); - -export default router; diff --git a/packages/backend/src/server/api/service/github.ts b/packages/backend/src/server/api/service/github.ts deleted file mode 100644 index 04dbd1f7ab2a..000000000000 --- a/packages/backend/src/server/api/service/github.ts +++ /dev/null @@ -1,259 +0,0 @@ -import Koa from 'koa'; -import Router from '@koa/router'; -import { OAuth2 } from 'oauth'; -import { v4 as uuid } from 'uuid'; -import { IsNull } from 'typeorm'; -import { getJson } from '@/misc/fetch.js'; -import config from '@/config/index.js'; -import { publishMainStream } from '@/services/stream.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import { Users, UserProfiles } from '@/models/index.js'; -import { ILocalUser } from '@/models/entities/user.js'; -import { redisClient } from '../../../db/redis.js'; -import signin from '../common/signin.js'; - -function getUserToken(ctx: Koa.BaseContext): string | null { - return ((ctx.headers['cookie'] || '').match(/igi=(\w+)/) || [null, null])[1]; -} - -function compareOrigin(ctx: Koa.BaseContext): boolean { - function normalizeUrl(url?: string): string { - return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; - } - - const referer = ctx.headers['referer']; - - return (normalizeUrl(referer) === normalizeUrl(config.url)); -} - -// Init router -const router = new Router(); - -router.get('/disconnect/github', async ctx => { - if (!compareOrigin(ctx)) { - ctx.throw(400, 'invalid origin'); - return; - } - - const userToken = getUserToken(ctx); - if (!userToken) { - ctx.throw(400, 'signin required'); - return; - } - - const user = await Users.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - delete profile.integrations.github; - - await UserProfiles.update(user.id, { - integrations: profile.integrations, - }); - - ctx.body = 'GitHubの連携を解除しました :v:'; - - // Publish i updated event - publishMainStream(user.id, 'meUpdated', await Users.pack(user, user, { - detail: true, - includeSecrets: true, - })); -}); - -async function getOath2() { - const meta = await fetchMeta(true); - - if (meta.enableGithubIntegration && meta.githubClientId && meta.githubClientSecret) { - return new OAuth2( - meta.githubClientId, - meta.githubClientSecret, - 'https://github.com/', - 'login/oauth/authorize', - 'login/oauth/access_token'); - } else { - return null; - } -} - -router.get('/connect/github', async ctx => { - if (!compareOrigin(ctx)) { - ctx.throw(400, 'invalid origin'); - return; - } - - const userToken = getUserToken(ctx); - if (!userToken) { - ctx.throw(400, 'signin required'); - return; - } - - const params = { - redirect_uri: `${config.url}/api/gh/cb`, - scope: ['read:user'], - state: uuid(), - }; - - redisClient.set(userToken, JSON.stringify(params)); - - const oauth2 = await getOath2(); - ctx.redirect(oauth2!.getAuthorizeUrl(params)); -}); - -router.get('/signin/github', async ctx => { - const sessid = uuid(); - - const params = { - redirect_uri: `${config.url}/api/gh/cb`, - scope: ['read:user'], - state: uuid(), - }; - - ctx.cookies.set('signin_with_github_sid', sessid, { - path: '/', - secure: config.url.startsWith('https'), - httpOnly: true, - }); - - redisClient.set(sessid, JSON.stringify(params)); - - const oauth2 = await getOath2(); - ctx.redirect(oauth2!.getAuthorizeUrl(params)); -}); - -router.get('/gh/cb', async ctx => { - const userToken = getUserToken(ctx); - - const oauth2 = await getOath2(); - - if (!userToken) { - const sessid = ctx.cookies.get('signin_with_github_sid'); - - if (!sessid) { - ctx.throw(400, 'invalid session'); - return; - } - - const code = ctx.query.code; - - if (!code || typeof code !== 'string') { - ctx.throw(400, 'invalid session'); - return; - } - - const { redirect_uri, state } = await new Promise((res, rej) => { - redisClient.get(sessid, async (_, state) => { - res(JSON.parse(state)); - }); - }); - - if (ctx.query.state !== state) { - ctx.throw(400, 'invalid session'); - return; - } - - const { accessToken } = await new Promise((res, rej) => - oauth2!.getOAuthAccessToken(code, { - redirect_uri, - }, (err, accessToken, refresh, result) => { - if (err) { - rej(err); - } else if (result.error) { - rej(result.error); - } else { - res({ accessToken }); - } - })); - - const { login, id } = (await getJson('https://api.github.com/user', 'application/vnd.github.v3+json', 10 * 1000, { - 'Authorization': `bearer ${accessToken}`, - })) as Record; - if (typeof login !== 'string' || typeof id !== 'string') { - ctx.throw(400, 'invalid session'); - return; - } - - const link = await UserProfiles.createQueryBuilder() - .where('"integrations"->\'github\'->>\'id\' = :id', { id: id }) - .andWhere('"userHost" IS NULL') - .getOne(); - - if (link == null) { - ctx.throw(404, `@${login}と連携しているMisskeyアカウントはありませんでした...`); - return; - } - - signin(ctx, await Users.findOneBy({ id: link.userId }) as ILocalUser, true); - } else { - const code = ctx.query.code; - - if (!code || typeof code !== 'string') { - ctx.throw(400, 'invalid session'); - return; - } - - const { redirect_uri, state } = await new Promise((res, rej) => { - redisClient.get(userToken, async (_, state) => { - res(JSON.parse(state)); - }); - }); - - if (ctx.query.state !== state) { - ctx.throw(400, 'invalid session'); - return; - } - - const { accessToken } = await new Promise((res, rej) => - oauth2!.getOAuthAccessToken( - code, - { redirect_uri }, - (err, accessToken, refresh, result) => { - if (err) { - rej(err); - } else if (result.error) { - rej(result.error); - } else { - res({ accessToken }); - } - })); - - const { login, id } = (await getJson('https://api.github.com/user', 'application/vnd.github.v3+json', 10 * 1000, { - 'Authorization': `bearer ${accessToken}`, - })) as Record; - - if (typeof login !== 'string' || typeof id !== 'string') { - ctx.throw(400, 'invalid session'); - return; - } - - const user = await Users.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - await UserProfiles.update(user.id, { - integrations: { - ...profile.integrations, - github: { - accessToken: accessToken, - id: id, - login: login, - }, - }, - }); - - ctx.body = `GitHub: @${login} を、Misskey: @${user.username} に接続しました!`; - - // Publish i updated event - publishMainStream(user.id, 'meUpdated', await Users.pack(user, user, { - detail: true, - includeSecrets: true, - })); - } -}); - -export default router; diff --git a/packages/backend/src/server/api/service/twitter.ts b/packages/backend/src/server/api/service/twitter.ts deleted file mode 100644 index 2b4f9f6daa65..000000000000 --- a/packages/backend/src/server/api/service/twitter.ts +++ /dev/null @@ -1,201 +0,0 @@ -import Koa from 'koa'; -import Router from '@koa/router'; -import { v4 as uuid } from 'uuid'; -import autwh from 'autwh'; -import { IsNull } from 'typeorm'; -import { publishMainStream } from '@/services/stream.js'; -import config from '@/config/index.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import { Users, UserProfiles } from '@/models/index.js'; -import { ILocalUser } from '@/models/entities/user.js'; -import signin from '../common/signin.js'; -import { redisClient } from '../../../db/redis.js'; - -function getUserToken(ctx: Koa.BaseContext): string | null { - return ((ctx.headers['cookie'] || '').match(/igi=(\w+)/) || [null, null])[1]; -} - -function compareOrigin(ctx: Koa.BaseContext): boolean { - function normalizeUrl(url?: string): string { - return url == null ? '' : url.endsWith('/') ? url.substr(0, url.length - 1) : url; - } - - const referer = ctx.headers['referer']; - - return (normalizeUrl(referer) === normalizeUrl(config.url)); -} - -// Init router -const router = new Router(); - -router.get('/disconnect/twitter', async ctx => { - if (!compareOrigin(ctx)) { - ctx.throw(400, 'invalid origin'); - return; - } - - const userToken = getUserToken(ctx); - if (userToken == null) { - ctx.throw(400, 'signin required'); - return; - } - - const user = await Users.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - delete profile.integrations.twitter; - - await UserProfiles.update(user.id, { - integrations: profile.integrations, - }); - - ctx.body = 'Twitterの連携を解除しました :v:'; - - // Publish i updated event - publishMainStream(user.id, 'meUpdated', await Users.pack(user, user, { - detail: true, - includeSecrets: true, - })); -}); - -async function getTwAuth() { - const meta = await fetchMeta(true); - - if (meta.enableTwitterIntegration && meta.twitterConsumerKey && meta.twitterConsumerSecret) { - return autwh({ - consumerKey: meta.twitterConsumerKey, - consumerSecret: meta.twitterConsumerSecret, - callbackUrl: `${config.url}/api/tw/cb`, - }); - } else { - return null; - } -} - -router.get('/connect/twitter', async ctx => { - if (!compareOrigin(ctx)) { - ctx.throw(400, 'invalid origin'); - return; - } - - const userToken = getUserToken(ctx); - if (userToken == null) { - ctx.throw(400, 'signin required'); - return; - } - - const twAuth = await getTwAuth(); - const twCtx = await twAuth!.begin(); - redisClient.set(userToken, JSON.stringify(twCtx)); - ctx.redirect(twCtx.url); -}); - -router.get('/signin/twitter', async ctx => { - const twAuth = await getTwAuth(); - const twCtx = await twAuth!.begin(); - - const sessid = uuid(); - - redisClient.set(sessid, JSON.stringify(twCtx)); - - ctx.cookies.set('signin_with_twitter_sid', sessid, { - path: '/', - secure: config.url.startsWith('https'), - httpOnly: true, - }); - - ctx.redirect(twCtx.url); -}); - -router.get('/tw/cb', async ctx => { - const userToken = getUserToken(ctx); - - const twAuth = await getTwAuth(); - - if (userToken == null) { - const sessid = ctx.cookies.get('signin_with_twitter_sid'); - - if (sessid == null) { - ctx.throw(400, 'invalid session'); - return; - } - - const get = new Promise((res, rej) => { - redisClient.get(sessid, async (_, twCtx) => { - res(twCtx); - }); - }); - - const twCtx = await get; - - const verifier = ctx.query.oauth_verifier; - if (!verifier || typeof verifier !== 'string') { - ctx.throw(400, 'invalid session'); - return; - } - - const result = await twAuth!.done(JSON.parse(twCtx), verifier); - - const link = await UserProfiles.createQueryBuilder() - .where('"integrations"->\'twitter\'->>\'userId\' = :id', { id: result.userId }) - .andWhere('"userHost" IS NULL') - .getOne(); - - if (link == null) { - ctx.throw(404, `@${result.screenName}と連携しているMisskeyアカウントはありませんでした...`); - return; - } - - signin(ctx, await Users.findOneBy({ id: link.userId }) as ILocalUser, true); - } else { - const verifier = ctx.query.oauth_verifier; - - if (!verifier || typeof verifier !== 'string') { - ctx.throw(400, 'invalid session'); - return; - } - - const get = new Promise((res, rej) => { - redisClient.get(userToken, async (_, twCtx) => { - res(twCtx); - }); - }); - - const twCtx = await get; - - const result = await twAuth!.done(JSON.parse(twCtx), verifier); - - const user = await Users.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); - - await UserProfiles.update(user.id, { - integrations: { - ...profile.integrations, - twitter: { - accessToken: result.accessToken, - accessTokenSecret: result.accessTokenSecret, - userId: result.userId, - screenName: result.screenName, - }, - }, - }); - - ctx.body = `Twitter: @${result.screenName} を、Misskey: @${user.username} に接続しました!`; - - // Publish i updated event - publishMainStream(user.id, 'meUpdated', await Users.pack(user, user, { - detail: true, - includeSecrets: true, - })); - } -}); - -export default router; From ed1d74aeb8c7949f6e0c3f04549d8c72848a81ee Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 01:17:33 +0900 Subject: [PATCH 126/180] wip --- packages/backend/src/app.module.ts | 185 +++++++++++++++++- .../src/models/entities/note-watching.ts | 52 ----- packages/backend/src/models/index.ts | 2 - 3 files changed, 184 insertions(+), 55 deletions(-) delete mode 100644 packages/backend/src/models/entities/note-watching.ts diff --git a/packages/backend/src/app.module.ts b/packages/backend/src/app.module.ts index ccad3a186c5b..1a2ac6060b2b 100644 --- a/packages/backend/src/app.module.ts +++ b/packages/backend/src/app.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { Notes, Users } from '@/models/index.js'; +import { AbuseUserReports, AccessTokens, Ads, AnnouncementReads, Announcements, AntennaNotes, Antennas, Apps, AttestationChallenges, AuthSessions, Blockings, ChannelFollowings, ChannelNotePinings, Channels, ClipNotes, Clips, DriveFiles, DriveFolders, Emojis, Followings, FollowRequests, GalleryLikes, GalleryPosts, Hashtags, Instances, MessagingMessages, Metas, ModerationLogs, MutedNotes, Mutings, NoteFavorites, NoteReactions, Notes, NoteThreadMutings, NoteUnreads, Notifications, PageLikes, Pages, PasswordResetRequests, Polls, PollVotes, PromoNotes, PromoReads, RegistrationTickets, RegistryItems, Relays, Signins, SwSubscriptions, UsedUsernames, UserGroupInvitations, UserGroupJoinings, UserGroups, UserIps, UserKeypairs, UserListJoinings, UserLists, UserNotePinings, UserPendings, UserProfiles, UserPublickeys, Users, UserSecurityKeys, Webhooks } from '@/models/index.js'; import { EndpointsModule } from '@/server/api/endpoints.module.js'; import { QueueModule } from '@/queue/queue.module.js'; import { ChartsModule } from './services/chart/charts.module'; @@ -25,6 +25,189 @@ import { db } from './db/postgre'; }, { provide: 'notesRepository', useValue: Notes, + }, { + provide: 'announcementsRepository', + useValue: Announcements, + }, { + provide: 'announcementReadsRepository', + useValue: AnnouncementReads, + }, { + provide: 'appsRepository', + useValue: Apps, + }, { + provide: 'noteFavoritesRepository', + useValue: NoteFavorites, + }, { + provide: 'noteThreadMutingsRepository', + useValue: NoteThreadMutings, + }, { + provide: 'noteReactionsRepository', + useValue: NoteReactions, + }, { + provide: 'noteUnreadsRepository', + useValue: NoteUnreads, + }, { + provide: 'pollsRepository', + useValue: Polls, + }, { + provide: 'pollVotesRepository', + useValue: PollVotes, + }, { + provide: 'serProfilesRepository', + useValue: UserProfiles, + }, { + provide: 'userKeypairsRepository', + useValue: UserKeypairs, + }, { + provide: 'userPendingsRepository', + useValue: UserPendings, + }, { + provide: 'attestationChallengesRepository', + useValue: AttestationChallenges, + }, { + provide: 'userSecurityKeysRepository', + useValue: UserSecurityKeys, + }, { + provide: 'userPublickeysRepository', + useValue: UserPublickeys, + }, { + provide: 'userListsRepository', + useValue: UserLists, + }, { + provide: 'userListJoiningsRepository', + useValue: UserListJoinings, + }, { + provide: 'userGroupsRepository', + useValue: UserGroups, + }, { + provide: 'userGroupJoiningsRepository', + useValue: UserGroupJoinings, + }, { + provide: 'userGroupInvitationsRepository', + useValue: UserGroupInvitations, + }, { + provide: 'userNotePiningsRepository', + useValue: UserNotePinings, + }, { + provide: 'userIpsRepository', + useValue: UserIps, + }, { + provide: 'usedUsernamesRepository', + useValue: UsedUsernames, + }, { + provide: 'followingsRepository', + useValue: Followings, + }, { + provide: 'followRequestsRepository', + useValue: FollowRequests, + }, { + provide: 'instancesRepository', + useValue: Instances, + }, { + provide: 'emojisRepository', + useValue: Emojis, + }, { + provide: 'driveFilesRepository', + useValue: DriveFiles, + }, { + provide: 'driveFoldersRepository', + useValue: DriveFolders, + }, { + provide: 'notificationsRepository', + useValue: Notifications, + }, { + provide: 'metasRepository', + useValue: Metas, + }, { + provide: 'mutingsRepository', + useValue: Mutings, + }, { + provide: 'blockingsRepository', + useValue: Blockings, + }, { + provide: 'swSubscriptionsRepository', + useValue: SwSubscriptions, + }, { + provide: 'hashtagsRepository', + useValue: Hashtags, + }, { + provide: 'abuseUserReportsRepository', + useValue: AbuseUserReports, + }, { + provide: 'registrationTicketsRepository', + useValue: RegistrationTickets, + }, { + provide: 'authSessionsRepository', + useValue: AuthSessions, + }, { + provide: 'accessTokensRepository', + useValue: AccessTokens, + }, { + provide: 'signinsRepository', + useValue: Signins, + }, { + provide: 'messagingMessagesRepository', + useValue: MessagingMessages, + }, { + provide: 'pagesRepository', + useValue: Pages, + }, { + provide: 'pageLikesRepository', + useValue: PageLikes, + }, { + provide: 'galleryPostsRepository', + useValue: GalleryPosts, + }, { + provide: 'galleryLikesRepository', + useValue: GalleryLikes, + }, { + provide: 'moderationLogsRepository', + useValue: ModerationLogs, + }, { + provide: 'clipsRepository', + useValue: Clips, + }, { + provide: 'clipNotesRepository', + useValue: ClipNotes, + }, { + provide: 'antennasRepository', + useValue: Antennas, + }, { + provide: 'antennaNotesRepository', + useValue: AntennaNotes, + }, { + provide: 'promoNotesRepository', + useValue: PromoNotes, + }, { + provide: 'promoReadsRepository', + useValue: PromoReads, + }, { + provide: 'relaysRepository', + useValue: Relays, + }, { + provide: 'mutedNotesRepository', + useValue: MutedNotes, + }, { + provide: 'channelsRepository', + useValue: Channels, + }, { + provide: 'channelFollowingsRepository', + useValue: ChannelFollowings, + }, { + provide: 'channelNotePiningsRepository', + useValue: ChannelNotePinings, + }, { + provide: 'registryItemsRepository', + useValue: RegistryItems, + }, { + provide: 'webhooksRepository', + useValue: Webhooks, + }, { + provide: 'adsRepository', + useValue: Ads, + }, { + provide: 'passwordResetRequestsRepository', + useValue: PasswordResetRequests, }], }) export class AppModule {} diff --git a/packages/backend/src/models/entities/note-watching.ts b/packages/backend/src/models/entities/note-watching.ts deleted file mode 100644 index ed82e7dfe7bf..000000000000 --- a/packages/backend/src/models/entities/note-watching.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { Note } from './note.js'; -import { id } from '../id.js'; - -@Entity() -@Index(['userId', 'noteId'], { unique: true }) -export class NoteWatching { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the NoteWatching.', - }) - public createdAt: Date; - - @Index() - @Column({ - ...id(), - comment: 'The watcher ID.', - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Index() - @Column({ - ...id(), - comment: 'The target Note ID.', - }) - public noteId: Note['id']; - - @ManyToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public note: Note | null; - - //#region Denormalized fields - @Index() - @Column({ - ...id(), - comment: '[Denormalized]', - }) - public noteUserId: Note['userId']; - //#endregion -} diff --git a/packages/backend/src/models/index.ts b/packages/backend/src/models/index.ts index e6ae6892337b..0ecaf66ff356 100644 --- a/packages/backend/src/models/index.ts +++ b/packages/backend/src/models/index.ts @@ -8,7 +8,6 @@ import { Poll } from './entities/poll.js'; import { PollVote } from './entities/poll-vote.js'; import { Meta } from './entities/meta.js'; import { SwSubscription } from './entities/sw-subscription.js'; -import { NoteWatching } from './entities/note-watching.js'; import { NoteThreadMuting } from './entities/note-thread-muting.js'; import { NoteUnread } from './entities/note-unread.js'; import { RegistrationTicket } from './entities/registration-tickets.js'; @@ -71,7 +70,6 @@ export const AnnouncementReads = db.getRepository(AnnouncementRead); export const Apps = db.getRepository(App); export const Notes = db.getRepository(Note); export const NoteFavorites = db.getRepository(NoteFavorite); -export const NoteWatchings = db.getRepository(NoteWatching); export const NoteThreadMutings = db.getRepository(NoteThreadMuting); export const NoteReactions = db.getRepository(NoteReaction); export const NoteUnreads = db.getRepository(NoteUnread); From ca8abfc05bab55d187306a014dc0a87db5d3a4f7 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 01:33:16 +0900 Subject: [PATCH 127/180] wip --- packages/backend/src/RepositoryModule.ts | 198 ++++++++++++++++++ packages/backend/src/app.module.ts | 196 +---------------- .../src/server/api/SignupApiService.ts | 4 +- .../api/endpoints/email-address/available.ts | 5 +- .../server/api/endpoints/i/update-email.ts | 3 +- packages/backend/src/services/CoreModule.ts | 108 ++++++++++ .../src/services/CreateNotificationService.ts | 31 ++- packages/backend/src/services/EmailService.ts | 43 +++- .../{charts.module.ts => ChartsModule.ts} | 28 +-- .../src/services/entities/EntityModule.ts | 69 ++++++ .../src/services/send-email-notification.ts | 36 ---- .../services/validate-email-for-account.ts | 37 ---- 12 files changed, 470 insertions(+), 288 deletions(-) create mode 100644 packages/backend/src/RepositoryModule.ts create mode 100644 packages/backend/src/services/CoreModule.ts rename packages/backend/src/services/chart/{charts.module.ts => ChartsModule.ts} (76%) create mode 100644 packages/backend/src/services/entities/EntityModule.ts delete mode 100644 packages/backend/src/services/send-email-notification.ts delete mode 100644 packages/backend/src/services/validate-email-for-account.ts diff --git a/packages/backend/src/RepositoryModule.ts b/packages/backend/src/RepositoryModule.ts new file mode 100644 index 000000000000..6a6c24b284fd --- /dev/null +++ b/packages/backend/src/RepositoryModule.ts @@ -0,0 +1,198 @@ +import { Module } from '@nestjs/common'; +import { AbuseUserReports, AccessTokens, Ads, AnnouncementReads, Announcements, AntennaNotes, Antennas, Apps, AttestationChallenges, AuthSessions, Blockings, ChannelFollowings, ChannelNotePinings, Channels, ClipNotes, Clips, DriveFiles, DriveFolders, Emojis, Followings, FollowRequests, GalleryLikes, GalleryPosts, Hashtags, Instances, MessagingMessages, Metas, ModerationLogs, MutedNotes, Mutings, NoteFavorites, NoteReactions, Notes, NoteThreadMutings, NoteUnreads, Notifications, PageLikes, Pages, PasswordResetRequests, Polls, PollVotes, PromoNotes, PromoReads, RegistrationTickets, RegistryItems, Relays, Signins, SwSubscriptions, UsedUsernames, UserGroupInvitations, UserGroupJoinings, UserGroups, UserIps, UserKeypairs, UserListJoinings, UserLists, UserNotePinings, UserPendings, UserProfiles, UserPublickeys, Users, UserSecurityKeys, Webhooks } from '@/models/index.js'; + +@Module({ + imports: [ + ], + providers: [{ + provide: 'usersRepository', + useValue: Users, + }, { + provide: 'notesRepository', + useValue: Notes, + }, { + provide: 'announcementsRepository', + useValue: Announcements, + }, { + provide: 'announcementReadsRepository', + useValue: AnnouncementReads, + }, { + provide: 'appsRepository', + useValue: Apps, + }, { + provide: 'noteFavoritesRepository', + useValue: NoteFavorites, + }, { + provide: 'noteThreadMutingsRepository', + useValue: NoteThreadMutings, + }, { + provide: 'noteReactionsRepository', + useValue: NoteReactions, + }, { + provide: 'noteUnreadsRepository', + useValue: NoteUnreads, + }, { + provide: 'pollsRepository', + useValue: Polls, + }, { + provide: 'pollVotesRepository', + useValue: PollVotes, + }, { + provide: 'serProfilesRepository', + useValue: UserProfiles, + }, { + provide: 'userKeypairsRepository', + useValue: UserKeypairs, + }, { + provide: 'userPendingsRepository', + useValue: UserPendings, + }, { + provide: 'attestationChallengesRepository', + useValue: AttestationChallenges, + }, { + provide: 'userSecurityKeysRepository', + useValue: UserSecurityKeys, + }, { + provide: 'userPublickeysRepository', + useValue: UserPublickeys, + }, { + provide: 'userListsRepository', + useValue: UserLists, + }, { + provide: 'userListJoiningsRepository', + useValue: UserListJoinings, + }, { + provide: 'userGroupsRepository', + useValue: UserGroups, + }, { + provide: 'userGroupJoiningsRepository', + useValue: UserGroupJoinings, + }, { + provide: 'userGroupInvitationsRepository', + useValue: UserGroupInvitations, + }, { + provide: 'userNotePiningsRepository', + useValue: UserNotePinings, + }, { + provide: 'userIpsRepository', + useValue: UserIps, + }, { + provide: 'usedUsernamesRepository', + useValue: UsedUsernames, + }, { + provide: 'followingsRepository', + useValue: Followings, + }, { + provide: 'followRequestsRepository', + useValue: FollowRequests, + }, { + provide: 'instancesRepository', + useValue: Instances, + }, { + provide: 'emojisRepository', + useValue: Emojis, + }, { + provide: 'driveFilesRepository', + useValue: DriveFiles, + }, { + provide: 'driveFoldersRepository', + useValue: DriveFolders, + }, { + provide: 'notificationsRepository', + useValue: Notifications, + }, { + provide: 'metasRepository', + useValue: Metas, + }, { + provide: 'mutingsRepository', + useValue: Mutings, + }, { + provide: 'blockingsRepository', + useValue: Blockings, + }, { + provide: 'swSubscriptionsRepository', + useValue: SwSubscriptions, + }, { + provide: 'hashtagsRepository', + useValue: Hashtags, + }, { + provide: 'abuseUserReportsRepository', + useValue: AbuseUserReports, + }, { + provide: 'registrationTicketsRepository', + useValue: RegistrationTickets, + }, { + provide: 'authSessionsRepository', + useValue: AuthSessions, + }, { + provide: 'accessTokensRepository', + useValue: AccessTokens, + }, { + provide: 'signinsRepository', + useValue: Signins, + }, { + provide: 'messagingMessagesRepository', + useValue: MessagingMessages, + }, { + provide: 'pagesRepository', + useValue: Pages, + }, { + provide: 'pageLikesRepository', + useValue: PageLikes, + }, { + provide: 'galleryPostsRepository', + useValue: GalleryPosts, + }, { + provide: 'galleryLikesRepository', + useValue: GalleryLikes, + }, { + provide: 'moderationLogsRepository', + useValue: ModerationLogs, + }, { + provide: 'clipsRepository', + useValue: Clips, + }, { + provide: 'clipNotesRepository', + useValue: ClipNotes, + }, { + provide: 'antennasRepository', + useValue: Antennas, + }, { + provide: 'antennaNotesRepository', + useValue: AntennaNotes, + }, { + provide: 'promoNotesRepository', + useValue: PromoNotes, + }, { + provide: 'promoReadsRepository', + useValue: PromoReads, + }, { + provide: 'relaysRepository', + useValue: Relays, + }, { + provide: 'mutedNotesRepository', + useValue: MutedNotes, + }, { + provide: 'channelsRepository', + useValue: Channels, + }, { + provide: 'channelFollowingsRepository', + useValue: ChannelFollowings, + }, { + provide: 'channelNotePiningsRepository', + useValue: ChannelNotePinings, + }, { + provide: 'registryItemsRepository', + useValue: RegistryItems, + }, { + provide: 'webhooksRepository', + useValue: Webhooks, + }, { + provide: 'adsRepository', + useValue: Ads, + }, { + provide: 'passwordResetRequestsRepository', + useValue: PasswordResetRequests, + }], +}) +export class RepositoryModule {} diff --git a/packages/backend/src/app.module.ts b/packages/backend/src/app.module.ts index 1a2ac6060b2b..bd28d17289ba 100644 --- a/packages/backend/src/app.module.ts +++ b/packages/backend/src/app.module.ts @@ -1,15 +1,16 @@ import { Module } from '@nestjs/common'; -import { AbuseUserReports, AccessTokens, Ads, AnnouncementReads, Announcements, AntennaNotes, Antennas, Apps, AttestationChallenges, AuthSessions, Blockings, ChannelFollowings, ChannelNotePinings, Channels, ClipNotes, Clips, DriveFiles, DriveFolders, Emojis, Followings, FollowRequests, GalleryLikes, GalleryPosts, Hashtags, Instances, MessagingMessages, Metas, ModerationLogs, MutedNotes, Mutings, NoteFavorites, NoteReactions, Notes, NoteThreadMutings, NoteUnreads, Notifications, PageLikes, Pages, PasswordResetRequests, Polls, PollVotes, PromoNotes, PromoReads, RegistrationTickets, RegistryItems, Relays, Signins, SwSubscriptions, UsedUsernames, UserGroupInvitations, UserGroupJoinings, UserGroups, UserIps, UserKeypairs, UserListJoinings, UserLists, UserNotePinings, UserPendings, UserProfiles, UserPublickeys, Users, UserSecurityKeys, Webhooks } from '@/models/index.js'; import { EndpointsModule } from '@/server/api/endpoints.module.js'; import { QueueModule } from '@/queue/queue.module.js'; -import { ChartsModule } from './services/chart/charts.module'; +import { CoreModule } from './services/CoreModule.js'; import { DI_SYMBOLS } from './di-symbols.js'; import { loadConfig } from './config.js'; import { db } from './db/postgre'; +import { RepositoryModule } from './RepositoryModule.js'; @Module({ imports: [ - ChartsModule, + RepositoryModule, + CoreModule, EndpointsModule, QueueModule, ], @@ -19,195 +20,6 @@ import { db } from './db/postgre'; }, { provide: DI_SYMBOLS.db, useValue: db, - }, { - provide: 'usersRepository', - useValue: Users, - }, { - provide: 'notesRepository', - useValue: Notes, - }, { - provide: 'announcementsRepository', - useValue: Announcements, - }, { - provide: 'announcementReadsRepository', - useValue: AnnouncementReads, - }, { - provide: 'appsRepository', - useValue: Apps, - }, { - provide: 'noteFavoritesRepository', - useValue: NoteFavorites, - }, { - provide: 'noteThreadMutingsRepository', - useValue: NoteThreadMutings, - }, { - provide: 'noteReactionsRepository', - useValue: NoteReactions, - }, { - provide: 'noteUnreadsRepository', - useValue: NoteUnreads, - }, { - provide: 'pollsRepository', - useValue: Polls, - }, { - provide: 'pollVotesRepository', - useValue: PollVotes, - }, { - provide: 'serProfilesRepository', - useValue: UserProfiles, - }, { - provide: 'userKeypairsRepository', - useValue: UserKeypairs, - }, { - provide: 'userPendingsRepository', - useValue: UserPendings, - }, { - provide: 'attestationChallengesRepository', - useValue: AttestationChallenges, - }, { - provide: 'userSecurityKeysRepository', - useValue: UserSecurityKeys, - }, { - provide: 'userPublickeysRepository', - useValue: UserPublickeys, - }, { - provide: 'userListsRepository', - useValue: UserLists, - }, { - provide: 'userListJoiningsRepository', - useValue: UserListJoinings, - }, { - provide: 'userGroupsRepository', - useValue: UserGroups, - }, { - provide: 'userGroupJoiningsRepository', - useValue: UserGroupJoinings, - }, { - provide: 'userGroupInvitationsRepository', - useValue: UserGroupInvitations, - }, { - provide: 'userNotePiningsRepository', - useValue: UserNotePinings, - }, { - provide: 'userIpsRepository', - useValue: UserIps, - }, { - provide: 'usedUsernamesRepository', - useValue: UsedUsernames, - }, { - provide: 'followingsRepository', - useValue: Followings, - }, { - provide: 'followRequestsRepository', - useValue: FollowRequests, - }, { - provide: 'instancesRepository', - useValue: Instances, - }, { - provide: 'emojisRepository', - useValue: Emojis, - }, { - provide: 'driveFilesRepository', - useValue: DriveFiles, - }, { - provide: 'driveFoldersRepository', - useValue: DriveFolders, - }, { - provide: 'notificationsRepository', - useValue: Notifications, - }, { - provide: 'metasRepository', - useValue: Metas, - }, { - provide: 'mutingsRepository', - useValue: Mutings, - }, { - provide: 'blockingsRepository', - useValue: Blockings, - }, { - provide: 'swSubscriptionsRepository', - useValue: SwSubscriptions, - }, { - provide: 'hashtagsRepository', - useValue: Hashtags, - }, { - provide: 'abuseUserReportsRepository', - useValue: AbuseUserReports, - }, { - provide: 'registrationTicketsRepository', - useValue: RegistrationTickets, - }, { - provide: 'authSessionsRepository', - useValue: AuthSessions, - }, { - provide: 'accessTokensRepository', - useValue: AccessTokens, - }, { - provide: 'signinsRepository', - useValue: Signins, - }, { - provide: 'messagingMessagesRepository', - useValue: MessagingMessages, - }, { - provide: 'pagesRepository', - useValue: Pages, - }, { - provide: 'pageLikesRepository', - useValue: PageLikes, - }, { - provide: 'galleryPostsRepository', - useValue: GalleryPosts, - }, { - provide: 'galleryLikesRepository', - useValue: GalleryLikes, - }, { - provide: 'moderationLogsRepository', - useValue: ModerationLogs, - }, { - provide: 'clipsRepository', - useValue: Clips, - }, { - provide: 'clipNotesRepository', - useValue: ClipNotes, - }, { - provide: 'antennasRepository', - useValue: Antennas, - }, { - provide: 'antennaNotesRepository', - useValue: AntennaNotes, - }, { - provide: 'promoNotesRepository', - useValue: PromoNotes, - }, { - provide: 'promoReadsRepository', - useValue: PromoReads, - }, { - provide: 'relaysRepository', - useValue: Relays, - }, { - provide: 'mutedNotesRepository', - useValue: MutedNotes, - }, { - provide: 'channelsRepository', - useValue: Channels, - }, { - provide: 'channelFollowingsRepository', - useValue: ChannelFollowings, - }, { - provide: 'channelNotePiningsRepository', - useValue: ChannelNotePinings, - }, { - provide: 'registryItemsRepository', - useValue: RegistryItems, - }, { - provide: 'webhooksRepository', - useValue: Webhooks, - }, { - provide: 'adsRepository', - useValue: Ads, - }, { - provide: 'passwordResetRequestsRepository', - useValue: PasswordResetRequests, }], }) export class AppModule {} diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts index 428aa9e8ad4c..cffb0e837fe7 100644 --- a/packages/backend/src/server/api/SignupApiService.ts +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -10,6 +10,7 @@ import { CaptchaService } from '@/services/CaptchaService.js'; import { IdService } from '@/services/IdService.js'; import { SignupService } from '@/services/SignupService.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { EmailService } from '@/services/EmailService.js'; import { SigninService } from '../SigninService.js'; import type Koa from 'koa'; @@ -37,6 +38,7 @@ export class SignupApiService { private captchaService: CaptchaService, private signupService: SignupService, private signinService: SigninService, + private emailService: EmailService, ) { } @@ -73,7 +75,7 @@ export class SignupApiService { return; } - const available = await validateEmailForAccount(emailAddress); + const available = await this.emailService.validateEmailForAccount(emailAddress); if (!available) { ctx.status = 400; return; diff --git a/packages/backend/src/server/api/endpoints/email-address/available.ts b/packages/backend/src/server/api/endpoints/email-address/available.ts index 696409bbf6a9..460987164715 100644 --- a/packages/backend/src/server/api/endpoints/email-address/available.ts +++ b/packages/backend/src/server/api/endpoints/email-address/available.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { validateEmailForAccount } from '@/services/validate-email-for-account.js'; +import { EmailService } from '@/services/EmailService'; export const meta = { tags: ['users'], @@ -35,9 +35,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + private emailService: EmailService, ) { super(meta, paramDef, async (ps, me) => { - return await validateEmailForAccount(ps.emailAddress); + return await this.emailService.validateEmailForAccount(ps.emailAddress); }); } } diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts index ba002404207d..a5900a7b9fee 100644 --- a/packages/backend/src/server/api/endpoints/i/update-email.ts +++ b/packages/backend/src/server/api/endpoints/i/update-email.ts @@ -5,7 +5,6 @@ import bcrypt from 'bcryptjs'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; import { UserProfiles } from '@/models/index.js'; -import { validateEmailForAccount } from '@/services/validate-email-for-account.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { EmailService } from '@/services/EmailService.js'; import { Config } from '@/config.js'; @@ -75,7 +74,7 @@ export default class extends Endpoint { } if (ps.email != null) { - const available = await validateEmailForAccount(ps.email); + const available = await this.emailService.validateEmailForAccount(ps.email); if (!available) { throw new ApiError(meta.errors.unavailable); } diff --git a/packages/backend/src/services/CoreModule.ts b/packages/backend/src/services/CoreModule.ts new file mode 100644 index 000000000000..d4dc71cc8118 --- /dev/null +++ b/packages/backend/src/services/CoreModule.ts @@ -0,0 +1,108 @@ +import { Module } from '@nestjs/common'; +import { DI_SYMBOLS } from '../di-symbols.js'; +import { ChartsModule } from './chart/ChartsModule.js'; +import { AccountUpdateService } from './AccountUpdateService.js'; +import { AiService } from './AiService.js'; +import { AntennaService } from './AntennaService.js'; +import { AppLockService } from './AppLockService.js'; +import { CaptchaService } from './CaptchaService.js'; +import { CreateNotificationService } from './CreateNotificationService.js'; +import { CreateSystemUserService } from './CreateSystemUserService.js'; +import { CustomEmojiService } from './CustomEmojiService.js'; +import { DeleteAccountService } from './DeleteAccountService.js'; +import { DownloadService } from './DownloadService.js'; +import { DriveService } from './DriveService.js'; +import { EmailService } from './EmailService.js'; +import { FederatedInstanceService } from './FederatedInstanceService.js'; +import { FetchInstanceMetadataService } from './FetchInstanceMetadataService.js'; +import { GlobalEventService } from './GlobalEventService.js'; +import { HashtagService } from './HashtagService.js'; +import { HttpRequestService } from './HttpRequestService.js'; +import { IdService } from './IdService.js'; +import { ImageProcessingService } from './ImageProcessingService.js'; +import { InstanceActorService } from './InstanceActorService.js'; +import { InternalStorageService } from './InternalStorageService.js'; +import { MessagingService } from './MessagingService.js'; +import { MetaService } from './MetaService.js'; +import { MfmService } from './MfmService.js'; +import { ModerationLogService } from './ModerationLogService.js'; +import { NoteCreateService } from './NoteCreateService.js'; +import { NoteDeleteService } from './NoteDeleteService.js'; +import { NotePiningService } from './NotePiningService.js'; +import { NoteReadService } from './NoteReadService.js'; +import { NotificationService } from './NotificationService.js'; +import { PollService } from './PollService.js'; +import { PushNotificationService } from './PushNotificationService.js'; +import { QueryService } from './QueryService.js'; +import { ReactionService } from './ReactionService.js'; +import { RelayService } from './RelayService.js'; +import { S3Service } from './S3Service.js'; +import { SignupService } from './SignupService.js'; +import { TwoFactorAuthenticationService } from './TwoFactorAuthenticationService.js'; +import { UserBlockingService } from './UserBlockingService.js'; +import { UserCacheService } from './UserCacheService.js'; +import { UserFollowingService } from './UserFollowingService.js'; +import { UserKeypairStoreService } from './UserKeypairStoreService.js'; +import { UserListService } from './UserListService.js'; +import { UserMutingService } from './UserMutingService.js'; +import { UserSuspendService } from './UserSuspendService.js'; +import { VideoProcessingService } from './VideoProcessingService.js'; +import { WebhookService } from './WebhookService.js'; +import { EntityModule } from './entities/EntityModule.js'; + +@Module({ + imports: [ + ChartsModule, + EntityModule, + ], + providers: [ + AccountUpdateService, + AiService, + AntennaService, + AppLockService, + CaptchaService, + CreateNotificationService, + CreateSystemUserService, + CustomEmojiService, + DeleteAccountService, + DownloadService, + DriveService, + EmailService, + FederatedInstanceService, + FetchInstanceMetadataService, + GlobalEventService, + HashtagService, + HttpRequestService, + IdService, + ImageProcessingService, + InstanceActorService, + InternalStorageService, + MessagingService, + MetaService, + MfmService, + ModerationLogService, + NoteCreateService, + NoteDeleteService, + NotePiningService, + NoteReadService, + NotificationService, + PollService, + PushNotificationService, + QueryService, + ReactionService, + RelayService, + S3Service, + SignupService, + TwoFactorAuthenticationService, + UserBlockingService, + UserCacheService, + UserFollowingService, + UserKeypairStoreService, + UserListService, + UserMutingService, + UserSuspendService, + VideoProcessingService, + WebhookService, + ], +}) +export class CoreModule {} diff --git a/packages/backend/src/services/CreateNotificationService.ts b/packages/backend/src/services/CreateNotificationService.ts index f7657a0fa6de..dc202d41df92 100644 --- a/packages/backend/src/services/CreateNotificationService.ts +++ b/packages/backend/src/services/CreateNotificationService.ts @@ -75,10 +75,37 @@ export class CreateNotificationService { this.globalEventServie.publishMainStream(notifieeId, 'unreadNotification', packed); pushNotification(notifieeId, 'notification', packed); - if (type === 'follow') sendEmailNotification.follow(notifieeId, await this.usersRepository.findOneByOrFail({ id: data.notifierId! })); - if (type === 'receiveFollowRequest') sendEmailNotification.receiveFollowRequest(notifieeId, await this.usersRepository.findOneByOrFail({ id: data.notifierId! })); + if (type === 'follow') this.#emailNotificationFollow(notifieeId, await this.usersRepository.findOneByOrFail({ id: data.notifierId! })); + if (type === 'receiveFollowRequest') this.#emailNotificationReceiveFollowRequest(notifieeId, await this.usersRepository.findOneByOrFail({ id: data.notifierId! })); }, 2000); return notification; } + + // TODO + //const locales = await import('../../../../locales/index.js'); + + // TODO: locale ファイルをクライアント用とサーバー用で分けたい + + async #emailNotificationFollow(userId: User['id'], follower: User) { + /* + const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); + if (!userProfile.email || !userProfile.emailNotificationTypes.includes('follow')) return; + const locale = locales[userProfile.lang || 'ja-JP']; + const i18n = new I18n(locale); + // TODO: render user information html + sendEmail(userProfile.email, i18n.t('_email._follow.title'), `${follower.name} (@${Acct.toString(follower)})`, `${follower.name} (@${Acct.toString(follower)})`); + */ + } + + async #emailNotificationReceiveFollowRequest(userId: User['id'], follower: User) { + /* + const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); + if (!userProfile.email || !userProfile.emailNotificationTypes.includes('receiveFollowRequest')) return; + const locale = locales[userProfile.lang || 'ja-JP']; + const i18n = new I18n(locale); + // TODO: render user information html + sendEmail(userProfile.email, i18n.t('_email._receiveFollowRequest.title'), `${follower.name} (@${Acct.toString(follower)})`, `${follower.name} (@${Acct.toString(follower)})`); + */ + } } diff --git a/packages/backend/src/services/EmailService.ts b/packages/backend/src/services/EmailService.ts index 639dc8aa7e5a..7aca58930545 100644 --- a/packages/backend/src/services/EmailService.ts +++ b/packages/backend/src/services/EmailService.ts @@ -1,9 +1,11 @@ import * as nodemailer from 'nodemailer'; import { Inject, Injectable } from '@nestjs/common'; -import type { MetaService } from '@/services/MetaService.js'; +import { validate as validateEmail } from 'deep-email-validator'; +import { MetaService } from '@/services/MetaService.js'; import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import Logger from '@/logger.js'; +import type { UserProfiles } from '@/models/index.js'; @Injectable() export class EmailService { @@ -13,6 +15,9 @@ export class EmailService { @Inject(DI_SYMBOLS.config) private config: Config, + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + private metaService: MetaService, ) { this.#logger = new Logger('email'); @@ -133,4 +138,38 @@ export class EmailService { throw err; } } + + public async validateEmailForAccount(emailAddress: string): Promise<{ + available: boolean; + reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp'; + }> { + const meta = await this.metaService.fetch(); + + const exist = await this.userProfilesRepository.countBy({ + emailVerified: true, + email: emailAddress, + }); + + const validated = meta.enableActiveEmailValidation ? await validateEmail({ + email: emailAddress, + validateRegex: true, + validateMx: true, + validateTypo: false, // TLDを見ているみたいだけどclubとか弾かれるので + validateDisposable: true, // 捨てアドかどうかチェック + validateSMTP: false, // 日本だと25ポートが殆どのプロバイダーで塞がれていてタイムアウトになるので + }) : { valid: true }; + + const available = exist === 0 && validated.valid; + + return { + available, + reason: available ? null : + exist !== 0 ? 'used' : + validated.reason === 'regex' ? 'format' : + validated.reason === 'disposable' ? 'disposable' : + validated.reason === 'mx' ? 'mx' : + validated.reason === 'smtp' ? 'smtp' : + null, + }; + } } diff --git a/packages/backend/src/services/chart/charts.module.ts b/packages/backend/src/services/chart/ChartsModule.ts similarity index 76% rename from packages/backend/src/services/chart/charts.module.ts rename to packages/backend/src/services/chart/ChartsModule.ts index 3ccbc7d3df53..4ddbc4ec1d05 100644 --- a/packages/backend/src/services/chart/charts.module.ts +++ b/packages/backend/src/services/chart/ChartsModule.ts @@ -17,20 +17,20 @@ import { ChartManagementService } from './ChartManagementService.js'; imports: [ ], providers: [ - FederationChart, - NotesChart, - UsersChart, - ActiveUsersChart, - InstanceChart, - PerUserNotesChart, - DriveChart, - PerUserReactionsChart, - HashtagChart, - PerUserFollowingChart, - PerUserDriveChart, - ApRequestChart, + FederationChart, + NotesChart, + UsersChart, + ActiveUsersChart, + InstanceChart, + PerUserNotesChart, + DriveChart, + PerUserReactionsChart, + HashtagChart, + PerUserFollowingChart, + PerUserDriveChart, + ApRequestChart, - ChartManagementService, + ChartManagementService, ], - }) +}) export class ChartsModule {} diff --git a/packages/backend/src/services/entities/EntityModule.ts b/packages/backend/src/services/entities/EntityModule.ts new file mode 100644 index 000000000000..471ab9c808a9 --- /dev/null +++ b/packages/backend/src/services/entities/EntityModule.ts @@ -0,0 +1,69 @@ +import { Module } from '@nestjs/common'; +import { AbuseUserReportEntityService } from './AbuseUserReportEntityService.js'; +import { AntennaEntityService } from './AntennaEntityService.js'; +import { AppEntityService } from './AppEntityService.js'; +import { AuthSessionEntityService } from './AuthSessionEntityService.js'; +import { BlockingEntityService } from './BlockingEntityService.js'; +import { ChannelEntityService } from './ChannelEntityService.js'; +import { ClipEntityService } from './ClipEntityService.js'; +import { DriveFileEntityService } from './DriveFileEntityService.js'; +import { DriveFolderEntityService } from './DriveFolderEntityService.js'; +import { EmojiEntityService } from './EmojiEntityService.js'; +import { FollowingEntityService } from './FollowingEntityService.js'; +import { FollowRequestEntityService } from './FollowRequestEntityService.js'; +import { GalleryLikeEntityService } from './GalleryLikeEntityService.js'; +import { GalleryPostEntityService } from './GalleryPostEntityService.js'; +import { HashtagEntityService } from './HashtagEntityService.js'; +import { InstanceEntityService } from './InstanceEntityService.js'; +import { MessagingMessageEntityService } from './MessagingMessageEntityService.js'; +import { ModerationLogEntityService } from './ModerationLogEntityService.js'; +import { MutingEntityService } from './MutingEntityService.js'; +import { NoteEntityService } from './NoteEntityService.js'; +import { NoteFavoriteEntityService } from './NoteFavoriteEntityService.js'; +import { NoteReactionEntityService } from './NoteReactionEntityService.js'; +import { NotificationEntityService } from './NotificationEntityService.js'; +import { PageEntityService } from './PageEntityService.js'; +import { PageLikeEntityService } from './PageLikeEntityService.js'; +import { SigninEntityService } from './SigninEntityService.js'; +import { UserEntityService } from './UserEntityService.js'; +import { UserGroupEntityService } from './UserGroupEntityService.js'; +import { UserGroupInvitationEntityService } from './UserGroupInvitationEntityService.js'; +import { UserListEntityService } from './UserListEntityService.js'; + +@Module({ + imports: [ + ], + providers: [ + AbuseUserReportEntityService, + AntennaEntityService, + AppEntityService, + AuthSessionEntityService, + BlockingEntityService, + ChannelEntityService, + ClipEntityService, + DriveFileEntityService, + DriveFolderEntityService, + EmojiEntityService, + FollowingEntityService, + FollowRequestEntityService, + GalleryLikeEntityService, + GalleryPostEntityService, + HashtagEntityService, + InstanceEntityService, + MessagingMessageEntityService, + ModerationLogEntityService, + MutingEntityService, + NoteEntityService, + NoteFavoriteEntityService, + NoteReactionEntityService, + NotificationEntityService, + PageEntityService, + PageLikeEntityService, + SigninEntityService, + UserEntityService, + UserGroupEntityService, + UserGroupInvitationEntityService, + UserListEntityService, + ], +}) +export class EntityModule {} diff --git a/packages/backend/src/services/send-email-notification.ts b/packages/backend/src/services/send-email-notification.ts deleted file mode 100644 index 4a2f94b425d9..000000000000 --- a/packages/backend/src/services/send-email-notification.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { UserProfiles } from '@/models/index.js'; -import { User } from '@/models/entities/user.js'; -import { sendEmail } from './send-email.js'; -import { I18n } from '@/misc/i18n.js'; -import * as Acct from '@/misc/acct.js'; -// TODO -//const locales = await import('../../../../locales/index.js'); - -// TODO: locale ファイルをクライアント用とサーバー用で分けたい - -async function follow(userId: User['id'], follower: User) { - /* - const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); - if (!userProfile.email || !userProfile.emailNotificationTypes.includes('follow')) return; - const locale = locales[userProfile.lang || 'ja-JP']; - const i18n = new I18n(locale); - // TODO: render user information html - sendEmail(userProfile.email, i18n.t('_email._follow.title'), `${follower.name} (@${Acct.toString(follower)})`, `${follower.name} (@${Acct.toString(follower)})`); - */ -} - -async function receiveFollowRequest(userId: User['id'], follower: User) { - /* - const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); - if (!userProfile.email || !userProfile.emailNotificationTypes.includes('receiveFollowRequest')) return; - const locale = locales[userProfile.lang || 'ja-JP']; - const i18n = new I18n(locale); - // TODO: render user information html - sendEmail(userProfile.email, i18n.t('_email._receiveFollowRequest.title'), `${follower.name} (@${Acct.toString(follower)})`, `${follower.name} (@${Acct.toString(follower)})`); - */ -} - -export const sendEmailNotification = { - follow, - receiveFollowRequest, -}; diff --git a/packages/backend/src/services/validate-email-for-account.ts b/packages/backend/src/services/validate-email-for-account.ts deleted file mode 100644 index b5fa99b9358b..000000000000 --- a/packages/backend/src/services/validate-email-for-account.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { validate as validateEmail } from 'deep-email-validator'; -import { UserProfiles } from '@/models/index.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; - -export async function validateEmailForAccount(emailAddress: string): Promise<{ - available: boolean; - reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp'; -}> { - const meta = await fetchMeta(); - - const exist = await UserProfiles.countBy({ - emailVerified: true, - email: emailAddress, - }); - - const validated = meta.enableActiveEmailValidation ? await validateEmail({ - email: emailAddress, - validateRegex: true, - validateMx: true, - validateTypo: false, // TLDを見ているみたいだけどclubとか弾かれるので - validateDisposable: true, // 捨てアドかどうかチェック - validateSMTP: false, // 日本だと25ポートが殆どのプロバイダーで塞がれていてタイムアウトになるので - }) : { valid: true }; - - const available = exist === 0 && validated.valid; - - return { - available, - reason: available ? null : - exist !== 0 ? 'used' : - validated.reason === 'regex' ? 'format' : - validated.reason === 'disposable' ? 'disposable' : - validated.reason === 'mx' ? 'mx' : - validated.reason === 'smtp' ? 'smtp' : - null, - }; -} From 8058cc949cc18b5d0cb88294fe9e5f85f276843b Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 01:36:10 +0900 Subject: [PATCH 128/180] wip --- packages/backend/src/app.module.ts | 6 +++--- packages/backend/src/di-symbols.ts | 2 +- .../backend/src/queue/DbQueueProcessorsService.ts | 4 ++-- .../queue/ObjectStorageQueueProcessorsService.ts | 4 ++-- .../src/queue/SystemQueueProcessorsService.ts | 4 ++-- packages/backend/src/queue/index.ts | 4 ++-- .../CheckExpiredMutingsProcessorService.ts | 4 ++-- .../processors/CleanChartsProcessorService.ts | 4 ++-- .../src/queue/processors/CleanProcessorService.ts | 4 ++-- .../processors/CleanRemoteFilesProcessorService.ts | 4 ++-- .../processors/DeleteAccountProcessorService.ts | 4 ++-- .../processors/DeleteDriveFilesProcessorService.ts | 4 ++-- .../queue/processors/DeleteFileProcessorService.ts | 4 ++-- .../queue/processors/DeliverProcessorService.ts | 4 ++-- .../EndedPollNotificationProcessorService.ts | 4 ++-- .../processors/ExportBlockingProcessorService.ts | 4 ++-- .../ExportCustomEmojisProcessorService.ts | 4 ++-- .../processors/ExportFollowingProcessorService.ts | 4 ++-- .../processors/ExportMutingProcessorService.ts | 4 ++-- .../processors/ExportNotesProcessorService.ts | 4 ++-- .../processors/ExportUserListsProcessorService.ts | 4 ++-- .../processors/ImportBlockingProcessorService.ts | 4 ++-- .../ImportCustomEmojisProcessorService.ts | 6 +++--- .../processors/ImportFollowingProcessorService.ts | 4 ++-- .../processors/ImportMutingProcessorService.ts | 4 ++-- .../processors/ImportUserListsProcessorService.ts | 4 ++-- .../src/queue/processors/InboxProcessorService.ts | 4 ++-- .../processors/ResyncChartsProcessorService.ts | 4 ++-- .../queue/processors/TickChartsProcessorService.ts | 4 ++-- .../processors/WebhookDeliverProcessorService.ts | 4 ++-- packages/backend/src/queue/queue.module.ts | 6 +++--- packages/backend/src/queue/queue.service.ts | 4 ++-- .../backend/src/server/ActivityPubServerService.ts | 4 ++-- packages/backend/src/server/FileServerService.ts | 4 ++-- .../backend/src/server/MediaProxyServerService.ts | 4 ++-- .../backend/src/server/NodeinfoServerService.ts | 4 ++-- packages/backend/src/server/ServerService.ts | 4 ++-- .../backend/src/server/WellKnownServerService.ts | 4 ++-- packages/backend/src/server/api/ApiCallService.ts | 2 +- .../backend/src/server/api/ApiServerService.ts | 4 ++-- .../backend/src/server/api/AuthenticateService.ts | 2 +- .../backend/src/server/api/RateLimiterService.ts | 4 ++-- .../backend/src/server/api/SigninApiService.ts | 4 ++-- packages/backend/src/server/api/SigninService.ts | 4 ++-- .../backend/src/server/api/SignupApiService.ts | 4 ++-- .../src/server/api/StreamingApiServerService.ts | 6 +++--- .../backend/src/server/api/common/GetterService.ts | 2 +- .../api/endpoints/admin/emoji/add-aliases-bulk.ts | 4 ++-- .../src/server/api/endpoints/admin/emoji/add.ts | 4 ++-- .../src/server/api/endpoints/admin/emoji/copy.ts | 4 ++-- .../api/endpoints/admin/emoji/delete-bulk.ts | 4 ++-- .../src/server/api/endpoints/admin/emoji/delete.ts | 4 ++-- .../endpoints/admin/emoji/remove-aliases-bulk.ts | 4 ++-- .../api/endpoints/admin/emoji/set-aliases-bulk.ts | 4 ++-- .../api/endpoints/admin/emoji/set-category-bulk.ts | 4 ++-- .../src/server/api/endpoints/admin/emoji/update.ts | 4 ++-- .../server/api/endpoints/admin/get-index-stats.ts | 4 ++-- .../server/api/endpoints/admin/get-table-stats.ts | 4 ++-- .../backend/src/server/api/endpoints/admin/meta.ts | 4 ++-- .../src/server/api/endpoints/admin/server-info.ts | 6 +++--- .../src/server/api/endpoints/admin/update-meta.ts | 4 ++-- .../server/api/endpoints/auth/session/generate.ts | 4 ++-- .../backend/src/server/api/endpoints/fetch-rss.ts | 4 ++-- .../src/server/api/endpoints/i/2fa/key-done.ts | 4 ++-- .../src/server/api/endpoints/i/update-email.ts | 4 ++-- packages/backend/src/server/api/endpoints/meta.ts | 4 ++-- .../src/server/api/endpoints/notes/search.ts | 4 ++-- .../src/server/api/endpoints/notes/translate.ts | 4 ++-- .../server/api/endpoints/request-reset-password.ts | 4 ++-- .../server/api/integration/DiscordServerService.ts | 6 +++--- .../server/api/integration/GithubServerService.ts | 6 +++--- .../server/api/integration/TwitterServerService.ts | 6 +++--- .../src/server/api/stream/ChannelsService.ts | 2 +- .../server/api/stream/channels/hybrid-timeline.ts | 2 +- .../backend/src/server/web/ClientServerService.ts | 4 ++-- packages/backend/src/server/web/FeedService.ts | 4 ++-- .../backend/src/server/web/UrlPreviewService.ts | 4 ++-- .../backend/src/services/AccountUpdateService.ts | 4 ++-- packages/backend/src/services/AiService.ts | 4 ++-- packages/backend/src/services/AppLockService.ts | 4 ++-- packages/backend/src/services/CaptchaService.ts | 4 ++-- packages/backend/src/services/CoreModule.ts | 2 +- .../src/services/CreateSystemUserService.ts | 4 ++-- .../backend/src/services/CustomEmojiService.ts | 14 +++++++------- packages/backend/src/services/DownloadService.ts | 4 ++-- packages/backend/src/services/DriveService.ts | 4 ++-- packages/backend/src/services/EmailService.ts | 4 ++-- .../backend/src/services/GlobalEventService.ts | 6 +++--- packages/backend/src/services/HashtagService.ts | 6 +++--- .../backend/src/services/HttpRequestService.ts | 4 ++-- packages/backend/src/services/IdService.ts | 4 ++-- .../backend/src/services/ImageProcessingService.ts | 6 +++--- .../backend/src/services/InternalStorageService.ts | 4 ++-- packages/backend/src/services/MessagingService.ts | 4 ++-- packages/backend/src/services/MetaService.ts | 6 +++--- packages/backend/src/services/MfmService.ts | 4 ++-- .../backend/src/services/ModerationLogService.ts | 2 +- packages/backend/src/services/NoteCreateService.ts | 4 ++-- packages/backend/src/services/NoteDeleteService.ts | 4 ++-- packages/backend/src/services/NotePiningService.ts | 10 +++++----- packages/backend/src/services/NoteReadService.ts | 2 +- .../backend/src/services/NotificationService.ts | 2 +- packages/backend/src/services/PollService.ts | 10 +++++----- .../src/services/PushNotificationService.ts | 4 ++-- packages/backend/src/services/QueryService.ts | 2 +- packages/backend/src/services/ReactionService.ts | 10 +++++----- packages/backend/src/services/S3Service.ts | 4 ++-- packages/backend/src/services/SignupService.ts | 6 +++--- .../src/services/TwoFactorAuthenticationService.ts | 4 ++-- .../backend/src/services/UserSuspendService.ts | 4 ++-- .../backend/src/services/VideoProcessingService.ts | 4 ++-- .../src/services/chart/charts/active-users.ts | 4 ++-- .../src/services/chart/charts/ap-request.ts | 4 ++-- .../backend/src/services/chart/charts/drive.ts | 4 ++-- .../src/services/chart/charts/federation.ts | 4 ++-- .../backend/src/services/chart/charts/hashtag.ts | 4 ++-- .../backend/src/services/chart/charts/instance.ts | 4 ++-- .../backend/src/services/chart/charts/notes.ts | 4 ++-- .../src/services/chart/charts/per-user-drive.ts | 4 ++-- .../services/chart/charts/per-user-following.ts | 4 ++-- .../src/services/chart/charts/per-user-notes.ts | 4 ++-- .../services/chart/charts/per-user-reactions.ts | 4 ++-- .../src/services/chart/charts/test-grouped.ts | 4 ++-- .../src/services/chart/charts/test-intersection.ts | 4 ++-- .../src/services/chart/charts/test-unique.ts | 4 ++-- packages/backend/src/services/chart/charts/test.ts | 4 ++-- .../backend/src/services/chart/charts/users.ts | 4 ++-- .../entities/AbuseUserReportEntityService.ts | 2 +- .../src/services/entities/AntennaEntityService.ts | 2 +- .../src/services/entities/AppEntityService.ts | 2 +- .../services/entities/AuthSessionEntityService.ts | 2 +- .../src/services/entities/BlockingEntityService.ts | 2 +- .../src/services/entities/ChannelEntityService.ts | 2 +- .../src/services/entities/ClipEntityService.ts | 2 +- .../services/entities/DriveFileEntityService.ts | 6 +++--- .../services/entities/DriveFolderEntityService.ts | 2 +- .../src/services/entities/EmojiEntityService.ts | 2 +- .../entities/FollowRequestEntityService.ts | 2 +- .../services/entities/FollowingEntityService.ts | 2 +- .../services/entities/GalleryLikeEntityService.ts | 2 +- .../services/entities/GalleryPostEntityService.ts | 2 +- .../src/services/entities/HashtagEntityService.ts | 2 +- .../src/services/entities/InstanceEntityService.ts | 2 +- .../entities/MessagingMessageEntityService.ts | 2 +- .../entities/ModerationLogEntityService.ts | 2 +- .../src/services/entities/MutingEntityService.ts | 2 +- .../src/services/entities/NoteEntityService.ts | 4 ++-- .../services/entities/NoteFavoriteEntityService.ts | 2 +- .../services/entities/NoteReactionEntityService.ts | 2 +- .../services/entities/NotificationEntityService.ts | 2 +- .../src/services/entities/PageEntityService.ts | 2 +- .../src/services/entities/PageLikeEntityService.ts | 2 +- .../src/services/entities/SigninEntityService.ts | 2 +- .../src/services/entities/UserEntityService.ts | 4 ++-- .../services/entities/UserGroupEntityService.ts | 2 +- .../entities/UserGroupInvitationEntityService.ts | 2 +- .../src/services/entities/UserListEntityService.ts | 2 +- .../src/services/remote/ResolveUserService.ts | 4 ++-- .../src/services/remote/WebfingerService.ts | 4 ++-- .../remote/activitypub/ApDbResolverService.ts | 4 ++-- .../remote/activitypub/ApDeliverManagerService.ts | 4 ++-- .../services/remote/activitypub/ApInboxService.ts | 4 ++-- .../services/remote/activitypub/ApMfmService.ts | 4 ++-- .../remote/activitypub/ApRendererService.ts | 4 ++-- .../remote/activitypub/ApRequestService.ts | 4 ++-- .../remote/activitypub/ApResolverService.ts | 4 ++-- .../remote/activitypub/models/ApImageService.ts | 4 ++-- .../remote/activitypub/models/ApMentionService.ts | 4 ++-- .../remote/activitypub/models/ApNoteService.ts | 4 ++-- .../remote/activitypub/models/ApPersonService.ts | 6 +++--- .../remote/activitypub/models/ApQuestionService.ts | 4 ++-- 171 files changed, 333 insertions(+), 333 deletions(-) diff --git a/packages/backend/src/app.module.ts b/packages/backend/src/app.module.ts index bd28d17289ba..3512e3b5969e 100644 --- a/packages/backend/src/app.module.ts +++ b/packages/backend/src/app.module.ts @@ -2,7 +2,7 @@ import { Module } from '@nestjs/common'; import { EndpointsModule } from '@/server/api/endpoints.module.js'; import { QueueModule } from '@/queue/queue.module.js'; import { CoreModule } from './services/CoreModule.js'; -import { DI_SYMBOLS } from './di-symbols.js'; +import { DI } from './di-symbols.js'; import { loadConfig } from './config.js'; import { db } from './db/postgre'; import { RepositoryModule } from './RepositoryModule.js'; @@ -15,10 +15,10 @@ import { RepositoryModule } from './RepositoryModule.js'; QueueModule, ], providers: [{ - provide: DI_SYMBOLS.config, + provide: DI.config, useValue: loadConfig(), }, { - provide: DI_SYMBOLS.db, + provide: DI.db, useValue: db, }], }) diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts index 634af59dfc3b..21300ebd925e 100644 --- a/packages/backend/src/di-symbols.ts +++ b/packages/backend/src/di-symbols.ts @@ -1,4 +1,4 @@ -export const DI_SYMBOLS = { +export const DI = { config: Symbol('config'), db: Symbol('db'), redis: Symbol('redis'), diff --git a/packages/backend/src/queue/DbQueueProcessorsService.ts b/packages/backend/src/queue/DbQueueProcessorsService.ts index 52cd483261b1..02529c4e2450 100644 --- a/packages/backend/src/queue/DbQueueProcessorsService.ts +++ b/packages/backend/src/queue/DbQueueProcessorsService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import type { DbJobData } from '@/queue/types.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type { DeleteDriveFilesProcessorService } from './processors/DeleteDriveFilesProcessorService.js'; import type { ExportCustomEmojisProcessorService } from './processors/ExportCustomEmojisProcessorService.js'; @@ -20,7 +20,7 @@ import type Bull from 'bull'; @Injectable() export class DbQueueProcessorsService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, private deleteDriveFilesProcessorService: DeleteDriveFilesProcessorService, diff --git a/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts b/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts index b2deb4d72a32..b7cf39905c96 100644 --- a/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts +++ b/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import type { ObjectStorageJobData } from '@/queue/types.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type { CleanRemoteFilesProcessorService } from './processors/CleanRemoteFilesProcessorService.js'; import type { DeleteFileProcessorService } from './processors/DeleteFileProcessorService.js'; @@ -9,7 +9,7 @@ import type Bull from 'bull'; @Injectable() export class ObjectStorageQueueProcessorsService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, private deleteFileProcessorService: DeleteFileProcessorService, diff --git a/packages/backend/src/queue/SystemQueueProcessorsService.ts b/packages/backend/src/queue/SystemQueueProcessorsService.ts index 63e5083d9d71..cb94e4e87da1 100644 --- a/packages/backend/src/queue/SystemQueueProcessorsService.ts +++ b/packages/backend/src/queue/SystemQueueProcessorsService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type { TickChartsProcessorService } from './processors/TickChartsProcessorService.js'; import type { ResyncChartsProcessorService } from './processors/ResyncChartsProcessorService.js'; @@ -11,7 +11,7 @@ import type Bull from 'bull'; @Injectable() export class SystemQueueProcessorsService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, private tickChartsProcessorService: TickChartsProcessorService, diff --git a/packages/backend/src/queue/index.ts b/packages/backend/src/queue/index.ts index 169fd4cec9b6..8e3edfb41903 100644 --- a/packages/backend/src/queue/index.ts +++ b/packages/backend/src/queue/index.ts @@ -1,5 +1,5 @@ -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { getJobInfo } from './get-job-info.js'; import { QueueService } from './queue.service.js'; import { SystemQueueProcessorsService } from './SystemQueueProcessorsService.js'; @@ -13,7 +13,7 @@ import { QueueLoggerService } from './QueueLoggerService.js'; import type { INestApplicationContext } from '@nestjs/common'; export default function(app: INestApplicationContext) { - const config = app.get(DI_SYMBOLS.config); + const config = app.get(DI.config); const queueLoggerService = app.get(QueueLoggerService); const queueLogger = queueLoggerService.logger; const queueService = app.get(QueueService); diff --git a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts index 80753cc357eb..3d5673cc819b 100644 --- a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts +++ b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, MoreThan } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Mutings } from '@/models/index.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; @@ -13,7 +13,7 @@ export class CheckExpiredMutingsProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('mutingsRepository') diff --git a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts index f0eaf64974c4..742047395b47 100644 --- a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, MoreThan } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type FederationChart from '@/services/chart/charts/federation.js'; @@ -23,7 +23,7 @@ export class CleanChartsProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, private federationChart: FederationChart, diff --git a/packages/backend/src/queue/processors/CleanProcessorService.ts b/packages/backend/src/queue/processors/CleanProcessorService.ts index 08aebdf22add..216ea0fa998d 100644 --- a/packages/backend/src/queue/processors/CleanProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanProcessorService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, LessThan, MoreThan } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { UserIps } from '@/models/index.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; @@ -12,7 +12,7 @@ export class CleanProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('userIpsRepository') diff --git a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts index 42fd69757141..360be350e07f 100644 --- a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan, Not } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { DriveFiles } from '@/models/index.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; @@ -13,7 +13,7 @@ export class CleanRemoteFilesProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('driveFilesRepository') diff --git a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts index dc49693b7bb5..56c4069b2249 100644 --- a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { DriveFiles, UserProfiles } from '@/models/index.js'; import { Notes, Users } from '@/models/index.js'; import type { Config } from '@/config.js'; @@ -17,7 +17,7 @@ export class DeleteAccountProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts index f47861c1deb9..ae805fa3975d 100644 --- a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Users, DriveFiles } from '@/models/index.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; @@ -14,7 +14,7 @@ export class DeleteDriveFilesProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/queue/processors/DeleteFileProcessorService.ts b/packages/backend/src/queue/processors/DeleteFileProcessorService.ts index d15f128b927d..4b9084243084 100644 --- a/packages/backend/src/queue/processors/DeleteFileProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteFileProcessorService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type { DriveService } from '@/services/DriveService.js'; @@ -12,7 +12,7 @@ export class DeleteFileProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, private driveService: DriveService, diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts index 6a74e8b7ab29..7e3821e43297 100644 --- a/packages/backend/src/queue/processors/DeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { DriveFiles , Instances } from '@/models/index.js'; import { Config } from '@/config.js'; import type Logger from '@/logger.js'; @@ -26,7 +26,7 @@ export class DeliverProcessorService { #latest: string | null; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('instancesRepository') diff --git a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts index e430b3ca03bc..f40a9601f7ad 100644 --- a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts +++ b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { PollVotes , Notes } from '@/models/index.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; @@ -14,7 +14,7 @@ export class EndedPollNotificationProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('notesRepository') diff --git a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts index 19c7522a7928..47f795252af1 100644 --- a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts @@ -2,7 +2,7 @@ import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { DriveFiles, UserProfiles , Notes , Users , Blockings } from '@/models/index.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; @@ -18,7 +18,7 @@ export class ExportBlockingProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts index 9935e5a01628..ae11917b423e 100644 --- a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts @@ -5,7 +5,7 @@ import { format as dateFormat } from 'date-fns'; import { ulid } from 'ulid'; import mime from 'mime-types'; import archiver from 'archiver'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Emojis, Users } from '@/models/index.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; @@ -20,7 +20,7 @@ export class ExportCustomEmojisProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts index f98d7d2911ea..a68d0a94e5e6 100644 --- a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts @@ -2,7 +2,7 @@ import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; import { In, MoreThan, Not } from 'typeorm'; import { format as dateFormat } from 'date-fns'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { Users } from '@/models/index.js'; import type { Followings, Mutings } from '@/models/index.js'; import type { Config } from '@/config.js'; @@ -20,7 +20,7 @@ export class ExportFollowingProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts index 9fd61054f0ad..6d652f17208b 100644 --- a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts @@ -2,7 +2,7 @@ import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Mutings, Users , Blockings } from '@/models/index.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; @@ -18,7 +18,7 @@ export class ExportMutingProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts index bb9523677565..54deb255aa24 100644 --- a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts @@ -2,7 +2,7 @@ import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Notes, Polls , Users } from '@/models/index.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; @@ -19,7 +19,7 @@ export class ExportNotesProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts index 241e7479a46e..6e7626f80e3f 100644 --- a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts @@ -2,7 +2,7 @@ import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; import { In, IsNull, MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { UserListJoinings, UserLists, Users } from '@/models/index.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; @@ -18,7 +18,7 @@ export class ExportUserListsProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts index 95e686bb8e3a..921630ce4c5d 100644 --- a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { Users } from '@/models/index.js'; import type { Blockings , DriveFiles } from '@/models/index.js'; import type { Config } from '@/config.js'; @@ -19,7 +19,7 @@ export class ImportBlockingProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts index 12375d5005da..0e3c1b982b97 100644 --- a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts @@ -2,7 +2,7 @@ import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan } from 'typeorm'; import unzipper from 'unzipper'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Emojis , DriveFiles , Users } from '@/models/index.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; @@ -21,10 +21,10 @@ export class ImportCustomEmojisProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, @Inject('usersRepository') diff --git a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts index 10fe8674e639..fa4f56741f53 100644 --- a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { Users } from '@/models/index.js'; import type { DriveFiles } from '@/models/index.js'; import type { Config } from '@/config.js'; @@ -19,7 +19,7 @@ export class ImportFollowingProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts index 1763eb1bf628..37c93a335fd1 100644 --- a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { DriveFiles } from '@/models/index.js'; import { Users } from '@/models/index.js'; @@ -21,7 +21,7 @@ export class ImportMutingProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts index ce55a1d0efe9..813abb3a352a 100644 --- a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { DriveFiles , UserListJoinings , UserLists } from '@/models/index.js'; import { Users } from '@/models/index.js'; import type { Config } from '@/config.js'; @@ -20,7 +20,7 @@ export class ImportUserListsProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index bf1118dc02ca..37e80912b7fe 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -2,7 +2,7 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import httpSignature from '@peertube/http-signature'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { Instances } from '@/models/index.js'; import type { DriveFiles } from '@/models/index.js'; import { Config } from '@/config.js'; @@ -33,7 +33,7 @@ export class InboxProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('instancesRepository') diff --git a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts index 28ed62b3e86d..064bc7ab1224 100644 --- a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, MoreThan } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type FederationChart from '@/services/chart/charts/federation.js'; @@ -23,7 +23,7 @@ export class ResyncChartsProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, private federationChart: FederationChart, diff --git a/packages/backend/src/queue/processors/TickChartsProcessorService.ts b/packages/backend/src/queue/processors/TickChartsProcessorService.ts index 8eb61fbe56f3..d27c284f7770 100644 --- a/packages/backend/src/queue/processors/TickChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/TickChartsProcessorService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, MoreThan } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type FederationChart from '@/services/chart/charts/federation.js'; @@ -23,7 +23,7 @@ export class TickChartsProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, private federationChart: FederationChart, diff --git a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts index da02acafb77c..ced2c34d167a 100644 --- a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Webhooks } from '@/models/index.js'; import { Config } from '@/config.js'; import type Logger from '@/logger.js'; @@ -15,7 +15,7 @@ export class WebhookDeliverProcessorService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('webhooksRepository') diff --git a/packages/backend/src/queue/queue.module.ts b/packages/backend/src/queue/queue.module.ts index bda128d128ef..27f8691722b5 100644 --- a/packages/backend/src/queue/queue.module.ts +++ b/packages/backend/src/queue/queue.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { initialize as initializeQueue } from './initialize.js'; import type Bull from 'bull'; @@ -19,8 +19,8 @@ export type WebhookDeliverQueue = Bull.Queue; providers: [ { provide: 'queue:system', useValue: initializeQueue('system') }, { provide: 'queue:endedPollNotification', useValue: initializeQueue('endedPollNotification') }, - { provide: 'queue:deliver', useFactory: (config: Config) => initializeQueue('deliver', config.deliverJobPerSec ?? 128), inject: [DI_SYMBOLS.config] }, - { provide: 'queue:inbox', useFactory: (config: Config) => initializeQueue('inbox', config.inboxJobPerSec ?? 16), inject: [DI_SYMBOLS.config] }, + { provide: 'queue:deliver', useFactory: (config: Config) => initializeQueue('deliver', config.deliverJobPerSec ?? 128), inject: [DI.config] }, + { provide: 'queue:inbox', useFactory: (config: Config) => initializeQueue('inbox', config.inboxJobPerSec ?? 16), inject: [DI.config] }, { provide: 'queue:db', useValue: initializeQueue('db') }, { provide: 'queue:objectStorage', useValue: initializeQueue('objectStorage') }, { provide: 'queue:webhookDeliver', useValue: initializeQueue('webhookDeliver', 64) }, diff --git a/packages/backend/src/queue/queue.service.ts b/packages/backend/src/queue/queue.service.ts index 7d20bda9e0a2..4d76c8be1374 100644 --- a/packages/backend/src/queue/queue.service.ts +++ b/packages/backend/src/queue/queue.service.ts @@ -4,7 +4,7 @@ import type { IActivity } from '@/services/remote/activitypub/type.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import type { Webhook, webhookEventTypes } from '@/models/entities/webhook.js'; import { Config } from '@/config.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from './queue.module.js'; import type { ThinUser } from './types.js'; import type httpSignature from '@peertube/http-signature'; @@ -12,7 +12,7 @@ import type httpSignature from '@peertube/http-signature'; @Injectable() export class QueueService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('queue:system') public systemQueue: SystemQueue, diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 46136de6da6a..5126f32f4760 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -3,7 +3,7 @@ import Router from '@koa/router'; import json from 'koa-json-body'; import httpSignature from '@peertube/http-signature'; import { Brackets, In, IsNull, LessThan, Not } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { Followings , Notes } from '@/models/index.js'; import type { Emojis, NoteReactions , UserProfiles , UserNotePinings , Users } from '@/models/index.js'; import * as url from '@/prelude/url.js'; @@ -25,7 +25,7 @@ const LD_JSON = 'application/ld+json; profile="https://www.w3.org/ns/activitystr @Injectable() export class ActivityPubServerService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index 437806e47810..30a62b35f78c 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -9,7 +9,7 @@ import send from 'koa-send'; import rename from 'rename'; import { Config } from '@/config.js'; import type { DriveFiles } from '@/models/index.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { createTemp } from '@/misc/create-temp.js'; import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; import { StatusError } from '@/misc/status-error.js'; @@ -37,7 +37,7 @@ const commonReadableHandlerGenerator = (ctx: Koa.Context) => (e: Error): void => @Injectable() export class FileServerService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('driveFilesRepository') diff --git a/packages/backend/src/server/MediaProxyServerService.ts b/packages/backend/src/server/MediaProxyServerService.ts index 1597c49cd7f5..282f7f6acef0 100644 --- a/packages/backend/src/server/MediaProxyServerService.ts +++ b/packages/backend/src/server/MediaProxyServerService.ts @@ -4,7 +4,7 @@ import Koa from 'koa'; import cors from '@koa/cors'; import Router from '@koa/router'; import sharp from 'sharp'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { Config } from '@/config.js'; import { isMimeImage } from '@/misc/is-mime-image.js'; import { detectType } from '@/misc/get-file-info.js'; @@ -21,7 +21,7 @@ const serverLogger = new Logger('server', 'gray', false); @Injectable() export class MediaProxyServerService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, private downloadService: DownloadService, diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index 20df97270acf..38c17b7fb5b7 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import Router from '@koa/router'; import { IsNull, MoreThan } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Notes , Users } from '@/models/index.js'; import { Config } from '@/config.js'; @@ -16,7 +16,7 @@ const nodeinfo2_0path = '/nodeinfo/2.0'; @Injectable() export class NodeinfoServerService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 55d10ca1218b..b17bd4c2dcd6 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -11,7 +11,7 @@ import { IsNull } from 'typeorm'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { Config } from '@/config.js'; import type { UserProfiles , Users } from '@/models/index.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import Logger from '@/logger.js'; import { envOption } from '@/env.js'; import * as Acct from '@/misc/acct.js'; @@ -32,7 +32,7 @@ const serverLogger = new Logger('server', 'gray', false); @Injectable() export class ServerService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/server/WellKnownServerService.ts b/packages/backend/src/server/WellKnownServerService.ts index a9068f84916b..f8a646ba02e2 100644 --- a/packages/backend/src/server/WellKnownServerService.ts +++ b/packages/backend/src/server/WellKnownServerService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import Router from '@koa/router'; import { IsNull, MoreThan } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; import { Config } from '@/config.js'; import { escapeAttribute, escapeValue } from '@/prelude/xml'; @@ -13,7 +13,7 @@ import type { FindOptionsWhere } from 'typeorm'; @Injectable() export class WellKnownServerService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index 3ec8c0106bd1..e190b327a777 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -1,6 +1,6 @@ import { performance } from 'perf_hooks'; import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { getIpHash } from '@/misc/get-ip-hash.js'; import type { CacheableLocalUser, User } from '@/models/entities/user.js'; import type { AccessToken } from '@/models/entities/access-token.js'; diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts index a9d3cf02091b..08c4dceb2e65 100644 --- a/packages/backend/src/server/api/ApiServerService.ts +++ b/packages/backend/src/server/api/ApiServerService.ts @@ -8,7 +8,7 @@ import { ModuleRef } from '@nestjs/core'; import { Config } from '@/config.js'; import type { Users } from '@/models/index.js'; import { Instances, AccessTokens } from '@/models/index.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import endpoints from './endpoints.js'; import { ApiCallService } from './ApiCallService.js'; @@ -23,7 +23,7 @@ export class ApiServerService { constructor( private moduleRef: ModuleRef, - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/server/api/AuthenticateService.ts b/packages/backend/src/server/api/AuthenticateService.ts index bbedfc2b2206..472a0fb734f8 100644 --- a/packages/backend/src/server/api/AuthenticateService.ts +++ b/packages/backend/src/server/api/AuthenticateService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { AccessTokens, Apps , Users } from '@/models/index.js'; import type { CacheableLocalUser, ILocalUser } from '@/models/entities/user.js'; import type { AccessToken } from '@/models/entities/access-token.js'; diff --git a/packages/backend/src/server/api/RateLimiterService.ts b/packages/backend/src/server/api/RateLimiterService.ts index 9c73955ee555..38e4faa2a0fc 100644 --- a/packages/backend/src/server/api/RateLimiterService.ts +++ b/packages/backend/src/server/api/RateLimiterService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import Limiter from 'ratelimiter'; import Redis from 'ioredis'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import Logger from '@/logger'; import type { IEndpointMeta } from './endpoints'; @@ -10,7 +10,7 @@ const logger = new Logger('limiter'); @Injectable() export class RateLimiterService { constructor( - @Inject(DI_SYMBOLS.redis) + @Inject(DI.redis) private redisClient: Redis.Redis, ) { } diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index e266cd9dfac9..968156f6bdc5 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -3,7 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; import * as speakeasy from 'speakeasy'; import { IsNull } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { UserSecurityKeys , Signins, UserProfiles } from '@/models/index.js'; import { AttestationChallenges, Users } from '@/models/index.js'; import { Config } from '@/config.js'; @@ -18,7 +18,7 @@ import type Koa from 'koa'; @Injectable() export class SigninApiService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/server/api/SigninService.ts b/packages/backend/src/server/api/SigninService.ts index 02bd73ad5057..89016e706f0e 100644 --- a/packages/backend/src/server/api/SigninService.ts +++ b/packages/backend/src/server/api/SigninService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Signins , Users } from '@/models/index.js'; import { Config } from '@/config.js'; import { IdService } from '@/services/IdService.js'; @@ -11,7 +11,7 @@ import type Koa from 'koa'; @Injectable() export class SigninService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('signinsRepository') diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts index cffb0e837fe7..d2d841ef1cf8 100644 --- a/packages/backend/src/server/api/SignupApiService.ts +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import rndstr from 'rndstr'; import bcrypt from 'bcryptjs'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { RegistrationTickets , UserPendings, UserProfiles , Users } from '@/models/index.js'; import { Config } from '@/config.js'; @@ -17,7 +17,7 @@ import type Koa from 'koa'; @Injectable() export class SignupApiService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts index 3db119879880..24d93b213af4 100644 --- a/packages/backend/src/server/api/StreamingApiServerService.ts +++ b/packages/backend/src/server/api/StreamingApiServerService.ts @@ -2,7 +2,7 @@ import { EventEmitter } from 'events'; import { Inject, Injectable } from '@nestjs/common'; import Redis from 'ioredis'; import * as websocket from 'websocket'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { Users } from '@/models/index.js'; import type { Blockings, ChannelFollowings, Followings, Mutings, UserProfiles } from '@/models/index.js'; import { Config } from '@/config.js'; @@ -17,10 +17,10 @@ import type * as http from 'node:http'; @Injectable() export class StreamingApiServerService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, - @Inject(DI_SYMBOLS.redis) + @Inject(DI.redis) private redisClient: Redis.Redis, @Inject('followingsRepository') diff --git a/packages/backend/src/server/api/common/GetterService.ts b/packages/backend/src/server/api/common/GetterService.ts index dfb181d9326a..0fe3aeae69fd 100644 --- a/packages/backend/src/server/api/common/GetterService.ts +++ b/packages/backend/src/server/api/common/GetterService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Notes , Users } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import type { User } from '@/models/entities/user.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts index 74ee4f40d6f5..4ce428fb209c 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource, In } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Emojis } from '@/models/index.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -30,7 +30,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, @Inject('emojisRepository') diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 4cfeabaec992..7ffe692cda70 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -4,7 +4,7 @@ import { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFiles , Emojis } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ModerationLogService } from '@/services/ModerationLogService.js'; import { EmojiEntityService } from '@/services/entities/EmojiEntityService.js'; @@ -39,7 +39,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, @Inject('driveFilesRepository') diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index 1d335d063065..1913510f77a5 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -4,7 +4,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Emojis } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { DriveService } from '@/services/DriveService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { EmojiEntityService } from '@/services/entities/EmojiEntityService.js'; @@ -51,7 +51,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, @Inject('emojisRepository') diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts index caad95e700e9..b29befdc1273 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource, In } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Emojis } from '@/models/index.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { ModerationLogService } from '@/services/ModerationLogService.js'; export const meta = { @@ -28,7 +28,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, @Inject('emojisRepository') diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts index e1795c7d21a5..535967c92eae 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Emojis } from '@/models/index.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { ModerationLogService } from '@/services/ModerationLogService.js'; import { ApiError } from '../../../error.js'; @@ -35,7 +35,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, @Inject('emojisRepository') diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts index 335198b82302..37eff1657876 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource, In } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Emojis } from '@/models/index.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -30,7 +30,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, @Inject('emojisRepository') diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts index 708a60b867d2..2e285cf924df 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource, In } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Emojis } from '@/models/index.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -30,7 +30,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, @Inject('emojisRepository') diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts index d5aa6049f317..ef69b9b02a04 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource, In } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Emojis } from '@/models/index.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -32,7 +32,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, @Inject('emojisRepository') diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index ce77966ca23c..cbdf902b6f04 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Emojis } from '@/models/index.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -43,7 +43,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, @Inject('emojisRepository') diff --git a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts index 09f54375aae4..e53d0bfcea9b 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -20,7 +20,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, ) { super(meta, paramDef, async () => { diff --git a/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts index 9259eb95ba55..41014cb167ed 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -31,7 +31,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, ) { super(meta, paramDef, async () => { diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 6ae5548939b3..3c365758d3d7 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -3,7 +3,7 @@ import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { MetaService } from '@/services/MetaService.js'; import { Config } from '@/config.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['meta'], @@ -345,7 +345,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, private metaService: MetaService, diff --git a/packages/backend/src/server/api/endpoints/admin/server-info.ts b/packages/backend/src/server/api/endpoints/admin/server-info.ts index 3d1287b26a5c..9c576dffe95d 100644 --- a/packages/backend/src/server/api/endpoints/admin/server-info.ts +++ b/packages/backend/src/server/api/endpoints/admin/server-info.ts @@ -4,7 +4,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource } from 'typeorm'; import Redis from 'ioredis'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -99,10 +99,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, - @Inject(DI_SYMBOLS.redis) + @Inject(DI.redis) private redisClient: Redis.Redis, ) { diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index d8cf64d870cc..05d084f03a43 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -4,7 +4,7 @@ import { Meta } from '@/models/entities/meta.js'; import { ModerationLogService } from '@/services/ModerationLogService.js'; import { DB_MAX_NOTE_TEXT_LENGTH } from '@/misc/hard-limits.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -112,7 +112,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, private moderationLogService: ModerationLogService, diff --git a/packages/backend/src/server/api/endpoints/auth/session/generate.ts b/packages/backend/src/server/api/endpoints/auth/session/generate.ts index aa4a332c3343..a4c1a45d6672 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/generate.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/generate.ts @@ -4,7 +4,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Apps, AuthSessions } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; import { Config } from '@/config.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -49,7 +49,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('appsRepository') diff --git a/packages/backend/src/server/api/endpoints/fetch-rss.ts b/packages/backend/src/server/api/endpoints/fetch-rss.ts index 65c2f597b941..f7146c5fc60d 100644 --- a/packages/backend/src/server/api/endpoints/fetch-rss.ts +++ b/packages/backend/src/server/api/endpoints/fetch-rss.ts @@ -2,7 +2,7 @@ import Parser from 'rss-parser'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Config } from '@/config.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { HttpRequestService } from '@/services/HttpRequestService.js'; const rssParser = new Parser(); @@ -27,7 +27,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, private httpRequestService: HttpRequestService, diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts index 6776ff76ebc5..17b5ea15fbf0 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts @@ -12,7 +12,7 @@ import { } from '@/models/index.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { Config } from '@/config.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { TwoFactorAuthenticationService } from '@/services/TwoFactorAuthenticationService.js'; @@ -40,7 +40,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('userProfilesRepository') diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts index a5900a7b9fee..80a6f202b456 100644 --- a/packages/backend/src/server/api/endpoints/i/update-email.ts +++ b/packages/backend/src/server/api/endpoints/i/update-email.ts @@ -8,7 +8,7 @@ import { UserProfiles } from '@/models/index.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { EmailService } from '@/services/EmailService.js'; import { Config } from '@/config.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../../error.js'; @@ -50,7 +50,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index e51697039a68..42ae52eb287e 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -9,7 +9,7 @@ import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { EmojiEntityService } from '@/services/entities/EmojiEntityService.js'; import { MetaService } from '@/services/MetaService.js'; import { Config } from '@/config.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['meta'], @@ -311,7 +311,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index d63666d5c95c..997c31a19f7a 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -5,7 +5,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { Config } from '@/config.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import es from '../../../../db/elasticsearch.js'; export const meta = { @@ -52,7 +52,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('notesRepository') diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts index c6f537303c75..ee7d6f29c8cf 100644 --- a/packages/backend/src/server/api/endpoints/notes/translate.ts +++ b/packages/backend/src/server/api/endpoints/notes/translate.ts @@ -4,7 +4,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Config } from '@/config.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { MetaService } from '@/services/MetaService.js'; import { HttpRequestService } from '@/services/HttpRequestService.js'; @@ -43,7 +43,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('notesRepository') diff --git a/packages/backend/src/server/api/endpoints/request-reset-password.ts b/packages/backend/src/server/api/endpoints/request-reset-password.ts index ec5c197afab9..05cfb08ff1bb 100644 --- a/packages/backend/src/server/api/endpoints/request-reset-password.ts +++ b/packages/backend/src/server/api/endpoints/request-reset-password.ts @@ -7,7 +7,7 @@ import { UserProfiles, PasswordResetRequests } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/services/IdService.js'; import { Config } from '@/config.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { EmailService } from '@/services/EmailService.js'; import { ApiError } from '../error.js'; @@ -41,7 +41,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/server/api/integration/DiscordServerService.ts b/packages/backend/src/server/api/integration/DiscordServerService.ts index a2ee8bb41f2c..0dab280de912 100644 --- a/packages/backend/src/server/api/integration/DiscordServerService.ts +++ b/packages/backend/src/server/api/integration/DiscordServerService.ts @@ -7,7 +7,7 @@ import { IsNull } from 'typeorm'; import { Config } from '@/config.js'; import type { UserProfiles , Users } from '@/models/index.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { HttpRequestService } from '@/services/HttpRequestService'; import type { ILocalUser } from '@/models/entities/user'; import { GlobalEventService } from '@/services/GlobalEventService'; @@ -19,10 +19,10 @@ import type Koa from 'koa'; @Injectable() export class DiscordServerService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, - @Inject(DI_SYMBOLS.redis) + @Inject(DI.redis) private redisClient: Redis, @Inject('usersRepository') diff --git a/packages/backend/src/server/api/integration/GithubServerService.ts b/packages/backend/src/server/api/integration/GithubServerService.ts index d50a619c367b..0a0016170bbe 100644 --- a/packages/backend/src/server/api/integration/GithubServerService.ts +++ b/packages/backend/src/server/api/integration/GithubServerService.ts @@ -6,7 +6,7 @@ import { v4 as uuid } from 'uuid'; import { IsNull } from 'typeorm'; import { Config } from '@/config.js'; import type { UserProfiles, Users } from '@/models/index.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { HttpRequestService } from '@/services/HttpRequestService'; import type { ILocalUser } from '@/models/entities/user'; import { GlobalEventService } from '@/services/GlobalEventService'; @@ -18,10 +18,10 @@ import type Koa from 'koa'; @Injectable() export class GithubServerService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, - @Inject(DI_SYMBOLS.redis) + @Inject(DI.redis) private redisClient: Redis, @Inject('usersRepository') diff --git a/packages/backend/src/server/api/integration/TwitterServerService.ts b/packages/backend/src/server/api/integration/TwitterServerService.ts index 8e7b7fc48d57..b7e81c78cbe2 100644 --- a/packages/backend/src/server/api/integration/TwitterServerService.ts +++ b/packages/backend/src/server/api/integration/TwitterServerService.ts @@ -7,7 +7,7 @@ import autwh from 'autwh'; import { Config } from '@/config.js'; import type { UserProfiles } from '@/models/index.js'; import { Users } from '@/models/index.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { HttpRequestService } from '@/services/HttpRequestService'; import type { ILocalUser } from '@/models/entities/user'; import { GlobalEventService } from '@/services/GlobalEventService'; @@ -19,10 +19,10 @@ import type Koa from 'koa'; @Injectable() export class TwitterServerService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, - @Inject(DI_SYMBOLS.redis) + @Inject(DI.redis) private redisClient: Redis, @Inject('usersRepository') diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts index 6a31af58cdac..d6005b1ee872 100644 --- a/packages/backend/src/server/api/stream/ChannelsService.ts +++ b/packages/backend/src/server/api/stream/ChannelsService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { HybridTimelineChannelService } from './channels/hybrid-timeline.js'; import { LocalTimelineChannelService } from './channels/local-timeline.js'; import { HomeTimelineChannelService } from './channels/home-timeline.js'; diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index 3ba88f041303..285146ffe361 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -4,7 +4,7 @@ import { checkWordMute } from '@/misc/check-word-mute.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { isInstanceMuted } from '@/misc/is-instance-muted.js'; import type { Packed } from '@/misc/schema.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { MetaService } from '@/services/MetaService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import Channel from '../channel.js'; diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 488fa47c79d0..bf9a9aab5309 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -16,7 +16,7 @@ import { In, IsNull } from 'typeorm'; import { Config } from '@/config.js'; import type { Pages , Channels, Clips, GalleryPosts , Notes, UserProfiles, Users } from '@/models/index.js'; import { getNoteSummary } from '@/misc/get-note-summary.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import * as Acct from '@/misc/acct.js'; import { MetaService } from '@/services/MetaService.js'; import { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from '@/queue/queue.module.js'; @@ -41,7 +41,7 @@ const swAssets = `${_dirname}/../../../../../built/_sw_dist_/`; @Injectable() export class ClientServerService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts index d0f6777c2eb4..d8ccb7037fd9 100644 --- a/packages/backend/src/server/web/FeedService.ts +++ b/packages/backend/src/server/web/FeedService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, IsNull } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { DriveFiles, Notes, UserProfiles , Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type { User } from '@/models/entities/user'; @@ -8,7 +8,7 @@ import type { User } from '@/models/entities/user'; @Injectable() export class FeedService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts index 1913bc495e74..a3ceb851aedd 100644 --- a/packages/backend/src/server/web/UrlPreviewService.ts +++ b/packages/backend/src/server/web/UrlPreviewService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import summaly from 'summaly'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; import { Config } from '@/config.js'; import { MetaService } from '@/services/MetaService.js'; @@ -14,7 +14,7 @@ export class UrlPreviewService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/services/AccountUpdateService.ts b/packages/backend/src/services/AccountUpdateService.ts index fa6b48af23e6..f1b767417638 100644 --- a/packages/backend/src/services/AccountUpdateService.ts +++ b/packages/backend/src/services/AccountUpdateService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { Users } from '@/models/index.js'; import type { Config } from '@/config.js'; import type { User } from '@/models/entities/user.js'; @@ -10,7 +10,7 @@ import type { ApDeliverManagerService } from '@/services/remote/activitypub/ApDe @Injectable() export class AccountUpdateService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/services/AiService.ts b/packages/backend/src/services/AiService.ts index 91d7825539b7..ac66d312ae73 100644 --- a/packages/backend/src/services/AiService.ts +++ b/packages/backend/src/services/AiService.ts @@ -5,7 +5,7 @@ import { Inject, Injectable } from '@nestjs/common'; import * as nsfw from 'nsfwjs'; import si from 'systeminformation'; import type { Config } from '@/config.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); @@ -18,7 +18,7 @@ export class AiService { #model: nsfw.NSFWJS; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, ) { } diff --git a/packages/backend/src/services/AppLockService.ts b/packages/backend/src/services/AppLockService.ts index 37516efd56be..f3c345493b13 100644 --- a/packages/backend/src/services/AppLockService.ts +++ b/packages/backend/src/services/AppLockService.ts @@ -2,7 +2,7 @@ import { promisify } from 'node:util'; import { Inject, Injectable } from '@nestjs/common'; import redisLock from 'redis-lock'; import Redis from 'ioredis'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; /** * Retry delay (ms) for lock acquisition @@ -14,7 +14,7 @@ export class AppLockService { #lock: (key: string, timeout?: number) => Promise<() => void>; constructor( - @Inject(DI_SYMBOLS.redis) + @Inject(DI.redis) private redisClient: Redis.Redis, ) { this.#lock = promisify(redisLock(this.redisClient, retryDelay)); diff --git a/packages/backend/src/services/CaptchaService.ts b/packages/backend/src/services/CaptchaService.ts index e35435676da8..5c4c8222d429 100644 --- a/packages/backend/src/services/CaptchaService.ts +++ b/packages/backend/src/services/CaptchaService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; import { Config } from '@/config.js'; import { HttpRequestService } from './HttpRequestService.js'; @@ -12,7 +12,7 @@ type CaptchaResponse = { @Injectable() export class CaptchaService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, private httpRequestService: HttpRequestService, diff --git a/packages/backend/src/services/CoreModule.ts b/packages/backend/src/services/CoreModule.ts index d4dc71cc8118..0e013e0876f7 100644 --- a/packages/backend/src/services/CoreModule.ts +++ b/packages/backend/src/services/CoreModule.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { DI_SYMBOLS } from '../di-symbols.js'; +import { DI } from '../di-symbols.js'; import { ChartsModule } from './chart/ChartsModule.js'; import { AccountUpdateService } from './AccountUpdateService.js'; import { AiService } from './AiService.js'; diff --git a/packages/backend/src/services/CreateSystemUserService.ts b/packages/backend/src/services/CreateSystemUserService.ts index 52694e691b92..82c462301747 100644 --- a/packages/backend/src/services/CreateSystemUserService.ts +++ b/packages/backend/src/services/CreateSystemUserService.ts @@ -9,13 +9,13 @@ import { UserProfile } from '@/models/entities/user-profile.js'; import { IdService } from '@/services/IdService.js'; import { UserKeypair } from '@/models/entities/user-keypair.js'; import { UsedUsername } from '@/models/entities/used-username.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import generateNativeUserToken from '@/misc/generate-native-user-token.js'; @Injectable() export class CreateSystemUserService { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, private idService: IdService, diff --git a/packages/backend/src/services/CustomEmojiService.ts b/packages/backend/src/services/CustomEmojiService.ts index abfad3915570..aed1011a674e 100644 --- a/packages/backend/src/services/CustomEmojiService.ts +++ b/packages/backend/src/services/CustomEmojiService.ts @@ -1,20 +1,20 @@ import { Inject, Injectable } from '@nestjs/common'; +import { DataSource } from 'typeorm'; import { Emojis } from '@/models/index.js'; -import type { GlobalEventService } from '@/services/GlobalEventService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; -import type { IdService } from '@/services/IdService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; +import { Config } from '@/config.js'; +import { IdService } from '@/services/IdService.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import type { Emoji } from '@/models/entities/emoji.js'; -import type { DataSource } from 'typeorm'; @Injectable() export class CustomEmojiService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, @Inject('emojisRepository') diff --git a/packages/backend/src/services/DownloadService.ts b/packages/backend/src/services/DownloadService.ts index 41e44e4f258a..31d4206b50e8 100644 --- a/packages/backend/src/services/DownloadService.ts +++ b/packages/backend/src/services/DownloadService.ts @@ -6,7 +6,7 @@ import IPCIDR from 'ip-cidr'; import PrivateIp from 'private-ip'; import got, * as Got from 'got'; import chalk from 'chalk'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { Config } from '@/config.js'; import Logger from '@/logger.js'; import { HttpRequestService } from '@/services/HttpRequestService.js'; @@ -20,7 +20,7 @@ export class DownloadService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, private httpRequestService: HttpRequestService, diff --git a/packages/backend/src/services/DriveService.ts b/packages/backend/src/services/DriveService.ts index 806e3eb94f70..5572b1a075fb 100644 --- a/packages/backend/src/services/DriveService.ts +++ b/packages/backend/src/services/DriveService.ts @@ -3,7 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { v4 as uuid } from 'uuid'; import sharp from 'sharp'; import { IsNull } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { DriveFiles , Users , DriveFolders , UserProfiles } from '@/models/index.js'; import { Config } from '@/config.js'; @@ -79,7 +79,7 @@ export class DriveService { #downloaderLogger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/services/EmailService.ts b/packages/backend/src/services/EmailService.ts index 7aca58930545..fdfab91131c7 100644 --- a/packages/backend/src/services/EmailService.ts +++ b/packages/backend/src/services/EmailService.ts @@ -2,7 +2,7 @@ import * as nodemailer from 'nodemailer'; import { Inject, Injectable } from '@nestjs/common'; import { validate as validateEmail } from 'deep-email-validator'; import { MetaService } from '@/services/MetaService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { Config } from '@/config.js'; import Logger from '@/logger.js'; import type { UserProfiles } from '@/models/index.js'; @@ -12,7 +12,7 @@ export class EmailService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('userProfilesRepository') diff --git a/packages/backend/src/services/GlobalEventService.ts b/packages/backend/src/services/GlobalEventService.ts index 0dcb0ddc41f1..adb06eb7b7b9 100644 --- a/packages/backend/src/services/GlobalEventService.ts +++ b/packages/backend/src/services/GlobalEventService.ts @@ -23,16 +23,16 @@ import type { UserStreamTypes, } from '@/server/api/stream/types.js'; import type { Packed } from '@/misc/schema.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { Config } from '@/config.js'; @Injectable() export class GlobalEventService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, - @Inject(DI_SYMBOLS.redis) + @Inject(DI.redis) private redisClient: Redis.Redis, ) { } diff --git a/packages/backend/src/services/HashtagService.ts b/packages/backend/src/services/HashtagService.ts index 7f08b4882008..96e19795694a 100644 --- a/packages/backend/src/services/HashtagService.ts +++ b/packages/backend/src/services/HashtagService.ts @@ -1,11 +1,11 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { Hashtags, Users } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; -import type { IdService } from '@/services/IdService.js'; +import { IdService } from '@/services/IdService.js'; import type { Hashtag } from '@/models/entities/hashtag.js'; -import type HashtagChart from '@/services/chart/charts/hashtag.js'; +import HashtagChart from '@/services/chart/charts/hashtag.js'; @Injectable() export class HashtagService { diff --git a/packages/backend/src/services/HttpRequestService.ts b/packages/backend/src/services/HttpRequestService.ts index 616194c79680..ca2d87b4a38e 100644 --- a/packages/backend/src/services/HttpRequestService.ts +++ b/packages/backend/src/services/HttpRequestService.ts @@ -4,7 +4,7 @@ import CacheableLookup from 'cacheable-lookup'; import fetch from 'node-fetch'; import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent'; import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { Config } from '@/config/types'; import { StatusError } from '@/misc/status-error.js'; import type { Response } from 'node-fetch'; @@ -33,7 +33,7 @@ export class HttpRequestService { public httpsAgent: https.Agent; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, ) { const cache = new CacheableLookup({ diff --git a/packages/backend/src/services/IdService.ts b/packages/backend/src/services/IdService.ts index 8e1b61e8fee8..8e39930462e1 100644 --- a/packages/backend/src/services/IdService.ts +++ b/packages/backend/src/services/IdService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { ulid } from 'ulid'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { genAid } from '@/misc/id/aid.js'; import { genMeid } from '@/misc/id/meid.js'; @@ -12,7 +12,7 @@ export class IdService { #metohd: string; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, ) { this.#metohd = config.id.toLowerCase(); diff --git a/packages/backend/src/services/ImageProcessingService.ts b/packages/backend/src/services/ImageProcessingService.ts index 07b83106548e..d215be213107 100644 --- a/packages/backend/src/services/ImageProcessingService.ts +++ b/packages/backend/src/services/ImageProcessingService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import sharp from 'sharp'; -import { DI_SYMBOLS } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; +import { DI } from '@/di-symbols.js'; +import { Config } from '@/config.js'; export type IImage = { data: Buffer; @@ -12,7 +12,7 @@ export type IImage = { @Injectable() export class ImageProcessingService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, ) { } diff --git a/packages/backend/src/services/InternalStorageService.ts b/packages/backend/src/services/InternalStorageService.ts index 41c72d83573c..426c27c05b3a 100644 --- a/packages/backend/src/services/InternalStorageService.ts +++ b/packages/backend/src/services/InternalStorageService.ts @@ -3,7 +3,7 @@ import * as Path from 'node:path'; import { fileURLToPath } from 'node:url'; import { dirname } from 'node:path'; import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; const _filename = fileURLToPath(import.meta.url); @@ -14,7 +14,7 @@ const path = Path.resolve(_dirname, '../../../../../files'); @Injectable() export class InternalStorageService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, ) { } diff --git a/packages/backend/src/services/MessagingService.ts b/packages/backend/src/services/MessagingService.ts index a819941b66b4..28b951efb525 100644 --- a/packages/backend/src/services/MessagingService.ts +++ b/packages/backend/src/services/MessagingService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, Not } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { MessagingMessages, Mutings, UserGroupJoinings, Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type { DriveFile } from '@/models/entities/drive-file'; @@ -21,7 +21,7 @@ import { PushNotificationService } from './PushNotificationService.js'; @Injectable() export class MessagingService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/services/MetaService.ts b/packages/backend/src/services/MetaService.ts index 5d9ca4ba2c83..ce995fdc3469 100644 --- a/packages/backend/src/services/MetaService.ts +++ b/packages/backend/src/services/MetaService.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; +import { DataSource } from 'typeorm'; import type { Users } from '@/models/index.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { Meta } from '@/models/entities/meta'; import type { OnApplicationShutdown } from '@nestjs/common'; -import type { DataSource } from 'typeorm'; @Injectable() export class MetaService implements OnApplicationShutdown { @@ -11,7 +11,7 @@ export class MetaService implements OnApplicationShutdown { #intervalId: NodeJS.Timer; constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, @Inject('usersRepository') diff --git a/packages/backend/src/services/MfmService.ts b/packages/backend/src/services/MfmService.ts index 9be544fae101..116a0f4d1d88 100644 --- a/packages/backend/src/services/MfmService.ts +++ b/packages/backend/src/services/MfmService.ts @@ -2,7 +2,7 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import * as parse5 from 'parse5'; import { JSDOM } from 'jsdom'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; import type { Config } from '@/config.js'; import { intersperse } from '@/prelude/array.js'; @@ -18,7 +18,7 @@ const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/; @Injectable() export class MfmService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, ) { } diff --git a/packages/backend/src/services/ModerationLogService.ts b/packages/backend/src/services/ModerationLogService.ts index 7551090e8cab..c4d0fb3640f9 100644 --- a/packages/backend/src/services/ModerationLogService.ts +++ b/packages/backend/src/services/ModerationLogService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { ModerationLogs } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; import type { IdService } from '@/services/IdService.js'; diff --git a/packages/backend/src/services/NoteCreateService.ts b/packages/backend/src/services/NoteCreateService.ts index c9b0428734ba..8a1750e42b21 100644 --- a/packages/backend/src/services/NoteCreateService.ts +++ b/packages/backend/src/services/NoteCreateService.ts @@ -27,7 +27,7 @@ import type { UserProfile } from '@/models/entities/user-profile.js'; import { db } from '@/db/postgre.js'; import { RelayService } from '@/services/RelayService.js'; import { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { Config } from '@/config.js'; import NotesChart from '@/services/chart/charts/notes.js'; import PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; @@ -135,7 +135,7 @@ type Option = { @Injectable() export class NoteCreateService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/services/NoteDeleteService.ts b/packages/backend/src/services/NoteDeleteService.ts index 988524e8c545..b77068ccdb34 100644 --- a/packages/backend/src/services/NoteDeleteService.ts +++ b/packages/backend/src/services/NoteDeleteService.ts @@ -13,7 +13,7 @@ import { deliverToFollowers, deliverToUser } from '@/services/remote/activitypub import { countSameRenotes } from '@/misc/count-same-renotes.js'; import type { RelayService } from '@/services/RelayService.js'; import type { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type NotesChart from '@/services/chart/charts/notes.js'; import type PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; @@ -23,7 +23,7 @@ import type { GlobalEventService } from '@/services/GlobalEventService.js'; @Injectable() export class NoteDeleteService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('notesRepository') diff --git a/packages/backend/src/services/NotePiningService.ts b/packages/backend/src/services/NotePiningService.ts index 46630c6fce6c..b4afc22bea7c 100644 --- a/packages/backend/src/services/NotePiningService.ts +++ b/packages/backend/src/services/NotePiningService.ts @@ -1,19 +1,19 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; import { Notes, UserNotePinings } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import type { User } from '@/models/entities/user.js'; import type { Note } from '@/models/entities/note.js'; -import type { IdService } from '@/services/IdService.js'; +import { IdService } from '@/services/IdService.js'; import type { UserNotePining } from '@/models/entities/user-note-pining.js'; -import type { RelayService } from '@/services/RelayService.js'; -import type { Config } from '@/config.js'; +import { RelayService } from '@/services/RelayService.js'; +import { Config } from '@/config.js'; @Injectable() export class NotePiningService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/services/NoteReadService.ts b/packages/backend/src/services/NoteReadService.ts index 9a427ad17d30..724942666813 100644 --- a/packages/backend/src/services/NoteReadService.ts +++ b/packages/backend/src/services/NoteReadService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, IsNull, Not } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { NoteUnreads , Users } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; import type { Channel } from '@/models/entities/channel.js'; diff --git a/packages/backend/src/services/NotificationService.ts b/packages/backend/src/services/NotificationService.ts index a672c48b554c..25d9cb92580d 100644 --- a/packages/backend/src/services/NotificationService.ts +++ b/packages/backend/src/services/NotificationService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Notifications , Users } from '@/models/index.js'; import type { User } from '@/models/entities/user'; import { UserEntityService } from './entities/UserEntityService.js'; diff --git a/packages/backend/src/services/PollService.ts b/packages/backend/src/services/PollService.ts index 7e780cf8bde9..d3dfa8322a30 100644 --- a/packages/backend/src/services/PollService.ts +++ b/packages/backend/src/services/PollService.ts @@ -1,14 +1,14 @@ import { Inject, Injectable } from '@nestjs/common'; import { Not } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Notes, Users , Blockings } from '@/models/index.js'; import { Polls , PollVotes } from '@/models/index.js'; import type { Note } from '@/models/entities/note.js'; -import type { RelayService } from '@/services/RelayService.js'; +import { RelayService } from '@/services/RelayService.js'; import type { CacheableUser } from '@/models/entities/user.js'; -import type { IdService } from '@/services/IdService.js'; -import type { GlobalEventService } from '@/services/GlobalEventService.js'; -import type { CreateNotificationService } from '@/services/CreateNotificationService.js'; +import { IdService } from '@/services/IdService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { CreateNotificationService } from '@/services/CreateNotificationService.js'; import renderUpdate from '@/services/remote/activitypub/renderer/update.js'; import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; import renderNote from '@/services/remote/activitypub/renderer/note.js'; diff --git a/packages/backend/src/services/PushNotificationService.ts b/packages/backend/src/services/PushNotificationService.ts index e9ff6b2f840e..94e7f177fa42 100644 --- a/packages/backend/src/services/PushNotificationService.ts +++ b/packages/backend/src/services/PushNotificationService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import push from 'web-push'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { SwSubscriptions } from '@/models/index.js'; import type { Users } from '@/models/index.js'; import { Config } from '@/config.js'; @@ -42,7 +42,7 @@ function truncateNotification(notification: Packed<'Notification'>): any { @Injectable() export class PushNotificationService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('swSubscriptionsRepository') diff --git a/packages/backend/src/services/QueryService.ts b/packages/backend/src/services/QueryService.ts index b5f1b06cfa81..1b371cc0cf19 100644 --- a/packages/backend/src/services/QueryService.ts +++ b/packages/backend/src/services/QueryService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Brackets } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { NoteThreadMutings , Blockings , ChannelFollowings , MutedNotes , Followings , Mutings , UserProfiles } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; import type { SelectQueryBuilder } from 'typeorm'; diff --git a/packages/backend/src/services/ReactionService.ts b/packages/backend/src/services/ReactionService.ts index c6450d8a90df..fc11e0d0e8b7 100644 --- a/packages/backend/src/services/ReactionService.ts +++ b/packages/backend/src/services/ReactionService.ts @@ -1,18 +1,18 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Blockings, Emojis, NoteReactions , Users , Notes } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import type { IRemoteUser, User } from '@/models/entities/user.js'; import type { Note } from '@/models/entities/note.js'; -import type { IdService } from '@/services/IdService.js'; +import { IdService } from '@/services/IdService.js'; import type { NoteReaction } from '@/models/entities/note-reaction.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; -import type { GlobalEventService } from '@/services/GlobalEventService.js'; -import type { CreateNotificationService } from '@/services/CreateNotificationService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { CreateNotificationService } from '@/services/CreateNotificationService.js'; import DeliverManager from '@/services/remote/activitypub/deliver-manager.js'; -import type PerUserReactionsChart from '@/services/chart/charts/per-user-reactions.js'; +import PerUserReactionsChart from '@/services/chart/charts/per-user-reactions.js'; import { toDbReaction } from '@/misc/reaction-lib.js'; @Injectable() diff --git a/packages/backend/src/services/S3Service.ts b/packages/backend/src/services/S3Service.ts index 14e351ed36dd..33e037786bfe 100644 --- a/packages/backend/src/services/S3Service.ts +++ b/packages/backend/src/services/S3Service.ts @@ -1,7 +1,7 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import S3 from 'aws-sdk/clients/s3.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type { Meta } from '@/models/entities/meta'; import type { HttpRequestService } from '../HttpRequestService.js'; @@ -9,7 +9,7 @@ import type { HttpRequestService } from '../HttpRequestService.js'; @Injectable() export class S3Service { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, private httpRequestService: HttpRequestService, diff --git a/packages/backend/src/services/SignupService.ts b/packages/backend/src/services/SignupService.ts index deb7927e47f7..fad945bf5fbf 100644 --- a/packages/backend/src/services/SignupService.ts +++ b/packages/backend/src/services/SignupService.ts @@ -2,7 +2,7 @@ import { generateKeyPair } from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; import { DataSource, IsNull } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { UsedUsernames } from '@/models/index.js'; import { Users } from '@/models/index.js'; import { Config } from '@/config.js'; @@ -18,10 +18,10 @@ import generateUserToken from './generate-native-user-token.js'; @Injectable() export class SignupService { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/services/TwoFactorAuthenticationService.ts b/packages/backend/src/services/TwoFactorAuthenticationService.ts index 40933c15df40..4d98e2fa95f3 100644 --- a/packages/backend/src/services/TwoFactorAuthenticationService.ts +++ b/packages/backend/src/services/TwoFactorAuthenticationService.ts @@ -1,7 +1,7 @@ import * as crypto from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import * as jsrsasign from 'jsrsasign'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; import { Config } from '@/config.js'; @@ -105,7 +105,7 @@ function PEMString(pemBuffer: Buffer, type = 'CERTIFICATE') { @Injectable() export class TwoFactorAuthenticationService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/services/UserSuspendService.ts b/packages/backend/src/services/UserSuspendService.ts index 5eb5c2514d4f..132a3fb1c47a 100644 --- a/packages/backend/src/services/UserSuspendService.ts +++ b/packages/backend/src/services/UserSuspendService.ts @@ -7,13 +7,13 @@ import renderDelete from '@/services/remote/activitypub/renderer/delete.js'; import renderUndo from '@/services/remote/activitypub/renderer/undo.js'; import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; @Injectable() export class UserSuspendService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/services/VideoProcessingService.ts b/packages/backend/src/services/VideoProcessingService.ts index c22169103ca3..d943c2f50da7 100644 --- a/packages/backend/src/services/VideoProcessingService.ts +++ b/packages/backend/src/services/VideoProcessingService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import FFmpeg from 'fluent-ffmpeg'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type { IImage, ImageProcessingService } from '@/services/ImageProcessingService.js'; import { createTempDir } from '@/misc/create-temp'; @@ -8,7 +8,7 @@ import { createTempDir } from '@/misc/create-temp'; @Injectable() export class VideoProcessingService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, private imageProcessingService: ImageProcessingService, diff --git a/packages/backend/src/services/chart/charts/active-users.ts b/packages/backend/src/services/chart/charts/active-users.ts index a007d45c2356..1750b210659e 100644 --- a/packages/backend/src/services/chart/charts/active-users.ts +++ b/packages/backend/src/services/chart/charts/active-users.ts @@ -2,7 +2,7 @@ import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { AppLockService } from '@/services/AppLockService.js'; import type { User } from '@/models/entities/user.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/active-users.js'; import type { KVs } from '../core.js'; @@ -18,7 +18,7 @@ const year = 1000 * 60 * 60 * 24 * 365; @Injectable() export default class ActiveUsersChart extends Chart { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/ap-request.ts b/packages/backend/src/services/chart/charts/ap-request.ts index 0ebc159c7823..d990132ad5bf 100644 --- a/packages/backend/src/services/chart/charts/ap-request.ts +++ b/packages/backend/src/services/chart/charts/ap-request.ts @@ -1,7 +1,7 @@ import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { AppLockService } from '@/services/AppLockService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/ap-request.js'; import type { KVs } from '../core.js'; @@ -13,7 +13,7 @@ import type { KVs } from '../core.js'; @Injectable() export default class ApRequestChart extends Chart { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/drive.ts b/packages/backend/src/services/chart/charts/drive.ts index 83c7e3afe4e6..c7b245e463c8 100644 --- a/packages/backend/src/services/chart/charts/drive.ts +++ b/packages/backend/src/services/chart/charts/drive.ts @@ -3,7 +3,7 @@ import { Not, IsNull , DataSource } from 'typeorm'; import { DriveFiles } from '@/models/index.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import { AppLockService } from '@/services/AppLockService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/drive.js'; import type { KVs } from '../core.js'; @@ -15,7 +15,7 @@ import type { KVs } from '../core.js'; @Injectable() export default class DriveChart extends Chart { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/federation.ts b/packages/backend/src/services/chart/charts/federation.ts index 3d5cd1da189b..a934bf9a6ba3 100644 --- a/packages/backend/src/services/chart/charts/federation.ts +++ b/packages/backend/src/services/chart/charts/federation.ts @@ -3,7 +3,7 @@ import { DataSource } from 'typeorm'; import { Followings, Instances } from '@/models/index.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { AppLockService } from '@/services/AppLockService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/federation.js'; import type { KVs } from '../core.js'; @@ -15,7 +15,7 @@ import type { KVs } from '../core.js'; @Injectable() export default class FederationChart extends Chart { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/hashtag.ts b/packages/backend/src/services/chart/charts/hashtag.ts index 3581bb347085..d08b842a46ad 100644 --- a/packages/backend/src/services/chart/charts/hashtag.ts +++ b/packages/backend/src/services/chart/charts/hashtag.ts @@ -3,7 +3,7 @@ import { DataSource } from 'typeorm'; import type { User } from '@/models/entities/user.js'; import { Users } from '@/models/index.js'; import { AppLockService } from '@/services/AppLockService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/hashtag.js'; import type { KVs } from '../core.js'; @@ -15,7 +15,7 @@ import type { KVs } from '../core.js'; @Injectable() export default class HashtagChart extends Chart { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/instance.ts b/packages/backend/src/services/chart/charts/instance.ts index 3b3029e947fb..893faf87ef4d 100644 --- a/packages/backend/src/services/chart/charts/instance.ts +++ b/packages/backend/src/services/chart/charts/instance.ts @@ -5,7 +5,7 @@ import type { DriveFile } from '@/models/entities/drive-file.js'; import type { Note } from '@/models/entities/note.js'; import { toPuny } from '@/misc/convert-host.js'; import { AppLockService } from '@/services/AppLockService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/instance.js'; import type { KVs } from '../core.js'; @@ -17,7 +17,7 @@ import type { KVs } from '../core.js'; @Injectable() export default class InstanceChart extends Chart { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/notes.ts b/packages/backend/src/services/chart/charts/notes.ts index ca0c736bbe59..e79f26fe300d 100644 --- a/packages/backend/src/services/chart/charts/notes.ts +++ b/packages/backend/src/services/chart/charts/notes.ts @@ -3,7 +3,7 @@ import { Not, IsNull , DataSource } from 'typeorm'; import { Notes } from '@/models/index.js'; import type { Note } from '@/models/entities/note.js'; import { AppLockService } from '@/services/AppLockService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/notes.js'; import type { KVs } from '../core.js'; @@ -15,7 +15,7 @@ import type { KVs } from '../core.js'; @Injectable() export default class NotesChart extends Chart { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/per-user-drive.ts b/packages/backend/src/services/chart/charts/per-user-drive.ts index fec15cfcba0e..bcadd83d0df4 100644 --- a/packages/backend/src/services/chart/charts/per-user-drive.ts +++ b/packages/backend/src/services/chart/charts/per-user-drive.ts @@ -3,7 +3,7 @@ import { DataSource } from 'typeorm'; import { DriveFiles } from '@/models/index.js'; import type { DriveFile } from '@/models/entities/drive-file.js'; import { AppLockService } from '@/services/AppLockService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/per-user-drive.js'; import type { KVs } from '../core.js'; @@ -15,7 +15,7 @@ import type { KVs } from '../core.js'; @Injectable() export default class PerUserDriveChart extends Chart { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/per-user-following.ts b/packages/backend/src/services/chart/charts/per-user-following.ts index e4a9d9d1ac53..0f84d5bba675 100644 --- a/packages/backend/src/services/chart/charts/per-user-following.ts +++ b/packages/backend/src/services/chart/charts/per-user-following.ts @@ -3,7 +3,7 @@ import { Not, IsNull , DataSource } from 'typeorm'; import { Followings, Users } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; import { AppLockService } from '@/services/AppLockService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/per-user-following.js'; import type { KVs } from '../core.js'; @@ -15,7 +15,7 @@ import type { KVs } from '../core.js'; @Injectable() export default class PerUserFollowingChart extends Chart { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/per-user-notes.ts b/packages/backend/src/services/chart/charts/per-user-notes.ts index 23dd5fbbee16..7ea2a87a9a2c 100644 --- a/packages/backend/src/services/chart/charts/per-user-notes.ts +++ b/packages/backend/src/services/chart/charts/per-user-notes.ts @@ -4,7 +4,7 @@ import type { User } from '@/models/entities/user.js'; import { Notes } from '@/models/index.js'; import type { Note } from '@/models/entities/note.js'; import { AppLockService } from '@/services/AppLockService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/per-user-notes.js'; import type { KVs } from '../core.js'; @@ -16,7 +16,7 @@ import type { KVs } from '../core.js'; @Injectable() export default class PerUserNotesChart extends Chart { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/per-user-reactions.ts b/packages/backend/src/services/chart/charts/per-user-reactions.ts index 5b1fb623d590..a64e0956690e 100644 --- a/packages/backend/src/services/chart/charts/per-user-reactions.ts +++ b/packages/backend/src/services/chart/charts/per-user-reactions.ts @@ -4,7 +4,7 @@ import type { User } from '@/models/entities/user.js'; import type { Note } from '@/models/entities/note.js'; import { Users } from '@/models/index.js'; import { AppLockService } from '@/services/AppLockService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/per-user-reactions.js'; import type { KVs } from '../core.js'; @@ -16,7 +16,7 @@ import type { KVs } from '../core.js'; @Injectable() export default class PerUserReactionsChart extends Chart { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/test-grouped.ts b/packages/backend/src/services/chart/charts/test-grouped.ts index befa514c7715..5ba4a51d1c69 100644 --- a/packages/backend/src/services/chart/charts/test-grouped.ts +++ b/packages/backend/src/services/chart/charts/test-grouped.ts @@ -1,7 +1,7 @@ import { Injectable, Inject } from '@nestjs/common'; import { DataSource , DataSource } from 'typeorm'; import { AppLockService } from '@/services/AppLockService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/test-grouped.js'; import type { KVs } from '../core.js'; @@ -15,7 +15,7 @@ export default class TestGroupedChart extends Chart { private total = {} as Record; constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/test-intersection.ts b/packages/backend/src/services/chart/charts/test-intersection.ts index 5ac8339ac165..36405c11e1d7 100644 --- a/packages/backend/src/services/chart/charts/test-intersection.ts +++ b/packages/backend/src/services/chart/charts/test-intersection.ts @@ -1,7 +1,7 @@ import { Injectable, Inject } from '@nestjs/common'; import { DataSource , DataSource } from 'typeorm'; import { AppLockService } from '@/services/AppLockService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/test-intersection.js'; import type { KVs } from '../core.js'; @@ -13,7 +13,7 @@ import type { KVs } from '../core.js'; @Injectable() export default class TestIntersectionChart extends Chart { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/test-unique.ts b/packages/backend/src/services/chart/charts/test-unique.ts index f304b844f109..0d72f740ad15 100644 --- a/packages/backend/src/services/chart/charts/test-unique.ts +++ b/packages/backend/src/services/chart/charts/test-unique.ts @@ -1,7 +1,7 @@ import { Injectable, Inject } from '@nestjs/common'; import { DataSource , DataSource } from 'typeorm'; import { AppLockService } from '@/services/AppLockService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/test-unique.js'; import type { KVs } from '../core.js'; @@ -13,7 +13,7 @@ import type { KVs } from '../core.js'; @Injectable() export default class TestUniqueChart extends Chart { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/test.ts b/packages/backend/src/services/chart/charts/test.ts index 2d800271f57d..72c2062b66a9 100644 --- a/packages/backend/src/services/chart/charts/test.ts +++ b/packages/backend/src/services/chart/charts/test.ts @@ -1,7 +1,7 @@ import { Injectable, Inject } from '@nestjs/common'; import { DataSource , DataSource } from 'typeorm'; import { AppLockService } from '@/services/AppLockService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/test.js'; import type { KVs } from '../core.js'; @@ -15,7 +15,7 @@ export default class TestChart extends Chart { public total = 0; // publicにするのはテストのため constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/chart/charts/users.ts b/packages/backend/src/services/chart/charts/users.ts index 10830b69baf3..fdc5be9174cd 100644 --- a/packages/backend/src/services/chart/charts/users.ts +++ b/packages/backend/src/services/chart/charts/users.ts @@ -3,7 +3,7 @@ import { Not, IsNull , DataSource } from 'typeorm'; import { Users } from '@/models/index.js'; import type { User } from '@/models/entities/user.js'; import { AppLockService } from '@/services/AppLockService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/users.js'; import type { KVs } from '../core.js'; @@ -15,7 +15,7 @@ import type { KVs } from '../core.js'; @Injectable() export default class UsersChart extends Chart { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, private appLockService: AppLockService, diff --git a/packages/backend/src/services/entities/AbuseUserReportEntityService.ts b/packages/backend/src/services/entities/AbuseUserReportEntityService.ts index 73cbc3f02521..8a218195819a 100644 --- a/packages/backend/src/services/entities/AbuseUserReportEntityService.ts +++ b/packages/backend/src/services/entities/AbuseUserReportEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { AbuseUserReports } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { AbuseUserReport } from '@/models/entities/abuse-user-report.js'; diff --git a/packages/backend/src/services/entities/AntennaEntityService.ts b/packages/backend/src/services/entities/AntennaEntityService.ts index 4f613cf0e67b..98a089b8bf9b 100644 --- a/packages/backend/src/services/entities/AntennaEntityService.ts +++ b/packages/backend/src/services/entities/AntennaEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { AntennaNotes, Antennas, UserGroupJoinings } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/entities/AppEntityService.ts b/packages/backend/src/services/entities/AppEntityService.ts index 0d468bb5181a..90e6f91e69c1 100644 --- a/packages/backend/src/services/entities/AppEntityService.ts +++ b/packages/backend/src/services/entities/AppEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { AccessTokens, Apps } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/entities/AuthSessionEntityService.ts b/packages/backend/src/services/entities/AuthSessionEntityService.ts index 438205658f4c..ec4ad82a634f 100644 --- a/packages/backend/src/services/entities/AuthSessionEntityService.ts +++ b/packages/backend/src/services/entities/AuthSessionEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { Apps } from '@/models/index.js'; import type { AuthSessions } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; diff --git a/packages/backend/src/services/entities/BlockingEntityService.ts b/packages/backend/src/services/entities/BlockingEntityService.ts index c53956e239e8..ea5d4f46ee04 100644 --- a/packages/backend/src/services/entities/BlockingEntityService.ts +++ b/packages/backend/src/services/entities/BlockingEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Blockings } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/entities/ChannelEntityService.ts b/packages/backend/src/services/entities/ChannelEntityService.ts index e7a763905ae9..d5cfa4907829 100644 --- a/packages/backend/src/services/entities/ChannelEntityService.ts +++ b/packages/backend/src/services/entities/ChannelEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { ChannelFollowings, Channels, DriveFiles, NoteUnreads } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/entities/ClipEntityService.ts b/packages/backend/src/services/entities/ClipEntityService.ts index a69a88df8e74..a40600eb5596 100644 --- a/packages/backend/src/services/entities/ClipEntityService.ts +++ b/packages/backend/src/services/entities/ClipEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Clips } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/entities/DriveFileEntityService.ts b/packages/backend/src/services/entities/DriveFileEntityService.ts index 3259cc08978c..26b0d12d88be 100644 --- a/packages/backend/src/services/entities/DriveFileEntityService.ts +++ b/packages/backend/src/services/entities/DriveFileEntityService.ts @@ -1,7 +1,7 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import { DataSource, In } from 'typeorm'; import * as mfm from 'mfm-js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Notes , DriveFiles } from '@/models/index.js'; import { Config } from '@/config.js'; import type { Packed } from '@/misc/schema.js'; @@ -22,10 +22,10 @@ type PackOptions = { @Injectable() export class DriveFileEntityService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, @Inject('notesRepository') diff --git a/packages/backend/src/services/entities/DriveFolderEntityService.ts b/packages/backend/src/services/entities/DriveFolderEntityService.ts index d3244270d81d..4b21728b8ecf 100644 --- a/packages/backend/src/services/entities/DriveFolderEntityService.ts +++ b/packages/backend/src/services/entities/DriveFolderEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { DriveFiles, DriveFolders } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/entities/EmojiEntityService.ts b/packages/backend/src/services/entities/EmojiEntityService.ts index 90b3ee40b4df..c47c4d090e5b 100644 --- a/packages/backend/src/services/entities/EmojiEntityService.ts +++ b/packages/backend/src/services/entities/EmojiEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Emojis } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/entities/FollowRequestEntityService.ts b/packages/backend/src/services/entities/FollowRequestEntityService.ts index 12d4a310cc1a..89c7568354bd 100644 --- a/packages/backend/src/services/entities/FollowRequestEntityService.ts +++ b/packages/backend/src/services/entities/FollowRequestEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { FollowRequests } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/entities/FollowingEntityService.ts b/packages/backend/src/services/entities/FollowingEntityService.ts index 774deecd16e3..cd7006097d6d 100644 --- a/packages/backend/src/services/entities/FollowingEntityService.ts +++ b/packages/backend/src/services/entities/FollowingEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Followings } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/entities/GalleryLikeEntityService.ts b/packages/backend/src/services/entities/GalleryLikeEntityService.ts index 66e94c094473..ecee6e155d1f 100644 --- a/packages/backend/src/services/entities/GalleryLikeEntityService.ts +++ b/packages/backend/src/services/entities/GalleryLikeEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { GalleryPosts } from '@/models/index.js'; import type { GalleryLikes } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; diff --git a/packages/backend/src/services/entities/GalleryPostEntityService.ts b/packages/backend/src/services/entities/GalleryPostEntityService.ts index bc0e8731f32b..7706d1ec4456 100644 --- a/packages/backend/src/services/entities/GalleryPostEntityService.ts +++ b/packages/backend/src/services/entities/GalleryPostEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { GalleryLikes, GalleryPosts } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/entities/HashtagEntityService.ts b/packages/backend/src/services/entities/HashtagEntityService.ts index 5d2756a6b295..9d6376c5897e 100644 --- a/packages/backend/src/services/entities/HashtagEntityService.ts +++ b/packages/backend/src/services/entities/HashtagEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Hashtags } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/entities/InstanceEntityService.ts b/packages/backend/src/services/entities/InstanceEntityService.ts index 73e4c249613b..529c06123f59 100644 --- a/packages/backend/src/services/entities/InstanceEntityService.ts +++ b/packages/backend/src/services/entities/InstanceEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Instances } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/entities/MessagingMessageEntityService.ts b/packages/backend/src/services/entities/MessagingMessageEntityService.ts index 7354ba42e067..95a19b7278e3 100644 --- a/packages/backend/src/services/entities/MessagingMessageEntityService.ts +++ b/packages/backend/src/services/entities/MessagingMessageEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { MessagingMessages } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/entities/ModerationLogEntityService.ts b/packages/backend/src/services/entities/ModerationLogEntityService.ts index 89483ddb8489..ee45f3317ec0 100644 --- a/packages/backend/src/services/entities/ModerationLogEntityService.ts +++ b/packages/backend/src/services/entities/ModerationLogEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { ModerationLogs } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/entities/MutingEntityService.ts b/packages/backend/src/services/entities/MutingEntityService.ts index 6773e913b844..21f783eb83ba 100644 --- a/packages/backend/src/services/entities/MutingEntityService.ts +++ b/packages/backend/src/services/entities/MutingEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Mutings } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/entities/NoteEntityService.ts b/packages/backend/src/services/entities/NoteEntityService.ts index ac13f2e28f6b..05d4067f447a 100644 --- a/packages/backend/src/services/entities/NoteEntityService.ts +++ b/packages/backend/src/services/entities/NoteEntityService.ts @@ -1,7 +1,7 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import { DataSource, In } from 'typeorm'; import * as mfm from 'mfm-js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Notes , Polls, PollVotes , DriveFiles , Channels , Followings , Users , NoteReactions } from '@/models/index.js'; import { Config } from '@/config.js'; import type { Packed } from '@/misc/schema.js'; @@ -18,7 +18,7 @@ import { DriveFileEntityService } from './DriveFileEntityService.js'; @Injectable() export class NoteEntityService { constructor( - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, @Inject('usersRepository') diff --git a/packages/backend/src/services/entities/NoteFavoriteEntityService.ts b/packages/backend/src/services/entities/NoteFavoriteEntityService.ts index 9f39a3b810ed..0d01f1009346 100644 --- a/packages/backend/src/services/entities/NoteFavoriteEntityService.ts +++ b/packages/backend/src/services/entities/NoteFavoriteEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { NoteFavorites } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/entities/NoteReactionEntityService.ts b/packages/backend/src/services/entities/NoteReactionEntityService.ts index 5a0220d53ed6..c0b0c28f9824 100644 --- a/packages/backend/src/services/entities/NoteReactionEntityService.ts +++ b/packages/backend/src/services/entities/NoteReactionEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { NoteReactions } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/entities/NotificationEntityService.ts b/packages/backend/src/services/entities/NotificationEntityService.ts index 86945113ba07..33656482be4f 100644 --- a/packages/backend/src/services/entities/NotificationEntityService.ts +++ b/packages/backend/src/services/entities/NotificationEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { AccessTokens, NoteReactions , Notifications } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Notification } from '@/models/entities/notification.js'; diff --git a/packages/backend/src/services/entities/PageEntityService.ts b/packages/backend/src/services/entities/PageEntityService.ts index 2d8c08833d5e..b19fda5279f7 100644 --- a/packages/backend/src/services/entities/PageEntityService.ts +++ b/packages/backend/src/services/entities/PageEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { DriveFiles } from '@/models/index.js'; import type { Pages , PageLikes } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; diff --git a/packages/backend/src/services/entities/PageLikeEntityService.ts b/packages/backend/src/services/entities/PageLikeEntityService.ts index d4a54c18eac9..91b930fe6cf5 100644 --- a/packages/backend/src/services/entities/PageLikeEntityService.ts +++ b/packages/backend/src/services/entities/PageLikeEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { PageLikes } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/entities/SigninEntityService.ts b/packages/backend/src/services/entities/SigninEntityService.ts index 7f52e0e9332b..b2d0987783ce 100644 --- a/packages/backend/src/services/entities/SigninEntityService.ts +++ b/packages/backend/src/services/entities/SigninEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Signins } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/entities/UserEntityService.ts b/packages/backend/src/services/entities/UserEntityService.ts index 84e0b39fe302..e5d0acb99a1e 100644 --- a/packages/backend/src/services/entities/UserEntityService.ts +++ b/packages/backend/src/services/entities/UserEntityService.ts @@ -1,7 +1,7 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import { EntityRepository, Repository, In, Not } from 'typeorm'; import Ajv from 'ajv'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { Pages } from '@/models/index.js'; import type { AntennaNotes, Instances, MessagingMessages, UserSecurityKeys , Blockings, Mutings , Followings, FollowRequests, Users, DriveFiles, NoteUnreads, ChannelFollowings, Notifications, UserNotePinings, UserProfiles , AnnouncementReads, Announcements, UserGroupJoinings } from '@/models/index.js'; import { Config } from '@/config.js'; @@ -44,7 +44,7 @@ function isRemoteUser(user: User | { host: User['host'] }): boolean { @Injectable() export class UserEntityService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/services/entities/UserGroupEntityService.ts b/packages/backend/src/services/entities/UserGroupEntityService.ts index 78c9b9985930..26f2ce0bcaab 100644 --- a/packages/backend/src/services/entities/UserGroupEntityService.ts +++ b/packages/backend/src/services/entities/UserGroupEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { UserGroupJoinings, UserGroups } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/entities/UserGroupInvitationEntityService.ts b/packages/backend/src/services/entities/UserGroupInvitationEntityService.ts index a8c144790237..24eabfd899fd 100644 --- a/packages/backend/src/services/entities/UserGroupInvitationEntityService.ts +++ b/packages/backend/src/services/entities/UserGroupInvitationEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { UserGroupInvitations } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/entities/UserListEntityService.ts b/packages/backend/src/services/entities/UserListEntityService.ts index ba5ce69c652e..6035df0b0317 100644 --- a/packages/backend/src/services/entities/UserListEntityService.ts +++ b/packages/backend/src/services/entities/UserListEntityService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { UserListJoinings, UserLists } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; diff --git a/packages/backend/src/services/remote/ResolveUserService.ts b/packages/backend/src/services/remote/ResolveUserService.ts index cbdb6a377d81..7e39b8cb3606 100644 --- a/packages/backend/src/services/remote/ResolveUserService.ts +++ b/packages/backend/src/services/remote/ResolveUserService.ts @@ -2,7 +2,7 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import chalk from 'chalk'; import { IsNull } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; import type { IRemoteUser, User } from '@/models/entities/user.js'; import type { Config } from '@/config.js'; @@ -17,7 +17,7 @@ export class ResolveUserService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/services/remote/WebfingerService.ts b/packages/backend/src/services/remote/WebfingerService.ts index 137c1569815a..b04c70c7d6cc 100644 --- a/packages/backend/src/services/remote/WebfingerService.ts +++ b/packages/backend/src/services/remote/WebfingerService.ts @@ -1,6 +1,6 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { query as urlQuery } from '@/prelude/url.js'; import type { HttpRequestService } from '@/services/HttpRequestService.js'; @@ -18,7 +18,7 @@ type IWebFinger = { @Injectable() export class WebfingerService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, private httpRequestService: HttpRequestService, diff --git a/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts b/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts index ee4630d80858..346e35e6d6ca 100644 --- a/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts +++ b/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import escapeRegexp from 'escape-regexp'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { MessagingMessages, Notes, UserPublickeys } from '@/models/index.js'; import { Users } from '@/models/index.js'; import type { Config } from '@/config.js'; @@ -35,7 +35,7 @@ export class ApDbResolverService { #publicKeyByUserIdCache: Cache; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('messagingMessagesRepository') diff --git a/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts b/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts index 13bba3cb96f5..10f11df1d557 100644 --- a/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts +++ b/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, Not } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Followings , Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type { ILocalUser, IRemoteUser, User } from '@/models/entities/user.js'; @@ -29,7 +29,7 @@ const isDirect = (recipe: any): recipe is IDirectRecipe => @Injectable() export class ApDeliverManagerService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/services/remote/activitypub/ApInboxService.ts b/packages/backend/src/services/remote/activitypub/ApInboxService.ts index 973f71100337..e352d814ad81 100644 --- a/packages/backend/src/services/remote/activitypub/ApInboxService.ts +++ b/packages/backend/src/services/remote/activitypub/ApInboxService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Followings, Notes , Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type { CacheableRemoteUser } from '@/models/entities/user.js'; @@ -33,7 +33,7 @@ export class ApInboxService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/services/remote/activitypub/ApMfmService.ts b/packages/backend/src/services/remote/activitypub/ApMfmService.ts index ff967e2b6592..222cf6168a17 100644 --- a/packages/backend/src/services/remote/activitypub/ApMfmService.ts +++ b/packages/backend/src/services/remote/activitypub/ApMfmService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type { MfmService } from '@/services/MfmService.js'; import type { IObject } from './type'; @@ -7,7 +7,7 @@ import type { IObject } from './type'; @Injectable() export class ApMfmService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, private mfmService: MfmService, diff --git a/packages/backend/src/services/remote/activitypub/ApRendererService.ts b/packages/backend/src/services/remote/activitypub/ApRendererService.ts index 82aee28acf26..3fd4ed58da3a 100644 --- a/packages/backend/src/services/remote/activitypub/ApRendererService.ts +++ b/packages/backend/src/services/remote/activitypub/ApRendererService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, IsNull } from 'typeorm'; import { v4 as uuid } from 'uuid'; import * as mfm from 'mfm-js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Polls , DriveFiles, Emojis, Notes, Users } from '@/models/index.js'; import type { Config } from '@/config.js'; import type { ILocalUser, IRemoteUser, User } from '@/models/entities/user.js'; @@ -24,7 +24,7 @@ import type { IIdentifier } from './models/identifier.js'; @Injectable() export class ApRendererService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/services/remote/activitypub/ApRequestService.ts b/packages/backend/src/services/remote/activitypub/ApRequestService.ts index e6f9bde77454..f8507e1bf3f7 100644 --- a/packages/backend/src/services/remote/activitypub/ApRequestService.ts +++ b/packages/backend/src/services/remote/activitypub/ApRequestService.ts @@ -1,7 +1,7 @@ import * as crypto from 'node:crypto'; import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type { User } from '@/models/entities/user.js'; import type { UserKeypairStoreService } from '@/services/UserKeypairStoreService.js'; @@ -28,7 +28,7 @@ type PrivateKey = { @Injectable() export class ApRequestService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, private userKeypairStoreService: UserKeypairStoreService, diff --git a/packages/backend/src/services/remote/activitypub/ApResolverService.ts b/packages/backend/src/services/remote/activitypub/ApResolverService.ts index 43fc1f51907e..ce983bfee61b 100644 --- a/packages/backend/src/services/remote/activitypub/ApResolverService.ts +++ b/packages/backend/src/services/remote/activitypub/ApResolverService.ts @@ -6,7 +6,7 @@ import type { Notes , Polls , NoteReactions , Users } from '@/models/index.js'; import type { Config } from '@/config.js'; import type { MetaService } from '@/services/MetaService.js'; import type { HttpRequestService } from '@/services/HttpRequestService.js'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import { isCollectionOrOrderedCollection } from './type.js'; import type { ApDbResolverService } from './ApDbResolverService.js'; import type { ApRendererService } from './ApRendererService.js'; @@ -16,7 +16,7 @@ import type { ApRequestService } from './ApRequestService.js'; @Injectable() export class ApResolverService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('usersRepository') diff --git a/packages/backend/src/services/remote/activitypub/models/ApImageService.ts b/packages/backend/src/services/remote/activitypub/models/ApImageService.ts index 6a5fe62673fd..d258bc197286 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApImageService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApImageService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { DriveFiles } from '@/models/index.js'; import type { Config } from '@/config.js'; import type { CacheableRemoteUser } from '@/models/entities/user.js'; @@ -17,7 +17,7 @@ export class ApImageService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('driveFilesRepository') diff --git a/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts b/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts index e2dd1697d0ff..1d1f3b123304 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; import type { Config } from '@/config.js'; import { toArray, unique } from '@/prelude/array.js'; @@ -12,7 +12,7 @@ import type { IObject , IApMention } from '../type.js'; @Injectable() export class ApMentionService { constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, private apResolverService: ApResolverService, diff --git a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts index 7bb446016c19..7571dc159dd6 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts @@ -1,6 +1,6 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Polls , Emojis, Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type { CacheableRemoteUser } from '@/models/entities/user.js'; @@ -35,7 +35,7 @@ export class ApNoteService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('pollsRepository') diff --git a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts index 50e1487c9dc1..cfeabde57bb3 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts @@ -1,7 +1,7 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { DataSource } from 'typeorm'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Followings , Instances, UserProfiles, UserPublickeys, Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type { CacheableUser, IRemoteUser } from '@/models/entities/user.js'; @@ -139,10 +139,10 @@ export class ApPersonService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, - @Inject(DI_SYMBOLS.db) + @Inject(DI.db) private db: DataSource, @Inject('usersRepository') diff --git a/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts b/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts index ec7405f94891..ebd067ea4856 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DI_SYMBOLS } from '@/di-symbols.js'; +import { DI } from '@/di-symbols.js'; import type { Notes , Polls } from '@/models/index.js'; import type { Config } from '@/config.js'; import type { IPoll } from '@/models/entities/poll.js'; @@ -14,7 +14,7 @@ export class ApQuestionService { #logger: Logger; constructor( - @Inject(DI_SYMBOLS.config) + @Inject(DI.config) private config: Config, @Inject('notesRepository') From f21613cd18f669ce20781569005f73ac6bbb1855 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 02:05:09 +0900 Subject: [PATCH 129/180] wip --- packages/backend/src/db/postgre.ts | 135 +++++++++--------- packages/backend/src/misc/antenna-cache.ts | 2 +- .../backend/src/misc/check-hit-antenna.ts | 6 +- packages/backend/src/misc/check-word-mute.ts | 4 +- .../backend/src/misc/fetch-proxy-account.ts | 2 +- packages/backend/src/misc/is-quote.ts | 2 +- packages/backend/src/misc/populate-emojis.ts | 4 +- ...buse-user-report.ts => AbuseUserReport.ts} | 0 .../{access-token.ts => AccessToken.ts} | 0 ...nouncement-read.ts => AnnouncementRead.ts} | 0 .../{antenna-note.ts => AntennaNote.ts} | 0 ...n-challenge.ts => AttestationChallenge.ts} | 0 .../{auth-session.ts => AuthSession.ts} | 0 ...annel-following.ts => ChannelFollowing.ts} | 0 ...el-note-pining.ts => ChannelNotePining.ts} | 0 .../entities/{clip-note.ts => ClipNote.ts} | 0 .../entities/{drive-file.ts => DriveFile.ts} | 0 .../{drive-folder.ts => DriveFolder.ts} | 0 .../{follow-request.ts => FollowRequest.ts} | 0 .../{gallery-like.ts => GalleryLike.ts} | 0 .../{gallery-post.ts => GalleryPost.ts} | 0 ...ssaging-message.ts => MessagingMessage.ts} | 0 .../{moderation-log.ts => ModerationLog.ts} | 0 .../entities/{muted-note.ts => MutedNote.ts} | 0 .../{note-favorite.ts => NoteFavorite.ts} | 0 .../{note-reaction.ts => NoteReaction.ts} | 0 ...e-thread-muting.ts => NoteThreadMuting.ts} | 0 .../{note-unread.ts => NoteUnread.ts} | 0 .../entities/{page-like.ts => PageLike.ts} | 0 ...set-request.ts => PasswordResetRequest.ts} | 0 .../entities/{poll-vote.ts => PollVote.ts} | 0 .../entities/{promo-note.ts => PromoNote.ts} | 0 .../entities/{promo-read.ts => PromoRead.ts} | 0 ...tion-tickets.ts => RegistrationTickets.ts} | 0 .../{registry-item.ts => RegistryItem.ts} | 0 .../{sw-subscription.ts => SwSubscription.ts} | 0 .../{used-username.ts => UsedUsername.ts} | 0 .../entities/{user-group.ts => UserGroup.ts} | 0 ...p-invitation.ts => UserGroupInvitation.ts} | 0 ...r-group-joining.ts => UserGroupJoining.ts} | 0 .../models/entities/{user-ip.ts => UserIp.ts} | 0 .../{user-keypair.ts => UserKeypair.ts} | 0 .../entities/{user-list.ts => UserList.ts} | 0 ...ser-list-joining.ts => UserListJoining.ts} | 0 ...{user-note-pining.ts => UserNotePining.ts} | 0 .../{user-pending.ts => UserPending.ts} | 0 .../{user-profile.ts => UserProfile.ts} | 0 .../{user-publickey.ts => UserPublickey.ts} | 0 ...ser-security-key.ts => UserSecurityKey.ts} | 0 .../DeleteAccountProcessorService.ts | 4 +- .../processors/DeliverProcessorService.ts | 2 +- .../ExportFollowingProcessorService.ts | 2 +- .../processors/ExportNotesProcessorService.ts | 4 +- .../queue/processors/InboxProcessorService.ts | 6 +- packages/backend/src/queue/queue.service.ts | 4 +- packages/backend/src/queue/types.ts | 8 +- .../src/server/ActivityPubServerService.ts | 6 +- .../src/server/WellKnownServerService.ts | 2 +- .../backend/src/server/api/ApiCallService.ts | 4 +- .../src/server/api/AuthenticateService.ts | 6 +- .../src/server/api/SigninApiService.ts | 2 +- .../backend/src/server/api/SigninService.ts | 2 +- .../src/server/api/common/GetterService.ts | 4 +- .../src/server/api/common/inject-featured.ts | 4 +- .../src/server/api/common/inject-promo.ts | 4 +- .../backend/src/server/api/endpoint-base.ts | 4 +- .../server/api/endpoints/admin/emoji/copy.ts | 2 +- .../server/api/endpoints/admin/emoji/list.ts | 2 +- .../api/endpoints/admin/suspend-user.ts | 2 +- .../server/api/endpoints/admin/update-meta.ts | 2 +- .../src/server/api/endpoints/ap/show.ts | 4 +- .../server/api/endpoints/channels/create.ts | 2 +- .../server/api/endpoints/drive/files/show.ts | 2 +- .../api/endpoints/gallery/posts/create.ts | 4 +- .../api/endpoints/gallery/posts/update.ts | 4 +- .../server/api/endpoints/hashtags/trend.ts | 2 +- .../src/server/api/endpoints/i/update.ts | 6 +- .../server/api/endpoints/i/webhooks/create.ts | 2 +- .../server/api/endpoints/i/webhooks/update.ts | 2 +- .../server/api/endpoints/messaging/history.ts | 2 +- .../endpoints/messaging/messages/create.ts | 4 +- .../src/server/api/endpoints/mute/create.ts | 2 +- .../api/endpoints/notes/conversation.ts | 2 +- .../src/server/api/endpoints/notes/create.ts | 8 +- .../server/api/endpoints/notes/polls/vote.ts | 2 +- .../server/api/endpoints/notes/reactions.ts | 2 +- .../src/server/api/endpoints/pages/create.ts | 2 +- .../src/server/api/endpoints/pages/show.ts | 2 +- .../src/server/api/endpoints/pinned-users.ts | 2 +- .../api/endpoints/username/available.ts | 2 +- .../api/endpoints/users/groups/create.ts | 4 +- .../users/groups/invitations/accept.ts | 2 +- .../api/endpoints/users/groups/invite.ts | 2 +- .../api/endpoints/users/lists/create.ts | 2 +- .../users/search-by-username-and-host.ts | 2 +- .../src/server/api/endpoints/users/search.ts | 2 +- .../src/server/api/endpoints/users/show.ts | 2 +- .../api/integration/DiscordServerService.ts | 13 +- .../api/integration/GithubServerService.ts | 12 +- .../api/integration/TwitterServerService.ts | 12 +- .../src/server/api/stream/channels/channel.ts | 2 +- .../server/api/stream/channels/messaging.ts | 4 +- .../server/api/stream/channels/user-list.ts | 2 +- .../backend/src/server/api/stream/index.ts | 10 +- .../backend/src/server/api/stream/types.ts | 35 +++-- .../backend/src/server/web/FeedService.ts | 2 +- .../src/services/AccountUpdateService.ts | 2 +- .../backend/src/services/AntennaService.ts | 6 +- .../src/services/CreateNotificationService.ts | 4 +- .../src/services/CreateSystemUserService.ts | 8 +- .../src/services/CustomEmojiService.ts | 4 +- packages/backend/src/services/DriveService.ts | 10 +- .../src/services/FederatedInstanceService.ts | 4 +- .../services/FetchInstanceMetadataService.ts | 2 +- .../src/services/GlobalEventService.ts | 12 +- .../backend/src/services/HashtagService.ts | 4 +- .../src/services/InstanceActorService.ts | 2 +- .../backend/src/services/MessagingService.ts | 10 +- packages/backend/src/services/MetaService.ts | 2 +- packages/backend/src/services/MfmService.ts | 2 +- .../src/services/ModerationLogService.ts | 2 +- .../backend/src/services/NoteCreateService.ts | 18 +-- .../backend/src/services/NoteDeleteService.ts | 4 +- .../backend/src/services/NotePiningService.ts | 6 +- .../backend/src/services/NoteReadService.ts | 6 +- .../src/services/NotificationService.ts | 2 +- packages/backend/src/services/PollService.ts | 4 +- packages/backend/src/services/QueryService.ts | 2 +- .../backend/src/services/ReactionService.ts | 6 +- packages/backend/src/services/RelayService.ts | 4 +- packages/backend/src/services/S3Service.ts | 6 +- .../backend/src/services/SignupService.ts | 19 +-- .../src/services/UserBlockingService.ts | 4 +- .../backend/src/services/UserCacheService.ts | 2 +- .../src/services/UserFollowingService.ts | 2 +- .../src/services/UserKeypairStoreService.ts | 4 +- .../backend/src/services/UserListService.ts | 6 +- .../backend/src/services/UserMutingService.ts | 2 +- .../src/services/UserSuspendService.ts | 2 +- .../backend/src/services/WebhookService.ts | 4 +- .../src/services/chart/charts/active-users.ts | 2 +- .../src/services/chart/charts/drive.ts | 2 +- .../src/services/chart/charts/hashtag.ts | 2 +- .../src/services/chart/charts/instance.ts | 4 +- .../src/services/chart/charts/notes.ts | 2 +- .../services/chart/charts/per-user-drive.ts | 2 +- .../chart/charts/per-user-following.ts | 2 +- .../services/chart/charts/per-user-notes.ts | 4 +- .../chart/charts/per-user-reactions.ts | 4 +- .../src/services/chart/charts/users.ts | 2 +- .../entities/AbuseUserReportEntityService.ts | 2 +- .../services/entities/AntennaEntityService.ts | 2 +- .../src/services/entities/AppEntityService.ts | 4 +- .../entities/AuthSessionEntityService.ts | 4 +- .../entities/BlockingEntityService.ts | 4 +- .../services/entities/ChannelEntityService.ts | 6 +- .../services/entities/ClipEntityService.ts | 6 +- .../entities/DriveFileEntityService.ts | 4 +- .../entities/DriveFolderEntityService.ts | 6 +- .../services/entities/EmojiEntityService.ts | 6 +- .../entities/FollowRequestEntityService.ts | 6 +- .../entities/FollowingEntityService.ts | 6 +- .../entities/GalleryLikeEntityService.ts | 6 +- .../entities/GalleryPostEntityService.ts | 6 +- .../services/entities/HashtagEntityService.ts | 6 +- .../entities/InstanceEntityService.ts | 6 +- .../entities/MessagingMessageEntityService.ts | 6 +- .../entities/ModerationLogEntityService.ts | 6 +- .../services/entities/MutingEntityService.ts | 6 +- .../services/entities/NoteEntityService.ts | 6 +- .../entities/NoteFavoriteEntityService.ts | 6 +- .../entities/NoteReactionEntityService.ts | 6 +- .../entities/NotificationEntityService.ts | 6 +- .../services/entities/PageEntityService.ts | 8 +- .../entities/PageLikeEntityService.ts | 6 +- .../services/entities/SigninEntityService.ts | 6 +- .../services/entities/UserEntityService.ts | 6 +- .../entities/UserGroupEntityService.ts | 6 +- .../UserGroupInvitationEntityService.ts | 6 +- .../entities/UserListEntityService.ts | 6 +- .../src/services/remote/ResolveUserService.ts | 2 +- .../remote/activitypub/ApDbResolverService.ts | 12 +- .../activitypub/ApDeliverManagerService.ts | 2 +- .../remote/activitypub/ApInboxService.ts | 2 +- .../remote/activitypub/ApRendererService.ts | 26 ++-- .../remote/activitypub/ApRequestService.ts | 2 +- .../remote/activitypub/ApResolverService.ts | 2 +- .../services/remote/activitypub/audience.ts | 2 +- .../remote/activitypub/misc/get-note-html.ts | 2 +- .../activitypub/models/ApImageService.ts | 4 +- .../activitypub/models/ApMentionService.ts | 2 +- .../activitypub/models/ApNoteService.ts | 8 +- .../activitypub/models/ApPersonService.ts | 14 +- .../activitypub/models/ApQuestionService.ts | 2 +- .../services/remote/activitypub/perform.ts | 2 +- 195 files changed, 424 insertions(+), 426 deletions(-) rename packages/backend/src/models/entities/{abuse-user-report.ts => AbuseUserReport.ts} (100%) rename packages/backend/src/models/entities/{access-token.ts => AccessToken.ts} (100%) rename packages/backend/src/models/entities/{announcement-read.ts => AnnouncementRead.ts} (100%) rename packages/backend/src/models/entities/{antenna-note.ts => AntennaNote.ts} (100%) rename packages/backend/src/models/entities/{attestation-challenge.ts => AttestationChallenge.ts} (100%) rename packages/backend/src/models/entities/{auth-session.ts => AuthSession.ts} (100%) rename packages/backend/src/models/entities/{channel-following.ts => ChannelFollowing.ts} (100%) rename packages/backend/src/models/entities/{channel-note-pining.ts => ChannelNotePining.ts} (100%) rename packages/backend/src/models/entities/{clip-note.ts => ClipNote.ts} (100%) rename packages/backend/src/models/entities/{drive-file.ts => DriveFile.ts} (100%) rename packages/backend/src/models/entities/{drive-folder.ts => DriveFolder.ts} (100%) rename packages/backend/src/models/entities/{follow-request.ts => FollowRequest.ts} (100%) rename packages/backend/src/models/entities/{gallery-like.ts => GalleryLike.ts} (100%) rename packages/backend/src/models/entities/{gallery-post.ts => GalleryPost.ts} (100%) rename packages/backend/src/models/entities/{messaging-message.ts => MessagingMessage.ts} (100%) rename packages/backend/src/models/entities/{moderation-log.ts => ModerationLog.ts} (100%) rename packages/backend/src/models/entities/{muted-note.ts => MutedNote.ts} (100%) rename packages/backend/src/models/entities/{note-favorite.ts => NoteFavorite.ts} (100%) rename packages/backend/src/models/entities/{note-reaction.ts => NoteReaction.ts} (100%) rename packages/backend/src/models/entities/{note-thread-muting.ts => NoteThreadMuting.ts} (100%) rename packages/backend/src/models/entities/{note-unread.ts => NoteUnread.ts} (100%) rename packages/backend/src/models/entities/{page-like.ts => PageLike.ts} (100%) rename packages/backend/src/models/entities/{password-reset-request.ts => PasswordResetRequest.ts} (100%) rename packages/backend/src/models/entities/{poll-vote.ts => PollVote.ts} (100%) rename packages/backend/src/models/entities/{promo-note.ts => PromoNote.ts} (100%) rename packages/backend/src/models/entities/{promo-read.ts => PromoRead.ts} (100%) rename packages/backend/src/models/entities/{registration-tickets.ts => RegistrationTickets.ts} (100%) rename packages/backend/src/models/entities/{registry-item.ts => RegistryItem.ts} (100%) rename packages/backend/src/models/entities/{sw-subscription.ts => SwSubscription.ts} (100%) rename packages/backend/src/models/entities/{used-username.ts => UsedUsername.ts} (100%) rename packages/backend/src/models/entities/{user-group.ts => UserGroup.ts} (100%) rename packages/backend/src/models/entities/{user-group-invitation.ts => UserGroupInvitation.ts} (100%) rename packages/backend/src/models/entities/{user-group-joining.ts => UserGroupJoining.ts} (100%) rename packages/backend/src/models/entities/{user-ip.ts => UserIp.ts} (100%) rename packages/backend/src/models/entities/{user-keypair.ts => UserKeypair.ts} (100%) rename packages/backend/src/models/entities/{user-list.ts => UserList.ts} (100%) rename packages/backend/src/models/entities/{user-list-joining.ts => UserListJoining.ts} (100%) rename packages/backend/src/models/entities/{user-note-pining.ts => UserNotePining.ts} (100%) rename packages/backend/src/models/entities/{user-pending.ts => UserPending.ts} (100%) rename packages/backend/src/models/entities/{user-profile.ts => UserProfile.ts} (100%) rename packages/backend/src/models/entities/{user-publickey.ts => UserPublickey.ts} (100%) rename packages/backend/src/models/entities/{user-security-key.ts => UserSecurityKey.ts} (100%) diff --git a/packages/backend/src/db/postgre.ts b/packages/backend/src/db/postgre.ts index 94d55e4310fd..3105b9084c6b 100644 --- a/packages/backend/src/db/postgre.ts +++ b/packages/backend/src/db/postgre.ts @@ -2,79 +2,79 @@ import pg from 'pg'; pg.types.setTypeParser(20, Number); -import { Logger, DataSource } from 'typeorm'; +import { DataSource } from 'typeorm'; import * as highlight from 'cli-highlight'; import config from '@/config/index.js'; +import { entities as charts } from '@/services/chart/entities.js'; -import { User } from '@/models/entities/user.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; -import { DriveFolder } from '@/models/entities/drive-folder.js'; -import { AccessToken } from '@/models/entities/access-token.js'; -import { App } from '@/models/entities/app.js'; -import { PollVote } from '@/models/entities/poll-vote.js'; -import { Note } from '@/models/entities/note.js'; -import { NoteReaction } from '@/models/entities/note-reaction.js'; -import { NoteWatching } from '@/models/entities/note-watching.js'; -import { NoteThreadMuting } from '@/models/entities/note-thread-muting.js'; -import { NoteUnread } from '@/models/entities/note-unread.js'; -import { Notification } from '@/models/entities/notification.js'; -import { Meta } from '@/models/entities/meta.js'; -import { Following } from '@/models/entities/following.js'; -import { Instance } from '@/models/entities/instance.js'; -import { Muting } from '@/models/entities/muting.js'; -import { SwSubscription } from '@/models/entities/sw-subscription.js'; -import { Blocking } from '@/models/entities/blocking.js'; -import { UserList } from '@/models/entities/user-list.js'; -import { UserListJoining } from '@/models/entities/user-list-joining.js'; -import { UserGroup } from '@/models/entities/user-group.js'; -import { UserGroupJoining } from '@/models/entities/user-group-joining.js'; -import { UserGroupInvitation } from '@/models/entities/user-group-invitation.js'; -import { Hashtag } from '@/models/entities/hashtag.js'; -import { NoteFavorite } from '@/models/entities/note-favorite.js'; -import { AbuseUserReport } from '@/models/entities/abuse-user-report.js'; -import { RegistrationTicket } from '@/models/entities/registration-tickets.js'; -import { MessagingMessage } from '@/models/entities/messaging-message.js'; -import { Signin } from '@/models/entities/signin.js'; -import { AuthSession } from '@/models/entities/auth-session.js'; -import { FollowRequest } from '@/models/entities/follow-request.js'; -import { Emoji } from '@/models/entities/emoji.js'; -import { UserNotePining } from '@/models/entities/user-note-pining.js'; -import { Poll } from '@/models/entities/poll.js'; -import { UserKeypair } from '@/models/entities/user-keypair.js'; -import { UserPublickey } from '@/models/entities/user-publickey.js'; -import { UserProfile } from '@/models/entities/user-profile.js'; -import { UserSecurityKey } from '@/models/entities/user-security-key.js'; -import { AttestationChallenge } from '@/models/entities/attestation-challenge.js'; -import { Page } from '@/models/entities/page.js'; -import { PageLike } from '@/models/entities/page-like.js'; -import { GalleryPost } from '@/models/entities/gallery-post.js'; -import { GalleryLike } from '@/models/entities/gallery-like.js'; -import { ModerationLog } from '@/models/entities/moderation-log.js'; -import { UsedUsername } from '@/models/entities/used-username.js'; -import { Announcement } from '@/models/entities/announcement.js'; -import { AnnouncementRead } from '@/models/entities/announcement-read.js'; -import { Clip } from '@/models/entities/clip.js'; -import { ClipNote } from '@/models/entities/clip-note.js'; -import { Antenna } from '@/models/entities/antenna.js'; -import { AntennaNote } from '@/models/entities/antenna-note.js'; -import { PromoNote } from '@/models/entities/promo-note.js'; -import { PromoRead } from '@/models/entities/promo-read.js'; -import { Relay } from '@/models/entities/relay.js'; -import { MutedNote } from '@/models/entities/muted-note.js'; -import { Channel } from '@/models/entities/channel.js'; -import { ChannelFollowing } from '@/models/entities/channel-following.js'; -import { ChannelNotePining } from '@/models/entities/channel-note-pining.js'; -import { RegistryItem } from '@/models/entities/registry-item.js'; -import { Ad } from '@/models/entities/ad.js'; -import { PasswordResetRequest } from '@/models/entities/password-reset-request.js'; -import { UserPending } from '@/models/entities/user-pending.js'; -import { Webhook } from '@/models/entities/webhook.js'; -import { UserIp } from '@/models/entities/user-ip.js'; +import { AbuseUserReport } from '@/models/entities/AbuseUserReport.js'; +import { AccessToken } from '@/models/entities/AccessToken.js'; +import { Ad } from '@/models/entities/Ad.js'; +import { Announcement } from '@/models/entities/Announcement.js'; +import { AnnouncementRead } from '@/models/entities/AnnouncementRead.js'; +import { Antenna } from '@/models/entities/Antenna.js'; +import { AntennaNote } from '@/models/entities/AntennaNote.js'; +import { App } from '@/models/entities/App.js'; +import { AttestationChallenge } from '@/models/entities/AttestationChallenge.js'; +import { AuthSession } from '@/models/entities/AuthSession.js'; +import { Blocking } from '@/models/entities/Blocking.js'; +import { ChannelFollowing } from '@/models/entities/ChannelFollowing.js'; +import { ChannelNotePining } from '@/models/entities/ChannelNotePining.js'; +import { Clip } from '@/models/entities/Clip.js'; +import { ClipNote } from '@/models/entities/ClipNote.js'; +import { DriveFile } from '@/models/entities/DriveFile.js'; +import { DriveFolder } from '@/models/entities/DriveFolder.js'; +import { Emoji } from '@/models/entities/Emoji.js'; +import { Following } from '@/models/entities/Following.js'; +import { FollowRequest } from '@/models/entities/FollowRequest.js'; +import { GalleryLike } from '@/models/entities/GalleryLike.js'; +import { GalleryPost } from '@/models/entities/GalleryPost.js'; +import { Hashtag } from '@/models/entities/Hashtag.js'; +import { Instance } from '@/models/entities/Instance.js'; +import { MessagingMessage } from '@/models/entities/MessagingMessage.js'; +import { Meta } from '@/models/entities/Meta.js'; +import { ModerationLog } from '@/models/entities/ModerationLog.js'; +import { MutedNote } from '@/models/entities/MutedNote.js'; +import { Muting } from '@/models/entities/Muting.js'; +import { Note } from '@/models/entities/Note.js'; +import { NoteFavorite } from '@/models/entities/NoteFavorite.js'; +import { NoteReaction } from '@/models/entities/NoteReaction.js'; +import { NoteThreadMuting } from '@/models/entities/NoteThreadMuting.js'; +import { NoteUnread } from '@/models/entities/NoteUnread.js'; +import { Notification } from '@/models/entities/Notification.js'; +import { Page } from '@/models/entities/Page.js'; +import { PageLike } from '@/models/entities/PageLike.js'; +import { PasswordResetRequest } from '@/models/entities/PasswordResetRequest.js'; +import { Poll } from '@/models/entities/Poll.js'; +import { PollVote } from '@/models/entities/PollVote.js'; +import { PromoNote } from '@/models/entities/PromoNote.js'; +import { PromoRead } from '@/models/entities/PromoRead.js'; +import { RegistrationTicket } from '@/models/entities/RegistrationTickets.js'; +import { RegistryItem } from '@/models/entities/RegistryItem.js'; +import { Relay } from '@/models/entities/Relay.js'; +import { Signin } from '@/models/entities/Signin.js'; +import { SwSubscription } from '@/models/entities/SwSubscription.js'; +import { UsedUsername } from '@/models/entities/UsedUsername.js'; +import { User } from '@/models/entities/User.js'; +import { UserGroup } from '@/models/entities/UserGroup.js'; +import { UserGroupInvitation } from '@/models/entities/UserGroupInvitation.js'; +import { UserGroupJoining } from '@/models/entities/UserGroupJoining.js'; +import { UserIp } from '@/models/entities/UserIp.js'; +import { UserKeypair } from '@/models/entities/UserKeypair.js'; +import { UserList } from '@/models/entities/UserList.js'; +import { UserListJoining } from '@/models/entities/UserListJoining.js'; +import { UserNotePining } from '@/models/entities/UserNotePining.js'; +import { UserPending } from '@/models/entities/UserPending.js'; +import { UserProfile } from '@/models/entities/UserProfile.js'; +import { UserPublickey } from '@/models/entities/UserPublickey.js'; +import { UserSecurityKey } from '@/models/entities/UserSecurityKey.js'; +import { Webhook } from '@/models/entities/Webhook.js'; +import { Channel } from '@/models/entities/Channel.js'; -import { entities as charts } from '@/services/chart/entities.js'; import { envOption } from '../env.js'; -import { dbLogger } from './logger.js'; import { redisClient } from './redis.js'; +import { dbLogger } from './logger.js'; +import type { Logger } from 'typeorm'; const sqlLogger = dbLogger.createSubLogger('sql', 'gray', false); @@ -138,7 +138,6 @@ export const entities = [ Note, NoteFavorite, NoteReaction, - NoteWatching, NoteThreadMuting, NoteUnread, Page, diff --git a/packages/backend/src/misc/antenna-cache.ts b/packages/backend/src/misc/antenna-cache.ts index dcf96c1610be..449b034db994 100644 --- a/packages/backend/src/misc/antenna-cache.ts +++ b/packages/backend/src/misc/antenna-cache.ts @@ -1,5 +1,5 @@ import { Antennas } from '@/models/index.js'; -import { Antenna } from '@/models/entities/antenna.js'; +import type { Antenna } from '@/models/entities/Antenna.js'; import { subsdcriber } from '../db/redis.js'; let antennasFetched = false; diff --git a/packages/backend/src/misc/check-hit-antenna.ts b/packages/backend/src/misc/check-hit-antenna.ts index d9cedee7df15..5269009ebff5 100644 --- a/packages/backend/src/misc/check-hit-antenna.ts +++ b/packages/backend/src/misc/check-hit-antenna.ts @@ -1,6 +1,6 @@ -import { Antenna } from '@/models/entities/antenna.js'; -import { Note } from '@/models/entities/note.js'; -import { User } from '@/models/entities/user.js'; +import { Antenna } from '@/models/entities/Antenna.js'; +import { Note } from '@/models/entities/Note.js'; +import { User } from '@/models/entities/User.js'; import { UserListJoinings, UserGroupJoinings, Blockings } from '@/models/index.js'; import { getFullApAccount } from './convert-host.js'; import * as Acct from '@/misc/acct.js'; diff --git a/packages/backend/src/misc/check-word-mute.ts b/packages/backend/src/misc/check-word-mute.ts index e411bbb4e16e..d10aca9e88af 100644 --- a/packages/backend/src/misc/check-word-mute.ts +++ b/packages/backend/src/misc/check-word-mute.ts @@ -1,6 +1,6 @@ import RE2 from 're2'; -import type { Note } from '@/models/entities/note.js'; -import type { User } from '@/models/entities/user.js'; +import type { Note } from '@/models/entities/Note.js'; +import type { User } from '@/models/entities/User.js'; type NoteLike = { userId: Note['userId']; diff --git a/packages/backend/src/misc/fetch-proxy-account.ts b/packages/backend/src/misc/fetch-proxy-account.ts index b61bba264ba6..24af320fe6e2 100644 --- a/packages/backend/src/misc/fetch-proxy-account.ts +++ b/packages/backend/src/misc/fetch-proxy-account.ts @@ -1,5 +1,5 @@ import { fetchMeta } from './fetch-meta.js'; -import { ILocalUser } from '@/models/entities/user.js'; +import { ILocalUser } from '@/models/entities/User.js'; import { Users } from '@/models/index.js'; export async function fetchProxyAccount(): Promise { diff --git a/packages/backend/src/misc/is-quote.ts b/packages/backend/src/misc/is-quote.ts index 779f548b0317..6ea71cd878fe 100644 --- a/packages/backend/src/misc/is-quote.ts +++ b/packages/backend/src/misc/is-quote.ts @@ -1,4 +1,4 @@ -import { Note } from '@/models/entities/note.js'; +import { Note } from '@/models/entities/Note.js'; export default function(note: Note): boolean { return note.renoteId != null && (note.text != null || note.hasPoll || (note.fileIds != null && note.fileIds.length > 0)); diff --git a/packages/backend/src/misc/populate-emojis.ts b/packages/backend/src/misc/populate-emojis.ts index 6a185d09f6b4..6c31312168f2 100644 --- a/packages/backend/src/misc/populate-emojis.ts +++ b/packages/backend/src/misc/populate-emojis.ts @@ -1,7 +1,7 @@ import { In, IsNull } from 'typeorm'; import { Emojis } from '@/models/index.js'; -import { Emoji } from '@/models/entities/emoji.js'; -import { Note } from '@/models/entities/note.js'; +import { Emoji } from '@/models/entities/Emoji.js'; +import { Note } from '@/models/entities/Note.js'; import { Cache } from './cache.js'; import { isSelfHost, toPunyNullable } from './convert-host.js'; import { decodeReaction } from './reaction-lib.js'; diff --git a/packages/backend/src/models/entities/abuse-user-report.ts b/packages/backend/src/models/entities/AbuseUserReport.ts similarity index 100% rename from packages/backend/src/models/entities/abuse-user-report.ts rename to packages/backend/src/models/entities/AbuseUserReport.ts diff --git a/packages/backend/src/models/entities/access-token.ts b/packages/backend/src/models/entities/AccessToken.ts similarity index 100% rename from packages/backend/src/models/entities/access-token.ts rename to packages/backend/src/models/entities/AccessToken.ts diff --git a/packages/backend/src/models/entities/announcement-read.ts b/packages/backend/src/models/entities/AnnouncementRead.ts similarity index 100% rename from packages/backend/src/models/entities/announcement-read.ts rename to packages/backend/src/models/entities/AnnouncementRead.ts diff --git a/packages/backend/src/models/entities/antenna-note.ts b/packages/backend/src/models/entities/AntennaNote.ts similarity index 100% rename from packages/backend/src/models/entities/antenna-note.ts rename to packages/backend/src/models/entities/AntennaNote.ts diff --git a/packages/backend/src/models/entities/attestation-challenge.ts b/packages/backend/src/models/entities/AttestationChallenge.ts similarity index 100% rename from packages/backend/src/models/entities/attestation-challenge.ts rename to packages/backend/src/models/entities/AttestationChallenge.ts diff --git a/packages/backend/src/models/entities/auth-session.ts b/packages/backend/src/models/entities/AuthSession.ts similarity index 100% rename from packages/backend/src/models/entities/auth-session.ts rename to packages/backend/src/models/entities/AuthSession.ts diff --git a/packages/backend/src/models/entities/channel-following.ts b/packages/backend/src/models/entities/ChannelFollowing.ts similarity index 100% rename from packages/backend/src/models/entities/channel-following.ts rename to packages/backend/src/models/entities/ChannelFollowing.ts diff --git a/packages/backend/src/models/entities/channel-note-pining.ts b/packages/backend/src/models/entities/ChannelNotePining.ts similarity index 100% rename from packages/backend/src/models/entities/channel-note-pining.ts rename to packages/backend/src/models/entities/ChannelNotePining.ts diff --git a/packages/backend/src/models/entities/clip-note.ts b/packages/backend/src/models/entities/ClipNote.ts similarity index 100% rename from packages/backend/src/models/entities/clip-note.ts rename to packages/backend/src/models/entities/ClipNote.ts diff --git a/packages/backend/src/models/entities/drive-file.ts b/packages/backend/src/models/entities/DriveFile.ts similarity index 100% rename from packages/backend/src/models/entities/drive-file.ts rename to packages/backend/src/models/entities/DriveFile.ts diff --git a/packages/backend/src/models/entities/drive-folder.ts b/packages/backend/src/models/entities/DriveFolder.ts similarity index 100% rename from packages/backend/src/models/entities/drive-folder.ts rename to packages/backend/src/models/entities/DriveFolder.ts diff --git a/packages/backend/src/models/entities/follow-request.ts b/packages/backend/src/models/entities/FollowRequest.ts similarity index 100% rename from packages/backend/src/models/entities/follow-request.ts rename to packages/backend/src/models/entities/FollowRequest.ts diff --git a/packages/backend/src/models/entities/gallery-like.ts b/packages/backend/src/models/entities/GalleryLike.ts similarity index 100% rename from packages/backend/src/models/entities/gallery-like.ts rename to packages/backend/src/models/entities/GalleryLike.ts diff --git a/packages/backend/src/models/entities/gallery-post.ts b/packages/backend/src/models/entities/GalleryPost.ts similarity index 100% rename from packages/backend/src/models/entities/gallery-post.ts rename to packages/backend/src/models/entities/GalleryPost.ts diff --git a/packages/backend/src/models/entities/messaging-message.ts b/packages/backend/src/models/entities/MessagingMessage.ts similarity index 100% rename from packages/backend/src/models/entities/messaging-message.ts rename to packages/backend/src/models/entities/MessagingMessage.ts diff --git a/packages/backend/src/models/entities/moderation-log.ts b/packages/backend/src/models/entities/ModerationLog.ts similarity index 100% rename from packages/backend/src/models/entities/moderation-log.ts rename to packages/backend/src/models/entities/ModerationLog.ts diff --git a/packages/backend/src/models/entities/muted-note.ts b/packages/backend/src/models/entities/MutedNote.ts similarity index 100% rename from packages/backend/src/models/entities/muted-note.ts rename to packages/backend/src/models/entities/MutedNote.ts diff --git a/packages/backend/src/models/entities/note-favorite.ts b/packages/backend/src/models/entities/NoteFavorite.ts similarity index 100% rename from packages/backend/src/models/entities/note-favorite.ts rename to packages/backend/src/models/entities/NoteFavorite.ts diff --git a/packages/backend/src/models/entities/note-reaction.ts b/packages/backend/src/models/entities/NoteReaction.ts similarity index 100% rename from packages/backend/src/models/entities/note-reaction.ts rename to packages/backend/src/models/entities/NoteReaction.ts diff --git a/packages/backend/src/models/entities/note-thread-muting.ts b/packages/backend/src/models/entities/NoteThreadMuting.ts similarity index 100% rename from packages/backend/src/models/entities/note-thread-muting.ts rename to packages/backend/src/models/entities/NoteThreadMuting.ts diff --git a/packages/backend/src/models/entities/note-unread.ts b/packages/backend/src/models/entities/NoteUnread.ts similarity index 100% rename from packages/backend/src/models/entities/note-unread.ts rename to packages/backend/src/models/entities/NoteUnread.ts diff --git a/packages/backend/src/models/entities/page-like.ts b/packages/backend/src/models/entities/PageLike.ts similarity index 100% rename from packages/backend/src/models/entities/page-like.ts rename to packages/backend/src/models/entities/PageLike.ts diff --git a/packages/backend/src/models/entities/password-reset-request.ts b/packages/backend/src/models/entities/PasswordResetRequest.ts similarity index 100% rename from packages/backend/src/models/entities/password-reset-request.ts rename to packages/backend/src/models/entities/PasswordResetRequest.ts diff --git a/packages/backend/src/models/entities/poll-vote.ts b/packages/backend/src/models/entities/PollVote.ts similarity index 100% rename from packages/backend/src/models/entities/poll-vote.ts rename to packages/backend/src/models/entities/PollVote.ts diff --git a/packages/backend/src/models/entities/promo-note.ts b/packages/backend/src/models/entities/PromoNote.ts similarity index 100% rename from packages/backend/src/models/entities/promo-note.ts rename to packages/backend/src/models/entities/PromoNote.ts diff --git a/packages/backend/src/models/entities/promo-read.ts b/packages/backend/src/models/entities/PromoRead.ts similarity index 100% rename from packages/backend/src/models/entities/promo-read.ts rename to packages/backend/src/models/entities/PromoRead.ts diff --git a/packages/backend/src/models/entities/registration-tickets.ts b/packages/backend/src/models/entities/RegistrationTickets.ts similarity index 100% rename from packages/backend/src/models/entities/registration-tickets.ts rename to packages/backend/src/models/entities/RegistrationTickets.ts diff --git a/packages/backend/src/models/entities/registry-item.ts b/packages/backend/src/models/entities/RegistryItem.ts similarity index 100% rename from packages/backend/src/models/entities/registry-item.ts rename to packages/backend/src/models/entities/RegistryItem.ts diff --git a/packages/backend/src/models/entities/sw-subscription.ts b/packages/backend/src/models/entities/SwSubscription.ts similarity index 100% rename from packages/backend/src/models/entities/sw-subscription.ts rename to packages/backend/src/models/entities/SwSubscription.ts diff --git a/packages/backend/src/models/entities/used-username.ts b/packages/backend/src/models/entities/UsedUsername.ts similarity index 100% rename from packages/backend/src/models/entities/used-username.ts rename to packages/backend/src/models/entities/UsedUsername.ts diff --git a/packages/backend/src/models/entities/user-group.ts b/packages/backend/src/models/entities/UserGroup.ts similarity index 100% rename from packages/backend/src/models/entities/user-group.ts rename to packages/backend/src/models/entities/UserGroup.ts diff --git a/packages/backend/src/models/entities/user-group-invitation.ts b/packages/backend/src/models/entities/UserGroupInvitation.ts similarity index 100% rename from packages/backend/src/models/entities/user-group-invitation.ts rename to packages/backend/src/models/entities/UserGroupInvitation.ts diff --git a/packages/backend/src/models/entities/user-group-joining.ts b/packages/backend/src/models/entities/UserGroupJoining.ts similarity index 100% rename from packages/backend/src/models/entities/user-group-joining.ts rename to packages/backend/src/models/entities/UserGroupJoining.ts diff --git a/packages/backend/src/models/entities/user-ip.ts b/packages/backend/src/models/entities/UserIp.ts similarity index 100% rename from packages/backend/src/models/entities/user-ip.ts rename to packages/backend/src/models/entities/UserIp.ts diff --git a/packages/backend/src/models/entities/user-keypair.ts b/packages/backend/src/models/entities/UserKeypair.ts similarity index 100% rename from packages/backend/src/models/entities/user-keypair.ts rename to packages/backend/src/models/entities/UserKeypair.ts diff --git a/packages/backend/src/models/entities/user-list.ts b/packages/backend/src/models/entities/UserList.ts similarity index 100% rename from packages/backend/src/models/entities/user-list.ts rename to packages/backend/src/models/entities/UserList.ts diff --git a/packages/backend/src/models/entities/user-list-joining.ts b/packages/backend/src/models/entities/UserListJoining.ts similarity index 100% rename from packages/backend/src/models/entities/user-list-joining.ts rename to packages/backend/src/models/entities/UserListJoining.ts diff --git a/packages/backend/src/models/entities/user-note-pining.ts b/packages/backend/src/models/entities/UserNotePining.ts similarity index 100% rename from packages/backend/src/models/entities/user-note-pining.ts rename to packages/backend/src/models/entities/UserNotePining.ts diff --git a/packages/backend/src/models/entities/user-pending.ts b/packages/backend/src/models/entities/UserPending.ts similarity index 100% rename from packages/backend/src/models/entities/user-pending.ts rename to packages/backend/src/models/entities/UserPending.ts diff --git a/packages/backend/src/models/entities/user-profile.ts b/packages/backend/src/models/entities/UserProfile.ts similarity index 100% rename from packages/backend/src/models/entities/user-profile.ts rename to packages/backend/src/models/entities/UserProfile.ts diff --git a/packages/backend/src/models/entities/user-publickey.ts b/packages/backend/src/models/entities/UserPublickey.ts similarity index 100% rename from packages/backend/src/models/entities/user-publickey.ts rename to packages/backend/src/models/entities/UserPublickey.ts diff --git a/packages/backend/src/models/entities/user-security-key.ts b/packages/backend/src/models/entities/UserSecurityKey.ts similarity index 100% rename from packages/backend/src/models/entities/user-security-key.ts rename to packages/backend/src/models/entities/UserSecurityKey.ts diff --git a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts index 56c4069b2249..9b0843d3cc2e 100644 --- a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts @@ -6,8 +6,8 @@ import { Notes, Users } from '@/models/index.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type { DriveService } from '@/services/DriveService.js'; -import type { DriveFile } from '@/models/entities/drive-file.js'; -import type { Note } from '@/models/entities/note.js'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; +import type { Note } from '@/models/entities/Note.js'; import type Bull from 'bull'; import type { DbUserDeleteJobData } from '../types.js'; import type { QueueLoggerService } from '../QueueLoggerService.js'; diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts index 7e3821e43297..842bd3b6ef6e 100644 --- a/packages/backend/src/queue/processors/DeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts @@ -10,7 +10,7 @@ import { ApRequestService } from '@/services/remote/activitypub/ApRequestService import { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; import { FetchInstanceMetadataService } from '@/services/FetchInstanceMetadataService.js'; import { Cache } from '@/misc/cache.js'; -import type { Instance } from '@/models/entities/instance.js'; +import type { Instance } from '@/models/entities/Instance.js'; import InstanceChart from '@/services/chart/charts/instance.js'; import ApRequestChart from '@/services/chart/charts/ap-request.js'; import FederationChart from '@/services/chart/charts/federation.js'; diff --git a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts index a68d0a94e5e6..ee80c927677b 100644 --- a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts @@ -10,7 +10,7 @@ import type Logger from '@/logger.js'; import type { DriveService } from '@/services/DriveService.js'; import { getFullApAccount } from '@/misc/convert-host.js'; import { createTemp } from '@/misc/create-temp.js'; -import type { Following } from '@/models/entities/following.js'; +import type { Following } from '@/models/entities/Following.js'; import type Bull from 'bull'; import type { DbUserJobData } from '../types.js'; import type { QueueLoggerService } from '../QueueLoggerService.js'; diff --git a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts index 54deb255aa24..0d08e4946b36 100644 --- a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts @@ -8,8 +8,8 @@ import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type { DriveService } from '@/services/DriveService.js'; import { createTemp } from '@/misc/create-temp.js'; -import type { Poll } from '@/models/entities/poll.js'; -import type { Note } from '@/models/entities/note.js'; +import type { Poll } from '@/models/entities/Poll.js'; +import type { Note } from '@/models/entities/Note.js'; import type Bull from 'bull'; import type { DbUserJobData } from '../types.js'; import type { QueueLoggerService } from '../QueueLoggerService.js'; diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index 37e80912b7fe..f74bf50013c5 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -13,14 +13,14 @@ import { ApRequestService } from '@/services/remote/activitypub/ApRequestService import { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; import { FetchInstanceMetadataService } from '@/services/FetchInstanceMetadataService.js'; import { Cache } from '@/misc/cache.js'; -import type { Instance } from '@/models/entities/instance.js'; +import type { Instance } from '@/models/entities/Instance.js'; import InstanceChart from '@/services/chart/charts/instance.js'; import ApRequestChart from '@/services/chart/charts/ap-request.js'; import FederationChart from '@/services/chart/charts/federation.js'; import { LdSignature } from '@/services/remote/activitypub/misc/ld-signature.js'; import { getApId } from '@/services/remote/activitypub/type.js'; -import type { CacheableRemoteUser } from '@/models/entities/user.js'; -import type { UserPublickey } from '@/models/entities/user-publickey.js'; +import type { CacheableRemoteUser } from '@/models/entities/User.js'; +import type { UserPublickey } from '@/models/entities/UserPublickey.js'; import { ApDbResolverService } from '@/services/remote/activitypub/ApDbResolverService.js'; import { StatusError } from '@/misc/status-error.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; diff --git a/packages/backend/src/queue/queue.service.ts b/packages/backend/src/queue/queue.service.ts index 4d76c8be1374..2289cab04dbe 100644 --- a/packages/backend/src/queue/queue.service.ts +++ b/packages/backend/src/queue/queue.service.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { v4 as uuid } from 'uuid'; import type { IActivity } from '@/services/remote/activitypub/type.js'; -import type { DriveFile } from '@/models/entities/drive-file.js'; -import type { Webhook, webhookEventTypes } from '@/models/entities/webhook.js'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; +import type { Webhook, webhookEventTypes } from '@/models/entities/Webhook.js'; import { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from './queue.module.js'; diff --git a/packages/backend/src/queue/types.ts b/packages/backend/src/queue/types.ts index cc47e7b376fa..e36e18a33bbe 100644 --- a/packages/backend/src/queue/types.ts +++ b/packages/backend/src/queue/types.ts @@ -1,7 +1,7 @@ -import type { DriveFile } from '@/models/entities/drive-file.js'; -import type { Note } from '@/models/entities/note'; -import type { User } from '@/models/entities/user.js'; -import type { Webhook } from '@/models/entities/webhook'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; +import type { Note } from '@/models/entities/Note.js'; +import type { User } from '@/models/entities/User.js'; +import type { Webhook } from '@/models/entities/Webhook.js'; import type { IActivity } from '@/services/remote/activitypub/type.js'; import type httpSignature from '@peertube/http-signature'; diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 5126f32f4760..b2f5bb679f13 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -11,11 +11,11 @@ import { Config } from '@/config.js'; import { isSelfHost } from '@/misc/convert-host.js'; import { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; import { QueueService } from '@/queue/queue.service.js'; -import type { ILocalUser, User } from '@/models/entities/user.js'; +import type { ILocalUser, User } from '@/models/entities/User.js'; import { UserKeypairStoreService } from '@/services/UserKeypairStoreService.js'; -import type { Following } from '@/models/entities/following.js'; +import type { Following } from '@/models/entities/Following.js'; import { countIf } from '@/prelude/array.js'; -import type { Note } from '@/models/entities/note.js'; +import type { Note } from '@/models/entities/Note.js'; import { QueryService } from '@/services/QueryService.js'; import type { FindOptionsWhere } from 'typeorm'; diff --git a/packages/backend/src/server/WellKnownServerService.ts b/packages/backend/src/server/WellKnownServerService.ts index f8a646ba02e2..a4843b866d29 100644 --- a/packages/backend/src/server/WellKnownServerService.ts +++ b/packages/backend/src/server/WellKnownServerService.ts @@ -5,7 +5,7 @@ import { DI } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; import { Config } from '@/config.js'; import { escapeAttribute, escapeValue } from '@/prelude/xml'; -import type { User } from '@/models/entities/user'; +import type { User } from '@/models/entities/User.js'; import * as Acct from '@/misc/acct.js'; import { NodeinfoServerService } from './NodeinfoServerService.js'; import type { FindOptionsWhere } from 'typeorm'; diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index e190b327a777..b78fb91669f1 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -2,8 +2,8 @@ import { performance } from 'perf_hooks'; import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import { getIpHash } from '@/misc/get-ip-hash.js'; -import type { CacheableLocalUser, User } from '@/models/entities/user.js'; -import type { AccessToken } from '@/models/entities/access-token.js'; +import type { CacheableLocalUser, User } from '@/models/entities/User.js'; +import type { AccessToken } from '@/models/entities/AccessToken.js'; import type Logger from '@/logger.js'; import type { UserIps } from '@/models/index.js'; import { MetaService } from '@/services/MetaService.js'; diff --git a/packages/backend/src/server/api/AuthenticateService.ts b/packages/backend/src/server/api/AuthenticateService.ts index 472a0fb734f8..dd891f467e61 100644 --- a/packages/backend/src/server/api/AuthenticateService.ts +++ b/packages/backend/src/server/api/AuthenticateService.ts @@ -1,10 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { AccessTokens, Apps , Users } from '@/models/index.js'; -import type { CacheableLocalUser, ILocalUser } from '@/models/entities/user.js'; -import type { AccessToken } from '@/models/entities/access-token.js'; +import type { CacheableLocalUser, ILocalUser } from '@/models/entities/User.js'; +import type { AccessToken } from '@/models/entities/AccessToken.js'; import { Cache } from '@/misc/cache.js'; -import type { App } from '@/models/entities/app.js'; +import type { App } from '@/models/entities/App.js'; import { UserCacheService } from '@/services/UserCacheService.js'; import isNativeToken from '@/misc/is-native-token.js'; diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index 968156f6bdc5..938174cd11c1 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -8,7 +8,7 @@ import type { UserSecurityKeys , Signins, UserProfiles } from '@/models/index.js import { AttestationChallenges, Users } from '@/models/index.js'; import { Config } from '@/config.js'; import { getIpHash } from '@/misc/get-ip-hash.js'; -import type { ILocalUser } from '@/models/entities/user.js'; +import type { ILocalUser } from '@/models/entities/User.js'; import { IdService } from '@/services/IdService.js'; import { TwoFactorAuthenticationService } from '@/services/TwoFactorAuthenticationService.js'; import { RateLimiterService } from './RateLimiterService.js'; diff --git a/packages/backend/src/server/api/SigninService.ts b/packages/backend/src/server/api/SigninService.ts index 89016e706f0e..f646364fb1d4 100644 --- a/packages/backend/src/server/api/SigninService.ts +++ b/packages/backend/src/server/api/SigninService.ts @@ -3,7 +3,7 @@ import { DI } from '@/di-symbols.js'; import type { Signins , Users } from '@/models/index.js'; import { Config } from '@/config.js'; import { IdService } from '@/services/IdService.js'; -import type { ILocalUser } from '@/models/entities/user.js'; +import type { ILocalUser } from '@/models/entities/User.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { SigninEntityService } from '@/services/entities/SigninEntityService.js'; import type Koa from 'koa'; diff --git a/packages/backend/src/server/api/common/GetterService.ts b/packages/backend/src/server/api/common/GetterService.ts index 0fe3aeae69fd..76931aa299bd 100644 --- a/packages/backend/src/server/api/common/GetterService.ts +++ b/packages/backend/src/server/api/common/GetterService.ts @@ -2,8 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { Notes , Users } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; -import type { User } from '@/models/entities/user.js'; -import type { Note } from '@/models/entities/note.js'; +import type { User } from '@/models/entities/User.js'; +import type { Note } from '@/models/entities/Note.js'; @Injectable() export class GetterService { diff --git a/packages/backend/src/server/api/common/inject-featured.ts b/packages/backend/src/server/api/common/inject-featured.ts index f7cdd365ed41..75126fa30416 100644 --- a/packages/backend/src/server/api/common/inject-featured.ts +++ b/packages/backend/src/server/api/common/inject-featured.ts @@ -1,6 +1,6 @@ import rndstr from 'rndstr'; -import { Note } from '@/models/entities/note.js'; -import { User } from '@/models/entities/user.js'; +import { Note } from '@/models/entities/Note.js'; +import { User } from '@/models/entities/User.js'; import { Notes, UserProfiles, NoteReactions } from '@/models/index.js'; import { generateMutedUserQuery } from './generate-muted-user-query.js'; import { generateBlockedUserQuery } from './generate-block-query.js'; diff --git a/packages/backend/src/server/api/common/inject-promo.ts b/packages/backend/src/server/api/common/inject-promo.ts index b0da8118b415..454f5dbb0ed9 100644 --- a/packages/backend/src/server/api/common/inject-promo.ts +++ b/packages/backend/src/server/api/common/inject-promo.ts @@ -1,6 +1,6 @@ import rndstr from 'rndstr'; -import { Note } from '@/models/entities/note.js'; -import { User } from '@/models/entities/user.js'; +import { Note } from '@/models/entities/Note.js'; +import { User } from '@/models/entities/User.js'; import { PromoReads, PromoNotes, Notes, Users } from '@/models/index.js'; export async function injectPromo(timeline: Note[], user?: User | null) { diff --git a/packages/backend/src/server/api/endpoint-base.ts b/packages/backend/src/server/api/endpoint-base.ts index 03034c3d93e1..0a7f9b3008a0 100644 --- a/packages/backend/src/server/api/endpoint-base.ts +++ b/packages/backend/src/server/api/endpoint-base.ts @@ -1,8 +1,8 @@ import * as fs from 'node:fs'; import Ajv from 'ajv'; import type { Schema, SchemaType } from '@/misc/schema.js'; -import type { CacheableLocalUser } from '@/models/entities/user.js'; -import type { AccessToken } from '@/models/entities/access-token.js'; +import type { CacheableLocalUser } from '@/models/entities/User.js'; +import type { AccessToken } from '@/models/entities/AccessToken.js'; import { ApiError } from './error.js'; import type { IEndpointMeta } from './endpoints.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index 1913510f77a5..06d7b8aaedca 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -3,7 +3,7 @@ import { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Emojis } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; -import type { DriveFile } from '@/models/entities/drive-file.js'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; import { DI } from '@/di-symbols.js'; import { DriveService } from '@/services/DriveService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts index 71dbc8a0cd7a..9ab27b82499e 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Emojis } from '@/models/index.js'; -import type { Emoji } from '@/models/entities/emoji.js'; +import type { Emoji } from '@/models/entities/Emoji.js'; import { QueryService } from '@/services/QueryService.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts index 3215d13f580b..9dbcbf81fadc 100644 --- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users , Followings , Notifications } from '@/models/index.js'; -import type { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/User.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ModerationLogService } from '@/services/ModerationLogService.js'; import { UserSuspendService } from '@/services/UserSuspendService.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 05d084f03a43..3ad316f1b86c 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource } from 'typeorm'; -import { Meta } from '@/models/entities/meta.js'; +import { Meta } from '@/models/entities/Meta.js'; import { ModerationLogService } from '@/services/ModerationLogService.js'; import { DB_MAX_NOTE_TEXT_LENGTH } from '@/misc/hard-limits.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index 61b2072b1307..c4e9c23be246 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -3,8 +3,8 @@ import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { extractDbHost } from '@/misc/convert-host.js'; import type { Users , Notes } from '@/models/index.js'; -import type { Note } from '@/models/entities/note.js'; -import type { CacheableLocalUser, User } from '@/models/entities/user.js'; +import type { Note } from '@/models/entities/Note.js'; +import type { CacheableLocalUser, User } from '@/models/entities/User.js'; import { isActor, isPost, getApId } from '@/services/remote/activitypub/type.js'; import type { SchemaType } from '@/misc/schema.js'; import { ApResolverService } from '@/services/remote/activitypub/ApResolverService.js'; diff --git a/packages/backend/src/server/api/endpoints/channels/create.ts b/packages/backend/src/server/api/endpoints/channels/create.ts index 06dd391e5769..c45e9b3a7a48 100644 --- a/packages/backend/src/server/api/endpoints/channels/create.ts +++ b/packages/backend/src/server/api/endpoints/channels/create.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Channels, DriveFiles } from '@/models/index.js'; -import type { Channel } from '@/models/entities/channel.js'; +import type { Channel } from '@/models/entities/Channel.js'; import { IdService } from '@/services/IdService.js'; import { ChannelEntityService } from '@/services/entities/ChannelEntityService.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/drive/files/show.ts b/packages/backend/src/server/api/endpoints/drive/files/show.ts index c694a07c612a..cb03a5c0666d 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/show.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/show.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { DriveFile } from '@/models/entities/drive-file.js'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { DriveFiles } from '@/models/index.js'; import { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts index 396215959924..d6247b4f05e1 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts @@ -3,8 +3,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { GalleryPosts } from '@/models/index.js'; import { DriveFiles } from '@/models/index.js'; -import { GalleryPost } from '@/models/entities/gallery-post.js'; -import type { DriveFile } from '@/models/entities/drive-file.js'; +import { GalleryPost } from '@/models/entities/GalleryPost.js'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; import { IdService } from '@/services/IdService.js'; import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts index ec89b1fd1712..2517c5e8b62d 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts @@ -2,8 +2,8 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFiles, GalleryPosts } from '@/models/index.js'; -import { GalleryPost } from '@/models/entities/gallery-post.js'; -import type { DriveFile } from '@/models/entities/drive-file.js'; +import { GalleryPost } from '@/models/entities/GalleryPost.js'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/hashtags/trend.ts b/packages/backend/src/server/api/endpoints/hashtags/trend.ts index e26b815a8fa1..6c184d54a167 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/trend.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/trend.ts @@ -2,7 +2,7 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Notes } from '@/models/index.js'; -import type { Note } from '@/models/entities/note.js'; +import type { Note } from '@/models/entities/Note.js'; import { safeForSql } from '@/misc/safe-for-sql.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { MetaService } from '@/services/MetaService.js'; diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 770346ce6968..c8ff35becaca 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -5,9 +5,9 @@ import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mf import { extractHashtags } from '@/misc/extract-hashtags.js'; import type { Users , DriveFiles , UserProfiles } from '@/models/index.js'; import { Pages } from '@/models/index.js'; -import type { User } from '@/models/entities/user.js'; -import { birthdaySchema, descriptionSchema, locationSchema, nameSchema } from '@/models/entities/user.js'; -import type { UserProfile } from '@/models/entities/user-profile.js'; +import type { User } from '@/models/entities/User.js'; +import { birthdaySchema, descriptionSchema, locationSchema, nameSchema } from '@/models/entities/User.js'; +import type { UserProfile } from '@/models/entities/UserProfile.js'; import { notificationTypes } from '@/types.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { langmap } from '@/misc/langmap.js'; diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts index 012605d5a088..418bdf59bcb3 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/services/IdService.js'; import type { Webhooks } from '@/models/index.js'; -import { webhookEventTypes } from '@/models/entities/webhook.js'; +import { webhookEventTypes } from '@/models/entities/Webhook.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/update.ts b/packages/backend/src/server/api/endpoints/i/webhooks/update.ts index 0fb14c655f0b..b195921e8d32 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/update.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/update.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Webhooks } from '@/models/index.js'; import { publishInternalEvent } from '@/services/stream.js'; -import { webhookEventTypes } from '@/models/entities/webhook.js'; +import { webhookEventTypes } from '@/models/entities/Webhook.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/messaging/history.ts b/packages/backend/src/server/api/endpoints/messaging/history.ts index 6e2f4f4f9ef0..9b37c1dc4d79 100644 --- a/packages/backend/src/server/api/endpoints/messaging/history.ts +++ b/packages/backend/src/server/api/endpoints/messaging/history.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Brackets } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { MessagingMessage } from '@/models/entities/messaging-message.js'; +import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; import type { Mutings , UserGroupJoinings , MessagingMessages } from '@/models/index.js'; import { MessagingMessageEntityService } from '@/services/entities/MessagingMessageEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts index d650913af744..1d74e1d6e5ed 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts @@ -2,8 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Blockings , UserGroupJoinings , DriveFiles , UserGroups } from '@/models/index.js'; import { MessagingMessages } from '@/models/index.js'; -import type { User } from '@/models/entities/user.js'; -import type { UserGroup } from '@/models/entities/user-group.js'; +import type { User } from '@/models/entities/User.js'; +import type { UserGroup } from '@/models/entities/UserGroup.js'; import { createMessage } from '@/services/messages/create.js'; import { GetterService } from '@/server/api/common/GetterService.js'; import { MessagingService } from '@/services/MessagingService.js'; diff --git a/packages/backend/src/server/api/endpoints/mute/create.ts b/packages/backend/src/server/api/endpoints/mute/create.ts index a96a7e9ff334..85c1f5ec2060 100644 --- a/packages/backend/src/server/api/endpoints/mute/create.ts +++ b/packages/backend/src/server/api/endpoints/mute/create.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/services/IdService.js'; import type { Mutings } from '@/models/index.js'; -import type { Muting } from '@/models/entities/muting.js'; +import type { Muting } from '@/models/entities/Muting.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/conversation.ts b/packages/backend/src/server/api/endpoints/notes/conversation.ts index c3848013c8ab..374e2bc66b31 100644 --- a/packages/backend/src/server/api/endpoints/notes/conversation.ts +++ b/packages/backend/src/server/api/endpoints/notes/conversation.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { Note } from '@/models/entities/note.js'; +import type { Note } from '@/models/entities/Note.js'; import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index a62047ceb772..79ba1aa28f8f 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -1,12 +1,12 @@ import ms from 'ms'; import { In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/User.js'; import type { Users , Notes , Blockings } from '@/models/index.js'; import { DriveFiles, Channels } from '@/models/index.js'; -import type { DriveFile } from '@/models/entities/drive-file.js'; -import type { Note } from '@/models/entities/note.js'; -import type { Channel } from '@/models/entities/channel.js'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; +import type { Note } from '@/models/entities/Note.js'; +import type { Channel } from '@/models/entities/Channel.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; 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 480a0e9b945e..56c426d0f48c 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -1,7 +1,7 @@ import { Not } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { Users , Blockings , Polls , PollVotes } from '@/models/index.js'; -import type { IRemoteUser } from '@/models/entities/user.js'; +import type { IRemoteUser } from '@/models/entities/User.js'; import { IdService } from '@/services/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/common/GetterService.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts index 47fb051795fd..f123b8b64988 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts @@ -1,7 +1,7 @@ import { DeepPartial } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { NoteReactions } from '@/models/index.js'; -import type { NoteReaction } from '@/models/entities/note-reaction.js'; +import type { NoteReaction } from '@/models/entities/NoteReaction.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteReactionEntityService } from '@/services/entities/NoteReactionEntityService.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/pages/create.ts b/packages/backend/src/server/api/endpoints/pages/create.ts index 8064c9e53fda..498e43d6d4f2 100644 --- a/packages/backend/src/server/api/endpoints/pages/create.ts +++ b/packages/backend/src/server/api/endpoints/pages/create.ts @@ -3,7 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Pages } from '@/models/index.js'; import { DriveFiles } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; -import { Page } from '@/models/entities/page.js'; +import { Page } from '@/models/entities/Page.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { PageEntityService } from '@/services/entities/PageEntityService.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/pages/show.ts b/packages/backend/src/server/api/endpoints/pages/show.ts index cd52aae8d599..4d231094aa6a 100644 --- a/packages/backend/src/server/api/endpoints/pages/show.ts +++ b/packages/backend/src/server/api/endpoints/pages/show.ts @@ -1,7 +1,7 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { Users , Pages } from '@/models/index.js'; -import type { Page } from '@/models/entities/page.js'; +import type { Page } from '@/models/entities/Page.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { PageEntityService } from '@/services/entities/PageEntityService.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/pinned-users.ts b/packages/backend/src/server/api/endpoints/pinned-users.ts index e77598332739..31a509e2a5b4 100644 --- a/packages/backend/src/server/api/endpoints/pinned-users.ts +++ b/packages/backend/src/server/api/endpoints/pinned-users.ts @@ -2,7 +2,7 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { Users } from '@/models/index.js'; import * as Acct from '@/misc/acct.js'; -import type { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/User.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { MetaService } from '@/services/MetaService.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/username/available.ts b/packages/backend/src/server/api/endpoints/username/available.ts index 756faac6c825..6ba5ce9cea18 100644 --- a/packages/backend/src/server/api/endpoints/username/available.ts +++ b/packages/backend/src/server/api/endpoints/username/available.ts @@ -2,7 +2,7 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { UsedUsernames , Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { localUsernameSchema } from '@/models/entities/user'; +import { localUsernameSchema } from '@/models/entities/User.js'; export const meta = { tags: ['users'], diff --git a/packages/backend/src/server/api/endpoints/users/groups/create.ts b/packages/backend/src/server/api/endpoints/users/groups/create.ts index f3300c24d7a3..2b07dd461047 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/create.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/create.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; -import type { UserGroup } from '@/models/entities/user-group.js'; -import type { UserGroupJoining } from '@/models/entities/user-group-joining.js'; +import type { UserGroup } from '@/models/entities/UserGroup.js'; +import type { UserGroupJoining } from '@/models/entities/UserGroupJoining.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserGroupEntityService } from '@/services/entities/UserGroupEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts b/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts index aff52446d692..8c068876d110 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserGroupInvitations , UserGroupJoinings } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; -import type { UserGroupJoining } from '@/models/entities/user-group-joining.js'; +import type { UserGroupJoining } from '@/models/entities/UserGroupJoining.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ApiError } from '../../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/users/groups/invite.ts b/packages/backend/src/server/api/endpoints/users/groups/invite.ts index 63fa2bf26f70..245668ed361b 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invite.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invite.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserGroups, UserGroupJoinings, UserGroupInvitations } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; -import type { UserGroupInvitation } from '@/models/entities/user-group-invitation.js'; +import type { UserGroupInvitation } from '@/models/entities/UserGroupInvitation.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/common/GetterService.js'; import { CreateNotificationService } from '@/services/CreateNotificationService.js'; diff --git a/packages/backend/src/server/api/endpoints/users/lists/create.ts b/packages/backend/src/server/api/endpoints/users/lists/create.ts index 7fab192c1a46..7ed71893fad9 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserLists } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; -import type { UserList } from '@/models/entities/user-list.js'; +import type { UserList } from '@/models/entities/UserList.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserListEntityService } from '@/services/entities/UserListEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index fee6a602858a..6c9d0f641554 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -2,7 +2,7 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { Users , Followings } from '@/models/index.js'; import { USER_ACTIVE_THRESHOLD } from '@/const.js'; -import type { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/User.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index afd1d44ab198..9d0e9c2360fd 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -2,7 +2,7 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { Users } from '@/models/index.js'; import { UserProfiles } from '@/models/index.js'; -import type { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/User.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts index 25d546dde7bf..0300f59a624c 100644 --- a/packages/backend/src/server/api/endpoints/users/show.ts +++ b/packages/backend/src/server/api/endpoints/users/show.ts @@ -1,7 +1,7 @@ import { In, IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { Users } from '@/models/index.js'; -import type { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/User.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { ResolveUserService } from '@/services/remote/ResolveUserService.js'; diff --git a/packages/backend/src/server/api/integration/DiscordServerService.ts b/packages/backend/src/server/api/integration/DiscordServerService.ts index 0dab280de912..1d4dc6f253ac 100644 --- a/packages/backend/src/server/api/integration/DiscordServerService.ts +++ b/packages/backend/src/server/api/integration/DiscordServerService.ts @@ -6,14 +6,13 @@ import { v4 as uuid } from 'uuid'; import { IsNull } from 'typeorm'; import { Config } from '@/config.js'; import type { UserProfiles , Users } from '@/models/index.js'; - import { DI } from '@/di-symbols.js'; -import { HttpRequestService } from '@/services/HttpRequestService'; -import type { ILocalUser } from '@/models/entities/user'; -import { GlobalEventService } from '@/services/GlobalEventService'; -import { MetaService } from '@/services/MetaService'; -import { UserEntityService } from '@/services/entities/UserEntityService'; -import { SigninService } from '../SigninService'; +import { HttpRequestService } from '@/services/HttpRequestService.js'; +import type { ILocalUser } from '@/models/entities/User.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { MetaService } from '@/services/MetaService.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { SigninService } from '../SigninService.js'; import type Koa from 'koa'; @Injectable() diff --git a/packages/backend/src/server/api/integration/GithubServerService.ts b/packages/backend/src/server/api/integration/GithubServerService.ts index 0a0016170bbe..739a385df34f 100644 --- a/packages/backend/src/server/api/integration/GithubServerService.ts +++ b/packages/backend/src/server/api/integration/GithubServerService.ts @@ -7,12 +7,12 @@ import { IsNull } from 'typeorm'; import { Config } from '@/config.js'; import type { UserProfiles, Users } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; -import { HttpRequestService } from '@/services/HttpRequestService'; -import type { ILocalUser } from '@/models/entities/user'; -import { GlobalEventService } from '@/services/GlobalEventService'; -import { MetaService } from '@/services/MetaService'; -import { UserEntityService } from '@/services/entities/UserEntityService'; -import { SigninService } from '../SigninService'; +import { HttpRequestService } from '@/services/HttpRequestService.js'; +import type { ILocalUser } from '@/models/entities/User.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { MetaService } from '@/services/MetaService.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { SigninService } from '../SigninService.js'; import type Koa from 'koa'; @Injectable() diff --git a/packages/backend/src/server/api/integration/TwitterServerService.ts b/packages/backend/src/server/api/integration/TwitterServerService.ts index b7e81c78cbe2..c48e9148a099 100644 --- a/packages/backend/src/server/api/integration/TwitterServerService.ts +++ b/packages/backend/src/server/api/integration/TwitterServerService.ts @@ -8,12 +8,12 @@ import { Config } from '@/config.js'; import type { UserProfiles } from '@/models/index.js'; import { Users } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; -import { HttpRequestService } from '@/services/HttpRequestService'; -import type { ILocalUser } from '@/models/entities/user'; -import { GlobalEventService } from '@/services/GlobalEventService'; -import { MetaService } from '@/services/MetaService'; -import { UserEntityService } from '@/services/entities/UserEntityService'; -import { SigninService } from '../SigninService'; +import { HttpRequestService } from '@/services/HttpRequestService.js'; +import type { ILocalUser } from '@/models/entities/User.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { MetaService } from '@/services/MetaService.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { SigninService } from '../SigninService.js'; import type Koa from 'koa'; @Injectable() diff --git a/packages/backend/src/server/api/stream/channels/channel.ts b/packages/backend/src/server/api/stream/channels/channel.ts index 42c5577b8990..04b06e4ad488 100644 --- a/packages/backend/src/server/api/stream/channels/channel.ts +++ b/packages/backend/src/server/api/stream/channels/channel.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notes, Users } from '@/models/index.js'; import { isUserRelated } from '@/misc/is-user-related.js'; -import type { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/User.js'; import type { Packed } from '@/misc/schema.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; diff --git a/packages/backend/src/server/api/stream/channels/messaging.ts b/packages/backend/src/server/api/stream/channels/messaging.ts index c7b1ed223109..bd1a5ce865fb 100644 --- a/packages/backend/src/server/api/stream/channels/messaging.ts +++ b/packages/backend/src/server/api/stream/channels/messaging.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserGroupJoinings , Users , MessagingMessages } from '@/models/index.js'; -import type { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; -import type { UserGroup } from '@/models/entities/user-group.js'; +import type { User, ILocalUser, IRemoteUser } from '@/models/entities/User.js'; +import type { UserGroup } from '@/models/entities/UserGroup.js'; import Channel from '../channel.js'; import { readUserMessagingMessage, readGroupMessagingMessage, deliverReadActivity } from '../../common/read-messaging-message.js'; import type { StreamMessages } from '../types.js'; diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts index bf6d97538cff..ed87bf7b5cdb 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notes, UserListJoinings, UserLists } from '@/models/index.js'; -import type { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/User.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import type { Packed } from '@/misc/schema.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; diff --git a/packages/backend/src/server/api/stream/index.ts b/packages/backend/src/server/api/stream/index.ts index 060d09062254..9c43f0318537 100644 --- a/packages/backend/src/server/api/stream/index.ts +++ b/packages/backend/src/server/api/stream/index.ts @@ -1,9 +1,9 @@ -import type { User } from '@/models/entities/user.js'; -import type { Channel as ChannelModel } from '@/models/entities/channel.js'; +import type { User } from '@/models/entities/User.js'; +import type { Channel as ChannelModel } from '@/models/entities/Channel.js'; import type { Followings, Mutings, UserProfiles, ChannelFollowings, Blockings } from '@/models/index.js'; -import type { AccessToken } from '@/models/entities/access-token.js'; -import type { UserProfile } from '@/models/entities/user-profile.js'; -import type { UserGroup } from '@/models/entities/user-group.js'; +import type { AccessToken } from '@/models/entities/AccessToken.js'; +import type { UserProfile } from '@/models/entities/UserProfile.js'; +import type { UserGroup } from '@/models/entities/UserGroup.js'; import type { Packed } from '@/misc/schema.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; import type { NoteReadService } from '@/services/NoteReadService.js'; diff --git a/packages/backend/src/server/api/stream/types.ts b/packages/backend/src/server/api/stream/types.ts index 3b0a75d793c0..000f9a25dd8c 100644 --- a/packages/backend/src/server/api/stream/types.ts +++ b/packages/backend/src/server/api/stream/types.ts @@ -1,21 +1,20 @@ -import { EventEmitter } from 'events'; -import Emitter from 'strict-event-emitter-types'; -import { Channel } from '@/models/entities/channel.js'; -import { User } from '@/models/entities/user.js'; -import { UserProfile } from '@/models/entities/user-profile.js'; -import { Note } from '@/models/entities/note.js'; -import { Antenna } from '@/models/entities/antenna.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; -import { DriveFolder } from '@/models/entities/drive-folder.js'; -import { Emoji } from '@/models/entities/emoji.js'; -import { UserList } from '@/models/entities/user-list.js'; -import { MessagingMessage } from '@/models/entities/messaging-message.js'; -import { UserGroup } from '@/models/entities/user-group.js'; -import { AbuseUserReport } from '@/models/entities/abuse-user-report.js'; -import { Signin } from '@/models/entities/signin.js'; -import { Page } from '@/models/entities/page.js'; -import { Packed } from '@/misc/schema.js'; -import { Webhook } from '@/models/entities/webhook'; +import type { Channel } from '@/models/entities/Channel.js'; +import type { User } from '@/models/entities/User.js'; +import type { UserProfile } from '@/models/entities/UserProfile.js'; +import type { Note } from '@/models/entities/Note.js'; +import type { Antenna } from '@/models/entities/Antenna.js'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; +import type { DriveFolder } from '@/models/entities/DriveFolder.js'; +import type { UserList } from '@/models/entities/UserList.js'; +import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; +import type { UserGroup } from '@/models/entities/UserGroup.js'; +import type { AbuseUserReport } from '@/models/entities/AbuseUserReport.js'; +import type { Signin } from '@/models/entities/Signin.js'; +import type { Page } from '@/models/entities/Page.js'; +import type { Packed } from '@/misc/schema.js'; +import type { Webhook } from '@/models/entities/Webhook.js'; +import type Emitter from 'strict-event-emitter-types'; +import type { EventEmitter } from 'events'; //#region Stream type-body definitions export interface InternalStreamTypes { diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts index d8ccb7037fd9..ce56de15f52f 100644 --- a/packages/backend/src/server/web/FeedService.ts +++ b/packages/backend/src/server/web/FeedService.ts @@ -3,7 +3,7 @@ import { In, IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { DriveFiles, Notes, UserProfiles , Users } from '@/models/index.js'; import { Config } from '@/config.js'; -import type { User } from '@/models/entities/user'; +import type { User } from '@/models/entities/User.js'; @Injectable() export class FeedService { diff --git a/packages/backend/src/services/AccountUpdateService.ts b/packages/backend/src/services/AccountUpdateService.ts index f1b767417638..d69e76064b5e 100644 --- a/packages/backend/src/services/AccountUpdateService.ts +++ b/packages/backend/src/services/AccountUpdateService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import { Users } from '@/models/index.js'; import type { Config } from '@/config.js'; -import type { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/User.js'; import type { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; import type { RelayService } from '@/services/RelayService.js'; import type { ApDeliverManagerService } from '@/services/remote/activitypub/ApDeliverManagerService.js'; diff --git a/packages/backend/src/services/AntennaService.ts b/packages/backend/src/services/AntennaService.ts index b0479fdda7c8..959ec6162d1b 100644 --- a/packages/backend/src/services/AntennaService.ts +++ b/packages/backend/src/services/AntennaService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import type { AntennaNotes, Mutings, Notes, Users } from '@/models/index.js'; -import type { Antenna } from '@/models/entities/antenna.js'; -import type { Note } from '@/models/entities/note.js'; -import type { User } from '@/models/entities/user.js'; +import type { Antenna } from '@/models/entities/Antenna.js'; +import type { Note } from '@/models/entities/Note.js'; +import type { User } from '@/models/entities/User.js'; import type { IdService } from '@/services/IdService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; diff --git a/packages/backend/src/services/CreateNotificationService.ts b/packages/backend/src/services/CreateNotificationService.ts index dc202d41df92..225f5a6f4dcc 100644 --- a/packages/backend/src/services/CreateNotificationService.ts +++ b/packages/backend/src/services/CreateNotificationService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Mutings, Notifications, UserProfiles , Users } from '@/models/index.js'; -import type { User } from '@/models/entities/user.js'; -import type { Notification } from '@/models/entities/notification.js'; +import type { User } from '@/models/entities/User.js'; +import type { Notification } from '@/models/entities/Notification.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { IdService } from '@/services/IdService.js'; import { NotificationEntityService } from './entities/NotificationEntityService.js'; diff --git a/packages/backend/src/services/CreateSystemUserService.ts b/packages/backend/src/services/CreateSystemUserService.ts index 82c462301747..f1bc3f488a27 100644 --- a/packages/backend/src/services/CreateSystemUserService.ts +++ b/packages/backend/src/services/CreateSystemUserService.ts @@ -4,11 +4,11 @@ import bcrypt from 'bcryptjs'; import { v4 as uuid } from 'uuid'; import { IsNull , DataSource } from 'typeorm'; import { genRsaKeyPair } from '@/misc/gen-key-pair.js'; -import { User } from '@/models/entities/user.js'; -import { UserProfile } from '@/models/entities/user-profile.js'; +import { User } from '@/models/entities/User.js'; +import { UserProfile } from '@/models/entities/UserProfile.js'; import { IdService } from '@/services/IdService.js'; -import { UserKeypair } from '@/models/entities/user-keypair.js'; -import { UsedUsername } from '@/models/entities/used-username.js'; +import { UserKeypair } from '@/models/entities/UserKeypair.js'; +import { UsedUsername } from '@/models/entities/UsedUsername.js'; import { DI } from '@/di-symbols.js'; import generateNativeUserToken from '@/misc/generate-native-user-token.js'; diff --git a/packages/backend/src/services/CustomEmojiService.ts b/packages/backend/src/services/CustomEmojiService.ts index aed1011a674e..12b7f7e88c3b 100644 --- a/packages/backend/src/services/CustomEmojiService.ts +++ b/packages/backend/src/services/CustomEmojiService.ts @@ -5,8 +5,8 @@ import { GlobalEventService } from '@/services/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { Config } from '@/config.js'; import { IdService } from '@/services/IdService.js'; -import type { DriveFile } from '@/models/entities/drive-file.js'; -import type { Emoji } from '@/models/entities/emoji.js'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; +import type { Emoji } from '@/models/entities/Emoji.js'; @Injectable() export class CustomEmojiService { diff --git a/packages/backend/src/services/DriveService.ts b/packages/backend/src/services/DriveService.ts index 5572b1a075fb..d601a1f1e1de 100644 --- a/packages/backend/src/services/DriveService.ts +++ b/packages/backend/src/services/DriveService.ts @@ -8,9 +8,9 @@ import type { DriveFiles , Users , DriveFolders , UserProfiles } from '@/models/ import { Config } from '@/config.js'; import Logger from '@/Logger.js'; -import type { IRemoteUser, User } from '@/models/entities/user.js'; +import type { IRemoteUser, User } from '@/models/entities/User.js'; import { MetaService } from '@/services/MetaService.js'; -import { DriveFile } from '@/models/entities/drive-file.js'; +import { DriveFile } from '@/models/entities/DriveFile.js'; import { IdService } from '@/services/IdService.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; @@ -22,14 +22,14 @@ import { VideoProcessingService } from '@/services/VideoProcessingService.js'; import { ImageProcessingService } from '@/services/ImageProcessingService.js'; import type { IImage } from '@/services/ImageProcessingService.js'; import { QueueService } from '@/queue/queue.service.js'; -import type { DriveFolder } from '@/models/entities/drive-folder.js'; +import type { DriveFolder } from '@/models/entities/DriveFolder.js'; import { createTemp } from '@/misc/create-temp.js'; import DriveChart from '@/services/chart/charts/drive.js'; import PerUserDriveChart from '@/services/chart/charts/per-user-drive.js'; import InstanceChart from '@/services/chart/charts/instance.js'; import { DownloadService } from '@/services/DownloadService.js'; -import { S3Service } from '@/services/drive/S3Service.js'; -import { InternalStorageService } from '@/services/drive/InternalStorageService.js'; +import { S3Service } from '@/services/S3Service.js'; +import { InternalStorageService } from '@/services/InternalStorageService.js'; import { DriveFileEntityService } from './entities/DriveFileEntityService.js'; import { UserEntityService } from './entities/UserEntityService.js'; import type S3 from 'aws-sdk/clients/s3.js'; diff --git a/packages/backend/src/services/FederatedInstanceService.ts b/packages/backend/src/services/FederatedInstanceService.ts index 31d52e470bab..09d069048a93 100644 --- a/packages/backend/src/services/FederatedInstanceService.ts +++ b/packages/backend/src/services/FederatedInstanceService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Instances } from '@/models/index.js'; -import type { Instance } from '@/models/entities/instance'; +import type { Instance } from '@/models/entities/Instance.js'; import { Cache } from '@/misc/cache.js'; -import type { IdService } from '@/services/IdService.js'; +import { IdService } from '@/services/IdService.js'; import { toPuny } from '@/misc/convert-host.js'; @Injectable() diff --git a/packages/backend/src/services/FetchInstanceMetadataService.ts b/packages/backend/src/services/FetchInstanceMetadataService.ts index 57485fe95f71..3515a29344cd 100644 --- a/packages/backend/src/services/FetchInstanceMetadataService.ts +++ b/packages/backend/src/services/FetchInstanceMetadataService.ts @@ -3,7 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { JSDOM } from 'jsdom'; import fetch from 'node-fetch'; import tinycolor from 'tinycolor2'; -import type { Instance } from '@/models/entities/instance.js'; +import type { Instance } from '@/models/entities/Instance.js'; import type { Instances } from '@/models/index.js'; import type { AppLockService } from '@/services/AppLockService.js'; import Logger from '@/logger.js'; diff --git a/packages/backend/src/services/GlobalEventService.ts b/packages/backend/src/services/GlobalEventService.ts index adb06eb7b7b9..c36de63fde63 100644 --- a/packages/backend/src/services/GlobalEventService.ts +++ b/packages/backend/src/services/GlobalEventService.ts @@ -1,11 +1,11 @@ import { Inject, Injectable } from '@nestjs/common'; import Redis from 'ioredis'; -import type { User } from '@/models/entities/user.js'; -import type { Note } from '@/models/entities/note.js'; -import type { UserList } from '@/models/entities/user-list.js'; -import type { UserGroup } from '@/models/entities/user-group.js'; -import type { Antenna } from '@/models/entities/antenna.js'; -import type { Channel } from '@/models/entities/channel.js'; +import type { User } from '@/models/entities/User.js'; +import type { Note } from '@/models/entities/Note.js'; +import type { UserList } from '@/models/entities/UserList.js'; +import type { UserGroup } from '@/models/entities/UserGroup.js'; +import type { Antenna } from '@/models/entities/Antenna.js'; +import type { Channel } from '@/models/entities/Channel.js'; import type { StreamChannels, AdminStreamTypes, diff --git a/packages/backend/src/services/HashtagService.ts b/packages/backend/src/services/HashtagService.ts index 96e19795694a..5eca011f1f62 100644 --- a/packages/backend/src/services/HashtagService.ts +++ b/packages/backend/src/services/HashtagService.ts @@ -1,10 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import { Hashtags, Users } from '@/models/index.js'; -import type { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/User.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { IdService } from '@/services/IdService.js'; -import type { Hashtag } from '@/models/entities/hashtag.js'; +import type { Hashtag } from '@/models/entities/Hashtag.js'; import HashtagChart from '@/services/chart/charts/hashtag.js'; @Injectable() diff --git a/packages/backend/src/services/InstanceActorService.ts b/packages/backend/src/services/InstanceActorService.ts index da64be3c2c0b..a2bb46425644 100644 --- a/packages/backend/src/services/InstanceActorService.ts +++ b/packages/backend/src/services/InstanceActorService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; -import type { ILocalUser } from '@/models/entities/user.js'; +import type { ILocalUser } from '@/models/entities/User.js'; import type { Users } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; import type { CreateSystemUserService } from './CreateSystemUserService.js'; diff --git a/packages/backend/src/services/MessagingService.ts b/packages/backend/src/services/MessagingService.ts index 28b951efb525..13422633e3af 100644 --- a/packages/backend/src/services/MessagingService.ts +++ b/packages/backend/src/services/MessagingService.ts @@ -3,11 +3,11 @@ import { In, Not } from 'typeorm'; import { DI } from '@/di-symbols.js'; import { MessagingMessages, Mutings, UserGroupJoinings, Users } from '@/models/index.js'; import { Config } from '@/config.js'; -import type { DriveFile } from '@/models/entities/drive-file'; -import type { MessagingMessage } from '@/models/entities/messaging-message'; -import type { Note } from '@/models/entities/note'; -import type { User, CacheableUser } from '@/models/entities/user'; -import type { UserGroup } from '@/models/entities/user-group'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; +import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; +import type { Note } from '@/models/entities/Note.js'; +import type { User, CacheableUser } from '@/models/entities/User.js'; +import type { UserGroup } from '@/models/entities/UserGroup.js'; import { QueueService } from '@/queue/queue.service.js'; import { toArray } from '@/prelude/array'; import { IdentifiableError } from '@/misc/identifiable-error'; diff --git a/packages/backend/src/services/MetaService.ts b/packages/backend/src/services/MetaService.ts index ce995fdc3469..599c480aada5 100644 --- a/packages/backend/src/services/MetaService.ts +++ b/packages/backend/src/services/MetaService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource } from 'typeorm'; import type { Users } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; -import { Meta } from '@/models/entities/meta'; +import { Meta } from '@/models/entities/Meta.js'; import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() diff --git a/packages/backend/src/services/MfmService.ts b/packages/backend/src/services/MfmService.ts index 116a0f4d1d88..8a1fd8fb8fff 100644 --- a/packages/backend/src/services/MfmService.ts +++ b/packages/backend/src/services/MfmService.ts @@ -6,7 +6,7 @@ import { DI } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; import type { Config } from '@/config.js'; import { intersperse } from '@/prelude/array.js'; -import type { IMentionedRemoteUsers } from '@/models/entities/note.js'; +import type { IMentionedRemoteUsers } from '@/models/entities/Note.js'; import * as TreeAdapter from '../../node_modules/parse5/dist/tree-adapters/default.js'; import type * as mfm from 'mfm-js'; diff --git a/packages/backend/src/services/ModerationLogService.ts b/packages/backend/src/services/ModerationLogService.ts index c4d0fb3640f9..06904f4ca080 100644 --- a/packages/backend/src/services/ModerationLogService.ts +++ b/packages/backend/src/services/ModerationLogService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { ModerationLogs } from '@/models/index.js'; -import type { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/User.js'; import type { IdService } from '@/services/IdService.js'; @Injectable() diff --git a/packages/backend/src/services/NoteCreateService.ts b/packages/backend/src/services/NoteCreateService.ts index 8a1750e42b21..31316f2fb134 100644 --- a/packages/backend/src/services/NoteCreateService.ts +++ b/packages/backend/src/services/NoteCreateService.ts @@ -4,26 +4,26 @@ import { Inject, Injectable } from '@nestjs/common'; import { extractMentions } from '@/misc/extract-mentions.js'; import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js'; import { extractHashtags } from '@/misc/extract-hashtags.js'; -import type { IMentionedRemoteUsers } from '@/models/entities/note.js'; -import { Note } from '@/models/entities/note.js'; +import type { IMentionedRemoteUsers } from '@/models/entities/Note.js'; +import { Note } from '@/models/entities/Note.js'; import type { Notes , Users } from '@/models/index.js'; import { Mutings, NoteWatchings, Instances, UserProfiles, MutedNotes, Channels, ChannelFollowings, NoteThreadMutings } from '@/models/index.js'; -import type { DriveFile } from '@/models/entities/drive-file.js'; -import type { App } from '@/models/entities/app.js'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; +import type { App } from '@/models/entities/App.js'; import { concat } from '@/prelude/array.js'; import { IdService } from '@/services/IdService.js'; -import type { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; -import type { IPoll } from '@/models/entities/poll.js'; -import { Poll } from '@/models/entities/poll.js'; +import type { User, ILocalUser, IRemoteUser } from '@/models/entities/User.js'; +import type { IPoll } from '@/models/entities/Poll.js'; +import { Poll } from '@/models/entities/Poll.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import { checkHitAntenna } from '@/misc/check-hit-antenna.js'; import { checkWordMute } from '@/misc/check-word-mute.js'; import { countSameRenotes } from '@/misc/count-same-renotes.js'; -import type { Channel } from '@/models/entities/channel.js'; +import type { Channel } from '@/models/entities/Channel.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { getAntennas } from '@/misc/antenna-cache.js'; import { Cache } from '@/misc/cache.js'; -import type { UserProfile } from '@/models/entities/user-profile.js'; +import type { UserProfile } from '@/models/entities/UserProfile.js'; import { db } from '@/db/postgre.js'; import { RelayService } from '@/services/RelayService.js'; import { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; diff --git a/packages/backend/src/services/NoteDeleteService.ts b/packages/backend/src/services/NoteDeleteService.ts index b77068ccdb34..72e2e71e0f16 100644 --- a/packages/backend/src/services/NoteDeleteService.ts +++ b/packages/backend/src/services/NoteDeleteService.ts @@ -5,8 +5,8 @@ import renderAnnounce from '@/services/remote/activitypub/renderer/announce.js'; import renderUndo from '@/services/remote/activitypub/renderer/undo.js'; import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; import renderTombstone from '@/services/remote/activitypub/renderer/tombstone.js'; -import type { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js'; -import type { Note, IMentionedRemoteUsers } from '@/models/entities/note.js'; +import type { User, ILocalUser, IRemoteUser } from '@/models/entities/User.js'; +import type { Note, IMentionedRemoteUsers } from '@/models/entities/Note.js'; import type { Notes } from '@/models/index.js'; import { Users, Instances } from '@/models/index.js'; import { deliverToFollowers, deliverToUser } from '@/services/remote/activitypub/deliver-manager.js'; diff --git a/packages/backend/src/services/NotePiningService.ts b/packages/backend/src/services/NotePiningService.ts index b4afc22bea7c..c3fcad76a202 100644 --- a/packages/backend/src/services/NotePiningService.ts +++ b/packages/backend/src/services/NotePiningService.ts @@ -3,10 +3,10 @@ import { DI } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; import { Notes, UserNotePinings } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; -import type { User } from '@/models/entities/user.js'; -import type { Note } from '@/models/entities/note.js'; +import type { User } from '@/models/entities/User.js'; +import type { Note } from '@/models/entities/Note.js'; import { IdService } from '@/services/IdService.js'; -import type { UserNotePining } from '@/models/entities/user-note-pining.js'; +import type { UserNotePining } from '@/models/entities/UserNotePining.js'; import { RelayService } from '@/services/RelayService.js'; import { Config } from '@/config.js'; diff --git a/packages/backend/src/services/NoteReadService.ts b/packages/backend/src/services/NoteReadService.ts index 724942666813..a0650d117d40 100644 --- a/packages/backend/src/services/NoteReadService.ts +++ b/packages/backend/src/services/NoteReadService.ts @@ -2,10 +2,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, IsNull, Not } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { NoteUnreads , Users } from '@/models/index.js'; -import type { User } from '@/models/entities/user.js'; -import type { Channel } from '@/models/entities/channel.js'; +import type { User } from '@/models/entities/User.js'; +import type { Channel } from '@/models/entities/Channel.js'; import type { Packed } from '@/misc/schema.js'; -import type { Note } from '@/models/entities/note.js'; +import type { Note } from '@/models/entities/Note.js'; import type { IdService } from '@/services/IdService.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; diff --git a/packages/backend/src/services/NotificationService.ts b/packages/backend/src/services/NotificationService.ts index 25d9cb92580d..d84e280827d7 100644 --- a/packages/backend/src/services/NotificationService.ts +++ b/packages/backend/src/services/NotificationService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { Notifications , Users } from '@/models/index.js'; -import type { User } from '@/models/entities/user'; +import type { User } from '@/models/entities/User.js'; import { UserEntityService } from './entities/UserEntityService.js'; import { GlobalEventService } from './GlobalEventService.js'; import { PushNotificationService } from './PushNotificationService.js'; diff --git a/packages/backend/src/services/PollService.ts b/packages/backend/src/services/PollService.ts index d3dfa8322a30..fe668b5af78e 100644 --- a/packages/backend/src/services/PollService.ts +++ b/packages/backend/src/services/PollService.ts @@ -3,9 +3,9 @@ import { Not } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { Notes, Users , Blockings } from '@/models/index.js'; import { Polls , PollVotes } from '@/models/index.js'; -import type { Note } from '@/models/entities/note.js'; +import type { Note } from '@/models/entities/Note.js'; import { RelayService } from '@/services/RelayService.js'; -import type { CacheableUser } from '@/models/entities/user.js'; +import type { CacheableUser } from '@/models/entities/User.js'; import { IdService } from '@/services/IdService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { CreateNotificationService } from '@/services/CreateNotificationService.js'; diff --git a/packages/backend/src/services/QueryService.ts b/packages/backend/src/services/QueryService.ts index 1b371cc0cf19..7a5d879ad1a7 100644 --- a/packages/backend/src/services/QueryService.ts +++ b/packages/backend/src/services/QueryService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Brackets } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { NoteThreadMutings , Blockings , ChannelFollowings , MutedNotes , Followings , Mutings , UserProfiles } from '@/models/index.js'; -import type { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/User.js'; import type { SelectQueryBuilder } from 'typeorm'; @Injectable() diff --git a/packages/backend/src/services/ReactionService.ts b/packages/backend/src/services/ReactionService.ts index fc11e0d0e8b7..859efb59d3c1 100644 --- a/packages/backend/src/services/ReactionService.ts +++ b/packages/backend/src/services/ReactionService.ts @@ -4,10 +4,10 @@ import { DI } from '@/di-symbols.js'; import type { Blockings, Emojis, NoteReactions , Users , Notes } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; -import type { IRemoteUser, User } from '@/models/entities/user.js'; -import type { Note } from '@/models/entities/note.js'; +import type { IRemoteUser, User } from '@/models/entities/User.js'; +import type { Note } from '@/models/entities/Note.js'; import { IdService } from '@/services/IdService.js'; -import type { NoteReaction } from '@/models/entities/note-reaction.js'; +import type { NoteReaction } from '@/models/entities/NoteReaction.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { CreateNotificationService } from '@/services/CreateNotificationService.js'; diff --git a/packages/backend/src/services/RelayService.ts b/packages/backend/src/services/RelayService.ts index b045579351b3..3bb472d29ff2 100644 --- a/packages/backend/src/services/RelayService.ts +++ b/packages/backend/src/services/RelayService.ts @@ -1,10 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; -import type { ILocalUser, User } from '@/models/entities/user.js'; +import type { ILocalUser, User } from '@/models/entities/User.js'; import type { Relays, Users } from '@/models/index.js'; import type { IdService } from '@/services/IdService.js'; import { Cache } from '@/misc/cache.js'; -import type { Relay } from '@/models/entities/relay.js'; +import type { Relay } from '@/models/entities/Relay.js'; import type { QueueService } from '@/queue/queue.service.js'; import type { CreateSystemUserService } from '@/services/CreateSystemUserService.js'; import type { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; diff --git a/packages/backend/src/services/S3Service.ts b/packages/backend/src/services/S3Service.ts index 33e037786bfe..7709f086d84a 100644 --- a/packages/backend/src/services/S3Service.ts +++ b/packages/backend/src/services/S3Service.ts @@ -2,9 +2,9 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import S3 from 'aws-sdk/clients/s3.js'; import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; -import type { Meta } from '@/models/entities/meta'; -import type { HttpRequestService } from '../HttpRequestService.js'; +import { Config } from '@/config.js'; +import type { Meta } from '@/models/entities/Meta.js'; +import { HttpRequestService } from '../HttpRequestService.js'; @Injectable() export class S3Service { diff --git a/packages/backend/src/services/SignupService.ts b/packages/backend/src/services/SignupService.ts index fad945bf5fbf..3da864c02447 100644 --- a/packages/backend/src/services/SignupService.ts +++ b/packages/backend/src/services/SignupService.ts @@ -6,14 +6,14 @@ import { DI } from '@/di-symbols.js'; import type { UsedUsernames } from '@/models/index.js'; import { Users } from '@/models/index.js'; import { Config } from '@/config.js'; -import { User } from '@/models/entities/user.js'; -import { UserProfile } from '@/models/entities/user-profile.js'; +import { User } from '@/models/entities/User.js'; +import { UserProfile } from '@/models/entities/UserProfile.js'; import { IdService } from '@/services/IdService.js'; import { toPunyNullable } from '@/misc/convert-host'; -import { UserKeypair } from '@/models/entities/user-keypair'; -import { UsedUsername } from '@/models/entities/used-username'; -import UsersChart from './chart/charts/users'; -import generateUserToken from './generate-native-user-token.js'; +import { UserKeypair } from '@/models/entities/UserKeypair.js'; +import { UsedUsername } from '@/models/entities/UsedUsername.js'; +import UsersChart from './chart/charts/users.js'; +import { UserEntityService } from './entities/UserEntityService.js'; @Injectable() export class SignupService { @@ -30,6 +30,7 @@ export class SignupService { @Inject('usedUsernamesRepository') private usedUsernamesRepository: typeof UsedUsernames, + private userEntityService: UserEntityService, private idService: IdService, private usersChart: UsersChart, ) { @@ -45,13 +46,13 @@ export class SignupService { let hash = passwordHash; // Validate username - if (!Users.validateLocalUsername(username)) { + if (!this.userEntityService.validateLocalUsername(username)) { throw new Error('INVALID_USERNAME'); } if (password != null && passwordHash == null) { // Validate password - if (!Users.validatePassword(password)) { + if (!this.userEntityService.validatePassword(password)) { throw new Error('INVALID_PASSWORD'); } @@ -64,7 +65,7 @@ export class SignupService { const secret = generateUserToken(); // Check username duplication - if (await Users.findOneBy({ usernameLower: username.toLowerCase(), host: IsNull() })) { + if (await this.usersRepository.findOneBy({ usernameLower: username.toLowerCase(), host: IsNull() })) { throw new Error('DUPLICATED_USERNAME'); } diff --git a/packages/backend/src/services/UserBlockingService.ts b/packages/backend/src/services/UserBlockingService.ts index cd2fddd3519b..7ce648c89336 100644 --- a/packages/backend/src/services/UserBlockingService.ts +++ b/packages/backend/src/services/UserBlockingService.ts @@ -3,8 +3,8 @@ import { Inject, Injectable } from '@nestjs/common'; import type { FollowRequests , Followings , UserLists , UserListJoinings , Users , Blockings } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; -import type { CacheableUser, User } from '@/models/entities/user.js'; -import type { Blocking } from '@/models/entities/blocking.js'; +import type { CacheableUser, User } from '@/models/entities/User.js'; +import type { Blocking } from '@/models/entities/Blocking.js'; import { QueueService } from '@/queue/queue.service.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; diff --git a/packages/backend/src/services/UserCacheService.ts b/packages/backend/src/services/UserCacheService.ts index 81d9c2b0eb9b..c5235917c6c8 100644 --- a/packages/backend/src/services/UserCacheService.ts +++ b/packages/backend/src/services/UserCacheService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Users } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; -import type { CacheableLocalUser, CacheableUser, ILocalUser } from '@/models/entities/user.js'; +import type { CacheableLocalUser, CacheableUser, ILocalUser } from '@/models/entities/User.js'; import type Redis from 'ioredis'; import type { OnApplicationShutdown } from '@nestjs/common'; diff --git a/packages/backend/src/services/UserFollowingService.ts b/packages/backend/src/services/UserFollowingService.ts index 16b27c940554..c602ea853db9 100644 --- a/packages/backend/src/services/UserFollowingService.ts +++ b/packages/backend/src/services/UserFollowingService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users , Followings, FollowRequests , UserProfiles , Instances , Blockings } from '@/models/index.js'; -import type { CacheableUser, ILocalUser, IRemoteUser, User } from '@/models/entities/user.js'; +import type { CacheableUser, ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { QueueService } from '@/queue/queue.service.js'; import PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; diff --git a/packages/backend/src/services/UserKeypairStoreService.ts b/packages/backend/src/services/UserKeypairStoreService.ts index 257d2172d4d5..a4859acdcbb6 100644 --- a/packages/backend/src/services/UserKeypairStoreService.ts +++ b/packages/backend/src/services/UserKeypairStoreService.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/User.js'; import type { UserKeypairs } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; -import type { UserKeypair } from '@/models/entities/user-keypair.js'; +import type { UserKeypair } from '@/models/entities/UserKeypair.js'; @Injectable() export class UserKeypairStoreService { diff --git a/packages/backend/src/services/UserListService.ts b/packages/backend/src/services/UserListService.ts index d984b30d6410..99212ccb54aa 100644 --- a/packages/backend/src/services/UserListService.ts +++ b/packages/backend/src/services/UserListService.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserListJoinings , Users } from '@/models/index.js'; -import type { User } from '@/models/entities/user.js'; -import type { UserList } from '@/models/entities/user-list.js'; -import type { UserListJoining } from '@/models/entities/user-list-joining.js'; +import type { User } from '@/models/entities/User.js'; +import type { UserList } from '@/models/entities/UserList.js'; +import type { UserListJoining } from '@/models/entities/UserListJoining.js'; import { IdService } from '@/services/IdService.js'; import { UserFollowingService } from '@/services/UserFollowingService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; diff --git a/packages/backend/src/services/UserMutingService.ts b/packages/backend/src/services/UserMutingService.ts index 6f3e6de4f1e2..84060cd37ebc 100644 --- a/packages/backend/src/services/UserMutingService.ts +++ b/packages/backend/src/services/UserMutingService.ts @@ -4,7 +4,7 @@ import type { Users , Mutings } from '@/models/index.js'; import type { IdService } from '@/services/IdService.js'; import type { QueueService } from '@/queue/queue.service.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; -import type { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/User.js'; @Injectable() export class UserMutingService { diff --git a/packages/backend/src/services/UserSuspendService.ts b/packages/backend/src/services/UserSuspendService.ts index 132a3fb1c47a..5336f9476d32 100644 --- a/packages/backend/src/services/UserSuspendService.ts +++ b/packages/backend/src/services/UserSuspendService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Not, IsNull } from 'typeorm'; import type { Followings , Users } from '@/models/index.js'; -import type { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/User.js'; import type { QueueService } from '@/queue/queue.service.js'; import renderDelete from '@/services/remote/activitypub/renderer/delete.js'; import renderUndo from '@/services/remote/activitypub/renderer/undo.js'; diff --git a/packages/backend/src/services/WebhookService.ts b/packages/backend/src/services/WebhookService.ts index a2bc12b7c13f..6bdbb38846fc 100644 --- a/packages/backend/src/services/WebhookService.ts +++ b/packages/backend/src/services/WebhookService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; +import Redis from 'ioredis'; import type { Webhooks } from '@/models/index.js'; -import type { Webhook } from '@/models/entities/webhook'; -import type Redis from 'ioredis'; +import type { Webhook } from '@/models/entities/Webhook.js'; import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() diff --git a/packages/backend/src/services/chart/charts/active-users.ts b/packages/backend/src/services/chart/charts/active-users.ts index 1750b210659e..4084f890bf85 100644 --- a/packages/backend/src/services/chart/charts/active-users.ts +++ b/packages/backend/src/services/chart/charts/active-users.ts @@ -1,7 +1,7 @@ import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { AppLockService } from '@/services/AppLockService.js'; -import type { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/User.js'; import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; import { name, schema } from './entities/active-users.js'; diff --git a/packages/backend/src/services/chart/charts/drive.ts b/packages/backend/src/services/chart/charts/drive.ts index c7b245e463c8..a1132b25113b 100644 --- a/packages/backend/src/services/chart/charts/drive.ts +++ b/packages/backend/src/services/chart/charts/drive.ts @@ -1,7 +1,7 @@ import { Injectable, Inject } from '@nestjs/common'; import { Not, IsNull , DataSource } from 'typeorm'; import { DriveFiles } from '@/models/index.js'; -import type { DriveFile } from '@/models/entities/drive-file.js'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; diff --git a/packages/backend/src/services/chart/charts/hashtag.ts b/packages/backend/src/services/chart/charts/hashtag.ts index d08b842a46ad..a30520dce006 100644 --- a/packages/backend/src/services/chart/charts/hashtag.ts +++ b/packages/backend/src/services/chart/charts/hashtag.ts @@ -1,6 +1,6 @@ import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; -import type { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/User.js'; import { Users } from '@/models/index.js'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/services/chart/charts/instance.ts b/packages/backend/src/services/chart/charts/instance.ts index 893faf87ef4d..7c721fc9c229 100644 --- a/packages/backend/src/services/chart/charts/instance.ts +++ b/packages/backend/src/services/chart/charts/instance.ts @@ -1,8 +1,8 @@ import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { DriveFiles, Followings, Users, Notes } from '@/models/index.js'; -import type { DriveFile } from '@/models/entities/drive-file.js'; -import type { Note } from '@/models/entities/note.js'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; +import type { Note } from '@/models/entities/Note.js'; import { toPuny } from '@/misc/convert-host.js'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/services/chart/charts/notes.ts b/packages/backend/src/services/chart/charts/notes.ts index e79f26fe300d..34ae6b24f64f 100644 --- a/packages/backend/src/services/chart/charts/notes.ts +++ b/packages/backend/src/services/chart/charts/notes.ts @@ -1,7 +1,7 @@ import { Injectable, Inject } from '@nestjs/common'; import { Not, IsNull , DataSource } from 'typeorm'; import { Notes } from '@/models/index.js'; -import type { Note } from '@/models/entities/note.js'; +import type { Note } from '@/models/entities/Note.js'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; diff --git a/packages/backend/src/services/chart/charts/per-user-drive.ts b/packages/backend/src/services/chart/charts/per-user-drive.ts index bcadd83d0df4..f469d5d416a5 100644 --- a/packages/backend/src/services/chart/charts/per-user-drive.ts +++ b/packages/backend/src/services/chart/charts/per-user-drive.ts @@ -1,7 +1,7 @@ import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { DriveFiles } from '@/models/index.js'; -import type { DriveFile } from '@/models/entities/drive-file.js'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; diff --git a/packages/backend/src/services/chart/charts/per-user-following.ts b/packages/backend/src/services/chart/charts/per-user-following.ts index 0f84d5bba675..b18a770169ce 100644 --- a/packages/backend/src/services/chart/charts/per-user-following.ts +++ b/packages/backend/src/services/chart/charts/per-user-following.ts @@ -1,7 +1,7 @@ import { Injectable, Inject } from '@nestjs/common'; import { Not, IsNull , DataSource } from 'typeorm'; import { Followings, Users } from '@/models/index.js'; -import type { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/User.js'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; diff --git a/packages/backend/src/services/chart/charts/per-user-notes.ts b/packages/backend/src/services/chart/charts/per-user-notes.ts index 7ea2a87a9a2c..5e2bb811a10d 100644 --- a/packages/backend/src/services/chart/charts/per-user-notes.ts +++ b/packages/backend/src/services/chart/charts/per-user-notes.ts @@ -1,8 +1,8 @@ import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; -import type { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/User.js'; import { Notes } from '@/models/index.js'; -import type { Note } from '@/models/entities/note.js'; +import type { Note } from '@/models/entities/Note.js'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; diff --git a/packages/backend/src/services/chart/charts/per-user-reactions.ts b/packages/backend/src/services/chart/charts/per-user-reactions.ts index a64e0956690e..46a049c1b5c5 100644 --- a/packages/backend/src/services/chart/charts/per-user-reactions.ts +++ b/packages/backend/src/services/chart/charts/per-user-reactions.ts @@ -1,7 +1,7 @@ import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; -import type { User } from '@/models/entities/user.js'; -import type { Note } from '@/models/entities/note.js'; +import type { User } from '@/models/entities/User.js'; +import type { Note } from '@/models/entities/Note.js'; import { Users } from '@/models/index.js'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/services/chart/charts/users.ts b/packages/backend/src/services/chart/charts/users.ts index fdc5be9174cd..19d88af3188f 100644 --- a/packages/backend/src/services/chart/charts/users.ts +++ b/packages/backend/src/services/chart/charts/users.ts @@ -1,7 +1,7 @@ import { Injectable, Inject } from '@nestjs/common'; import { Not, IsNull , DataSource } from 'typeorm'; import { Users } from '@/models/index.js'; -import type { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/User.js'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; diff --git a/packages/backend/src/services/entities/AbuseUserReportEntityService.ts b/packages/backend/src/services/entities/AbuseUserReportEntityService.ts index 8a218195819a..135b22d6a3be 100644 --- a/packages/backend/src/services/entities/AbuseUserReportEntityService.ts +++ b/packages/backend/src/services/entities/AbuseUserReportEntityService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { AbuseUserReports } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; -import type { AbuseUserReport } from '@/models/entities/abuse-user-report.js'; +import type { AbuseUserReport } from '@/models/entities/AbuseUserReport.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() diff --git a/packages/backend/src/services/entities/AntennaEntityService.ts b/packages/backend/src/services/entities/AntennaEntityService.ts index 98a089b8bf9b..72a1994b8a50 100644 --- a/packages/backend/src/services/entities/AntennaEntityService.ts +++ b/packages/backend/src/services/entities/AntennaEntityService.ts @@ -3,7 +3,7 @@ import { DI } from '@/di-symbols.js'; import type { AntennaNotes, Antennas, UserGroupJoinings } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { Antenna } from '@/models/entities/antenna.js'; +import type { Antenna } from '@/models/entities/Antenna.js'; @Injectable() export class AntennaEntityService { diff --git a/packages/backend/src/services/entities/AppEntityService.ts b/packages/backend/src/services/entities/AppEntityService.ts index 90e6f91e69c1..ca172f3f0c94 100644 --- a/packages/backend/src/services/entities/AppEntityService.ts +++ b/packages/backend/src/services/entities/AppEntityService.ts @@ -3,8 +3,8 @@ import { DI } from '@/di-symbols.js'; import type { AccessTokens, Apps } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { App } from '@/models/entities/app.js'; -import type { User } from '@/models/entities/user.js'; +import type { App } from '@/models/entities/App.js'; +import type { User } from '@/models/entities/User.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() diff --git a/packages/backend/src/services/entities/AuthSessionEntityService.ts b/packages/backend/src/services/entities/AuthSessionEntityService.ts index ec4ad82a634f..d6b131e38fe2 100644 --- a/packages/backend/src/services/entities/AuthSessionEntityService.ts +++ b/packages/backend/src/services/entities/AuthSessionEntityService.ts @@ -4,8 +4,8 @@ import { Apps } from '@/models/index.js'; import type { AuthSessions } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { AuthSession } from '@/models/entities/auth-session.js'; -import type { User } from '@/models/entities/user.js'; +import type { AuthSession } from '@/models/entities/AuthSession.js'; +import type { User } from '@/models/entities/User.js'; import { UserEntityService } from './UserEntityService.js'; import { AppEntityService } from './AppEntityService.js'; diff --git a/packages/backend/src/services/entities/BlockingEntityService.ts b/packages/backend/src/services/entities/BlockingEntityService.ts index ea5d4f46ee04..61671f8e5aa1 100644 --- a/packages/backend/src/services/entities/BlockingEntityService.ts +++ b/packages/backend/src/services/entities/BlockingEntityService.ts @@ -3,8 +3,8 @@ import { DI } from '@/di-symbols.js'; import type { Blockings } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { Blocking } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; +import type { Blocking } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() diff --git a/packages/backend/src/services/entities/ChannelEntityService.ts b/packages/backend/src/services/entities/ChannelEntityService.ts index d5cfa4907829..8e04d0e5c689 100644 --- a/packages/backend/src/services/entities/ChannelEntityService.ts +++ b/packages/backend/src/services/entities/ChannelEntityService.ts @@ -3,9 +3,9 @@ import { DI } from '@/di-symbols.js'; import type { ChannelFollowings, Channels, DriveFiles, NoteUnreads } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; -import type { Channel } from '@/models/entities/channel.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { Channel } from '@/models/entities/Channel.js'; import { UserEntityService } from './UserEntityService.js'; import { DriveFileEntityService } from './DriveFileEntityService.js'; diff --git a/packages/backend/src/services/entities/ClipEntityService.ts b/packages/backend/src/services/entities/ClipEntityService.ts index a40600eb5596..e45dfa8da13a 100644 --- a/packages/backend/src/services/entities/ClipEntityService.ts +++ b/packages/backend/src/services/entities/ClipEntityService.ts @@ -3,9 +3,9 @@ import { DI } from '@/di-symbols.js'; import type { Clips } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; -import type { Clip } from '@/models/entities/clip.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { Clip } from '@/models/entities/Clip.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() diff --git a/packages/backend/src/services/entities/DriveFileEntityService.ts b/packages/backend/src/services/entities/DriveFileEntityService.ts index 26b0d12d88be..c49d605be993 100644 --- a/packages/backend/src/services/entities/DriveFileEntityService.ts +++ b/packages/backend/src/services/entities/DriveFileEntityService.ts @@ -6,8 +6,8 @@ import type { Notes , DriveFiles } from '@/models/index.js'; import { Config } from '@/config.js'; import type { Packed } from '@/misc/schema.js'; import { awaitAll } from '@/prelude/await-all.js'; -import type { User } from '@/models/entities/user'; -import type { DriveFile } from '@/models/entities/drive-file.js'; +import type { User } from '@/models/entities/User.js'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; import { appendQuery, query } from '@/prelude/url.js'; import { toPuny } from '@/misc/convert-host.js'; import { UserEntityService } from './UserEntityService.js'; diff --git a/packages/backend/src/services/entities/DriveFolderEntityService.ts b/packages/backend/src/services/entities/DriveFolderEntityService.ts index 4b21728b8ecf..a480b1bb0d91 100644 --- a/packages/backend/src/services/entities/DriveFolderEntityService.ts +++ b/packages/backend/src/services/entities/DriveFolderEntityService.ts @@ -3,9 +3,9 @@ import { DI } from '@/di-symbols.js'; import type { DriveFiles, DriveFolders } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; -import type { DriveFolder } from '@/models/entities/drive-folder.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { DriveFolder } from '@/models/entities/DriveFolder.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() diff --git a/packages/backend/src/services/entities/EmojiEntityService.ts b/packages/backend/src/services/entities/EmojiEntityService.ts index c47c4d090e5b..ec1018b29860 100644 --- a/packages/backend/src/services/entities/EmojiEntityService.ts +++ b/packages/backend/src/services/entities/EmojiEntityService.ts @@ -3,9 +3,9 @@ import { DI } from '@/di-symbols.js'; import type { Emojis } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; -import type { Emoji } from '@/models/entities/emoji.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { Emoji } from '@/models/entities/Emoji.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() diff --git a/packages/backend/src/services/entities/FollowRequestEntityService.ts b/packages/backend/src/services/entities/FollowRequestEntityService.ts index 89c7568354bd..a484d318601b 100644 --- a/packages/backend/src/services/entities/FollowRequestEntityService.ts +++ b/packages/backend/src/services/entities/FollowRequestEntityService.ts @@ -3,9 +3,9 @@ import { DI } from '@/di-symbols.js'; import type { FollowRequests } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; -import type { FollowRequest } from '@/models/entities/follow-request.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { FollowRequest } from '@/models/entities/FollowRequest.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() diff --git a/packages/backend/src/services/entities/FollowingEntityService.ts b/packages/backend/src/services/entities/FollowingEntityService.ts index cd7006097d6d..16c82c99528c 100644 --- a/packages/backend/src/services/entities/FollowingEntityService.ts +++ b/packages/backend/src/services/entities/FollowingEntityService.ts @@ -3,9 +3,9 @@ import { DI } from '@/di-symbols.js'; import type { Followings } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; -import type { Following } from '@/models/entities/following.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { Following } from '@/models/entities/Following.js'; import { UserEntityService } from './UserEntityService.js'; type LocalFollowerFollowing = Following & { diff --git a/packages/backend/src/services/entities/GalleryLikeEntityService.ts b/packages/backend/src/services/entities/GalleryLikeEntityService.ts index ecee6e155d1f..98eb1785c51c 100644 --- a/packages/backend/src/services/entities/GalleryLikeEntityService.ts +++ b/packages/backend/src/services/entities/GalleryLikeEntityService.ts @@ -4,9 +4,9 @@ import { GalleryPosts } from '@/models/index.js'; import type { GalleryLikes } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; -import type { GalleryLike } from '@/models/entities/gallery-like.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { GalleryLike } from '@/models/entities/GalleryLike.js'; import { UserEntityService } from './UserEntityService.js'; import { GalleryPostEntityService } from './GalleryPostEntityService.js'; diff --git a/packages/backend/src/services/entities/GalleryPostEntityService.ts b/packages/backend/src/services/entities/GalleryPostEntityService.ts index 7706d1ec4456..8e29c7bc49f3 100644 --- a/packages/backend/src/services/entities/GalleryPostEntityService.ts +++ b/packages/backend/src/services/entities/GalleryPostEntityService.ts @@ -3,9 +3,9 @@ import { DI } from '@/di-symbols.js'; import type { GalleryLikes, GalleryPosts } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; -import type { GalleryPost } from '@/models/entities/gallery-post.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { GalleryPost } from '@/models/entities/GalleryPost.js'; import { UserEntityService } from './UserEntityService.js'; import { DriveFileEntityService } from './DriveFileEntityService.js'; diff --git a/packages/backend/src/services/entities/HashtagEntityService.ts b/packages/backend/src/services/entities/HashtagEntityService.ts index 9d6376c5897e..1b923f97fa1a 100644 --- a/packages/backend/src/services/entities/HashtagEntityService.ts +++ b/packages/backend/src/services/entities/HashtagEntityService.ts @@ -3,9 +3,9 @@ import { DI } from '@/di-symbols.js'; import type { Hashtags } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; -import type { Hashtag } from '@/models/entities/hashtag.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { Hashtag } from '@/models/entities/Hashtag.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() diff --git a/packages/backend/src/services/entities/InstanceEntityService.ts b/packages/backend/src/services/entities/InstanceEntityService.ts index 529c06123f59..edb733c6123c 100644 --- a/packages/backend/src/services/entities/InstanceEntityService.ts +++ b/packages/backend/src/services/entities/InstanceEntityService.ts @@ -3,9 +3,9 @@ import { DI } from '@/di-symbols.js'; import type { Instances } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; -import type { Instance } from '@/models/entities/instance.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { Instance } from '@/models/entities/Instance.js'; import { MetaService } from '../MetaService.js'; import { UserEntityService } from './UserEntityService.js'; diff --git a/packages/backend/src/services/entities/MessagingMessageEntityService.ts b/packages/backend/src/services/entities/MessagingMessageEntityService.ts index 95a19b7278e3..152e2339fdd8 100644 --- a/packages/backend/src/services/entities/MessagingMessageEntityService.ts +++ b/packages/backend/src/services/entities/MessagingMessageEntityService.ts @@ -3,9 +3,9 @@ import { DI } from '@/di-symbols.js'; import type { MessagingMessages } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; -import type { MessagingMessage } from '@/models/entities/messaging-message.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; import { UserEntityService } from './UserEntityService.js'; import { DriveFileEntityService } from './DriveFileEntityService.js'; diff --git a/packages/backend/src/services/entities/ModerationLogEntityService.ts b/packages/backend/src/services/entities/ModerationLogEntityService.ts index ee45f3317ec0..b94dfd4e252c 100644 --- a/packages/backend/src/services/entities/ModerationLogEntityService.ts +++ b/packages/backend/src/services/entities/ModerationLogEntityService.ts @@ -3,9 +3,9 @@ import { DI } from '@/di-symbols.js'; import type { ModerationLogs } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; -import type { ModerationLog } from '@/models/entities/moderation-log.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { ModerationLog } from '@/models/entities/ModerationLog.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() diff --git a/packages/backend/src/services/entities/MutingEntityService.ts b/packages/backend/src/services/entities/MutingEntityService.ts index 21f783eb83ba..af9a5d6c1f5b 100644 --- a/packages/backend/src/services/entities/MutingEntityService.ts +++ b/packages/backend/src/services/entities/MutingEntityService.ts @@ -3,9 +3,9 @@ import { DI } from '@/di-symbols.js'; import type { Mutings } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; -import type { Muting } from '@/models/entities/muting.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { Muting } from '@/models/entities/Muting.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() diff --git a/packages/backend/src/services/entities/NoteEntityService.ts b/packages/backend/src/services/entities/NoteEntityService.ts index 05d4067f447a..3a96a04fefb2 100644 --- a/packages/backend/src/services/entities/NoteEntityService.ts +++ b/packages/backend/src/services/entities/NoteEntityService.ts @@ -8,9 +8,9 @@ import type { Packed } from '@/misc/schema.js'; import { nyaize } from '@/misc/nyaize.js'; import { awaitAll } from '@/prelude/await-all.js'; import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '@/misc/reaction-lib.js'; -import type { User } from '@/models/entities/user.js'; -import type { Note } from '@/models/entities/note.js'; -import type { NoteReaction } from '@/models/entities/note-reaction.js'; +import type { User } from '@/models/entities/User.js'; +import type { Note } from '@/models/entities/Note.js'; +import type { NoteReaction } from '@/models/entities/NoteReaction.js'; import { aggregateNoteEmojis, populateEmojis, prefetchEmojis } from '@/misc/populate-emojis.js'; import { UserEntityService } from './UserEntityService.js'; import { DriveFileEntityService } from './DriveFileEntityService.js'; diff --git a/packages/backend/src/services/entities/NoteFavoriteEntityService.ts b/packages/backend/src/services/entities/NoteFavoriteEntityService.ts index 0d01f1009346..096f3880ecc7 100644 --- a/packages/backend/src/services/entities/NoteFavoriteEntityService.ts +++ b/packages/backend/src/services/entities/NoteFavoriteEntityService.ts @@ -3,9 +3,9 @@ import { DI } from '@/di-symbols.js'; import type { NoteFavorites } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; -import type { NoteFavorite } from '@/models/entities/note-favorite.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { NoteFavorite } from '@/models/entities/NoteFavorite.js'; import { UserEntityService } from './UserEntityService.js'; import { NoteEntityService } from './NoteEntityService.js'; diff --git a/packages/backend/src/services/entities/NoteReactionEntityService.ts b/packages/backend/src/services/entities/NoteReactionEntityService.ts index c0b0c28f9824..c1d7d00a98ea 100644 --- a/packages/backend/src/services/entities/NoteReactionEntityService.ts +++ b/packages/backend/src/services/entities/NoteReactionEntityService.ts @@ -3,9 +3,9 @@ import { DI } from '@/di-symbols.js'; import type { NoteReactions } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; -import type { NoteReaction } from '@/models/entities/note-reaction.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { NoteReaction } from '@/models/entities/NoteReaction.js'; import { convertLegacyReaction } from '@/misc/reaction-lib.js'; import { UserEntityService } from './UserEntityService.js'; import { NoteEntityService } from './NoteEntityService.js'; diff --git a/packages/backend/src/services/entities/NotificationEntityService.ts b/packages/backend/src/services/entities/NotificationEntityService.ts index 33656482be4f..797c21fc62de 100644 --- a/packages/backend/src/services/entities/NotificationEntityService.ts +++ b/packages/backend/src/services/entities/NotificationEntityService.ts @@ -3,10 +3,10 @@ import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { AccessTokens, NoteReactions , Notifications } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; -import type { Notification } from '@/models/entities/notification.js'; +import type { Notification } from '@/models/entities/Notification.js'; import { aggregateNoteEmojis, prefetchEmojis } from '@/misc/populate-emojis.js'; -import type { NoteReaction } from '@/models/entities/note-reaction.js'; -import type { Note } from '@/models/entities/note.js'; +import type { NoteReaction } from '@/models/entities/NoteReaction.js'; +import type { Note } from '@/models/entities/Note.js'; import type { Packed } from '@/misc/schema.js'; import { UserEntityService } from './UserEntityService.js'; import { NoteEntityService } from './NoteEntityService.js'; diff --git a/packages/backend/src/services/entities/PageEntityService.ts b/packages/backend/src/services/entities/PageEntityService.ts index b19fda5279f7..de37a0794c65 100644 --- a/packages/backend/src/services/entities/PageEntityService.ts +++ b/packages/backend/src/services/entities/PageEntityService.ts @@ -4,10 +4,10 @@ import { DriveFiles } from '@/models/index.js'; import type { Pages , PageLikes } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; -import type { Page } from '@/models/entities/page.js'; -import type { DriveFile } from '@/models/entities/drive-file.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { Page } from '@/models/entities/Page.js'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() diff --git a/packages/backend/src/services/entities/PageLikeEntityService.ts b/packages/backend/src/services/entities/PageLikeEntityService.ts index 91b930fe6cf5..3bd3cbf84369 100644 --- a/packages/backend/src/services/entities/PageLikeEntityService.ts +++ b/packages/backend/src/services/entities/PageLikeEntityService.ts @@ -3,9 +3,9 @@ import { DI } from '@/di-symbols.js'; import type { PageLikes } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; -import type { PageLike } from '@/models/entities/page-like.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { PageLike } from '@/models/entities/PageLike.js'; import { UserEntityService } from './UserEntityService.js'; import { PageEntityService } from './PageEntityService.js'; diff --git a/packages/backend/src/services/entities/SigninEntityService.ts b/packages/backend/src/services/entities/SigninEntityService.ts index b2d0987783ce..88bcb7301766 100644 --- a/packages/backend/src/services/entities/SigninEntityService.ts +++ b/packages/backend/src/services/entities/SigninEntityService.ts @@ -3,9 +3,9 @@ import { DI } from '@/di-symbols.js'; import type { Signins } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; -import type { Signin } from '@/models/entities/signin.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { Signin } from '@/models/entities/Signin.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() diff --git a/packages/backend/src/services/entities/UserEntityService.ts b/packages/backend/src/services/entities/UserEntityService.ts index e5d0acb99a1e..11417d36250f 100644 --- a/packages/backend/src/services/entities/UserEntityService.ts +++ b/packages/backend/src/services/entities/UserEntityService.ts @@ -12,9 +12,9 @@ import { populateEmojis } from '@/misc/populate-emojis.js'; import { getAntennas } from '@/misc/antenna-cache.js'; import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js'; import { Cache } from '@/misc/cache.js'; -import type { Instance } from '@/models/entities/instance.js'; -import type { ILocalUser, IRemoteUser, User } from '@/models/entities/user.js'; -import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/entities/user.js'; +import type { Instance } from '@/models/entities/Instance.js'; +import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; +import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/entities/User.js'; import { NoteEntityService } from './NoteEntityService.js'; const userInstanceCache = new Cache(1000 * 60 * 60 * 3); diff --git a/packages/backend/src/services/entities/UserGroupEntityService.ts b/packages/backend/src/services/entities/UserGroupEntityService.ts index 26f2ce0bcaab..591da729c9e9 100644 --- a/packages/backend/src/services/entities/UserGroupEntityService.ts +++ b/packages/backend/src/services/entities/UserGroupEntityService.ts @@ -3,9 +3,9 @@ import { DI } from '@/di-symbols.js'; import type { UserGroupJoinings, UserGroups } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; -import type { UserGroup } from '@/models/entities/user-group.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { UserGroup } from '@/models/entities/UserGroup.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() diff --git a/packages/backend/src/services/entities/UserGroupInvitationEntityService.ts b/packages/backend/src/services/entities/UserGroupInvitationEntityService.ts index 24eabfd899fd..f9331be8efdb 100644 --- a/packages/backend/src/services/entities/UserGroupInvitationEntityService.ts +++ b/packages/backend/src/services/entities/UserGroupInvitationEntityService.ts @@ -3,9 +3,9 @@ import { DI } from '@/di-symbols.js'; import type { UserGroupInvitations } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; -import type { UserGroupInvitation } from '@/models/entities/user-group-invitation.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { UserGroupInvitation } from '@/models/entities/UserGroupInvitation.js'; import { UserEntityService } from './UserEntityService.js'; import { UserGroupEntityService } from './UserGroupEntityService.js'; diff --git a/packages/backend/src/services/entities/UserListEntityService.ts b/packages/backend/src/services/entities/UserListEntityService.ts index 6035df0b0317..7c42c5789eec 100644 --- a/packages/backend/src/services/entities/UserListEntityService.ts +++ b/packages/backend/src/services/entities/UserListEntityService.ts @@ -3,9 +3,9 @@ import { DI } from '@/di-symbols.js'; import type { UserListJoinings, UserLists } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; -import type { } from '@/models/entities/blocking.js'; -import type { User } from '@/models/entities/user.js'; -import type { UserList } from '@/models/entities/user-list.js'; +import type { } from '@/models/entities/Blocking.js'; +import type { User } from '@/models/entities/User.js'; +import type { UserList } from '@/models/entities/UserList.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() diff --git a/packages/backend/src/services/remote/ResolveUserService.ts b/packages/backend/src/services/remote/ResolveUserService.ts index 7e39b8cb3606..7c50d5b1863d 100644 --- a/packages/backend/src/services/remote/ResolveUserService.ts +++ b/packages/backend/src/services/remote/ResolveUserService.ts @@ -4,7 +4,7 @@ import chalk from 'chalk'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; -import type { IRemoteUser, User } from '@/models/entities/user.js'; +import type { IRemoteUser, User } from '@/models/entities/User.js'; import type { Config } from '@/config.js'; import { toPuny } from '@/misc/convert-host.js'; import type Logger from '@/logger.js'; diff --git a/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts b/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts index 346e35e6d6ca..43784a7e41fe 100644 --- a/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts +++ b/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts @@ -3,13 +3,13 @@ import escapeRegexp from 'escape-regexp'; import { DI } from '@/di-symbols.js'; import type { MessagingMessages, Notes, UserPublickeys } from '@/models/index.js'; import { Users } from '@/models/index.js'; -import type { Config } from '@/config.js'; -import type { CacheableRemoteUser, CacheableUser } from '@/models/entities/user.js'; +import { Config } from '@/config.js'; +import type { CacheableRemoteUser, CacheableUser } from '@/models/entities/User.js'; import { Cache } from '@/misc/cache.js'; -import type { UserPublickey } from '@/models/entities/user-publickey.js'; -import type { UserCacheService } from '@/services/UserCacheService.js'; -import type { Note } from '@/models/entities/note.js'; -import type { MessagingMessage } from '@/models/entities/messaging-message.js'; +import type { UserPublickey } from '@/models/entities/UserPublickey.js'; +import { UserCacheService } from '@/services/UserCacheService.js'; +import type { Note } from '@/models/entities/Note.js'; +import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; import { getApId } from './type.js'; import type { IObject } from './type.js'; diff --git a/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts b/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts index 10f11df1d557..8f42039be6b2 100644 --- a/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts +++ b/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts @@ -3,7 +3,7 @@ import { IsNull, Not } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { Followings , Users } from '@/models/index.js'; import { Config } from '@/config.js'; -import type { ILocalUser, IRemoteUser, User } from '@/models/entities/user.js'; +import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; import { QueueService } from '@/queue/queue.service.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; diff --git a/packages/backend/src/services/remote/activitypub/ApInboxService.ts b/packages/backend/src/services/remote/activitypub/ApInboxService.ts index e352d814ad81..ea46a7ff0f01 100644 --- a/packages/backend/src/services/remote/activitypub/ApInboxService.ts +++ b/packages/backend/src/services/remote/activitypub/ApInboxService.ts @@ -3,7 +3,7 @@ import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { Followings, Notes , Users } from '@/models/index.js'; import { Config } from '@/config.js'; -import type { CacheableRemoteUser } from '@/models/entities/user.js'; +import type { CacheableRemoteUser } from '@/models/entities/User.js'; import { UserFollowingService } from '@/services/UserFollowingService.js'; import { ReactionService } from '@/services/ReactionService.js'; import { RelayService } from '@/services/RelayService.js'; diff --git a/packages/backend/src/services/remote/activitypub/ApRendererService.ts b/packages/backend/src/services/remote/activitypub/ApRendererService.ts index 3fd4ed58da3a..b2ab76b318e2 100644 --- a/packages/backend/src/services/remote/activitypub/ApRendererService.ts +++ b/packages/backend/src/services/remote/activitypub/ApRendererService.ts @@ -4,19 +4,19 @@ import { v4 as uuid } from 'uuid'; import * as mfm from 'mfm-js'; import { DI } from '@/di-symbols.js'; import type { Polls , DriveFiles, Emojis, Notes, Users } from '@/models/index.js'; -import type { Config } from '@/config.js'; -import type { ILocalUser, IRemoteUser, User } from '@/models/entities/user.js'; -import type { IMentionedRemoteUsers, Note } from '@/models/entities/note.js'; -import type { Blocking } from '@/models/entities/blocking.js'; -import type { Relay } from '@/models/entities/relay.js'; -import type { DriveFile } from '@/models/entities/drive-file.js'; -import type { NoteReaction } from '@/models/entities/note-reaction.js'; -import type { Emoji } from '@/models/entities/emoji.js'; -import type { Poll } from '@/models/entities/poll.js'; -import type { MessagingMessage } from '@/models/entities/messaging-message.js'; -import type { PollVote } from '@/models/entities/poll-vote.js'; -import type { UserKeypairStoreService } from '@/services/UserKeypairStoreService.js'; -import type { MfmService } from '@/services/MfmService.js'; +import { Config } from '@/config.js'; +import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; +import type { IMentionedRemoteUsers, Note } from '@/models/entities/Note.js'; +import type { Blocking } from '@/models/entities/Blocking.js'; +import type { Relay } from '@/models/entities/Relay.js'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; +import type { NoteReaction } from '@/models/entities/NoteReaction.js'; +import type { Emoji } from '@/models/entities/Emoji.js'; +import type { Poll } from '@/models/entities/Poll.js'; +import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; +import type { PollVote } from '@/models/entities/PollVote.js'; +import { UserKeypairStoreService } from '@/services/UserKeypairStoreService.js'; +import { MfmService } from '@/services/MfmService.js'; import { LdSignature } from './misc/ld-signature.js'; import type { IActivity } from './type.js'; import type { IIdentifier } from './models/identifier.js'; diff --git a/packages/backend/src/services/remote/activitypub/ApRequestService.ts b/packages/backend/src/services/remote/activitypub/ApRequestService.ts index f8507e1bf3f7..65ba3334bea4 100644 --- a/packages/backend/src/services/remote/activitypub/ApRequestService.ts +++ b/packages/backend/src/services/remote/activitypub/ApRequestService.ts @@ -3,7 +3,7 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; -import type { User } from '@/models/entities/user.js'; +import type { User } from '@/models/entities/User.js'; import type { UserKeypairStoreService } from '@/services/UserKeypairStoreService.js'; import type { HttpRequestService } from '@/services/HttpRequestService.js'; diff --git a/packages/backend/src/services/remote/activitypub/ApResolverService.ts b/packages/backend/src/services/remote/activitypub/ApResolverService.ts index ce983bfee61b..04fea46a1dfb 100644 --- a/packages/backend/src/services/remote/activitypub/ApResolverService.ts +++ b/packages/backend/src/services/remote/activitypub/ApResolverService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { ILocalUser } from '@/models/entities/user.js'; +import type { ILocalUser } from '@/models/entities/User.js'; import type { InstanceActorService } from '@/services/InstanceActorService.js'; import { extractDbHost, isSelfHost } from '@/misc/convert-host.js'; import type { Notes , Polls , NoteReactions , Users } from '@/models/index.js'; diff --git a/packages/backend/src/services/remote/activitypub/audience.ts b/packages/backend/src/services/remote/activitypub/audience.ts index 846ccf9c0047..6d6395317651 100644 --- a/packages/backend/src/services/remote/activitypub/audience.ts +++ b/packages/backend/src/services/remote/activitypub/audience.ts @@ -3,7 +3,7 @@ import Resolver from './resolver.js'; import { resolvePerson } from './models/person.js'; import { unique, concat } from '@/prelude/array.js'; import promiseLimit from 'promise-limit'; -import { User, CacheableRemoteUser, CacheableUser } from '@/models/entities/user.js'; +import { User, CacheableRemoteUser, CacheableUser } from '@/models/entities/User.js'; type Visibility = 'public' | 'home' | 'followers' | 'specified'; diff --git a/packages/backend/src/services/remote/activitypub/misc/get-note-html.ts b/packages/backend/src/services/remote/activitypub/misc/get-note-html.ts index e6c38745b161..af23a04a717a 100644 --- a/packages/backend/src/services/remote/activitypub/misc/get-note-html.ts +++ b/packages/backend/src/services/remote/activitypub/misc/get-note-html.ts @@ -1,5 +1,5 @@ import * as mfm from 'mfm-js'; -import type { Note } from '@/models/entities/note.js'; +import type { Note } from '@/models/entities/Note.js'; import { toHtml } from '../../../../mfm/to-html.js'; export default function(note: Note) { diff --git a/packages/backend/src/services/remote/activitypub/models/ApImageService.ts b/packages/backend/src/services/remote/activitypub/models/ApImageService.ts index d258bc197286..b524ed25384a 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApImageService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApImageService.ts @@ -2,8 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { DriveFiles } from '@/models/index.js'; import type { Config } from '@/config.js'; -import type { CacheableRemoteUser } from '@/models/entities/user.js'; -import type { DriveFile } from '@/models/entities/drive-file.js'; +import type { CacheableRemoteUser } from '@/models/entities/User.js'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { MetaService } from '@/services/MetaService.js'; import { truncate } from '@/misc/truncate.js'; import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js'; diff --git a/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts b/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts index 1d1f3b123304..15f170d56541 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts @@ -4,7 +4,7 @@ import { DI } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; import type { Config } from '@/config.js'; import { toArray, unique } from '@/prelude/array.js'; -import type { CacheableUser } from '@/models/entities/user.js'; +import type { CacheableUser } from '@/models/entities/User.js'; import { isMention } from '../type.js'; import type { ApResolverService } from '../ApResolverService.js'; import type { IObject , IApMention } from '../type.js'; diff --git a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts index 7571dc159dd6..3459373f9dc3 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts @@ -3,14 +3,14 @@ import promiseLimit from 'promise-limit'; import { DI } from '@/di-symbols.js'; import type { Polls , Emojis, Users } from '@/models/index.js'; import { Config } from '@/config.js'; -import type { CacheableRemoteUser } from '@/models/entities/user.js'; +import type { CacheableRemoteUser } from '@/models/entities/User.js'; import { extractDbHost, toPuny } from '@/misc/convert-host'; -import type { Note } from '@/models/entities/note.js'; +import type { Note } from '@/models/entities/Note.js'; import { toArray, toSingle, unique } from '@/prelude/array.js'; -import type { Emoji } from '@/models/entities/emoji.js'; +import type { Emoji } from '@/models/entities/Emoji.js'; import { MetaService } from '@/services/MetaService.js'; import { AppLockService } from '@/services/AppLockService.js'; -import type { DriveFile } from '@/models/entities/drive-file.js'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; import { NoteCreateService } from '@/services/NoteCreateService.js'; import type Logger from '@/logger.js'; import { IdService } from '@/services/IdService.js'; diff --git a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts index cfeabde57bb3..50dd1549aed9 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts @@ -4,28 +4,28 @@ import { DataSource } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { Followings , Instances, UserProfiles, UserPublickeys, Users } from '@/models/index.js'; import { Config } from '@/config.js'; -import type { CacheableUser, IRemoteUser } from '@/models/entities/user.js'; -import { User } from '@/models/entities/user.js'; +import type { CacheableUser, IRemoteUser } from '@/models/entities/User.js'; +import { User } from '@/models/entities/User.js'; import { toPuny } from '@/misc/convert-host.js'; import { truncate } from '@/misc/truncate.js'; import { UserCacheService } from '@/services/UserCacheService.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import type Logger from '@/logger.js'; -import type { Note } from '@/models/entities/note.js'; +import type { Note } from '@/models/entities/Note.js'; import { IdService } from '@/services/IdService.js'; import { MfmService } from '@/services/MfmService.js'; -import type { Emoji } from '@/models/entities/emoji.js'; +import type { Emoji } from '@/models/entities/Emoji.js'; import { toArray } from '@/prelude/array.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; import { FetchInstanceMetadataService } from '@/services/FetchInstanceMetadataService.js'; -import { UserProfile } from '@/models/entities/user-profile.js'; -import { UserPublickey } from '@/models/entities/user-publickey.js'; +import { UserProfile } from '@/models/entities/UserProfile.js'; +import { UserPublickey } from '@/models/entities/UserPublickey.js'; import UsersChart from '@/services/chart/charts/users.js'; import InstanceChart from '@/services/chart/charts/instance.js'; import { HashtagService } from '@/services/HashtagService.js'; -import { UserNotePining } from '@/models/entities/user-note-pining.js'; +import { UserNotePining } from '@/models/entities/UserNotePining.js'; import { StatusError } from '@/misc/status-error.js'; import { getApId, getApType, getOneApHrefNullable, isActor, isCollection, isCollectionOrOrderedCollection, isPropertyValue } from '../type.js'; import { ApMfmService } from '../ApMfmService.js'; diff --git a/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts b/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts index ebd067ea4856..0f10efecbb55 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { Notes , Polls } from '@/models/index.js'; import type { Config } from '@/config.js'; -import type { IPoll } from '@/models/entities/poll.js'; +import type { IPoll } from '@/models/entities/Poll.js'; import type Logger from '@/logger.js'; import { isQuestion } from '../type.js'; import type { ApLoggerService } from '../ApLoggerService.js'; diff --git a/packages/backend/src/services/remote/activitypub/perform.ts b/packages/backend/src/services/remote/activitypub/perform.ts index a3c10ba94562..e143f56f31ef 100644 --- a/packages/backend/src/services/remote/activitypub/perform.ts +++ b/packages/backend/src/services/remote/activitypub/perform.ts @@ -1,5 +1,5 @@ import { IObject } from './type.js'; -import { CacheableRemoteUser } from '@/models/entities/user.js'; +import { CacheableRemoteUser } from '@/models/entities/User.js'; import { performActivity } from './kernel/index.js'; import { updatePerson } from './models/person.js'; From 92710a442f2275c70a21e600ed5b26e7f073f450 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 02:11:23 +0900 Subject: [PATCH 130/180] wip --- packages/backend/src/db/postgre.ts | 4 +- packages/backend/src/models/index.ts | 126 +++++++++--------- .../api/endpoints/admin/announcements/list.ts | 2 +- .../backend/src/services/UserCacheService.ts | 16 ++- .../services/entities/UserEntityService.ts | 7 +- 5 files changed, 82 insertions(+), 73 deletions(-) diff --git a/packages/backend/src/db/postgre.ts b/packages/backend/src/db/postgre.ts index 3105b9084c6b..c61de59fb3f8 100644 --- a/packages/backend/src/db/postgre.ts +++ b/packages/backend/src/db/postgre.ts @@ -4,7 +4,6 @@ pg.types.setTypeParser(20, Number); import { DataSource } from 'typeorm'; import * as highlight from 'cli-highlight'; -import config from '@/config/index.js'; import { entities as charts } from '@/services/chart/entities.js'; import { AbuseUserReport } from '@/models/entities/AbuseUserReport.js'; @@ -71,6 +70,7 @@ import { UserSecurityKey } from '@/models/entities/UserSecurityKey.js'; import { Webhook } from '@/models/entities/Webhook.js'; import { Channel } from '@/models/entities/Channel.js'; +import { loadConfig } from '@/config.js'; import { envOption } from '../env.js'; import { redisClient } from './redis.js'; import { dbLogger } from './logger.js'; @@ -179,6 +179,8 @@ export const entities = [ const log = process.env.NODE_ENV !== 'production'; +const config = loadConfig(); + export const db = new DataSource({ type: 'postgres', host: config.db.host, diff --git a/packages/backend/src/models/index.ts b/packages/backend/src/models/index.ts index 0ecaf66ff356..24a78e67bbb5 100644 --- a/packages/backend/src/models/index.ts +++ b/packages/backend/src/models/index.ts @@ -1,69 +1,69 @@ import { } from 'typeorm'; import { db } from '@/db/postgre.js'; -import { Announcement } from './entities/announcement.js'; -import { AnnouncementRead } from './entities/announcement-read.js'; -import { Instance } from './entities/instance.js'; -import { Poll } from './entities/poll.js'; -import { PollVote } from './entities/poll-vote.js'; -import { Meta } from './entities/meta.js'; -import { SwSubscription } from './entities/sw-subscription.js'; -import { NoteThreadMuting } from './entities/note-thread-muting.js'; -import { NoteUnread } from './entities/note-unread.js'; -import { RegistrationTicket } from './entities/registration-tickets.js'; -import { User } from './entities/user.js'; -import { Note } from './entities/note.js'; -import { DriveFile } from './entities/drive-file.js'; -import { DriveFolder } from './entities/drive-folder.js'; -import { AccessToken } from './entities/access-token.js'; -import { UserNotePining } from './entities/user-note-pining.js'; -import { Signin } from './entities/signin.js'; -import { MessagingMessage } from './entities/messaging-message.js'; -import { UserList } from './entities/user-list.js'; -import { UserListJoining } from './entities/user-list-joining.js'; -import { UserGroup } from './entities/user-group.js'; -import { UserGroupJoining } from './entities/user-group-joining.js'; -import { UserGroupInvitation } from './entities/user-group-invitation.js'; -import { FollowRequest } from './entities/follow-request.js'; -import { Muting } from './entities/muting.js'; -import { Blocking } from './entities/blocking.js'; -import { NoteReaction } from './entities/note-reaction.js'; -import { Notification } from './entities/notification.js'; -import { NoteFavorite } from './entities/note-favorite.js'; -import { UserPublickey } from './entities/user-publickey.js'; -import { UserKeypair } from './entities/user-keypair.js'; -import { App } from './entities/app.js'; -import { Following } from './entities/following.js'; -import { AbuseUserReport } from './entities/abuse-user-report.js'; -import { AuthSession } from './entities/auth-session.js'; -import { UserProfile } from './entities/user-profile.js'; -import { AttestationChallenge } from './entities/attestation-challenge.js'; -import { UserSecurityKey } from './entities/user-security-key.js'; -import { Hashtag } from './entities/hashtag.js'; -import { Page } from './entities/page.js'; -import { PageLike } from './entities/page-like.js'; -import { GalleryPost } from './entities/gallery-post.js'; -import { GalleryLike } from './entities/gallery-like.js'; -import { ModerationLog } from './entities/moderation-log.js'; -import { UsedUsername } from './entities/used-username.js'; -import { Clip } from './entities/clip.js'; -import { ClipNote } from './entities/clip-note.js'; -import { Antenna } from './entities/antenna.js'; -import { AntennaNote } from './entities/antenna-note.js'; -import { PromoNote } from './entities/promo-note.js'; -import { PromoRead } from './entities/promo-read.js'; -import { Emoji } from './entities/emoji.js'; -import { Relay } from './entities/relay.js'; -import { Channel } from './entities/channel.js'; -import { MutedNote } from './entities/muted-note.js'; -import { ChannelFollowing } from './entities/channel-following.js'; -import { ChannelNotePining } from './entities/channel-note-pining.js'; -import { RegistryItem } from './entities/registry-item.js'; -import { Ad } from './entities/ad.js'; -import { PasswordResetRequest } from './entities/password-reset-request.js'; -import { UserPending } from './entities/user-pending.js'; -import { Webhook } from './entities/webhook.js'; -import { UserIp } from './entities/user-ip.js'; +import { AbuseUserReport } from '@/models/entities/AbuseUserReport.js'; +import { AccessToken } from '@/models/entities/AccessToken.js'; +import { Ad } from '@/models/entities/Ad.js'; +import { Announcement } from '@/models/entities/Announcement.js'; +import { AnnouncementRead } from '@/models/entities/AnnouncementRead.js'; +import { Antenna } from '@/models/entities/Antenna.js'; +import { AntennaNote } from '@/models/entities/AntennaNote.js'; +import { App } from '@/models/entities/App.js'; +import { AttestationChallenge } from '@/models/entities/AttestationChallenge.js'; +import { AuthSession } from '@/models/entities/AuthSession.js'; +import { Blocking } from '@/models/entities/Blocking.js'; +import { ChannelFollowing } from '@/models/entities/ChannelFollowing.js'; +import { ChannelNotePining } from '@/models/entities/ChannelNotePining.js'; +import { Clip } from '@/models/entities/Clip.js'; +import { ClipNote } from '@/models/entities/ClipNote.js'; +import { DriveFile } from '@/models/entities/DriveFile.js'; +import { DriveFolder } from '@/models/entities/DriveFolder.js'; +import { Emoji } from '@/models/entities/Emoji.js'; +import { Following } from '@/models/entities/Following.js'; +import { FollowRequest } from '@/models/entities/FollowRequest.js'; +import { GalleryLike } from '@/models/entities/GalleryLike.js'; +import { GalleryPost } from '@/models/entities/GalleryPost.js'; +import { Hashtag } from '@/models/entities/Hashtag.js'; +import { Instance } from '@/models/entities/Instance.js'; +import { MessagingMessage } from '@/models/entities/MessagingMessage.js'; +import { Meta } from '@/models/entities/Meta.js'; +import { ModerationLog } from '@/models/entities/ModerationLog.js'; +import { MutedNote } from '@/models/entities/MutedNote.js'; +import { Muting } from '@/models/entities/Muting.js'; +import { Note } from '@/models/entities/Note.js'; +import { NoteFavorite } from '@/models/entities/NoteFavorite.js'; +import { NoteReaction } from '@/models/entities/NoteReaction.js'; +import { NoteThreadMuting } from '@/models/entities/NoteThreadMuting.js'; +import { NoteUnread } from '@/models/entities/NoteUnread.js'; +import { Notification } from '@/models/entities/Notification.js'; +import { Page } from '@/models/entities/Page.js'; +import { PageLike } from '@/models/entities/PageLike.js'; +import { PasswordResetRequest } from '@/models/entities/PasswordResetRequest.js'; +import { Poll } from '@/models/entities/Poll.js'; +import { PollVote } from '@/models/entities/PollVote.js'; +import { PromoNote } from '@/models/entities/PromoNote.js'; +import { PromoRead } from '@/models/entities/PromoRead.js'; +import { RegistrationTicket } from '@/models/entities/RegistrationTickets.js'; +import { RegistryItem } from '@/models/entities/RegistryItem.js'; +import { Relay } from '@/models/entities/Relay.js'; +import { Signin } from '@/models/entities/Signin.js'; +import { SwSubscription } from '@/models/entities/SwSubscription.js'; +import { UsedUsername } from '@/models/entities/UsedUsername.js'; +import { User } from '@/models/entities/User.js'; +import { UserGroup } from '@/models/entities/UserGroup.js'; +import { UserGroupInvitation } from '@/models/entities/UserGroupInvitation.js'; +import { UserGroupJoining } from '@/models/entities/UserGroupJoining.js'; +import { UserIp } from '@/models/entities/UserIp.js'; +import { UserKeypair } from '@/models/entities/UserKeypair.js'; +import { UserList } from '@/models/entities/UserList.js'; +import { UserListJoining } from '@/models/entities/UserListJoining.js'; +import { UserNotePining } from '@/models/entities/UserNotePining.js'; +import { UserPending } from '@/models/entities/UserPending.js'; +import { UserProfile } from '@/models/entities/UserProfile.js'; +import { UserPublickey } from '@/models/entities/UserPublickey.js'; +import { UserSecurityKey } from '@/models/entities/UserSecurityKey.js'; +import { Webhook } from '@/models/entities/Webhook.js'; +import { Channel } from '@/models/entities/Channel.js'; export const Announcements = db.getRepository(Announcement); export const AnnouncementReads = db.getRepository(AnnouncementRead); diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts index ae33b3b98b85..88123c159ed8 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Announcements , AnnouncementReads } from '@/models/index.js'; -import type { Announcement } from '@/models/entities/announcement.js'; +import type { Announcement } from '@/models/entities/Announcement.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; diff --git a/packages/backend/src/services/UserCacheService.ts b/packages/backend/src/services/UserCacheService.ts index c5235917c6c8..6839a3352647 100644 --- a/packages/backend/src/services/UserCacheService.ts +++ b/packages/backend/src/services/UserCacheService.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; -import { Users } from '@/models/index.js'; +import Redis from 'ioredis'; +import type { Users } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; import type { CacheableLocalUser, CacheableUser, ILocalUser } from '@/models/entities/User.js'; -import type Redis from 'ioredis'; +import { UserEntityService } from './entities/UserEntityService'; import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() @@ -15,6 +16,11 @@ export class UserCacheService implements OnApplicationShutdown { constructor( @Inject('redisSubscriber') private redisSubscriber: Redis.Redis, + + @Inject('usersRepository') + private usersRepository: typeof Users, + + private userEntityService: UserEntityService, ) { this.onMessage = this.onMessage.bind(this); @@ -36,21 +42,21 @@ export class UserCacheService implements OnApplicationShutdown { case 'userChangeSilencedState': case 'userChangeModeratorState': case 'remoteUserUpdated': { - const user = await Users.findOneByOrFail({ id: body.id }); + const user = await this.usersRepository.findOneByOrFail({ id: body.id }); this.userByIdCache.set(user.id, user); for (const [k, v] of this.uriPersonCache.cache.entries()) { if (v.value?.id === user.id) { this.uriPersonCache.set(k, user); } } - if (Users.isLocalUser(user)) { + if (this.userEntityService.isLocalUser(user)) { this.localUserByNativeTokenCache.set(user.token, user); this.localUserByIdCache.set(user.id, user); } break; } case 'userTokenRegenerated': { - const user = await Users.findOneByOrFail({ id: body.id }) as ILocalUser; + const user = await this.usersRepository.findOneByOrFail({ id: body.id }) as ILocalUser; this.localUserByNativeTokenCache.delete(body.oldToken); this.localUserByNativeTokenCache.set(body.newToken, user); break; diff --git a/packages/backend/src/services/entities/UserEntityService.ts b/packages/backend/src/services/entities/UserEntityService.ts index 11417d36250f..d07a3278a4bb 100644 --- a/packages/backend/src/services/entities/UserEntityService.ts +++ b/packages/backend/src/services/entities/UserEntityService.ts @@ -17,8 +17,6 @@ import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/entities/User.js'; import { NoteEntityService } from './NoteEntityService.js'; -const userInstanceCache = new Cache(1000 * 60 * 60 * 3); - type IsUserDetailed = Detailed extends true ? Packed<'UserDetailed'> : Packed<'UserLite'>; type IsMeAndIsUserDetailed = Detailed extends true ? @@ -43,6 +41,8 @@ function isRemoteUser(user: User | { host: User['host'] }): boolean { @Injectable() export class UserEntityService { + #userInstanceCache: Cache; + constructor( @Inject(DI.config) private config: Config, @@ -108,6 +108,7 @@ export class UserEntityService { @Inject(forwardRef(() => NoteEntityService)) private noteEntityService: NoteEntityService, ) { + this.#userInstanceCache = new Cache(1000 * 60 * 60 * 3); } //#region Validators @@ -364,7 +365,7 @@ export class UserEntityService { isModerator: user.isModerator || falsy, isBot: user.isBot || falsy, isCat: user.isCat || falsy, - instance: user.host ? userInstanceCache.fetch(user.host, + instance: user.host ? this.#userInstanceCache.fetch(user.host, () => this.instancesRepository.findOneBy({ host: user.host! }), v => v != null, ).then(instance => instance ? { From b5cb4351a8881919432c2b35ea92434c35ce6cf6 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 02:14:33 +0900 Subject: [PATCH 131/180] o --- .../src/models/entities/AbuseUserReport.ts | 79 --- .../src/models/entities/AccessToken.ts | 90 ---- .../src/models/entities/AnnouncementRead.ts | 36 -- .../src/models/entities/AntennaNote.ts | 43 -- .../models/entities/AttestationChallenge.ts | 46 -- .../src/models/entities/AuthSession.ts | 43 -- .../src/models/entities/ChannelFollowing.ts | 43 -- .../src/models/entities/ChannelNotePining.ts | 35 -- .../backend/src/models/entities/ClipNote.ts | 37 -- .../backend/src/models/entities/DriveFile.ts | 192 -------- .../src/models/entities/DriveFolder.ts | 49 -- .../src/models/entities/FollowRequest.ts | 85 ---- .../src/models/entities/GalleryLike.ts | 33 -- .../src/models/entities/GalleryPost.ts | 79 --- .../src/models/entities/MessagingMessage.ts | 89 ---- .../src/models/entities/ModerationLog.ts | 32 -- .../backend/src/models/entities/MutedNote.ts | 48 -- .../src/models/entities/NoteFavorite.ts | 35 -- .../src/models/entities/NoteReaction.ts | 44 -- .../src/models/entities/NoteThreadMuting.ts | 33 -- .../backend/src/models/entities/NoteUnread.ts | 63 --- .../backend/src/models/entities/PageLike.ts | 33 -- .../models/entities/PasswordResetRequest.ts | 30 -- .../backend/src/models/entities/PollVote.ts | 40 -- .../backend/src/models/entities/PromoNote.ts | 28 -- .../backend/src/models/entities/PromoRead.ts | 35 -- .../models/entities/RegistrationTickets.ts | 17 - .../src/models/entities/RegistryItem.ts | 58 --- .../src/models/entities/SwSubscription.ts | 37 -- .../src/models/entities/UsedUsername.ts | 20 - .../backend/src/models/entities/UserGroup.ts | 46 -- .../models/entities/UserGroupInvitation.ts | 42 -- .../src/models/entities/UserGroupJoining.ts | 42 -- .../backend/src/models/entities/UserIp.ts | 24 - .../src/models/entities/UserKeypair.ts | 33 -- .../backend/src/models/entities/UserList.ts | 33 -- .../src/models/entities/UserListJoining.ts | 42 -- .../src/models/entities/UserNotePining.ts | 35 -- .../src/models/entities/UserPending.ts | 32 -- .../src/models/entities/UserProfile.ts | 232 --------- .../src/models/entities/UserPublickey.ts | 34 -- .../src/models/entities/UserSecurityKey.ts | 48 -- packages/backend/src/models/entities/ad.ts | 59 --- .../src/models/entities/announcement.ts | 43 -- .../backend/src/models/entities/antenna.ts | 99 ---- packages/backend/src/models/entities/app.ts | 60 --- .../backend/src/models/entities/blocking.ts | 42 -- .../backend/src/models/entities/channel.ts | 75 --- packages/backend/src/models/entities/clip.ts | 44 -- packages/backend/src/models/entities/emoji.ts | 58 --- .../backend/src/models/entities/following.ts | 82 ---- .../backend/src/models/entities/hashtag.ts | 87 ---- .../backend/src/models/entities/instance.ts | 164 ------- packages/backend/src/models/entities/meta.ts | 462 ------------------ .../backend/src/models/entities/muting.ts | 48 -- packages/backend/src/models/entities/note.ts | 242 --------- .../src/models/entities/notification.ts | 173 ------- packages/backend/src/models/entities/page.ts | 121 ----- packages/backend/src/models/entities/poll.ts | 72 --- packages/backend/src/models/entities/relay.ts | 19 - .../backend/src/models/entities/signin.ts | 35 -- packages/backend/src/models/entities/user.ts | 255 ---------- .../backend/src/models/entities/webhook.ts | 73 --- 63 files changed, 4488 deletions(-) delete mode 100644 packages/backend/src/models/entities/AbuseUserReport.ts delete mode 100644 packages/backend/src/models/entities/AccessToken.ts delete mode 100644 packages/backend/src/models/entities/AnnouncementRead.ts delete mode 100644 packages/backend/src/models/entities/AntennaNote.ts delete mode 100644 packages/backend/src/models/entities/AttestationChallenge.ts delete mode 100644 packages/backend/src/models/entities/AuthSession.ts delete mode 100644 packages/backend/src/models/entities/ChannelFollowing.ts delete mode 100644 packages/backend/src/models/entities/ChannelNotePining.ts delete mode 100644 packages/backend/src/models/entities/ClipNote.ts delete mode 100644 packages/backend/src/models/entities/DriveFile.ts delete mode 100644 packages/backend/src/models/entities/DriveFolder.ts delete mode 100644 packages/backend/src/models/entities/FollowRequest.ts delete mode 100644 packages/backend/src/models/entities/GalleryLike.ts delete mode 100644 packages/backend/src/models/entities/GalleryPost.ts delete mode 100644 packages/backend/src/models/entities/MessagingMessage.ts delete mode 100644 packages/backend/src/models/entities/ModerationLog.ts delete mode 100644 packages/backend/src/models/entities/MutedNote.ts delete mode 100644 packages/backend/src/models/entities/NoteFavorite.ts delete mode 100644 packages/backend/src/models/entities/NoteReaction.ts delete mode 100644 packages/backend/src/models/entities/NoteThreadMuting.ts delete mode 100644 packages/backend/src/models/entities/NoteUnread.ts delete mode 100644 packages/backend/src/models/entities/PageLike.ts delete mode 100644 packages/backend/src/models/entities/PasswordResetRequest.ts delete mode 100644 packages/backend/src/models/entities/PollVote.ts delete mode 100644 packages/backend/src/models/entities/PromoNote.ts delete mode 100644 packages/backend/src/models/entities/PromoRead.ts delete mode 100644 packages/backend/src/models/entities/RegistrationTickets.ts delete mode 100644 packages/backend/src/models/entities/RegistryItem.ts delete mode 100644 packages/backend/src/models/entities/SwSubscription.ts delete mode 100644 packages/backend/src/models/entities/UsedUsername.ts delete mode 100644 packages/backend/src/models/entities/UserGroup.ts delete mode 100644 packages/backend/src/models/entities/UserGroupInvitation.ts delete mode 100644 packages/backend/src/models/entities/UserGroupJoining.ts delete mode 100644 packages/backend/src/models/entities/UserIp.ts delete mode 100644 packages/backend/src/models/entities/UserKeypair.ts delete mode 100644 packages/backend/src/models/entities/UserList.ts delete mode 100644 packages/backend/src/models/entities/UserListJoining.ts delete mode 100644 packages/backend/src/models/entities/UserNotePining.ts delete mode 100644 packages/backend/src/models/entities/UserPending.ts delete mode 100644 packages/backend/src/models/entities/UserProfile.ts delete mode 100644 packages/backend/src/models/entities/UserPublickey.ts delete mode 100644 packages/backend/src/models/entities/UserSecurityKey.ts delete mode 100644 packages/backend/src/models/entities/ad.ts delete mode 100644 packages/backend/src/models/entities/announcement.ts delete mode 100644 packages/backend/src/models/entities/antenna.ts delete mode 100644 packages/backend/src/models/entities/app.ts delete mode 100644 packages/backend/src/models/entities/blocking.ts delete mode 100644 packages/backend/src/models/entities/channel.ts delete mode 100644 packages/backend/src/models/entities/clip.ts delete mode 100644 packages/backend/src/models/entities/emoji.ts delete mode 100644 packages/backend/src/models/entities/following.ts delete mode 100644 packages/backend/src/models/entities/hashtag.ts delete mode 100644 packages/backend/src/models/entities/instance.ts delete mode 100644 packages/backend/src/models/entities/meta.ts delete mode 100644 packages/backend/src/models/entities/muting.ts delete mode 100644 packages/backend/src/models/entities/note.ts delete mode 100644 packages/backend/src/models/entities/notification.ts delete mode 100644 packages/backend/src/models/entities/page.ts delete mode 100644 packages/backend/src/models/entities/poll.ts delete mode 100644 packages/backend/src/models/entities/relay.ts delete mode 100644 packages/backend/src/models/entities/signin.ts delete mode 100644 packages/backend/src/models/entities/user.ts delete mode 100644 packages/backend/src/models/entities/webhook.ts diff --git a/packages/backend/src/models/entities/AbuseUserReport.ts b/packages/backend/src/models/entities/AbuseUserReport.ts deleted file mode 100644 index 6ac5635528de..000000000000 --- a/packages/backend/src/models/entities/AbuseUserReport.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -export class AbuseUserReport { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the AbuseUserReport.', - }) - public createdAt: Date; - - @Index() - @Column(id()) - public targetUserId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public targetUser: User | null; - - @Index() - @Column(id()) - public reporterId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public reporter: User | null; - - @Column({ - ...id(), - nullable: true, - }) - public assigneeId: User['id'] | null; - - @ManyToOne(type => User, { - onDelete: 'SET NULL', - }) - @JoinColumn() - public assignee: User | null; - - @Index() - @Column('boolean', { - default: false, - }) - public resolved: boolean; - - @Column('boolean', { - default: false - }) - public forwarded: boolean; - - @Column('varchar', { - length: 2048, - }) - public comment: string; - - //#region Denormalized fields - @Index() - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', - }) - public targetUserHost: string | null; - - @Index() - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', - }) - public reporterHost: string | null; - //#endregion -} diff --git a/packages/backend/src/models/entities/AccessToken.ts b/packages/backend/src/models/entities/AccessToken.ts deleted file mode 100644 index c6e2141a46dc..000000000000 --- a/packages/backend/src/models/entities/AccessToken.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { Entity, PrimaryColumn, Index, Column, ManyToOne, JoinColumn } from 'typeorm'; -import { User } from './user.js'; -import { App } from './app.js'; -import { id } from '../id.js'; - -@Entity() -export class AccessToken { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the AccessToken.', - }) - public createdAt: Date; - - @Column('timestamp with time zone', { - nullable: true, - }) - public lastUsedAt: Date | null; - - @Index() - @Column('varchar', { - length: 128, - }) - public token: string; - - @Index() - @Column('varchar', { - length: 128, - nullable: true, - }) - public session: string | null; - - @Index() - @Column('varchar', { - length: 128, - }) - public hash: string; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column({ - ...id(), - nullable: true, - }) - public appId: App['id'] | null; - - @ManyToOne(type => App, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public app: App | null; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public name: string | null; - - @Column('varchar', { - length: 512, - nullable: true, - }) - public description: string | null; - - @Column('varchar', { - length: 512, - nullable: true, - }) - public iconUrl: string | null; - - @Column('varchar', { - length: 64, array: true, - default: '{}', - }) - public permission: string[]; - - @Column('boolean', { - default: false, - }) - public fetched: boolean; -} diff --git a/packages/backend/src/models/entities/AnnouncementRead.ts b/packages/backend/src/models/entities/AnnouncementRead.ts deleted file mode 100644 index e4d256a8642e..000000000000 --- a/packages/backend/src/models/entities/AnnouncementRead.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { Announcement } from './announcement.js'; -import { id } from '../id.js'; - -@Entity() -@Index(['userId', 'announcementId'], { unique: true }) -export class AnnouncementRead { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the AnnouncementRead.', - }) - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Index() - @Column(id()) - public announcementId: Announcement['id']; - - @ManyToOne(type => Announcement, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public announcement: Announcement | null; -} diff --git a/packages/backend/src/models/entities/AntennaNote.ts b/packages/backend/src/models/entities/AntennaNote.ts deleted file mode 100644 index fcca493fe071..000000000000 --- a/packages/backend/src/models/entities/AntennaNote.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Entity, Index, JoinColumn, Column, ManyToOne, PrimaryColumn } from 'typeorm'; -import { Note } from './note.js'; -import { Antenna } from './antenna.js'; -import { id } from '../id.js'; - -@Entity() -@Index(['noteId', 'antennaId'], { unique: true }) -export class AntennaNote { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column({ - ...id(), - comment: 'The note ID.', - }) - public noteId: Note['id']; - - @ManyToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public note: Note | null; - - @Index() - @Column({ - ...id(), - comment: 'The antenna ID.', - }) - public antennaId: Antenna['id']; - - @ManyToOne(type => Antenna, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public antenna: Antenna | null; - - @Index() - @Column('boolean', { - default: false, - }) - public read: boolean; -} diff --git a/packages/backend/src/models/entities/AttestationChallenge.ts b/packages/backend/src/models/entities/AttestationChallenge.ts deleted file mode 100644 index c40df23293bb..000000000000 --- a/packages/backend/src/models/entities/AttestationChallenge.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { PrimaryColumn, Entity, JoinColumn, Column, ManyToOne, Index } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -export class AttestationChallenge { - @PrimaryColumn(id()) - public id: string; - - @Index() - @PrimaryColumn(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Index() - @Column('varchar', { - length: 64, - comment: 'Hex-encoded sha256 hash of the challenge.', - }) - public challenge: string; - - @Column('timestamp with time zone', { - comment: 'The date challenge was created for expiry purposes.', - }) - public createdAt: Date; - - @Column('boolean', { - comment: - 'Indicates that the challenge is only for registration purposes if true to prevent the challenge for being used as authentication.', - default: false, - }) - public registrationChallenge: boolean; - - constructor(data: Partial) { - if (data == null) return; - - for (const [k, v] of Object.entries(data)) { - (this as any)[k] = v; - } - } -} diff --git a/packages/backend/src/models/entities/AuthSession.ts b/packages/backend/src/models/entities/AuthSession.ts deleted file mode 100644 index 295d1b486c4b..000000000000 --- a/packages/backend/src/models/entities/AuthSession.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Entity, PrimaryColumn, Index, Column, ManyToOne, JoinColumn } from 'typeorm'; -import { User } from './user.js'; -import { App } from './app.js'; -import { id } from '../id.js'; - -@Entity() -export class AuthSession { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the AuthSession.', - }) - public createdAt: Date; - - @Index() - @Column('varchar', { - length: 128, - }) - public token: string; - - @Column({ - ...id(), - nullable: true, - }) - public userId: User['id'] | null; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - nullable: true, - }) - @JoinColumn() - public user: User | null; - - @Column(id()) - public appId: App['id']; - - @ManyToOne(type => App, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public app: App | null; -} diff --git a/packages/backend/src/models/entities/ChannelFollowing.ts b/packages/backend/src/models/entities/ChannelFollowing.ts deleted file mode 100644 index 029dd6cf1a07..000000000000 --- a/packages/backend/src/models/entities/ChannelFollowing.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; -import { Channel } from './channel.js'; - -@Entity() -@Index(['followerId', 'followeeId'], { unique: true }) -export class ChannelFollowing { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the ChannelFollowing.', - }) - public createdAt: Date; - - @Index() - @Column({ - ...id(), - comment: 'The followee channel ID.', - }) - public followeeId: Channel['id']; - - @ManyToOne(type => Channel, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public followee: Channel | null; - - @Index() - @Column({ - ...id(), - comment: 'The follower user ID.', - }) - public followerId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public follower: User | null; -} diff --git a/packages/backend/src/models/entities/ChannelNotePining.ts b/packages/backend/src/models/entities/ChannelNotePining.ts deleted file mode 100644 index 23be3b69d46b..000000000000 --- a/packages/backend/src/models/entities/ChannelNotePining.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { Note } from './note.js'; -import { Channel } from './channel.js'; -import { id } from '../id.js'; - -@Entity() -@Index(['channelId', 'noteId'], { unique: true }) -export class ChannelNotePining { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the ChannelNotePining.', - }) - public createdAt: Date; - - @Index() - @Column(id()) - public channelId: Channel['id']; - - @ManyToOne(type => Channel, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public channel: Channel | null; - - @Column(id()) - public noteId: Note['id']; - - @ManyToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public note: Note | null; -} diff --git a/packages/backend/src/models/entities/ClipNote.ts b/packages/backend/src/models/entities/ClipNote.ts deleted file mode 100644 index 6f3688550830..000000000000 --- a/packages/backend/src/models/entities/ClipNote.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Entity, Index, JoinColumn, Column, ManyToOne, PrimaryColumn } from 'typeorm'; -import { Note } from './note.js'; -import { Clip } from './clip.js'; -import { id } from '../id.js'; - -@Entity() -@Index(['noteId', 'clipId'], { unique: true }) -export class ClipNote { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column({ - ...id(), - comment: 'The note ID.', - }) - public noteId: Note['id']; - - @ManyToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public note: Note | null; - - @Index() - @Column({ - ...id(), - comment: 'The clip ID.', - }) - public clipId: Clip['id']; - - @ManyToOne(type => Clip, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public clip: Clip | null; -} diff --git a/packages/backend/src/models/entities/DriveFile.ts b/packages/backend/src/models/entities/DriveFile.ts deleted file mode 100644 index d410b1d429dc..000000000000 --- a/packages/backend/src/models/entities/DriveFile.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './user.js'; -import { DriveFolder } from './drive-folder.js'; - -@Entity() -@Index(['userId', 'folderId', 'id']) -export class DriveFile { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the DriveFile.', - }) - public createdAt: Date; - - @Index() - @Column({ - ...id(), - nullable: true, - comment: 'The owner ID.', - }) - public userId: User['id'] | null; - - @ManyToOne(type => User, { - onDelete: 'SET NULL', - }) - @JoinColumn() - public user: User | null; - - @Index() - @Column('varchar', { - length: 128, nullable: true, - comment: 'The host of owner. It will be null if the user in local.', - }) - public userHost: string | null; - - @Index() - @Column('varchar', { - length: 32, - comment: 'The MD5 hash of the DriveFile.', - }) - public md5: string; - - @Column('varchar', { - length: 256, - comment: 'The file name of the DriveFile.', - }) - public name: string; - - @Index() - @Column('varchar', { - length: 128, - comment: 'The content type (MIME) of the DriveFile.', - }) - public type: string; - - @Column('integer', { - comment: 'The file size (bytes) of the DriveFile.', - }) - public size: number; - - @Column('varchar', { - length: 512, nullable: true, - comment: 'The comment of the DriveFile.', - }) - public comment: string | null; - - @Column('varchar', { - length: 128, nullable: true, - comment: 'The BlurHash string.', - }) - public blurhash: string | null; - - @Column('jsonb', { - default: {}, - comment: 'The any properties of the DriveFile. For example, it includes image width/height.', - }) - public properties: { width?: number; height?: number; orientation?: number; avgColor?: string }; - - @Column('boolean') - public storedInternal: boolean; - - @Column('varchar', { - length: 512, - comment: 'The URL of the DriveFile.', - }) - public url: string; - - @Column('varchar', { - length: 512, nullable: true, - comment: 'The URL of the thumbnail of the DriveFile.', - }) - public thumbnailUrl: string | null; - - @Column('varchar', { - length: 512, nullable: true, - comment: 'The URL of the webpublic of the DriveFile.', - }) - public webpublicUrl: string | null; - - @Column('varchar', { - length: 128, nullable: true, - }) - public webpublicType: string | null; - - @Index({ unique: true }) - @Column('varchar', { - length: 256, nullable: true, - }) - public accessKey: string | null; - - @Index({ unique: true }) - @Column('varchar', { - length: 256, nullable: true, - }) - public thumbnailAccessKey: string | null; - - @Index({ unique: true }) - @Column('varchar', { - length: 256, nullable: true, - }) - public webpublicAccessKey: string | null; - - @Index() - @Column('varchar', { - length: 512, nullable: true, - comment: 'The URI of the DriveFile. it will be null when the DriveFile is local.', - }) - public uri: string | null; - - @Column('varchar', { - length: 512, nullable: true, - }) - public src: string | null; - - @Index() - @Column({ - ...id(), - nullable: true, - comment: 'The parent folder ID. If null, it means the DriveFile is located in root.', - }) - public folderId: DriveFolder['id'] | null; - - @ManyToOne(type => DriveFolder, { - onDelete: 'SET NULL', - }) - @JoinColumn() - public folder: DriveFolder | null; - - @Index() - @Column('boolean', { - default: false, - comment: 'Whether the DriveFile is NSFW.', - }) - public isSensitive: boolean; - - @Index() - @Column('boolean', { - default: false, - comment: 'Whether the DriveFile is NSFW. (predict)', - }) - public maybeSensitive: boolean; - - @Index() - @Column('boolean', { - default: false, - }) - public maybePorn: boolean; - - /** - * 外部の(信頼されていない)URLへの直リンクか否か - */ - @Index() - @Column('boolean', { - default: false, - comment: 'Whether the DriveFile is direct link to remote server.', - }) - public isLink: boolean; - - @Column('jsonb', { - default: {}, - nullable: true, - }) - public requestHeaders: Record | null; - - @Column('varchar', { - length: 128, nullable: true, - }) - public requestIp: string | null; -} diff --git a/packages/backend/src/models/entities/DriveFolder.ts b/packages/backend/src/models/entities/DriveFolder.ts deleted file mode 100644 index d4022c6ebc14..000000000000 --- a/packages/backend/src/models/entities/DriveFolder.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { JoinColumn, ManyToOne, Entity, PrimaryColumn, Index, Column } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -export class DriveFolder { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the DriveFolder.', - }) - public createdAt: Date; - - @Column('varchar', { - length: 128, - comment: 'The name of the DriveFolder.', - }) - public name: string; - - @Index() - @Column({ - ...id(), - nullable: true, - comment: 'The owner ID.', - }) - public userId: User['id'] | null; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Index() - @Column({ - ...id(), - nullable: true, - comment: 'The parent folder ID. If null, it means the DriveFolder is located in root.', - }) - public parentId: DriveFolder['id'] | null; - - @ManyToOne(type => DriveFolder, { - onDelete: 'SET NULL', - }) - @JoinColumn() - public parent: DriveFolder | null; -} diff --git a/packages/backend/src/models/entities/FollowRequest.ts b/packages/backend/src/models/entities/FollowRequest.ts deleted file mode 100644 index 89946f6d35df..000000000000 --- a/packages/backend/src/models/entities/FollowRequest.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -@Index(['followerId', 'followeeId'], { unique: true }) -export class FollowRequest { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the FollowRequest.', - }) - public createdAt: Date; - - @Index() - @Column({ - ...id(), - comment: 'The followee user ID.', - }) - public followeeId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public followee: User | null; - - @Index() - @Column({ - ...id(), - comment: 'The follower user ID.', - }) - public followerId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public follower: User | null; - - @Column('varchar', { - length: 128, nullable: true, - comment: 'id of Follow Activity.', - }) - public requestId: string | null; - - //#region Denormalized fields - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', - }) - public followerHost: string | null; - - @Column('varchar', { - length: 512, nullable: true, - comment: '[Denormalized]', - }) - public followerInbox: string | null; - - @Column('varchar', { - length: 512, nullable: true, - comment: '[Denormalized]', - }) - public followerSharedInbox: string | null; - - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', - }) - public followeeHost: string | null; - - @Column('varchar', { - length: 512, nullable: true, - comment: '[Denormalized]', - }) - public followeeInbox: string | null; - - @Column('varchar', { - length: 512, nullable: true, - comment: '[Denormalized]', - }) - public followeeSharedInbox: string | null; - //#endregion -} diff --git a/packages/backend/src/models/entities/GalleryLike.ts b/packages/backend/src/models/entities/GalleryLike.ts deleted file mode 100644 index 4ce166d194fc..000000000000 --- a/packages/backend/src/models/entities/GalleryLike.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; -import { GalleryPost } from './gallery-post.js'; - -@Entity() -@Index(['userId', 'postId'], { unique: true }) -export class GalleryLike { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone') - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column(id()) - public postId: GalleryPost['id']; - - @ManyToOne(type => GalleryPost, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public post: GalleryPost | null; -} diff --git a/packages/backend/src/models/entities/GalleryPost.ts b/packages/backend/src/models/entities/GalleryPost.ts deleted file mode 100644 index 774cb946e970..000000000000 --- a/packages/backend/src/models/entities/GalleryPost.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; -import { DriveFile } from './drive-file.js'; - -@Entity() -export class GalleryPost { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the GalleryPost.', - }) - public createdAt: Date; - - @Index() - @Column('timestamp with time zone', { - comment: 'The updated date of the GalleryPost.', - }) - public updatedAt: Date; - - @Column('varchar', { - length: 256, - }) - public title: string; - - @Column('varchar', { - length: 2048, nullable: true, - }) - public description: string | null; - - @Index() - @Column({ - ...id(), - comment: 'The ID of author.', - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Index() - @Column({ - ...id(), - array: true, default: '{}', - }) - public fileIds: DriveFile['id'][]; - - @Index() - @Column('boolean', { - default: false, - comment: 'Whether the post is sensitive.', - }) - public isSensitive: boolean; - - @Index() - @Column('integer', { - default: 0, - }) - public likedCount: number; - - @Index() - @Column('varchar', { - length: 128, array: true, default: '{}', - }) - public tags: string[]; - - constructor(data: Partial) { - if (data == null) return; - - for (const [k, v] of Object.entries(data)) { - (this as any)[k] = v; - } - } -} diff --git a/packages/backend/src/models/entities/MessagingMessage.ts b/packages/backend/src/models/entities/MessagingMessage.ts deleted file mode 100644 index 099fb7aa013b..000000000000 --- a/packages/backend/src/models/entities/MessagingMessage.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { DriveFile } from './drive-file.js'; -import { id } from '../id.js'; -import { UserGroup } from './user-group.js'; - -@Entity() -export class MessagingMessage { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the MessagingMessage.', - }) - public createdAt: Date; - - @Index() - @Column({ - ...id(), - comment: 'The sender user ID.', - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Index() - @Column({ - ...id(), nullable: true, - comment: 'The recipient user ID.', - }) - public recipientId: User['id'] | null; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public recipient: User | null; - - @Index() - @Column({ - ...id(), nullable: true, - comment: 'The recipient group ID.', - }) - public groupId: UserGroup['id'] | null; - - @ManyToOne(type => UserGroup, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public group: UserGroup | null; - - @Column('varchar', { - length: 4096, nullable: true, - }) - public text: string | null; - - @Column('boolean', { - default: false, - }) - public isRead: boolean; - - @Column('varchar', { - length: 512, nullable: true, - }) - public uri: string | null; - - @Column({ - ...id(), - array: true, default: '{}', - }) - public reads: User['id'][]; - - @Column({ - ...id(), - nullable: true, - }) - public fileId: DriveFile['id'] | null; - - @ManyToOne(type => DriveFile, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public file: DriveFile | null; -} diff --git a/packages/backend/src/models/entities/ModerationLog.ts b/packages/backend/src/models/entities/ModerationLog.ts deleted file mode 100644 index c99e55078269..000000000000 --- a/packages/backend/src/models/entities/ModerationLog.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -export class ModerationLog { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the ModerationLog.', - }) - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column('varchar', { - length: 128, - }) - public type: string; - - @Column('jsonb') - public info: Record; -} diff --git a/packages/backend/src/models/entities/MutedNote.ts b/packages/backend/src/models/entities/MutedNote.ts deleted file mode 100644 index 96a4fa8e3336..000000000000 --- a/packages/backend/src/models/entities/MutedNote.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Entity, Index, JoinColumn, Column, ManyToOne, PrimaryColumn } from 'typeorm'; -import { Note } from './note.js'; -import { User } from './user.js'; -import { id } from '../id.js'; -import { mutedNoteReasons } from '../../types.js'; - -@Entity() -@Index(['noteId', 'userId'], { unique: true }) -export class MutedNote { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column({ - ...id(), - comment: 'The note ID.', - }) - public noteId: Note['id']; - - @ManyToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public note: Note | null; - - @Index() - @Column({ - ...id(), - comment: 'The user ID.', - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - /** - * ミュートされた理由。 - */ - @Index() - @Column('enum', { - enum: mutedNoteReasons, - comment: 'The reason of the MutedNote.', - }) - public reason: typeof mutedNoteReasons[number]; -} diff --git a/packages/backend/src/models/entities/NoteFavorite.ts b/packages/backend/src/models/entities/NoteFavorite.ts deleted file mode 100644 index fe065b77a8eb..000000000000 --- a/packages/backend/src/models/entities/NoteFavorite.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { Note } from './note.js'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -@Index(['userId', 'noteId'], { unique: true }) -export class NoteFavorite { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the NoteFavorite.', - }) - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column(id()) - public noteId: Note['id']; - - @ManyToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public note: Note | null; -} diff --git a/packages/backend/src/models/entities/NoteReaction.ts b/packages/backend/src/models/entities/NoteReaction.ts deleted file mode 100644 index d7bc60989813..000000000000 --- a/packages/backend/src/models/entities/NoteReaction.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { Note } from './note.js'; -import { id } from '../id.js'; - -@Entity() -@Index(['userId', 'noteId'], { unique: true }) -export class NoteReaction { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the NoteReaction.', - }) - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user?: User | null; - - @Index() - @Column(id()) - public noteId: Note['id']; - - @ManyToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public note?: Note | null; - - // TODO: 対象noteのuserIdを非正規化したい(「受け取ったリアクション一覧」のようなものを(JOIN無しで)実装したいため) - - @Column('varchar', { - length: 260, - }) - public reaction: string; -} diff --git a/packages/backend/src/models/entities/NoteThreadMuting.ts b/packages/backend/src/models/entities/NoteThreadMuting.ts deleted file mode 100644 index 8c5f7bbab426..000000000000 --- a/packages/backend/src/models/entities/NoteThreadMuting.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { Note } from './note.js'; -import { id } from '../id.js'; - -@Entity() -@Index(['userId', 'threadId'], { unique: true }) -export class NoteThreadMuting { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - }) - public createdAt: Date; - - @Index() - @Column({ - ...id(), - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Index() - @Column('varchar', { - length: 256, - }) - public threadId: string; -} diff --git a/packages/backend/src/models/entities/NoteUnread.ts b/packages/backend/src/models/entities/NoteUnread.ts deleted file mode 100644 index a7acf254d388..000000000000 --- a/packages/backend/src/models/entities/NoteUnread.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { Note } from './note.js'; -import { id } from '../id.js'; -import { Channel } from './channel.js'; - -@Entity() -@Index(['userId', 'noteId'], { unique: true }) -export class NoteUnread { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Index() - @Column(id()) - public noteId: Note['id']; - - @ManyToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public note: Note | null; - - /** - * メンションか否か - */ - @Index() - @Column('boolean') - public isMentioned: boolean; - - /** - * ダイレクト投稿か否か - */ - @Index() - @Column('boolean') - public isSpecified: boolean; - - //#region Denormalized fields - @Index() - @Column({ - ...id(), - comment: '[Denormalized]', - }) - public noteUserId: User['id']; - - @Index() - @Column({ - ...id(), - nullable: true, - comment: '[Denormalized]', - }) - public noteChannelId: Channel['id'] | null; - //#endregion -} diff --git a/packages/backend/src/models/entities/PageLike.ts b/packages/backend/src/models/entities/PageLike.ts deleted file mode 100644 index 17f4ebf520ee..000000000000 --- a/packages/backend/src/models/entities/PageLike.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; -import { Page } from './page.js'; - -@Entity() -@Index(['userId', 'pageId'], { unique: true }) -export class PageLike { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone') - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column(id()) - public pageId: Page['id']; - - @ManyToOne(type => Page, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public page: Page | null; -} diff --git a/packages/backend/src/models/entities/PasswordResetRequest.ts b/packages/backend/src/models/entities/PasswordResetRequest.ts deleted file mode 100644 index 05e62cc5ab83..000000000000 --- a/packages/backend/src/models/entities/PasswordResetRequest.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { PrimaryColumn, Entity, Index, Column, ManyToOne, JoinColumn } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './user.js'; - -@Entity() -export class PasswordResetRequest { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone') - public createdAt: Date; - - @Index({ unique: true }) - @Column('varchar', { - length: 256, - }) - public token: string; - - @Index() - @Column({ - ...id(), - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; -} diff --git a/packages/backend/src/models/entities/PollVote.ts b/packages/backend/src/models/entities/PollVote.ts deleted file mode 100644 index fca1cd009944..000000000000 --- a/packages/backend/src/models/entities/PollVote.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { Note } from './note.js'; -import { id } from '../id.js'; - -@Entity() -@Index(['userId', 'noteId', 'choice'], { unique: true }) -export class PollVote { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the PollVote.', - }) - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Index() - @Column(id()) - public noteId: Note['id']; - - @ManyToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public note: Note | null; - - @Column('integer') - public choice: number; -} diff --git a/packages/backend/src/models/entities/PromoNote.ts b/packages/backend/src/models/entities/PromoNote.ts deleted file mode 100644 index d110b81e93ec..000000000000 --- a/packages/backend/src/models/entities/PromoNote.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm'; -import { Note } from './note.js'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -export class PromoNote { - @PrimaryColumn(id()) - public noteId: Note['id']; - - @OneToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public note: Note | null; - - @Column('timestamp with time zone') - public expiresAt: Date; - - //#region Denormalized fields - @Index() - @Column({ - ...id(), - comment: '[Denormalized]', - }) - public userId: User['id']; - //#endregion -} diff --git a/packages/backend/src/models/entities/PromoRead.ts b/packages/backend/src/models/entities/PromoRead.ts deleted file mode 100644 index a63b79cd1ebe..000000000000 --- a/packages/backend/src/models/entities/PromoRead.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { Note } from './note.js'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -@Index(['userId', 'noteId'], { unique: true }) -export class PromoRead { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the PromoRead.', - }) - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column(id()) - public noteId: Note['id']; - - @ManyToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public note: Note | null; -} diff --git a/packages/backend/src/models/entities/RegistrationTickets.ts b/packages/backend/src/models/entities/RegistrationTickets.ts deleted file mode 100644 index 139e40f85e4c..000000000000 --- a/packages/backend/src/models/entities/RegistrationTickets.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { PrimaryColumn, Entity, Index, Column } from 'typeorm'; -import { id } from '../id.js'; - -@Entity() -export class RegistrationTicket { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone') - public createdAt: Date; - - @Index({ unique: true }) - @Column('varchar', { - length: 64, - }) - public code: string; -} diff --git a/packages/backend/src/models/entities/RegistryItem.ts b/packages/backend/src/models/entities/RegistryItem.ts deleted file mode 100644 index 283796df9166..000000000000 --- a/packages/backend/src/models/entities/RegistryItem.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; - -// TODO: 同じdomain、同じscope、同じkeyのレコードは二つ以上存在しないように制約付けたい -@Entity() -export class RegistryItem { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the RegistryItem.', - }) - public createdAt: Date; - - @Column('timestamp with time zone', { - comment: 'The updated date of the RegistryItem.', - }) - public updatedAt: Date; - - @Index() - @Column({ - ...id(), - comment: 'The owner ID.', - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column('varchar', { - length: 1024, - comment: 'The key of the RegistryItem.', - }) - public key: string; - - @Column('jsonb', { - default: {}, nullable: true, - comment: 'The value of the RegistryItem.', - }) - public value: any | null; - - @Index() - @Column('varchar', { - length: 1024, array: true, default: '{}', - }) - public scope: string[]; - - // サードパーティアプリに開放するときのためのカラム - @Index() - @Column('varchar', { - length: 512, nullable: true, - }) - public domain: string | null; -} diff --git a/packages/backend/src/models/entities/SwSubscription.ts b/packages/backend/src/models/entities/SwSubscription.ts deleted file mode 100644 index 59144d348b61..000000000000 --- a/packages/backend/src/models/entities/SwSubscription.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -export class SwSubscription { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone') - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column('varchar', { - length: 512, - }) - public endpoint: string; - - @Column('varchar', { - length: 256, - }) - public auth: string; - - @Column('varchar', { - length: 128, - }) - public publickey: string; -} diff --git a/packages/backend/src/models/entities/UsedUsername.ts b/packages/backend/src/models/entities/UsedUsername.ts deleted file mode 100644 index eb90bef6ca1f..000000000000 --- a/packages/backend/src/models/entities/UsedUsername.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { PrimaryColumn, Entity, Column } from 'typeorm'; - -@Entity() -export class UsedUsername { - @PrimaryColumn('varchar', { - length: 128, - }) - public username: string; - - @Column('timestamp with time zone') - public createdAt: Date; - - constructor(data: Partial) { - if (data == null) return; - - for (const [k, v] of Object.entries(data)) { - (this as any)[k] = v; - } - } -} diff --git a/packages/backend/src/models/entities/UserGroup.ts b/packages/backend/src/models/entities/UserGroup.ts deleted file mode 100644 index 8d5de1d926d1..000000000000 --- a/packages/backend/src/models/entities/UserGroup.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -export class UserGroup { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the UserGroup.', - }) - public createdAt: Date; - - @Column('varchar', { - length: 256, - }) - public name: string; - - @Index() - @Column({ - ...id(), - comment: 'The ID of owner.', - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column('boolean', { - default: false, - }) - public isPrivate: boolean; - - constructor(data: Partial) { - if (data == null) return; - - for (const [k, v] of Object.entries(data)) { - (this as any)[k] = v; - } - } -} diff --git a/packages/backend/src/models/entities/UserGroupInvitation.ts b/packages/backend/src/models/entities/UserGroupInvitation.ts deleted file mode 100644 index 10f357049fa8..000000000000 --- a/packages/backend/src/models/entities/UserGroupInvitation.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { UserGroup } from './user-group.js'; -import { id } from '../id.js'; - -@Entity() -@Index(['userId', 'userGroupId'], { unique: true }) -export class UserGroupInvitation { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the UserGroupInvitation.', - }) - public createdAt: Date; - - @Index() - @Column({ - ...id(), - comment: 'The user ID.', - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Index() - @Column({ - ...id(), - comment: 'The group ID.', - }) - public userGroupId: UserGroup['id']; - - @ManyToOne(type => UserGroup, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public userGroup: UserGroup | null; -} diff --git a/packages/backend/src/models/entities/UserGroupJoining.ts b/packages/backend/src/models/entities/UserGroupJoining.ts deleted file mode 100644 index 62a814218a3d..000000000000 --- a/packages/backend/src/models/entities/UserGroupJoining.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { UserGroup } from './user-group.js'; -import { id } from '../id.js'; - -@Entity() -@Index(['userId', 'userGroupId'], { unique: true }) -export class UserGroupJoining { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the UserGroupJoining.', - }) - public createdAt: Date; - - @Index() - @Column({ - ...id(), - comment: 'The user ID.', - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Index() - @Column({ - ...id(), - comment: 'The group ID.', - }) - public userGroupId: UserGroup['id']; - - @ManyToOne(type => UserGroup, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public userGroup: UserGroup | null; -} diff --git a/packages/backend/src/models/entities/UserIp.ts b/packages/backend/src/models/entities/UserIp.ts deleted file mode 100644 index 543e9e7289e8..000000000000 --- a/packages/backend/src/models/entities/UserIp.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; -import { id } from '../id.js'; -import { Note } from './note.js'; -import { User } from './user.js'; - -@Entity() -@Index(['userId', 'ip'], { unique: true }) -export class UserIp { - @PrimaryGeneratedColumn() - public id: string; - - @Column('timestamp with time zone', { - }) - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @Column('varchar', { - length: 128, - }) - public ip: string; -} diff --git a/packages/backend/src/models/entities/UserKeypair.ts b/packages/backend/src/models/entities/UserKeypair.ts deleted file mode 100644 index 85fa06297795..000000000000 --- a/packages/backend/src/models/entities/UserKeypair.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { PrimaryColumn, Entity, JoinColumn, Column, OneToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -export class UserKeypair { - @PrimaryColumn(id()) - public userId: User['id']; - - @OneToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column('varchar', { - length: 4096, - }) - public publicKey: string; - - @Column('varchar', { - length: 4096, - }) - public privateKey: string; - - constructor(data: Partial) { - if (data == null) return; - - for (const [k, v] of Object.entries(data)) { - (this as any)[k] = v; - } - } -} diff --git a/packages/backend/src/models/entities/UserList.ts b/packages/backend/src/models/entities/UserList.ts deleted file mode 100644 index ca69394e93c8..000000000000 --- a/packages/backend/src/models/entities/UserList.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -export class UserList { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the UserList.', - }) - public createdAt: Date; - - @Index() - @Column({ - ...id(), - comment: 'The owner ID.', - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column('varchar', { - length: 128, - comment: 'The name of the UserList.', - }) - public name: string; -} diff --git a/packages/backend/src/models/entities/UserListJoining.ts b/packages/backend/src/models/entities/UserListJoining.ts deleted file mode 100644 index 12f28c414977..000000000000 --- a/packages/backend/src/models/entities/UserListJoining.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { UserList } from './user-list.js'; -import { id } from '../id.js'; - -@Entity() -@Index(['userId', 'userListId'], { unique: true }) -export class UserListJoining { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the UserListJoining.', - }) - public createdAt: Date; - - @Index() - @Column({ - ...id(), - comment: 'The user ID.', - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Index() - @Column({ - ...id(), - comment: 'The list ID.', - }) - public userListId: UserList['id']; - - @ManyToOne(type => UserList, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public userList: UserList | null; -} diff --git a/packages/backend/src/models/entities/UserNotePining.ts b/packages/backend/src/models/entities/UserNotePining.ts deleted file mode 100644 index c91ab7fdd865..000000000000 --- a/packages/backend/src/models/entities/UserNotePining.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { Note } from './note.js'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -@Index(['userId', 'noteId'], { unique: true }) -export class UserNotePining { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the UserNotePinings.', - }) - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column(id()) - public noteId: Note['id']; - - @ManyToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public note: Note | null; -} diff --git a/packages/backend/src/models/entities/UserPending.ts b/packages/backend/src/models/entities/UserPending.ts deleted file mode 100644 index 763794884161..000000000000 --- a/packages/backend/src/models/entities/UserPending.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { PrimaryColumn, Entity, Index, Column } from 'typeorm'; -import { id } from '../id.js'; - -@Entity() -export class UserPending { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone') - public createdAt: Date; - - @Index({ unique: true }) - @Column('varchar', { - length: 128, - }) - public code: string; - - @Column('varchar', { - length: 128, - }) - public username: string; - - @Column('varchar', { - length: 128, - }) - public email: string; - - @Column('varchar', { - length: 128, - }) - public password: string; -} diff --git a/packages/backend/src/models/entities/UserProfile.ts b/packages/backend/src/models/entities/UserProfile.ts deleted file mode 100644 index 3654b0a99460..000000000000 --- a/packages/backend/src/models/entities/UserProfile.ts +++ /dev/null @@ -1,232 +0,0 @@ -import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm'; -import { ffVisibility, notificationTypes } from '@/types.js'; -import { id } from '../id.js'; -import { User } from './user.js'; -import { Page } from './page.js'; - -// TODO: このテーブルで管理している情報すべてレジストリで管理するようにしても良いかも -// ただ、「emailVerified が true なユーザーを find する」のようなクエリは書けなくなるからウーン -@Entity() -export class UserProfile { - @PrimaryColumn(id()) - public userId: User['id']; - - @OneToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column('varchar', { - length: 128, nullable: true, - comment: 'The location of the User.', - }) - public location: string | null; - - @Column('char', { - length: 10, nullable: true, - comment: 'The birthday (YYYY-MM-DD) of the User.', - }) - public birthday: string | null; - - @Column('varchar', { - length: 2048, nullable: true, - comment: 'The description (bio) of the User.', - }) - public description: string | null; - - @Column('jsonb', { - default: [], - }) - public fields: { - name: string; - value: string; - }[]; - - @Column('varchar', { - length: 32, nullable: true, - }) - public lang: string | null; - - @Column('varchar', { - length: 512, nullable: true, - comment: 'Remote URL of the user.', - }) - public url: string | null; - - @Column('varchar', { - length: 128, nullable: true, - comment: 'The email address of the User.', - }) - public email: string | null; - - @Column('varchar', { - length: 128, nullable: true, - }) - public emailVerifyCode: string | null; - - @Column('boolean', { - default: false, - }) - public emailVerified: boolean; - - @Column('jsonb', { - default: ['follow', 'receiveFollowRequest', 'groupInvited'], - }) - public emailNotificationTypes: string[]; - - @Column('boolean', { - default: false, - }) - public publicReactions: boolean; - - @Column('enum', { - enum: ffVisibility, - default: 'public', - }) - public ffVisibility: typeof ffVisibility[number]; - - @Column('varchar', { - length: 128, nullable: true, - }) - public twoFactorTempSecret: string | null; - - @Column('varchar', { - length: 128, nullable: true, - }) - public twoFactorSecret: string | null; - - @Column('boolean', { - default: false, - }) - public twoFactorEnabled: boolean; - - @Column('boolean', { - default: false, - }) - public securityKeysAvailable: boolean; - - @Column('boolean', { - default: false, - }) - public usePasswordLessLogin: boolean; - - @Column('varchar', { - length: 128, nullable: true, - comment: 'The password hash of the User. It will be null if the origin of the user is local.', - }) - public password: string | null; - - @Column('varchar', { - length: 8192, default: '', - }) - public moderationNote: string | null; - - // TODO: そのうち消す - @Column('jsonb', { - default: {}, - comment: 'The client-specific data of the User.', - }) - public clientData: Record; - - // TODO: そのうち消す - @Column('jsonb', { - default: {}, - comment: 'The room data of the User.', - }) - public room: Record; - - @Column('boolean', { - default: false, - }) - public autoAcceptFollowed: boolean; - - @Column('boolean', { - default: false, - comment: 'Whether reject index by crawler.', - }) - public noCrawle: boolean; - - @Column('boolean', { - default: false, - }) - public alwaysMarkNsfw: boolean; - - @Column('boolean', { - default: false, - }) - public autoSensitive: boolean; - - @Column('boolean', { - default: false, - }) - public carefulBot: boolean; - - @Column('boolean', { - default: true, - }) - public injectFeaturedNote: boolean; - - @Column('boolean', { - default: true, - }) - public receiveAnnouncementEmail: boolean; - - @Column({ - ...id(), - nullable: true, - }) - public pinnedPageId: Page['id'] | null; - - @OneToOne(type => Page, { - onDelete: 'SET NULL', - }) - @JoinColumn() - public pinnedPage: Page | null; - - @Column('jsonb', { - default: {}, - }) - public integrations: Record; - - @Index() - @Column('boolean', { - default: false, select: false, - }) - public enableWordMute: boolean; - - @Column('jsonb', { - default: [], - }) - public mutedWords: string[][]; - - @Column('jsonb', { - default: [], - comment: 'List of instances muted by the user.', - }) - public mutedInstances: string[]; - - @Column('enum', { - enum: notificationTypes, - array: true, - default: [], - }) - public mutingNotificationTypes: typeof notificationTypes[number][]; - - //#region Denormalized fields - @Index() - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', - }) - public userHost: string | null; - //#endregion - - constructor(data: Partial) { - if (data == null) return; - - for (const [k, v] of Object.entries(data)) { - (this as any)[k] = v; - } - } -} diff --git a/packages/backend/src/models/entities/UserPublickey.ts b/packages/backend/src/models/entities/UserPublickey.ts deleted file mode 100644 index 31ed60de8228..000000000000 --- a/packages/backend/src/models/entities/UserPublickey.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -export class UserPublickey { - @PrimaryColumn(id()) - public userId: User['id']; - - @OneToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Index({ unique: true }) - @Column('varchar', { - length: 256, - }) - public keyId: string; - - @Column('varchar', { - length: 4096, - }) - public keyPem: string; - - constructor(data: Partial) { - if (data == null) return; - - for (const [k, v] of Object.entries(data)) { - (this as any)[k] = v; - } - } -} diff --git a/packages/backend/src/models/entities/UserSecurityKey.ts b/packages/backend/src/models/entities/UserSecurityKey.ts deleted file mode 100644 index c4f2a852e228..000000000000 --- a/packages/backend/src/models/entities/UserSecurityKey.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { PrimaryColumn, Entity, JoinColumn, Column, ManyToOne, Index } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -export class UserSecurityKey { - @PrimaryColumn('varchar', { - comment: 'Variable-length id given to navigator.credentials.get()', - }) - public id: string; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Index() - @Column('varchar', { - comment: - 'Variable-length public key used to verify attestations (hex-encoded).', - }) - public publicKey: string; - - @Column('timestamp with time zone', { - comment: - 'The date of the last time the UserSecurityKey was successfully validated.', - }) - public lastUsed: Date; - - @Column('varchar', { - comment: 'User-defined name for this key', - length: 30, - }) - public name: string; - - constructor(data: Partial) { - if (data == null) return; - - for (const [k, v] of Object.entries(data)) { - (this as any)[k] = v; - } - } -} diff --git a/packages/backend/src/models/entities/ad.ts b/packages/backend/src/models/entities/ad.ts deleted file mode 100644 index 36b758f205b6..000000000000 --- a/packages/backend/src/models/entities/ad.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Entity, Index, Column, PrimaryColumn } from 'typeorm'; -import { id } from '../id.js'; - -@Entity() -export class Ad { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Ad.', - }) - public createdAt: Date; - - @Index() - @Column('timestamp with time zone', { - comment: 'The expired date of the Ad.', - }) - public expiresAt: Date; - - @Column('varchar', { - length: 32, nullable: false, - }) - public place: string; - - // 今は使われていないが将来的に活用される可能性はある - @Column('varchar', { - length: 32, nullable: false, - }) - public priority: string; - - @Column('integer', { - default: 1, nullable: false, - }) - public ratio: number; - - @Column('varchar', { - length: 1024, nullable: false, - }) - public url: string; - - @Column('varchar', { - length: 1024, nullable: false, - }) - public imageUrl: string; - - @Column('varchar', { - length: 8192, nullable: false, - }) - public memo: string; - - constructor(data: Partial) { - if (data == null) return; - - for (const [k, v] of Object.entries(data)) { - (this as any)[k] = v; - } - } -} diff --git a/packages/backend/src/models/entities/announcement.ts b/packages/backend/src/models/entities/announcement.ts deleted file mode 100644 index beb2f82462a6..000000000000 --- a/packages/backend/src/models/entities/announcement.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Entity, Index, Column, PrimaryColumn } from 'typeorm'; -import { id } from '../id.js'; - -@Entity() -export class Announcement { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Announcement.', - }) - public createdAt: Date; - - @Column('timestamp with time zone', { - comment: 'The updated date of the Announcement.', - nullable: true, - }) - public updatedAt: Date | null; - - @Column('varchar', { - length: 8192, nullable: false, - }) - public text: string; - - @Column('varchar', { - length: 256, nullable: false, - }) - public title: string; - - @Column('varchar', { - length: 1024, nullable: true, - }) - public imageUrl: string | null; - - constructor(data: Partial) { - if (data == null) return; - - for (const [k, v] of Object.entries(data)) { - (this as any)[k] = v; - } - } -} diff --git a/packages/backend/src/models/entities/antenna.ts b/packages/backend/src/models/entities/antenna.ts deleted file mode 100644 index 6c8bb13e5067..000000000000 --- a/packages/backend/src/models/entities/antenna.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; -import { UserList } from './user-list.js'; -import { UserGroupJoining } from './user-group-joining.js'; - -@Entity() -export class Antenna { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the Antenna.', - }) - public createdAt: Date; - - @Index() - @Column({ - ...id(), - comment: 'The owner ID.', - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column('varchar', { - length: 128, - comment: 'The name of the Antenna.', - }) - public name: string; - - @Column('enum', { enum: ['home', 'all', 'users', 'list', 'group'] }) - public src: 'home' | 'all' | 'users' | 'list' | 'group'; - - @Column({ - ...id(), - nullable: true, - }) - public userListId: UserList['id'] | null; - - @ManyToOne(type => UserList, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public userList: UserList | null; - - @Column({ - ...id(), - nullable: true, - }) - public userGroupJoiningId: UserGroupJoining['id'] | null; - - @ManyToOne(type => UserGroupJoining, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public userGroupJoining: UserGroupJoining | null; - - @Column('varchar', { - length: 1024, array: true, - default: '{}', - }) - public users: string[]; - - @Column('jsonb', { - default: [], - }) - public keywords: string[][]; - - @Column('jsonb', { - default: [], - }) - public excludeKeywords: string[][]; - - @Column('boolean', { - default: false, - }) - public caseSensitive: boolean; - - @Column('boolean', { - default: false, - }) - public withReplies: boolean; - - @Column('boolean') - public withFile: boolean; - - @Column('varchar', { - length: 2048, nullable: true, - }) - public expression: string | null; - - @Column('boolean') - public notify: boolean; -} diff --git a/packages/backend/src/models/entities/app.ts b/packages/backend/src/models/entities/app.ts deleted file mode 100644 index 46c11548a527..000000000000 --- a/packages/backend/src/models/entities/app.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Entity, PrimaryColumn, Column, Index, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -export class App { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the App.', - }) - public createdAt: Date; - - @Index() - @Column({ - ...id(), - nullable: true, - comment: 'The owner ID.', - }) - public userId: User['id'] | null; - - @ManyToOne(type => User, { - onDelete: 'SET NULL', - nullable: true, - }) - public user: User | null; - - @Index() - @Column('varchar', { - length: 64, - comment: 'The secret key of the App.', - }) - public secret: string; - - @Column('varchar', { - length: 128, - comment: 'The name of the App.', - }) - public name: string; - - @Column('varchar', { - length: 512, - comment: 'The description of the App.', - }) - public description: string; - - @Column('varchar', { - length: 64, array: true, - comment: 'The permission of the App.', - }) - public permission: string[]; - - @Column('varchar', { - length: 512, nullable: true, - comment: 'The callbackUrl of the App.', - }) - public callbackUrl: string | null; -} diff --git a/packages/backend/src/models/entities/blocking.ts b/packages/backend/src/models/entities/blocking.ts deleted file mode 100644 index 4ac73a00b5dd..000000000000 --- a/packages/backend/src/models/entities/blocking.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -@Index(['blockerId', 'blockeeId'], { unique: true }) -export class Blocking { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Blocking.', - }) - public createdAt: Date; - - @Index() - @Column({ - ...id(), - comment: 'The blockee user ID.', - }) - public blockeeId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public blockee: User | null; - - @Index() - @Column({ - ...id(), - comment: 'The blocker user ID.', - }) - public blockerId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public blocker: User | null; -} diff --git a/packages/backend/src/models/entities/channel.ts b/packages/backend/src/models/entities/channel.ts deleted file mode 100644 index abf6668bd285..000000000000 --- a/packages/backend/src/models/entities/channel.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; -import { DriveFile } from './drive-file.js'; - -@Entity() -export class Channel { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Channel.', - }) - public createdAt: Date; - - @Index() - @Column('timestamp with time zone', { - nullable: true, - }) - public lastNotedAt: Date | null; - - @Index() - @Column({ - ...id(), - nullable: true, - comment: 'The owner ID.', - }) - public userId: User['id'] | null; - - @ManyToOne(type => User, { - onDelete: 'SET NULL', - }) - @JoinColumn() - public user: User | null; - - @Column('varchar', { - length: 128, - comment: 'The name of the Channel.', - }) - public name: string; - - @Column('varchar', { - length: 2048, nullable: true, - comment: 'The description of the Channel.', - }) - public description: string | null; - - @Column({ - ...id(), - nullable: true, - comment: 'The ID of banner Channel.', - }) - public bannerId: DriveFile['id'] | null; - - @ManyToOne(type => DriveFile, { - onDelete: 'SET NULL', - }) - @JoinColumn() - public banner: DriveFile | null; - - @Index() - @Column('integer', { - default: 0, - comment: 'The count of notes.', - }) - public notesCount: number; - - @Index() - @Column('integer', { - default: 0, - comment: 'The count of users.', - }) - public usersCount: number; -} diff --git a/packages/backend/src/models/entities/clip.ts b/packages/backend/src/models/entities/clip.ts deleted file mode 100644 index 1386684c32c9..000000000000 --- a/packages/backend/src/models/entities/clip.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -export class Clip { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the Clip.', - }) - public createdAt: Date; - - @Index() - @Column({ - ...id(), - comment: 'The owner ID.', - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column('varchar', { - length: 128, - comment: 'The name of the Clip.', - }) - public name: string; - - @Column('boolean', { - default: false, - }) - public isPublic: boolean; - - @Column('varchar', { - length: 2048, nullable: true, - comment: 'The description of the Clip.', - }) - public description: string | null; -} diff --git a/packages/backend/src/models/entities/emoji.ts b/packages/backend/src/models/entities/emoji.ts deleted file mode 100644 index 7332dd1857a8..000000000000 --- a/packages/backend/src/models/entities/emoji.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { PrimaryColumn, Entity, Index, Column } from 'typeorm'; -import { id } from '../id.js'; - -@Entity() -@Index(['name', 'host'], { unique: true }) -export class Emoji { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - nullable: true, - }) - public updatedAt: Date | null; - - @Index() - @Column('varchar', { - length: 128, - }) - public name: string; - - @Index() - @Column('varchar', { - length: 128, nullable: true, - }) - public host: string | null; - - @Column('varchar', { - length: 128, nullable: true, - }) - public category: string | null; - - @Column('varchar', { - length: 512, - }) - public originalUrl: string; - - @Column('varchar', { - length: 512, - default: '', - }) - public publicUrl: string; - - @Column('varchar', { - length: 512, nullable: true, - }) - public uri: string | null; - - // publicUrlの方のtypeが入る - @Column('varchar', { - length: 64, nullable: true, - }) - public type: string | null; - - @Column('varchar', { - array: true, length: 128, default: '{}', - }) - public aliases: string[]; -} diff --git a/packages/backend/src/models/entities/following.ts b/packages/backend/src/models/entities/following.ts deleted file mode 100644 index b283ca7e8af8..000000000000 --- a/packages/backend/src/models/entities/following.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -@Index(['followerId', 'followeeId'], { unique: true }) -export class Following { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Following.', - }) - public createdAt: Date; - - @Index() - @Column({ - ...id(), - comment: 'The followee user ID.', - }) - public followeeId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public followee: User | null; - - @Index() - @Column({ - ...id(), - comment: 'The follower user ID.', - }) - public followerId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public follower: User | null; - - //#region Denormalized fields - @Index() - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', - }) - public followerHost: string | null; - - @Column('varchar', { - length: 512, nullable: true, - comment: '[Denormalized]', - }) - public followerInbox: string | null; - - @Column('varchar', { - length: 512, nullable: true, - comment: '[Denormalized]', - }) - public followerSharedInbox: string | null; - - @Index() - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', - }) - public followeeHost: string | null; - - @Column('varchar', { - length: 512, nullable: true, - comment: '[Denormalized]', - }) - public followeeInbox: string | null; - - @Column('varchar', { - length: 512, nullable: true, - comment: '[Denormalized]', - }) - public followeeSharedInbox: string | null; - //#endregion -} diff --git a/packages/backend/src/models/entities/hashtag.ts b/packages/backend/src/models/entities/hashtag.ts deleted file mode 100644 index 6bd991f6299a..000000000000 --- a/packages/backend/src/models/entities/hashtag.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { Entity, PrimaryColumn, Index, Column } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -export class Hashtag { - @PrimaryColumn(id()) - public id: string; - - @Index({ unique: true }) - @Column('varchar', { - length: 128, - }) - public name: string; - - @Column({ - ...id(), - array: true, - }) - public mentionedUserIds: User['id'][]; - - @Index() - @Column('integer', { - default: 0, - }) - public mentionedUsersCount: number; - - @Column({ - ...id(), - array: true, - }) - public mentionedLocalUserIds: User['id'][]; - - @Index() - @Column('integer', { - default: 0, - }) - public mentionedLocalUsersCount: number; - - @Column({ - ...id(), - array: true, - }) - public mentionedRemoteUserIds: User['id'][]; - - @Index() - @Column('integer', { - default: 0, - }) - public mentionedRemoteUsersCount: number; - - @Column({ - ...id(), - array: true, - }) - public attachedUserIds: User['id'][]; - - @Index() - @Column('integer', { - default: 0, - }) - public attachedUsersCount: number; - - @Column({ - ...id(), - array: true, - }) - public attachedLocalUserIds: User['id'][]; - - @Index() - @Column('integer', { - default: 0, - }) - public attachedLocalUsersCount: number; - - @Column({ - ...id(), - array: true, - }) - public attachedRemoteUserIds: User['id'][]; - - @Index() - @Column('integer', { - default: 0, - }) - public attachedRemoteUsersCount: number; -} diff --git a/packages/backend/src/models/entities/instance.ts b/packages/backend/src/models/entities/instance.ts deleted file mode 100644 index 7ea9234384e1..000000000000 --- a/packages/backend/src/models/entities/instance.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { Entity, PrimaryColumn, Index, Column } from 'typeorm'; -import { id } from '../id.js'; - -@Entity() -export class Instance { - @PrimaryColumn(id()) - public id: string; - - /** - * このインスタンスを捕捉した日時 - */ - @Index() - @Column('timestamp with time zone', { - comment: 'The caught date of the Instance.', - }) - public caughtAt: Date; - - /** - * ホスト - */ - @Index({ unique: true }) - @Column('varchar', { - length: 128, - comment: 'The host of the Instance.', - }) - public host: string; - - /** - * インスタンスのユーザー数 - */ - @Column('integer', { - default: 0, - comment: 'The count of the users of the Instance.', - }) - public usersCount: number; - - /** - * インスタンスの投稿数 - */ - @Column('integer', { - default: 0, - comment: 'The count of the notes of the Instance.', - }) - public notesCount: number; - - /** - * このインスタンスのユーザーからフォローされている、自インスタンスのユーザーの数 - */ - @Column('integer', { - default: 0, - }) - public followingCount: number; - - /** - * このインスタンスのユーザーをフォローしている、自インスタンスのユーザーの数 - */ - @Column('integer', { - default: 0, - }) - public followersCount: number; - - /** - * 直近のリクエスト送信日時 - */ - @Column('timestamp with time zone', { - nullable: true, - }) - public latestRequestSentAt: Date | null; - - /** - * 直近のリクエスト送信時のHTTPステータスコード - */ - @Column('integer', { - nullable: true, - }) - public latestStatus: number | null; - - /** - * 直近のリクエスト受信日時 - */ - @Column('timestamp with time zone', { - nullable: true, - }) - public latestRequestReceivedAt: Date | null; - - /** - * このインスタンスと最後にやり取りした日時 - */ - @Column('timestamp with time zone') - public lastCommunicatedAt: Date; - - /** - * このインスタンスと不通かどうか - */ - @Column('boolean', { - default: false, - }) - public isNotResponding: boolean; - - /** - * このインスタンスへの配信を停止するか - */ - @Index() - @Column('boolean', { - default: false, - }) - public isSuspended: boolean; - - @Column('varchar', { - length: 64, nullable: true, - comment: 'The software of the Instance.', - }) - public softwareName: string | null; - - @Column('varchar', { - length: 64, nullable: true, - }) - public softwareVersion: string | null; - - @Column('boolean', { - nullable: true, - }) - public openRegistrations: boolean | null; - - @Column('varchar', { - length: 256, nullable: true, - }) - public name: string | null; - - @Column('varchar', { - length: 4096, nullable: true, - }) - public description: string | null; - - @Column('varchar', { - length: 128, nullable: true, - }) - public maintainerName: string | null; - - @Column('varchar', { - length: 256, nullable: true, - }) - public maintainerEmail: string | null; - - @Column('varchar', { - length: 256, nullable: true, - }) - public iconUrl: string | null; - - @Column('varchar', { - length: 256, nullable: true, - }) - public faviconUrl: string | null; - - @Column('varchar', { - length: 64, nullable: true, - }) - public themeColor: string | null; - - @Column('timestamp with time zone', { - nullable: true, - }) - public infoUpdatedAt: Date | null; -} diff --git a/packages/backend/src/models/entities/meta.ts b/packages/backend/src/models/entities/meta.ts deleted file mode 100644 index d33ff2519e69..000000000000 --- a/packages/backend/src/models/entities/meta.ts +++ /dev/null @@ -1,462 +0,0 @@ -import { Entity, Column, PrimaryColumn, ManyToOne, JoinColumn } from 'typeorm'; -import { id } from '../id.js'; -import { User } from './user.js'; -import { Clip } from './clip.js'; - -@Entity() -export class Meta { - @PrimaryColumn({ - type: 'varchar', - length: 32, - }) - public id: string; - - @Column('varchar', { - length: 128, nullable: true, - }) - public name: string | null; - - @Column('varchar', { - length: 1024, nullable: true, - }) - public description: string | null; - - /** - * メンテナの名前 - */ - @Column('varchar', { - length: 128, nullable: true, - }) - public maintainerName: string | null; - - /** - * メンテナの連絡先 - */ - @Column('varchar', { - length: 128, nullable: true, - }) - public maintainerEmail: string | null; - - @Column('boolean', { - default: false, - }) - public disableRegistration: boolean; - - @Column('boolean', { - default: false, - }) - public disableLocalTimeline: boolean; - - @Column('boolean', { - default: false, - }) - public disableGlobalTimeline: boolean; - - @Column('boolean', { - default: false, - }) - public useStarForReactionFallback: boolean; - - @Column('varchar', { - length: 64, array: true, default: '{}', - }) - public langs: string[]; - - @Column('varchar', { - length: 256, array: true, default: '{}', - }) - public pinnedUsers: string[]; - - @Column('varchar', { - length: 256, array: true, default: '{}', - }) - public hiddenTags: string[]; - - @Column('varchar', { - length: 256, array: true, default: '{}', - }) - public blockedHosts: string[]; - - @Column('varchar', { - length: 512, array: true, default: '{/featured,/channels,/explore,/pages,/about-misskey}', - }) - public pinnedPages: string[]; - - @Column({ - ...id(), - nullable: true, - }) - public pinnedClipId: Clip['id'] | null; - - @Column('varchar', { - length: 512, - nullable: true, - }) - public themeColor: string | null; - - @Column('varchar', { - length: 512, - nullable: true, - default: '/assets/ai.png', - }) - public mascotImageUrl: string | null; - - @Column('varchar', { - length: 512, - nullable: true, - }) - public bannerUrl: string | null; - - @Column('varchar', { - length: 512, - nullable: true, - }) - public backgroundImageUrl: string | null; - - @Column('varchar', { - length: 512, - nullable: true, - }) - public logoImageUrl: string | null; - - @Column('varchar', { - length: 512, - nullable: true, - default: 'https://xn--931a.moe/aiart/yubitun.png', - }) - public errorImageUrl: string | null; - - @Column('varchar', { - length: 512, - nullable: true, - }) - public iconUrl: string | null; - - @Column('boolean', { - default: true, - }) - public cacheRemoteFiles: boolean; - - @Column({ - ...id(), - nullable: true, - }) - public proxyAccountId: User['id'] | null; - - @ManyToOne(type => User, { - onDelete: 'SET NULL', - }) - @JoinColumn() - public proxyAccount: User | null; - - @Column('boolean', { - default: false, - }) - public emailRequiredForSignup: boolean; - - @Column('boolean', { - default: false, - }) - public enableHcaptcha: boolean; - - @Column('varchar', { - length: 64, - nullable: true, - }) - public hcaptchaSiteKey: string | null; - - @Column('varchar', { - length: 64, - nullable: true, - }) - public hcaptchaSecretKey: string | null; - - @Column('boolean', { - default: false, - }) - public enableRecaptcha: boolean; - - @Column('varchar', { - length: 64, - nullable: true, - }) - public recaptchaSiteKey: string | null; - - @Column('varchar', { - length: 64, - nullable: true, - }) - public recaptchaSecretKey: string | null; - - @Column('enum', { - enum: ['none', 'all', 'local', 'remote'], - default: 'none', - }) - public sensitiveMediaDetection: 'none' | 'all' | 'local' | 'remote'; - - @Column('enum', { - enum: ['medium', 'low', 'high', 'veryLow', 'veryHigh'], - default: 'medium', - }) - public sensitiveMediaDetectionSensitivity: 'medium' | 'low' | 'high' | 'veryLow' | 'veryHigh'; - - @Column('boolean', { - default: false, - }) - public setSensitiveFlagAutomatically: boolean; - - @Column('boolean', { - default: false, - }) - public enableSensitiveMediaDetectionForVideos: boolean; - - @Column('integer', { - default: 1024, - comment: 'Drive capacity of a local user (MB)', - }) - public localDriveCapacityMb: number; - - @Column('integer', { - default: 32, - comment: 'Drive capacity of a remote user (MB)', - }) - public remoteDriveCapacityMb: number; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public summalyProxy: string | null; - - @Column('boolean', { - default: false, - }) - public enableEmail: boolean; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public email: string | null; - - @Column('boolean', { - default: false, - }) - public smtpSecure: boolean; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public smtpHost: string | null; - - @Column('integer', { - nullable: true, - }) - public smtpPort: number | null; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public smtpUser: string | null; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public smtpPass: string | null; - - @Column('boolean', { - default: false, - }) - public enableServiceWorker: boolean; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public swPublicKey: string | null; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public swPrivateKey: string | null; - - @Column('boolean', { - default: false, - }) - public enableTwitterIntegration: boolean; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public twitterConsumerKey: string | null; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public twitterConsumerSecret: string | null; - - @Column('boolean', { - default: false, - }) - public enableGithubIntegration: boolean; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public githubClientId: string | null; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public githubClientSecret: string | null; - - @Column('boolean', { - default: false, - }) - public enableDiscordIntegration: boolean; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public discordClientId: string | null; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public discordClientSecret: string | null; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public deeplAuthKey: string | null; - - @Column('boolean', { - default: false, - }) - public deeplIsPro: boolean; - - @Column('varchar', { - length: 512, - nullable: true, - }) - public ToSUrl: string | null; - - @Column('varchar', { - length: 512, - default: 'https://github.com/misskey-dev/misskey', - nullable: false, - }) - public repositoryUrl: string; - - @Column('varchar', { - length: 512, - default: 'https://github.com/misskey-dev/misskey/issues/new', - nullable: true, - }) - public feedbackUrl: string | null; - - @Column('varchar', { - length: 8192, - nullable: true, - }) - public defaultLightTheme: string | null; - - @Column('varchar', { - length: 8192, - nullable: true, - }) - public defaultDarkTheme: string | null; - - @Column('boolean', { - default: false, - }) - public useObjectStorage: boolean; - - @Column('varchar', { - length: 512, - nullable: true, - }) - public objectStorageBucket: string | null; - - @Column('varchar', { - length: 512, - nullable: true, - }) - public objectStoragePrefix: string | null; - - @Column('varchar', { - length: 512, - nullable: true, - }) - public objectStorageBaseUrl: string | null; - - @Column('varchar', { - length: 512, - nullable: true, - }) - public objectStorageEndpoint: string | null; - - @Column('varchar', { - length: 512, - nullable: true, - }) - public objectStorageRegion: string | null; - - @Column('varchar', { - length: 512, - nullable: true, - }) - public objectStorageAccessKey: string | null; - - @Column('varchar', { - length: 512, - nullable: true, - }) - public objectStorageSecretKey: string | null; - - @Column('integer', { - nullable: true, - }) - public objectStoragePort: number | null; - - @Column('boolean', { - default: true, - }) - public objectStorageUseSSL: boolean; - - @Column('boolean', { - default: true, - }) - public objectStorageUseProxy: boolean; - - @Column('boolean', { - default: false, - }) - public objectStorageSetPublicRead: boolean; - - @Column('boolean', { - default: true, - }) - public objectStorageS3ForcePathStyle: boolean; - - @Column('boolean', { - default: false, - }) - public enableIpLogging: boolean; - - @Column('boolean', { - default: true, - }) - public enableActiveEmailValidation: boolean; -} diff --git a/packages/backend/src/models/entities/muting.ts b/packages/backend/src/models/entities/muting.ts deleted file mode 100644 index 8f9e69063adc..000000000000 --- a/packages/backend/src/models/entities/muting.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -@Index(['muterId', 'muteeId'], { unique: true }) -export class Muting { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Muting.', - }) - public createdAt: Date; - - @Index() - @Column('timestamp with time zone', { - nullable: true, - }) - public expiresAt: Date | null; - - @Index() - @Column({ - ...id(), - comment: 'The mutee user ID.', - }) - public muteeId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public mutee: User | null; - - @Index() - @Column({ - ...id(), - comment: 'The muter user ID.', - }) - public muterId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public muter: User | null; -} diff --git a/packages/backend/src/models/entities/note.ts b/packages/backend/src/models/entities/note.ts deleted file mode 100644 index 0ffeb85f69a8..000000000000 --- a/packages/backend/src/models/entities/note.ts +++ /dev/null @@ -1,242 +0,0 @@ -import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { DriveFile } from './drive-file.js'; -import { id } from '../id.js'; -import { noteVisibilities } from '../../types.js'; -import { Channel } from './channel.js'; - -@Entity() -@Index('IDX_NOTE_TAGS', { synchronize: false }) -@Index('IDX_NOTE_MENTIONS', { synchronize: false }) -@Index('IDX_NOTE_VISIBLE_USER_IDS', { synchronize: false }) -export class Note { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Note.', - }) - public createdAt: Date; - - @Index() - @Column({ - ...id(), - nullable: true, - comment: 'The ID of reply target.', - }) - public replyId: Note['id'] | null; - - @ManyToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public reply: Note | null; - - @Index() - @Column({ - ...id(), - nullable: true, - comment: 'The ID of renote target.', - }) - public renoteId: Note['id'] | null; - - @ManyToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public renote: Note | null; - - @Index() - @Column('varchar', { - length: 256, nullable: true, - }) - public threadId: string | null; - - @Column('text', { - nullable: true, - }) - public text: string | null; - - @Column('varchar', { - length: 256, nullable: true, - }) - public name: string | null; - - @Column('varchar', { - length: 512, nullable: true, - }) - public cw: string | null; - - @Index() - @Column({ - ...id(), - comment: 'The ID of author.', - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column('boolean', { - default: false, - }) - public localOnly: boolean; - - @Column('smallint', { - default: 0, - }) - public renoteCount: number; - - @Column('smallint', { - default: 0, - }) - public repliesCount: number; - - @Column('jsonb', { - default: {}, - }) - public reactions: Record; - - /** - * public ... 公開 - * home ... ホームタイムライン(ユーザーページのタイムライン含む)のみに流す - * followers ... フォロワーのみ - * specified ... visibleUserIds で指定したユーザーのみ - */ - @Column('enum', { enum: noteVisibilities }) - public visibility: typeof noteVisibilities[number]; - - @Index({ unique: true }) - @Column('varchar', { - length: 512, nullable: true, - comment: 'The URI of a note. it will be null when the note is local.', - }) - public uri: string | null; - - @Column('varchar', { - length: 512, nullable: true, - comment: 'The human readable url of a note. it will be null when the note is local.', - }) - public url: string | null; - - @Column('integer', { - default: 0, select: false, - }) - public score: number; - - @Index() - @Column({ - ...id(), - array: true, default: '{}', - }) - public fileIds: DriveFile['id'][]; - - @Index() - @Column('varchar', { - length: 256, array: true, default: '{}', - }) - public attachedFileTypes: string[]; - - @Index() - @Column({ - ...id(), - array: true, default: '{}', - }) - public visibleUserIds: User['id'][]; - - @Index() - @Column({ - ...id(), - array: true, default: '{}', - }) - public mentions: User['id'][]; - - @Column('text', { - default: '[]', - }) - public mentionedRemoteUsers: string; - - @Column('varchar', { - length: 128, array: true, default: '{}', - }) - public emojis: string[]; - - @Index() - @Column('varchar', { - length: 128, array: true, default: '{}', - }) - public tags: string[]; - - @Column('boolean', { - default: false, - }) - public hasPoll: boolean; - - @Index() - @Column({ - ...id(), - nullable: true, - comment: 'The ID of source channel.', - }) - public channelId: Channel['id'] | null; - - @ManyToOne(type => Channel, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public channel: Channel | null; - - //#region Denormalized fields - @Index() - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', - }) - public userHost: string | null; - - @Column({ - ...id(), - nullable: true, - comment: '[Denormalized]', - }) - public replyUserId: User['id'] | null; - - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', - }) - public replyUserHost: string | null; - - @Column({ - ...id(), - nullable: true, - comment: '[Denormalized]', - }) - public renoteUserId: User['id'] | null; - - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', - }) - public renoteUserHost: string | null; - //#endregion - - constructor(data: Partial) { - if (data == null) return; - - for (const [k, v] of Object.entries(data)) { - (this as any)[k] = v; - } - } -} - -export type IMentionedRemoteUsers = { - uri: string; - url?: string; - username: string; - host: string; -}[]; diff --git a/packages/backend/src/models/entities/notification.ts b/packages/backend/src/models/entities/notification.ts deleted file mode 100644 index db3dba363292..000000000000 --- a/packages/backend/src/models/entities/notification.ts +++ /dev/null @@ -1,173 +0,0 @@ -import { Entity, Index, JoinColumn, ManyToOne, Column, PrimaryColumn } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; -import { Note } from './note.js'; -import { FollowRequest } from './follow-request.js'; -import { UserGroupInvitation } from './user-group-invitation.js'; -import { AccessToken } from './access-token.js'; -import { notificationTypes } from '@/types.js'; - -@Entity() -export class Notification { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Notification.', - }) - public createdAt: Date; - - /** - * 通知の受信者 - */ - @Index() - @Column({ - ...id(), - comment: 'The ID of recipient user of the Notification.', - }) - public notifieeId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public notifiee: User | null; - - /** - * 通知の送信者(initiator) - */ - @Index() - @Column({ - ...id(), - nullable: true, - comment: 'The ID of sender user of the Notification.', - }) - public notifierId: User['id'] | null; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public notifier: User | null; - - /** - * 通知の種類。 - * follow - フォローされた - * mention - 投稿で自分が言及された - * reply - (自分または自分がWatchしている)投稿が返信された - * renote - (自分または自分がWatchしている)投稿がRenoteされた - * quote - (自分または自分がWatchしている)投稿が引用Renoteされた - * reaction - (自分または自分がWatchしている)投稿にリアクションされた - * pollVote - (自分または自分がWatchしている)投稿のアンケートに投票された - * pollEnded - 自分のアンケートもしくは自分が投票したアンケートが終了した - * receiveFollowRequest - フォローリクエストされた - * followRequestAccepted - 自分の送ったフォローリクエストが承認された - * groupInvited - グループに招待された - * app - アプリ通知 - */ - @Index() - @Column('enum', { - enum: notificationTypes, - comment: 'The type of the Notification.', - }) - public type: typeof notificationTypes[number]; - - /** - * 通知が読まれたかどうか - */ - @Index() - @Column('boolean', { - default: false, - comment: 'Whether the Notification is read.', - }) - public isRead: boolean; - - @Column({ - ...id(), - nullable: true, - }) - public noteId: Note['id'] | null; - - @ManyToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public note: Note | null; - - @Column({ - ...id(), - nullable: true, - }) - public followRequestId: FollowRequest['id'] | null; - - @ManyToOne(type => FollowRequest, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public followRequest: FollowRequest | null; - - @Column({ - ...id(), - nullable: true, - }) - public userGroupInvitationId: UserGroupInvitation['id'] | null; - - @ManyToOne(type => UserGroupInvitation, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public userGroupInvitation: UserGroupInvitation | null; - - @Column('varchar', { - length: 128, nullable: true, - }) - public reaction: string | null; - - @Column('integer', { - nullable: true, - }) - public choice: number | null; - - /** - * アプリ通知のbody - */ - @Column('varchar', { - length: 2048, nullable: true, - }) - public customBody: string | null; - - /** - * アプリ通知のheader - * (省略時はアプリ名で表示されることを期待) - */ - @Column('varchar', { - length: 256, nullable: true, - }) - public customHeader: string | null; - - /** - * アプリ通知のicon(URL) - * (省略時はアプリアイコンで表示されることを期待) - */ - @Column('varchar', { - length: 1024, nullable: true, - }) - public customIcon: string | null; - - /** - * アプリ通知のアプリ(のトークン) - */ - @Index() - @Column({ - ...id(), - nullable: true, - }) - public appAccessTokenId: AccessToken['id'] | null; - - @ManyToOne(type => AccessToken, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public appAccessToken: AccessToken | null; -} diff --git a/packages/backend/src/models/entities/page.ts b/packages/backend/src/models/entities/page.ts deleted file mode 100644 index baad3a36fa46..000000000000 --- a/packages/backend/src/models/entities/page.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; -import { DriveFile } from './drive-file.js'; - -@Entity() -@Index(['userId', 'name'], { unique: true }) -export class Page { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Page.', - }) - public createdAt: Date; - - @Index() - @Column('timestamp with time zone', { - comment: 'The updated date of the Page.', - }) - public updatedAt: Date; - - @Column('varchar', { - length: 256, - }) - public title: string; - - @Index() - @Column('varchar', { - length: 256, - }) - public name: string; - - @Column('varchar', { - length: 256, nullable: true, - }) - public summary: string | null; - - @Column('boolean') - public alignCenter: boolean; - - @Column('boolean', { - default: false, - }) - public hideTitleWhenPinned: boolean; - - @Column('varchar', { - length: 32, - }) - public font: string; - - @Index() - @Column({ - ...id(), - comment: 'The ID of author.', - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column({ - ...id(), - nullable: true, - }) - public eyeCatchingImageId: DriveFile['id'] | null; - - @ManyToOne(type => DriveFile, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public eyeCatchingImage: DriveFile | null; - - @Column('jsonb', { - default: [], - }) - public content: Record[]; - - @Column('jsonb', { - default: [], - }) - public variables: Record[]; - - @Column('varchar', { - length: 16384, - default: '', - }) - public script: string; - - /** - * public ... 公開 - * followers ... フォロワーのみ - * specified ... visibleUserIds で指定したユーザーのみ - */ - @Column('enum', { enum: ['public', 'followers', 'specified'] }) - public visibility: 'public' | 'followers' | 'specified'; - - @Index() - @Column({ - ...id(), - array: true, default: '{}', - }) - public visibleUserIds: User['id'][]; - - @Column('integer', { - default: 0, - }) - public likedCount: number; - - constructor(data: Partial) { - if (data == null) return; - - for (const [k, v] of Object.entries(data)) { - (this as any)[k] = v; - } - } -} diff --git a/packages/backend/src/models/entities/poll.ts b/packages/backend/src/models/entities/poll.ts deleted file mode 100644 index 83d0873cc50a..000000000000 --- a/packages/backend/src/models/entities/poll.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm'; -import { id } from '../id.js'; -import { Note } from './note.js'; -import { User } from './user.js'; -import { noteVisibilities } from '../../types.js'; - -@Entity() -export class Poll { - @PrimaryColumn(id()) - public noteId: Note['id']; - - @OneToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public note: Note | null; - - @Column('timestamp with time zone', { - nullable: true, - }) - public expiresAt: Date | null; - - @Column('boolean') - public multiple: boolean; - - @Column('varchar', { - length: 128, array: true, default: '{}', - }) - public choices: string[]; - - @Column('integer', { - array: true, - }) - public votes: number[]; - - //#region Denormalized fields - @Column('enum', { - enum: noteVisibilities, - comment: '[Denormalized]', - }) - public noteVisibility: typeof noteVisibilities[number]; - - @Index() - @Column({ - ...id(), - comment: '[Denormalized]', - }) - public userId: User['id']; - - @Index() - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', - }) - public userHost: string | null; - //#endregion - - constructor(data: Partial) { - if (data == null) return; - - for (const [k, v] of Object.entries(data)) { - (this as any)[k] = v; - } - } -} - -export type IPoll = { - choices: string[]; - votes?: number[]; - multiple: boolean; - expiresAt: Date | null; -}; diff --git a/packages/backend/src/models/entities/relay.ts b/packages/backend/src/models/entities/relay.ts deleted file mode 100644 index 94d192957411..000000000000 --- a/packages/backend/src/models/entities/relay.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { PrimaryColumn, Entity, Index, Column } from 'typeorm'; -import { id } from '../id.js'; - -@Entity() -export class Relay { - @PrimaryColumn(id()) - public id: string; - - @Index({ unique: true }) - @Column('varchar', { - length: 512, nullable: false, - }) - public inbox: string; - - @Column('enum', { - enum: ['requesting', 'accepted', 'rejected'], - }) - public status: 'requesting' | 'accepted' | 'rejected'; -} diff --git a/packages/backend/src/models/entities/signin.ts b/packages/backend/src/models/entities/signin.ts deleted file mode 100644 index ba81f45e4995..000000000000 --- a/packages/backend/src/models/entities/signin.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; - -@Entity() -export class Signin { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the Signin.', - }) - public createdAt: Date; - - @Index() - @Column(id()) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column('varchar', { - length: 128, - }) - public ip: string; - - @Column('jsonb') - public headers: Record; - - @Column('boolean') - public success: boolean; -} diff --git a/packages/backend/src/models/entities/user.ts b/packages/backend/src/models/entities/user.ts deleted file mode 100644 index 76f8e7935e35..000000000000 --- a/packages/backend/src/models/entities/user.ts +++ /dev/null @@ -1,255 +0,0 @@ -import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm'; -import { id } from '../id.js'; -import { DriveFile } from './drive-file.js'; - -@Entity() -@Index(['usernameLower', 'host'], { unique: true }) -export class User { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the User.', - }) - public createdAt: Date; - - @Index() - @Column('timestamp with time zone', { - nullable: true, - comment: 'The updated date of the User.', - }) - public updatedAt: Date | null; - - @Column('timestamp with time zone', { - nullable: true, - }) - public lastFetchedAt: Date | null; - - @Index() - @Column('timestamp with time zone', { - nullable: true, - }) - public lastActiveDate: Date | null; - - @Column('boolean', { - default: false, - }) - public hideOnlineStatus: boolean; - - @Column('varchar', { - length: 128, - comment: 'The username of the User.', - }) - public username: string; - - @Index() - @Column('varchar', { - length: 128, select: false, - comment: 'The username (lowercased) of the User.', - }) - public usernameLower: string; - - @Column('varchar', { - length: 128, nullable: true, - comment: 'The name of the User.', - }) - public name: string | null; - - @Column('integer', { - default: 0, - comment: 'The count of followers.', - }) - public followersCount: number; - - @Column('integer', { - default: 0, - comment: 'The count of following.', - }) - public followingCount: number; - - @Column('integer', { - default: 0, - comment: 'The count of notes.', - }) - public notesCount: number; - - @Column({ - ...id(), - nullable: true, - comment: 'The ID of avatar DriveFile.', - }) - public avatarId: DriveFile['id'] | null; - - @OneToOne(type => DriveFile, { - onDelete: 'SET NULL', - }) - @JoinColumn() - public avatar: DriveFile | null; - - @Column({ - ...id(), - nullable: true, - comment: 'The ID of banner DriveFile.', - }) - public bannerId: DriveFile['id'] | null; - - @OneToOne(type => DriveFile, { - onDelete: 'SET NULL', - }) - @JoinColumn() - public banner: DriveFile | null; - - @Index() - @Column('varchar', { - length: 128, array: true, default: '{}', - }) - public tags: string[]; - - @Column('boolean', { - default: false, - comment: 'Whether the User is suspended.', - }) - public isSuspended: boolean; - - @Column('boolean', { - default: false, - comment: 'Whether the User is silenced.', - }) - public isSilenced: boolean; - - @Column('boolean', { - default: false, - comment: 'Whether the User is locked.', - }) - public isLocked: boolean; - - @Column('boolean', { - default: false, - comment: 'Whether the User is a bot.', - }) - public isBot: boolean; - - @Column('boolean', { - default: false, - comment: 'Whether the User is a cat.', - }) - public isCat: boolean; - - @Column('boolean', { - default: false, - comment: 'Whether the User is the admin.', - }) - public isAdmin: boolean; - - @Column('boolean', { - default: false, - comment: 'Whether the User is a moderator.', - }) - public isModerator: boolean; - - @Index() - @Column('boolean', { - default: true, - comment: 'Whether the User is explorable.', - }) - public isExplorable: boolean; - - // アカウントが削除されたかどうかのフラグだが、完全に削除される際は物理削除なので実質削除されるまでの「削除が進行しているかどうか」のフラグ - @Column('boolean', { - default: false, - comment: 'Whether the User is deleted.', - }) - public isDeleted: boolean; - - @Column('varchar', { - length: 128, array: true, default: '{}', - }) - public emojis: string[]; - - @Index() - @Column('varchar', { - length: 128, nullable: true, - comment: 'The host of the User. It will be null if the origin of the user is local.', - }) - public host: string | null; - - @Column('varchar', { - length: 512, nullable: true, - comment: 'The inbox URL of the User. It will be null if the origin of the user is local.', - }) - public inbox: string | null; - - @Column('varchar', { - length: 512, nullable: true, - comment: 'The sharedInbox URL of the User. It will be null if the origin of the user is local.', - }) - public sharedInbox: string | null; - - @Column('varchar', { - length: 512, nullable: true, - comment: 'The featured URL of the User. It will be null if the origin of the user is local.', - }) - public featured: string | null; - - @Index() - @Column('varchar', { - length: 512, nullable: true, - comment: 'The URI of the User. It will be null if the origin of the user is local.', - }) - public uri: string | null; - - @Column('varchar', { - length: 512, nullable: true, - comment: 'The URI of the user Follower Collection. It will be null if the origin of the user is local.', - }) - public followersUri: string | null; - - @Column('boolean', { - default: false, - comment: 'Whether to show users replying to other users in the timeline.', - }) - public showTimelineReplies: boolean; - - @Index({ unique: true }) - @Column('char', { - length: 16, nullable: true, unique: true, - comment: 'The native access token of the User. It will be null if the origin of the user is local.', - }) - public token: string | null; - - @Column('integer', { - nullable: true, - comment: 'Overrides user drive capacity limit', - }) - public driveCapacityOverrideMb: number | null; - - constructor(data: Partial) { - if (data == null) return; - - for (const [k, v] of Object.entries(data)) { - (this as any)[k] = v; - } - } -} - -export interface ILocalUser extends User { - host: null; -} - -export interface IRemoteUser extends User { - host: string; -} - -export type CacheableLocalUser = ILocalUser; - -export type CacheableRemoteUser = IRemoteUser; - -export type CacheableUser = CacheableLocalUser | CacheableRemoteUser; - -export const localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const; -export const passwordSchema = { type: 'string', minLength: 1 } as const; -export const nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const; -export const descriptionSchema = { type: 'string', minLength: 1, maxLength: 500 } as const; -export const locationSchema = { type: 'string', minLength: 1, maxLength: 50 } as const; -export const birthdaySchema = { type: 'string', pattern: /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.toString().slice(1, -1) } as const; diff --git a/packages/backend/src/models/entities/webhook.ts b/packages/backend/src/models/entities/webhook.ts deleted file mode 100644 index 56b411f87965..000000000000 --- a/packages/backend/src/models/entities/webhook.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { id } from '../id.js'; - -export const webhookEventTypes = ['mention', 'unfollow', 'follow', 'followed', 'note', 'reply', 'renote', 'reaction'] as const; - -@Entity() -export class Webhook { - @PrimaryColumn(id()) - public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the Antenna.', - }) - public createdAt: Date; - - @Index() - @Column({ - ...id(), - comment: 'The owner ID.', - }) - public userId: User['id']; - - @ManyToOne(type => User, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public user: User | null; - - @Column('varchar', { - length: 128, - comment: 'The name of the Antenna.', - }) - public name: string; - - @Index() - @Column('varchar', { - length: 128, array: true, default: '{}', - }) - public on: (typeof webhookEventTypes)[number][]; - - @Column('varchar', { - length: 1024, - }) - public url: string; - - @Column('varchar', { - length: 1024, - }) - public secret: string; - - @Index() - @Column('boolean', { - default: true, - }) - public active: boolean; - - /** - * 直近のリクエスト送信日時 - */ - @Column('timestamp with time zone', { - nullable: true, - }) - public latestSentAt: Date | null; - - /** - * 直近のリクエスト送信時のHTTPステータスコード - */ - @Column('integer', { - nullable: true, - }) - public latestStatus: number | null; -} From 7819cdf244fd2854d7982c5085f34daa4c0868f9 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 02:14:55 +0900 Subject: [PATCH 132/180] a --- .../src/models/entities/AbuseUserReport.ts | 79 +++ .../src/models/entities/AccessToken.ts | 90 ++++ packages/backend/src/models/entities/Ad.ts | 59 +++ .../src/models/entities/Announcement.ts | 43 ++ .../src/models/entities/AnnouncementRead.ts | 36 ++ .../backend/src/models/entities/Antenna.ts | 99 ++++ .../src/models/entities/AntennaNote.ts | 43 ++ packages/backend/src/models/entities/App.ts | 60 +++ .../models/entities/AttestationChallenge.ts | 46 ++ .../src/models/entities/AuthSession.ts | 43 ++ .../backend/src/models/entities/Blocking.ts | 42 ++ .../backend/src/models/entities/Channel.ts | 75 +++ .../src/models/entities/ChannelFollowing.ts | 43 ++ .../src/models/entities/ChannelNotePining.ts | 35 ++ packages/backend/src/models/entities/Clip.ts | 44 ++ .../backend/src/models/entities/ClipNote.ts | 37 ++ .../backend/src/models/entities/DriveFile.ts | 192 ++++++++ .../src/models/entities/DriveFolder.ts | 49 ++ packages/backend/src/models/entities/Emoji.ts | 58 +++ .../src/models/entities/FollowRequest.ts | 85 ++++ .../backend/src/models/entities/Following.ts | 82 ++++ .../src/models/entities/GalleryLike.ts | 33 ++ .../src/models/entities/GalleryPost.ts | 79 +++ .../backend/src/models/entities/Hashtag.ts | 87 ++++ .../backend/src/models/entities/Instance.ts | 164 +++++++ .../src/models/entities/MessagingMessage.ts | 89 ++++ packages/backend/src/models/entities/Meta.ts | 462 ++++++++++++++++++ .../src/models/entities/ModerationLog.ts | 32 ++ .../backend/src/models/entities/MutedNote.ts | 48 ++ .../backend/src/models/entities/Muting.ts | 48 ++ packages/backend/src/models/entities/Note.ts | 242 +++++++++ .../src/models/entities/NoteFavorite.ts | 35 ++ .../src/models/entities/NoteReaction.ts | 44 ++ .../src/models/entities/NoteThreadMuting.ts | 33 ++ .../backend/src/models/entities/NoteUnread.ts | 63 +++ .../src/models/entities/Notification.ts | 173 +++++++ packages/backend/src/models/entities/Page.ts | 121 +++++ .../backend/src/models/entities/PageLike.ts | 33 ++ .../models/entities/PasswordResetRequest.ts | 30 ++ packages/backend/src/models/entities/Poll.ts | 72 +++ .../backend/src/models/entities/PollVote.ts | 40 ++ .../backend/src/models/entities/PromoNote.ts | 28 ++ .../backend/src/models/entities/PromoRead.ts | 35 ++ .../models/entities/RegistrationTickets.ts | 17 + .../src/models/entities/RegistryItem.ts | 58 +++ packages/backend/src/models/entities/Relay.ts | 19 + .../backend/src/models/entities/Signin.ts | 35 ++ .../src/models/entities/SwSubscription.ts | 37 ++ .../src/models/entities/UsedUsername.ts | 20 + packages/backend/src/models/entities/User.ts | 255 ++++++++++ .../backend/src/models/entities/UserGroup.ts | 46 ++ .../models/entities/UserGroupInvitation.ts | 42 ++ .../src/models/entities/UserGroupJoining.ts | 42 ++ .../backend/src/models/entities/UserIp.ts | 24 + .../src/models/entities/UserKeypair.ts | 33 ++ .../backend/src/models/entities/UserList.ts | 33 ++ .../src/models/entities/UserListJoining.ts | 42 ++ .../src/models/entities/UserNotePining.ts | 35 ++ .../src/models/entities/UserPending.ts | 32 ++ .../src/models/entities/UserProfile.ts | 232 +++++++++ .../src/models/entities/UserPublickey.ts | 34 ++ .../src/models/entities/UserSecurityKey.ts | 48 ++ .../backend/src/models/entities/Webhook.ts | 73 +++ 63 files changed, 4488 insertions(+) create mode 100644 packages/backend/src/models/entities/AbuseUserReport.ts create mode 100644 packages/backend/src/models/entities/AccessToken.ts create mode 100644 packages/backend/src/models/entities/Ad.ts create mode 100644 packages/backend/src/models/entities/Announcement.ts create mode 100644 packages/backend/src/models/entities/AnnouncementRead.ts create mode 100644 packages/backend/src/models/entities/Antenna.ts create mode 100644 packages/backend/src/models/entities/AntennaNote.ts create mode 100644 packages/backend/src/models/entities/App.ts create mode 100644 packages/backend/src/models/entities/AttestationChallenge.ts create mode 100644 packages/backend/src/models/entities/AuthSession.ts create mode 100644 packages/backend/src/models/entities/Blocking.ts create mode 100644 packages/backend/src/models/entities/Channel.ts create mode 100644 packages/backend/src/models/entities/ChannelFollowing.ts create mode 100644 packages/backend/src/models/entities/ChannelNotePining.ts create mode 100644 packages/backend/src/models/entities/Clip.ts create mode 100644 packages/backend/src/models/entities/ClipNote.ts create mode 100644 packages/backend/src/models/entities/DriveFile.ts create mode 100644 packages/backend/src/models/entities/DriveFolder.ts create mode 100644 packages/backend/src/models/entities/Emoji.ts create mode 100644 packages/backend/src/models/entities/FollowRequest.ts create mode 100644 packages/backend/src/models/entities/Following.ts create mode 100644 packages/backend/src/models/entities/GalleryLike.ts create mode 100644 packages/backend/src/models/entities/GalleryPost.ts create mode 100644 packages/backend/src/models/entities/Hashtag.ts create mode 100644 packages/backend/src/models/entities/Instance.ts create mode 100644 packages/backend/src/models/entities/MessagingMessage.ts create mode 100644 packages/backend/src/models/entities/Meta.ts create mode 100644 packages/backend/src/models/entities/ModerationLog.ts create mode 100644 packages/backend/src/models/entities/MutedNote.ts create mode 100644 packages/backend/src/models/entities/Muting.ts create mode 100644 packages/backend/src/models/entities/Note.ts create mode 100644 packages/backend/src/models/entities/NoteFavorite.ts create mode 100644 packages/backend/src/models/entities/NoteReaction.ts create mode 100644 packages/backend/src/models/entities/NoteThreadMuting.ts create mode 100644 packages/backend/src/models/entities/NoteUnread.ts create mode 100644 packages/backend/src/models/entities/Notification.ts create mode 100644 packages/backend/src/models/entities/Page.ts create mode 100644 packages/backend/src/models/entities/PageLike.ts create mode 100644 packages/backend/src/models/entities/PasswordResetRequest.ts create mode 100644 packages/backend/src/models/entities/Poll.ts create mode 100644 packages/backend/src/models/entities/PollVote.ts create mode 100644 packages/backend/src/models/entities/PromoNote.ts create mode 100644 packages/backend/src/models/entities/PromoRead.ts create mode 100644 packages/backend/src/models/entities/RegistrationTickets.ts create mode 100644 packages/backend/src/models/entities/RegistryItem.ts create mode 100644 packages/backend/src/models/entities/Relay.ts create mode 100644 packages/backend/src/models/entities/Signin.ts create mode 100644 packages/backend/src/models/entities/SwSubscription.ts create mode 100644 packages/backend/src/models/entities/UsedUsername.ts create mode 100644 packages/backend/src/models/entities/User.ts create mode 100644 packages/backend/src/models/entities/UserGroup.ts create mode 100644 packages/backend/src/models/entities/UserGroupInvitation.ts create mode 100644 packages/backend/src/models/entities/UserGroupJoining.ts create mode 100644 packages/backend/src/models/entities/UserIp.ts create mode 100644 packages/backend/src/models/entities/UserKeypair.ts create mode 100644 packages/backend/src/models/entities/UserList.ts create mode 100644 packages/backend/src/models/entities/UserListJoining.ts create mode 100644 packages/backend/src/models/entities/UserNotePining.ts create mode 100644 packages/backend/src/models/entities/UserPending.ts create mode 100644 packages/backend/src/models/entities/UserProfile.ts create mode 100644 packages/backend/src/models/entities/UserPublickey.ts create mode 100644 packages/backend/src/models/entities/UserSecurityKey.ts create mode 100644 packages/backend/src/models/entities/Webhook.ts diff --git a/packages/backend/src/models/entities/AbuseUserReport.ts b/packages/backend/src/models/entities/AbuseUserReport.ts new file mode 100644 index 000000000000..6ac5635528de --- /dev/null +++ b/packages/backend/src/models/entities/AbuseUserReport.ts @@ -0,0 +1,79 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +export class AbuseUserReport { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the AbuseUserReport.', + }) + public createdAt: Date; + + @Index() + @Column(id()) + public targetUserId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public targetUser: User | null; + + @Index() + @Column(id()) + public reporterId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public reporter: User | null; + + @Column({ + ...id(), + nullable: true, + }) + public assigneeId: User['id'] | null; + + @ManyToOne(type => User, { + onDelete: 'SET NULL', + }) + @JoinColumn() + public assignee: User | null; + + @Index() + @Column('boolean', { + default: false, + }) + public resolved: boolean; + + @Column('boolean', { + default: false + }) + public forwarded: boolean; + + @Column('varchar', { + length: 2048, + }) + public comment: string; + + //#region Denormalized fields + @Index() + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]', + }) + public targetUserHost: string | null; + + @Index() + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]', + }) + public reporterHost: string | null; + //#endregion +} diff --git a/packages/backend/src/models/entities/AccessToken.ts b/packages/backend/src/models/entities/AccessToken.ts new file mode 100644 index 000000000000..c6e2141a46dc --- /dev/null +++ b/packages/backend/src/models/entities/AccessToken.ts @@ -0,0 +1,90 @@ +import { Entity, PrimaryColumn, Index, Column, ManyToOne, JoinColumn } from 'typeorm'; +import { User } from './user.js'; +import { App } from './app.js'; +import { id } from '../id.js'; + +@Entity() +export class AccessToken { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the AccessToken.', + }) + public createdAt: Date; + + @Column('timestamp with time zone', { + nullable: true, + }) + public lastUsedAt: Date | null; + + @Index() + @Column('varchar', { + length: 128, + }) + public token: string; + + @Index() + @Column('varchar', { + length: 128, + nullable: true, + }) + public session: string | null; + + @Index() + @Column('varchar', { + length: 128, + }) + public hash: string; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column({ + ...id(), + nullable: true, + }) + public appId: App['id'] | null; + + @ManyToOne(type => App, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public app: App | null; + + @Column('varchar', { + length: 128, + nullable: true, + }) + public name: string | null; + + @Column('varchar', { + length: 512, + nullable: true, + }) + public description: string | null; + + @Column('varchar', { + length: 512, + nullable: true, + }) + public iconUrl: string | null; + + @Column('varchar', { + length: 64, array: true, + default: '{}', + }) + public permission: string[]; + + @Column('boolean', { + default: false, + }) + public fetched: boolean; +} diff --git a/packages/backend/src/models/entities/Ad.ts b/packages/backend/src/models/entities/Ad.ts new file mode 100644 index 000000000000..36b758f205b6 --- /dev/null +++ b/packages/backend/src/models/entities/Ad.ts @@ -0,0 +1,59 @@ +import { Entity, Index, Column, PrimaryColumn } from 'typeorm'; +import { id } from '../id.js'; + +@Entity() +export class Ad { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the Ad.', + }) + public createdAt: Date; + + @Index() + @Column('timestamp with time zone', { + comment: 'The expired date of the Ad.', + }) + public expiresAt: Date; + + @Column('varchar', { + length: 32, nullable: false, + }) + public place: string; + + // 今は使われていないが将来的に活用される可能性はある + @Column('varchar', { + length: 32, nullable: false, + }) + public priority: string; + + @Column('integer', { + default: 1, nullable: false, + }) + public ratio: number; + + @Column('varchar', { + length: 1024, nullable: false, + }) + public url: string; + + @Column('varchar', { + length: 1024, nullable: false, + }) + public imageUrl: string; + + @Column('varchar', { + length: 8192, nullable: false, + }) + public memo: string; + + constructor(data: Partial) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} diff --git a/packages/backend/src/models/entities/Announcement.ts b/packages/backend/src/models/entities/Announcement.ts new file mode 100644 index 000000000000..beb2f82462a6 --- /dev/null +++ b/packages/backend/src/models/entities/Announcement.ts @@ -0,0 +1,43 @@ +import { Entity, Index, Column, PrimaryColumn } from 'typeorm'; +import { id } from '../id.js'; + +@Entity() +export class Announcement { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the Announcement.', + }) + public createdAt: Date; + + @Column('timestamp with time zone', { + comment: 'The updated date of the Announcement.', + nullable: true, + }) + public updatedAt: Date | null; + + @Column('varchar', { + length: 8192, nullable: false, + }) + public text: string; + + @Column('varchar', { + length: 256, nullable: false, + }) + public title: string; + + @Column('varchar', { + length: 1024, nullable: true, + }) + public imageUrl: string | null; + + constructor(data: Partial) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} diff --git a/packages/backend/src/models/entities/AnnouncementRead.ts b/packages/backend/src/models/entities/AnnouncementRead.ts new file mode 100644 index 000000000000..e4d256a8642e --- /dev/null +++ b/packages/backend/src/models/entities/AnnouncementRead.ts @@ -0,0 +1,36 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { Announcement } from './announcement.js'; +import { id } from '../id.js'; + +@Entity() +@Index(['userId', 'announcementId'], { unique: true }) +export class AnnouncementRead { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the AnnouncementRead.', + }) + public createdAt: Date; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Index() + @Column(id()) + public announcementId: Announcement['id']; + + @ManyToOne(type => Announcement, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public announcement: Announcement | null; +} diff --git a/packages/backend/src/models/entities/Antenna.ts b/packages/backend/src/models/entities/Antenna.ts new file mode 100644 index 000000000000..6c8bb13e5067 --- /dev/null +++ b/packages/backend/src/models/entities/Antenna.ts @@ -0,0 +1,99 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; +import { UserList } from './user-list.js'; +import { UserGroupJoining } from './user-group-joining.js'; + +@Entity() +export class Antenna { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the Antenna.', + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + comment: 'The owner ID.', + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column('varchar', { + length: 128, + comment: 'The name of the Antenna.', + }) + public name: string; + + @Column('enum', { enum: ['home', 'all', 'users', 'list', 'group'] }) + public src: 'home' | 'all' | 'users' | 'list' | 'group'; + + @Column({ + ...id(), + nullable: true, + }) + public userListId: UserList['id'] | null; + + @ManyToOne(type => UserList, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public userList: UserList | null; + + @Column({ + ...id(), + nullable: true, + }) + public userGroupJoiningId: UserGroupJoining['id'] | null; + + @ManyToOne(type => UserGroupJoining, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public userGroupJoining: UserGroupJoining | null; + + @Column('varchar', { + length: 1024, array: true, + default: '{}', + }) + public users: string[]; + + @Column('jsonb', { + default: [], + }) + public keywords: string[][]; + + @Column('jsonb', { + default: [], + }) + public excludeKeywords: string[][]; + + @Column('boolean', { + default: false, + }) + public caseSensitive: boolean; + + @Column('boolean', { + default: false, + }) + public withReplies: boolean; + + @Column('boolean') + public withFile: boolean; + + @Column('varchar', { + length: 2048, nullable: true, + }) + public expression: string | null; + + @Column('boolean') + public notify: boolean; +} diff --git a/packages/backend/src/models/entities/AntennaNote.ts b/packages/backend/src/models/entities/AntennaNote.ts new file mode 100644 index 000000000000..fcca493fe071 --- /dev/null +++ b/packages/backend/src/models/entities/AntennaNote.ts @@ -0,0 +1,43 @@ +import { Entity, Index, JoinColumn, Column, ManyToOne, PrimaryColumn } from 'typeorm'; +import { Note } from './note.js'; +import { Antenna } from './antenna.js'; +import { id } from '../id.js'; + +@Entity() +@Index(['noteId', 'antennaId'], { unique: true }) +export class AntennaNote { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column({ + ...id(), + comment: 'The note ID.', + }) + public noteId: Note['id']; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public note: Note | null; + + @Index() + @Column({ + ...id(), + comment: 'The antenna ID.', + }) + public antennaId: Antenna['id']; + + @ManyToOne(type => Antenna, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public antenna: Antenna | null; + + @Index() + @Column('boolean', { + default: false, + }) + public read: boolean; +} diff --git a/packages/backend/src/models/entities/App.ts b/packages/backend/src/models/entities/App.ts new file mode 100644 index 000000000000..46c11548a527 --- /dev/null +++ b/packages/backend/src/models/entities/App.ts @@ -0,0 +1,60 @@ +import { Entity, PrimaryColumn, Column, Index, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +export class App { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the App.', + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + nullable: true, + comment: 'The owner ID.', + }) + public userId: User['id'] | null; + + @ManyToOne(type => User, { + onDelete: 'SET NULL', + nullable: true, + }) + public user: User | null; + + @Index() + @Column('varchar', { + length: 64, + comment: 'The secret key of the App.', + }) + public secret: string; + + @Column('varchar', { + length: 128, + comment: 'The name of the App.', + }) + public name: string; + + @Column('varchar', { + length: 512, + comment: 'The description of the App.', + }) + public description: string; + + @Column('varchar', { + length: 64, array: true, + comment: 'The permission of the App.', + }) + public permission: string[]; + + @Column('varchar', { + length: 512, nullable: true, + comment: 'The callbackUrl of the App.', + }) + public callbackUrl: string | null; +} diff --git a/packages/backend/src/models/entities/AttestationChallenge.ts b/packages/backend/src/models/entities/AttestationChallenge.ts new file mode 100644 index 000000000000..c40df23293bb --- /dev/null +++ b/packages/backend/src/models/entities/AttestationChallenge.ts @@ -0,0 +1,46 @@ +import { PrimaryColumn, Entity, JoinColumn, Column, ManyToOne, Index } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +export class AttestationChallenge { + @PrimaryColumn(id()) + public id: string; + + @Index() + @PrimaryColumn(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Index() + @Column('varchar', { + length: 64, + comment: 'Hex-encoded sha256 hash of the challenge.', + }) + public challenge: string; + + @Column('timestamp with time zone', { + comment: 'The date challenge was created for expiry purposes.', + }) + public createdAt: Date; + + @Column('boolean', { + comment: + 'Indicates that the challenge is only for registration purposes if true to prevent the challenge for being used as authentication.', + default: false, + }) + public registrationChallenge: boolean; + + constructor(data: Partial) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} diff --git a/packages/backend/src/models/entities/AuthSession.ts b/packages/backend/src/models/entities/AuthSession.ts new file mode 100644 index 000000000000..295d1b486c4b --- /dev/null +++ b/packages/backend/src/models/entities/AuthSession.ts @@ -0,0 +1,43 @@ +import { Entity, PrimaryColumn, Index, Column, ManyToOne, JoinColumn } from 'typeorm'; +import { User } from './user.js'; +import { App } from './app.js'; +import { id } from '../id.js'; + +@Entity() +export class AuthSession { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the AuthSession.', + }) + public createdAt: Date; + + @Index() + @Column('varchar', { + length: 128, + }) + public token: string; + + @Column({ + ...id(), + nullable: true, + }) + public userId: User['id'] | null; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + nullable: true, + }) + @JoinColumn() + public user: User | null; + + @Column(id()) + public appId: App['id']; + + @ManyToOne(type => App, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public app: App | null; +} diff --git a/packages/backend/src/models/entities/Blocking.ts b/packages/backend/src/models/entities/Blocking.ts new file mode 100644 index 000000000000..4ac73a00b5dd --- /dev/null +++ b/packages/backend/src/models/entities/Blocking.ts @@ -0,0 +1,42 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +@Index(['blockerId', 'blockeeId'], { unique: true }) +export class Blocking { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the Blocking.', + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + comment: 'The blockee user ID.', + }) + public blockeeId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public blockee: User | null; + + @Index() + @Column({ + ...id(), + comment: 'The blocker user ID.', + }) + public blockerId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public blocker: User | null; +} diff --git a/packages/backend/src/models/entities/Channel.ts b/packages/backend/src/models/entities/Channel.ts new file mode 100644 index 000000000000..abf6668bd285 --- /dev/null +++ b/packages/backend/src/models/entities/Channel.ts @@ -0,0 +1,75 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; +import { DriveFile } from './drive-file.js'; + +@Entity() +export class Channel { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the Channel.', + }) + public createdAt: Date; + + @Index() + @Column('timestamp with time zone', { + nullable: true, + }) + public lastNotedAt: Date | null; + + @Index() + @Column({ + ...id(), + nullable: true, + comment: 'The owner ID.', + }) + public userId: User['id'] | null; + + @ManyToOne(type => User, { + onDelete: 'SET NULL', + }) + @JoinColumn() + public user: User | null; + + @Column('varchar', { + length: 128, + comment: 'The name of the Channel.', + }) + public name: string; + + @Column('varchar', { + length: 2048, nullable: true, + comment: 'The description of the Channel.', + }) + public description: string | null; + + @Column({ + ...id(), + nullable: true, + comment: 'The ID of banner Channel.', + }) + public bannerId: DriveFile['id'] | null; + + @ManyToOne(type => DriveFile, { + onDelete: 'SET NULL', + }) + @JoinColumn() + public banner: DriveFile | null; + + @Index() + @Column('integer', { + default: 0, + comment: 'The count of notes.', + }) + public notesCount: number; + + @Index() + @Column('integer', { + default: 0, + comment: 'The count of users.', + }) + public usersCount: number; +} diff --git a/packages/backend/src/models/entities/ChannelFollowing.ts b/packages/backend/src/models/entities/ChannelFollowing.ts new file mode 100644 index 000000000000..029dd6cf1a07 --- /dev/null +++ b/packages/backend/src/models/entities/ChannelFollowing.ts @@ -0,0 +1,43 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; +import { Channel } from './channel.js'; + +@Entity() +@Index(['followerId', 'followeeId'], { unique: true }) +export class ChannelFollowing { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the ChannelFollowing.', + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + comment: 'The followee channel ID.', + }) + public followeeId: Channel['id']; + + @ManyToOne(type => Channel, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public followee: Channel | null; + + @Index() + @Column({ + ...id(), + comment: 'The follower user ID.', + }) + public followerId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public follower: User | null; +} diff --git a/packages/backend/src/models/entities/ChannelNotePining.ts b/packages/backend/src/models/entities/ChannelNotePining.ts new file mode 100644 index 000000000000..23be3b69d46b --- /dev/null +++ b/packages/backend/src/models/entities/ChannelNotePining.ts @@ -0,0 +1,35 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { Note } from './note.js'; +import { Channel } from './channel.js'; +import { id } from '../id.js'; + +@Entity() +@Index(['channelId', 'noteId'], { unique: true }) +export class ChannelNotePining { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the ChannelNotePining.', + }) + public createdAt: Date; + + @Index() + @Column(id()) + public channelId: Channel['id']; + + @ManyToOne(type => Channel, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public channel: Channel | null; + + @Column(id()) + public noteId: Note['id']; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public note: Note | null; +} diff --git a/packages/backend/src/models/entities/Clip.ts b/packages/backend/src/models/entities/Clip.ts new file mode 100644 index 000000000000..1386684c32c9 --- /dev/null +++ b/packages/backend/src/models/entities/Clip.ts @@ -0,0 +1,44 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +export class Clip { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the Clip.', + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + comment: 'The owner ID.', + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column('varchar', { + length: 128, + comment: 'The name of the Clip.', + }) + public name: string; + + @Column('boolean', { + default: false, + }) + public isPublic: boolean; + + @Column('varchar', { + length: 2048, nullable: true, + comment: 'The description of the Clip.', + }) + public description: string | null; +} diff --git a/packages/backend/src/models/entities/ClipNote.ts b/packages/backend/src/models/entities/ClipNote.ts new file mode 100644 index 000000000000..6f3688550830 --- /dev/null +++ b/packages/backend/src/models/entities/ClipNote.ts @@ -0,0 +1,37 @@ +import { Entity, Index, JoinColumn, Column, ManyToOne, PrimaryColumn } from 'typeorm'; +import { Note } from './note.js'; +import { Clip } from './clip.js'; +import { id } from '../id.js'; + +@Entity() +@Index(['noteId', 'clipId'], { unique: true }) +export class ClipNote { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column({ + ...id(), + comment: 'The note ID.', + }) + public noteId: Note['id']; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public note: Note | null; + + @Index() + @Column({ + ...id(), + comment: 'The clip ID.', + }) + public clipId: Clip['id']; + + @ManyToOne(type => Clip, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public clip: Clip | null; +} diff --git a/packages/backend/src/models/entities/DriveFile.ts b/packages/backend/src/models/entities/DriveFile.ts new file mode 100644 index 000000000000..d410b1d429dc --- /dev/null +++ b/packages/backend/src/models/entities/DriveFile.ts @@ -0,0 +1,192 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { id } from '../id.js'; +import { User } from './user.js'; +import { DriveFolder } from './drive-folder.js'; + +@Entity() +@Index(['userId', 'folderId', 'id']) +export class DriveFile { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the DriveFile.', + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + nullable: true, + comment: 'The owner ID.', + }) + public userId: User['id'] | null; + + @ManyToOne(type => User, { + onDelete: 'SET NULL', + }) + @JoinColumn() + public user: User | null; + + @Index() + @Column('varchar', { + length: 128, nullable: true, + comment: 'The host of owner. It will be null if the user in local.', + }) + public userHost: string | null; + + @Index() + @Column('varchar', { + length: 32, + comment: 'The MD5 hash of the DriveFile.', + }) + public md5: string; + + @Column('varchar', { + length: 256, + comment: 'The file name of the DriveFile.', + }) + public name: string; + + @Index() + @Column('varchar', { + length: 128, + comment: 'The content type (MIME) of the DriveFile.', + }) + public type: string; + + @Column('integer', { + comment: 'The file size (bytes) of the DriveFile.', + }) + public size: number; + + @Column('varchar', { + length: 512, nullable: true, + comment: 'The comment of the DriveFile.', + }) + public comment: string | null; + + @Column('varchar', { + length: 128, nullable: true, + comment: 'The BlurHash string.', + }) + public blurhash: string | null; + + @Column('jsonb', { + default: {}, + comment: 'The any properties of the DriveFile. For example, it includes image width/height.', + }) + public properties: { width?: number; height?: number; orientation?: number; avgColor?: string }; + + @Column('boolean') + public storedInternal: boolean; + + @Column('varchar', { + length: 512, + comment: 'The URL of the DriveFile.', + }) + public url: string; + + @Column('varchar', { + length: 512, nullable: true, + comment: 'The URL of the thumbnail of the DriveFile.', + }) + public thumbnailUrl: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: 'The URL of the webpublic of the DriveFile.', + }) + public webpublicUrl: string | null; + + @Column('varchar', { + length: 128, nullable: true, + }) + public webpublicType: string | null; + + @Index({ unique: true }) + @Column('varchar', { + length: 256, nullable: true, + }) + public accessKey: string | null; + + @Index({ unique: true }) + @Column('varchar', { + length: 256, nullable: true, + }) + public thumbnailAccessKey: string | null; + + @Index({ unique: true }) + @Column('varchar', { + length: 256, nullable: true, + }) + public webpublicAccessKey: string | null; + + @Index() + @Column('varchar', { + length: 512, nullable: true, + comment: 'The URI of the DriveFile. it will be null when the DriveFile is local.', + }) + public uri: string | null; + + @Column('varchar', { + length: 512, nullable: true, + }) + public src: string | null; + + @Index() + @Column({ + ...id(), + nullable: true, + comment: 'The parent folder ID. If null, it means the DriveFile is located in root.', + }) + public folderId: DriveFolder['id'] | null; + + @ManyToOne(type => DriveFolder, { + onDelete: 'SET NULL', + }) + @JoinColumn() + public folder: DriveFolder | null; + + @Index() + @Column('boolean', { + default: false, + comment: 'Whether the DriveFile is NSFW.', + }) + public isSensitive: boolean; + + @Index() + @Column('boolean', { + default: false, + comment: 'Whether the DriveFile is NSFW. (predict)', + }) + public maybeSensitive: boolean; + + @Index() + @Column('boolean', { + default: false, + }) + public maybePorn: boolean; + + /** + * 外部の(信頼されていない)URLへの直リンクか否か + */ + @Index() + @Column('boolean', { + default: false, + comment: 'Whether the DriveFile is direct link to remote server.', + }) + public isLink: boolean; + + @Column('jsonb', { + default: {}, + nullable: true, + }) + public requestHeaders: Record | null; + + @Column('varchar', { + length: 128, nullable: true, + }) + public requestIp: string | null; +} diff --git a/packages/backend/src/models/entities/DriveFolder.ts b/packages/backend/src/models/entities/DriveFolder.ts new file mode 100644 index 000000000000..d4022c6ebc14 --- /dev/null +++ b/packages/backend/src/models/entities/DriveFolder.ts @@ -0,0 +1,49 @@ +import { JoinColumn, ManyToOne, Entity, PrimaryColumn, Index, Column } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +export class DriveFolder { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the DriveFolder.', + }) + public createdAt: Date; + + @Column('varchar', { + length: 128, + comment: 'The name of the DriveFolder.', + }) + public name: string; + + @Index() + @Column({ + ...id(), + nullable: true, + comment: 'The owner ID.', + }) + public userId: User['id'] | null; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Index() + @Column({ + ...id(), + nullable: true, + comment: 'The parent folder ID. If null, it means the DriveFolder is located in root.', + }) + public parentId: DriveFolder['id'] | null; + + @ManyToOne(type => DriveFolder, { + onDelete: 'SET NULL', + }) + @JoinColumn() + public parent: DriveFolder | null; +} diff --git a/packages/backend/src/models/entities/Emoji.ts b/packages/backend/src/models/entities/Emoji.ts new file mode 100644 index 000000000000..7332dd1857a8 --- /dev/null +++ b/packages/backend/src/models/entities/Emoji.ts @@ -0,0 +1,58 @@ +import { PrimaryColumn, Entity, Index, Column } from 'typeorm'; +import { id } from '../id.js'; + +@Entity() +@Index(['name', 'host'], { unique: true }) +export class Emoji { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + nullable: true, + }) + public updatedAt: Date | null; + + @Index() + @Column('varchar', { + length: 128, + }) + public name: string; + + @Index() + @Column('varchar', { + length: 128, nullable: true, + }) + public host: string | null; + + @Column('varchar', { + length: 128, nullable: true, + }) + public category: string | null; + + @Column('varchar', { + length: 512, + }) + public originalUrl: string; + + @Column('varchar', { + length: 512, + default: '', + }) + public publicUrl: string; + + @Column('varchar', { + length: 512, nullable: true, + }) + public uri: string | null; + + // publicUrlの方のtypeが入る + @Column('varchar', { + length: 64, nullable: true, + }) + public type: string | null; + + @Column('varchar', { + array: true, length: 128, default: '{}', + }) + public aliases: string[]; +} diff --git a/packages/backend/src/models/entities/FollowRequest.ts b/packages/backend/src/models/entities/FollowRequest.ts new file mode 100644 index 000000000000..89946f6d35df --- /dev/null +++ b/packages/backend/src/models/entities/FollowRequest.ts @@ -0,0 +1,85 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +@Index(['followerId', 'followeeId'], { unique: true }) +export class FollowRequest { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the FollowRequest.', + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + comment: 'The followee user ID.', + }) + public followeeId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public followee: User | null; + + @Index() + @Column({ + ...id(), + comment: 'The follower user ID.', + }) + public followerId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public follower: User | null; + + @Column('varchar', { + length: 128, nullable: true, + comment: 'id of Follow Activity.', + }) + public requestId: string | null; + + //#region Denormalized fields + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]', + }) + public followerHost: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: '[Denormalized]', + }) + public followerInbox: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: '[Denormalized]', + }) + public followerSharedInbox: string | null; + + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]', + }) + public followeeHost: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: '[Denormalized]', + }) + public followeeInbox: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: '[Denormalized]', + }) + public followeeSharedInbox: string | null; + //#endregion +} diff --git a/packages/backend/src/models/entities/Following.ts b/packages/backend/src/models/entities/Following.ts new file mode 100644 index 000000000000..b283ca7e8af8 --- /dev/null +++ b/packages/backend/src/models/entities/Following.ts @@ -0,0 +1,82 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +@Index(['followerId', 'followeeId'], { unique: true }) +export class Following { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the Following.', + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + comment: 'The followee user ID.', + }) + public followeeId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public followee: User | null; + + @Index() + @Column({ + ...id(), + comment: 'The follower user ID.', + }) + public followerId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public follower: User | null; + + //#region Denormalized fields + @Index() + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]', + }) + public followerHost: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: '[Denormalized]', + }) + public followerInbox: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: '[Denormalized]', + }) + public followerSharedInbox: string | null; + + @Index() + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]', + }) + public followeeHost: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: '[Denormalized]', + }) + public followeeInbox: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: '[Denormalized]', + }) + public followeeSharedInbox: string | null; + //#endregion +} diff --git a/packages/backend/src/models/entities/GalleryLike.ts b/packages/backend/src/models/entities/GalleryLike.ts new file mode 100644 index 000000000000..4ce166d194fc --- /dev/null +++ b/packages/backend/src/models/entities/GalleryLike.ts @@ -0,0 +1,33 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; +import { GalleryPost } from './gallery-post.js'; + +@Entity() +@Index(['userId', 'postId'], { unique: true }) +export class GalleryLike { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone') + public createdAt: Date; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column(id()) + public postId: GalleryPost['id']; + + @ManyToOne(type => GalleryPost, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public post: GalleryPost | null; +} diff --git a/packages/backend/src/models/entities/GalleryPost.ts b/packages/backend/src/models/entities/GalleryPost.ts new file mode 100644 index 000000000000..774cb946e970 --- /dev/null +++ b/packages/backend/src/models/entities/GalleryPost.ts @@ -0,0 +1,79 @@ +import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; +import { DriveFile } from './drive-file.js'; + +@Entity() +export class GalleryPost { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the GalleryPost.', + }) + public createdAt: Date; + + @Index() + @Column('timestamp with time zone', { + comment: 'The updated date of the GalleryPost.', + }) + public updatedAt: Date; + + @Column('varchar', { + length: 256, + }) + public title: string; + + @Column('varchar', { + length: 2048, nullable: true, + }) + public description: string | null; + + @Index() + @Column({ + ...id(), + comment: 'The ID of author.', + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Index() + @Column({ + ...id(), + array: true, default: '{}', + }) + public fileIds: DriveFile['id'][]; + + @Index() + @Column('boolean', { + default: false, + comment: 'Whether the post is sensitive.', + }) + public isSensitive: boolean; + + @Index() + @Column('integer', { + default: 0, + }) + public likedCount: number; + + @Index() + @Column('varchar', { + length: 128, array: true, default: '{}', + }) + public tags: string[]; + + constructor(data: Partial) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} diff --git a/packages/backend/src/models/entities/Hashtag.ts b/packages/backend/src/models/entities/Hashtag.ts new file mode 100644 index 000000000000..6bd991f6299a --- /dev/null +++ b/packages/backend/src/models/entities/Hashtag.ts @@ -0,0 +1,87 @@ +import { Entity, PrimaryColumn, Index, Column } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +export class Hashtag { + @PrimaryColumn(id()) + public id: string; + + @Index({ unique: true }) + @Column('varchar', { + length: 128, + }) + public name: string; + + @Column({ + ...id(), + array: true, + }) + public mentionedUserIds: User['id'][]; + + @Index() + @Column('integer', { + default: 0, + }) + public mentionedUsersCount: number; + + @Column({ + ...id(), + array: true, + }) + public mentionedLocalUserIds: User['id'][]; + + @Index() + @Column('integer', { + default: 0, + }) + public mentionedLocalUsersCount: number; + + @Column({ + ...id(), + array: true, + }) + public mentionedRemoteUserIds: User['id'][]; + + @Index() + @Column('integer', { + default: 0, + }) + public mentionedRemoteUsersCount: number; + + @Column({ + ...id(), + array: true, + }) + public attachedUserIds: User['id'][]; + + @Index() + @Column('integer', { + default: 0, + }) + public attachedUsersCount: number; + + @Column({ + ...id(), + array: true, + }) + public attachedLocalUserIds: User['id'][]; + + @Index() + @Column('integer', { + default: 0, + }) + public attachedLocalUsersCount: number; + + @Column({ + ...id(), + array: true, + }) + public attachedRemoteUserIds: User['id'][]; + + @Index() + @Column('integer', { + default: 0, + }) + public attachedRemoteUsersCount: number; +} diff --git a/packages/backend/src/models/entities/Instance.ts b/packages/backend/src/models/entities/Instance.ts new file mode 100644 index 000000000000..7ea9234384e1 --- /dev/null +++ b/packages/backend/src/models/entities/Instance.ts @@ -0,0 +1,164 @@ +import { Entity, PrimaryColumn, Index, Column } from 'typeorm'; +import { id } from '../id.js'; + +@Entity() +export class Instance { + @PrimaryColumn(id()) + public id: string; + + /** + * このインスタンスを捕捉した日時 + */ + @Index() + @Column('timestamp with time zone', { + comment: 'The caught date of the Instance.', + }) + public caughtAt: Date; + + /** + * ホスト + */ + @Index({ unique: true }) + @Column('varchar', { + length: 128, + comment: 'The host of the Instance.', + }) + public host: string; + + /** + * インスタンスのユーザー数 + */ + @Column('integer', { + default: 0, + comment: 'The count of the users of the Instance.', + }) + public usersCount: number; + + /** + * インスタンスの投稿数 + */ + @Column('integer', { + default: 0, + comment: 'The count of the notes of the Instance.', + }) + public notesCount: number; + + /** + * このインスタンスのユーザーからフォローされている、自インスタンスのユーザーの数 + */ + @Column('integer', { + default: 0, + }) + public followingCount: number; + + /** + * このインスタンスのユーザーをフォローしている、自インスタンスのユーザーの数 + */ + @Column('integer', { + default: 0, + }) + public followersCount: number; + + /** + * 直近のリクエスト送信日時 + */ + @Column('timestamp with time zone', { + nullable: true, + }) + public latestRequestSentAt: Date | null; + + /** + * 直近のリクエスト送信時のHTTPステータスコード + */ + @Column('integer', { + nullable: true, + }) + public latestStatus: number | null; + + /** + * 直近のリクエスト受信日時 + */ + @Column('timestamp with time zone', { + nullable: true, + }) + public latestRequestReceivedAt: Date | null; + + /** + * このインスタンスと最後にやり取りした日時 + */ + @Column('timestamp with time zone') + public lastCommunicatedAt: Date; + + /** + * このインスタンスと不通かどうか + */ + @Column('boolean', { + default: false, + }) + public isNotResponding: boolean; + + /** + * このインスタンスへの配信を停止するか + */ + @Index() + @Column('boolean', { + default: false, + }) + public isSuspended: boolean; + + @Column('varchar', { + length: 64, nullable: true, + comment: 'The software of the Instance.', + }) + public softwareName: string | null; + + @Column('varchar', { + length: 64, nullable: true, + }) + public softwareVersion: string | null; + + @Column('boolean', { + nullable: true, + }) + public openRegistrations: boolean | null; + + @Column('varchar', { + length: 256, nullable: true, + }) + public name: string | null; + + @Column('varchar', { + length: 4096, nullable: true, + }) + public description: string | null; + + @Column('varchar', { + length: 128, nullable: true, + }) + public maintainerName: string | null; + + @Column('varchar', { + length: 256, nullable: true, + }) + public maintainerEmail: string | null; + + @Column('varchar', { + length: 256, nullable: true, + }) + public iconUrl: string | null; + + @Column('varchar', { + length: 256, nullable: true, + }) + public faviconUrl: string | null; + + @Column('varchar', { + length: 64, nullable: true, + }) + public themeColor: string | null; + + @Column('timestamp with time zone', { + nullable: true, + }) + public infoUpdatedAt: Date | null; +} diff --git a/packages/backend/src/models/entities/MessagingMessage.ts b/packages/backend/src/models/entities/MessagingMessage.ts new file mode 100644 index 000000000000..099fb7aa013b --- /dev/null +++ b/packages/backend/src/models/entities/MessagingMessage.ts @@ -0,0 +1,89 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { DriveFile } from './drive-file.js'; +import { id } from '../id.js'; +import { UserGroup } from './user-group.js'; + +@Entity() +export class MessagingMessage { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the MessagingMessage.', + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + comment: 'The sender user ID.', + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Index() + @Column({ + ...id(), nullable: true, + comment: 'The recipient user ID.', + }) + public recipientId: User['id'] | null; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public recipient: User | null; + + @Index() + @Column({ + ...id(), nullable: true, + comment: 'The recipient group ID.', + }) + public groupId: UserGroup['id'] | null; + + @ManyToOne(type => UserGroup, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public group: UserGroup | null; + + @Column('varchar', { + length: 4096, nullable: true, + }) + public text: string | null; + + @Column('boolean', { + default: false, + }) + public isRead: boolean; + + @Column('varchar', { + length: 512, nullable: true, + }) + public uri: string | null; + + @Column({ + ...id(), + array: true, default: '{}', + }) + public reads: User['id'][]; + + @Column({ + ...id(), + nullable: true, + }) + public fileId: DriveFile['id'] | null; + + @ManyToOne(type => DriveFile, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public file: DriveFile | null; +} diff --git a/packages/backend/src/models/entities/Meta.ts b/packages/backend/src/models/entities/Meta.ts new file mode 100644 index 000000000000..d33ff2519e69 --- /dev/null +++ b/packages/backend/src/models/entities/Meta.ts @@ -0,0 +1,462 @@ +import { Entity, Column, PrimaryColumn, ManyToOne, JoinColumn } from 'typeorm'; +import { id } from '../id.js'; +import { User } from './user.js'; +import { Clip } from './clip.js'; + +@Entity() +export class Meta { + @PrimaryColumn({ + type: 'varchar', + length: 32, + }) + public id: string; + + @Column('varchar', { + length: 128, nullable: true, + }) + public name: string | null; + + @Column('varchar', { + length: 1024, nullable: true, + }) + public description: string | null; + + /** + * メンテナの名前 + */ + @Column('varchar', { + length: 128, nullable: true, + }) + public maintainerName: string | null; + + /** + * メンテナの連絡先 + */ + @Column('varchar', { + length: 128, nullable: true, + }) + public maintainerEmail: string | null; + + @Column('boolean', { + default: false, + }) + public disableRegistration: boolean; + + @Column('boolean', { + default: false, + }) + public disableLocalTimeline: boolean; + + @Column('boolean', { + default: false, + }) + public disableGlobalTimeline: boolean; + + @Column('boolean', { + default: false, + }) + public useStarForReactionFallback: boolean; + + @Column('varchar', { + length: 64, array: true, default: '{}', + }) + public langs: string[]; + + @Column('varchar', { + length: 256, array: true, default: '{}', + }) + public pinnedUsers: string[]; + + @Column('varchar', { + length: 256, array: true, default: '{}', + }) + public hiddenTags: string[]; + + @Column('varchar', { + length: 256, array: true, default: '{}', + }) + public blockedHosts: string[]; + + @Column('varchar', { + length: 512, array: true, default: '{/featured,/channels,/explore,/pages,/about-misskey}', + }) + public pinnedPages: string[]; + + @Column({ + ...id(), + nullable: true, + }) + public pinnedClipId: Clip['id'] | null; + + @Column('varchar', { + length: 512, + nullable: true, + }) + public themeColor: string | null; + + @Column('varchar', { + length: 512, + nullable: true, + default: '/assets/ai.png', + }) + public mascotImageUrl: string | null; + + @Column('varchar', { + length: 512, + nullable: true, + }) + public bannerUrl: string | null; + + @Column('varchar', { + length: 512, + nullable: true, + }) + public backgroundImageUrl: string | null; + + @Column('varchar', { + length: 512, + nullable: true, + }) + public logoImageUrl: string | null; + + @Column('varchar', { + length: 512, + nullable: true, + default: 'https://xn--931a.moe/aiart/yubitun.png', + }) + public errorImageUrl: string | null; + + @Column('varchar', { + length: 512, + nullable: true, + }) + public iconUrl: string | null; + + @Column('boolean', { + default: true, + }) + public cacheRemoteFiles: boolean; + + @Column({ + ...id(), + nullable: true, + }) + public proxyAccountId: User['id'] | null; + + @ManyToOne(type => User, { + onDelete: 'SET NULL', + }) + @JoinColumn() + public proxyAccount: User | null; + + @Column('boolean', { + default: false, + }) + public emailRequiredForSignup: boolean; + + @Column('boolean', { + default: false, + }) + public enableHcaptcha: boolean; + + @Column('varchar', { + length: 64, + nullable: true, + }) + public hcaptchaSiteKey: string | null; + + @Column('varchar', { + length: 64, + nullable: true, + }) + public hcaptchaSecretKey: string | null; + + @Column('boolean', { + default: false, + }) + public enableRecaptcha: boolean; + + @Column('varchar', { + length: 64, + nullable: true, + }) + public recaptchaSiteKey: string | null; + + @Column('varchar', { + length: 64, + nullable: true, + }) + public recaptchaSecretKey: string | null; + + @Column('enum', { + enum: ['none', 'all', 'local', 'remote'], + default: 'none', + }) + public sensitiveMediaDetection: 'none' | 'all' | 'local' | 'remote'; + + @Column('enum', { + enum: ['medium', 'low', 'high', 'veryLow', 'veryHigh'], + default: 'medium', + }) + public sensitiveMediaDetectionSensitivity: 'medium' | 'low' | 'high' | 'veryLow' | 'veryHigh'; + + @Column('boolean', { + default: false, + }) + public setSensitiveFlagAutomatically: boolean; + + @Column('boolean', { + default: false, + }) + public enableSensitiveMediaDetectionForVideos: boolean; + + @Column('integer', { + default: 1024, + comment: 'Drive capacity of a local user (MB)', + }) + public localDriveCapacityMb: number; + + @Column('integer', { + default: 32, + comment: 'Drive capacity of a remote user (MB)', + }) + public remoteDriveCapacityMb: number; + + @Column('varchar', { + length: 128, + nullable: true, + }) + public summalyProxy: string | null; + + @Column('boolean', { + default: false, + }) + public enableEmail: boolean; + + @Column('varchar', { + length: 128, + nullable: true, + }) + public email: string | null; + + @Column('boolean', { + default: false, + }) + public smtpSecure: boolean; + + @Column('varchar', { + length: 128, + nullable: true, + }) + public smtpHost: string | null; + + @Column('integer', { + nullable: true, + }) + public smtpPort: number | null; + + @Column('varchar', { + length: 128, + nullable: true, + }) + public smtpUser: string | null; + + @Column('varchar', { + length: 128, + nullable: true, + }) + public smtpPass: string | null; + + @Column('boolean', { + default: false, + }) + public enableServiceWorker: boolean; + + @Column('varchar', { + length: 128, + nullable: true, + }) + public swPublicKey: string | null; + + @Column('varchar', { + length: 128, + nullable: true, + }) + public swPrivateKey: string | null; + + @Column('boolean', { + default: false, + }) + public enableTwitterIntegration: boolean; + + @Column('varchar', { + length: 128, + nullable: true, + }) + public twitterConsumerKey: string | null; + + @Column('varchar', { + length: 128, + nullable: true, + }) + public twitterConsumerSecret: string | null; + + @Column('boolean', { + default: false, + }) + public enableGithubIntegration: boolean; + + @Column('varchar', { + length: 128, + nullable: true, + }) + public githubClientId: string | null; + + @Column('varchar', { + length: 128, + nullable: true, + }) + public githubClientSecret: string | null; + + @Column('boolean', { + default: false, + }) + public enableDiscordIntegration: boolean; + + @Column('varchar', { + length: 128, + nullable: true, + }) + public discordClientId: string | null; + + @Column('varchar', { + length: 128, + nullable: true, + }) + public discordClientSecret: string | null; + + @Column('varchar', { + length: 128, + nullable: true, + }) + public deeplAuthKey: string | null; + + @Column('boolean', { + default: false, + }) + public deeplIsPro: boolean; + + @Column('varchar', { + length: 512, + nullable: true, + }) + public ToSUrl: string | null; + + @Column('varchar', { + length: 512, + default: 'https://github.com/misskey-dev/misskey', + nullable: false, + }) + public repositoryUrl: string; + + @Column('varchar', { + length: 512, + default: 'https://github.com/misskey-dev/misskey/issues/new', + nullable: true, + }) + public feedbackUrl: string | null; + + @Column('varchar', { + length: 8192, + nullable: true, + }) + public defaultLightTheme: string | null; + + @Column('varchar', { + length: 8192, + nullable: true, + }) + public defaultDarkTheme: string | null; + + @Column('boolean', { + default: false, + }) + public useObjectStorage: boolean; + + @Column('varchar', { + length: 512, + nullable: true, + }) + public objectStorageBucket: string | null; + + @Column('varchar', { + length: 512, + nullable: true, + }) + public objectStoragePrefix: string | null; + + @Column('varchar', { + length: 512, + nullable: true, + }) + public objectStorageBaseUrl: string | null; + + @Column('varchar', { + length: 512, + nullable: true, + }) + public objectStorageEndpoint: string | null; + + @Column('varchar', { + length: 512, + nullable: true, + }) + public objectStorageRegion: string | null; + + @Column('varchar', { + length: 512, + nullable: true, + }) + public objectStorageAccessKey: string | null; + + @Column('varchar', { + length: 512, + nullable: true, + }) + public objectStorageSecretKey: string | null; + + @Column('integer', { + nullable: true, + }) + public objectStoragePort: number | null; + + @Column('boolean', { + default: true, + }) + public objectStorageUseSSL: boolean; + + @Column('boolean', { + default: true, + }) + public objectStorageUseProxy: boolean; + + @Column('boolean', { + default: false, + }) + public objectStorageSetPublicRead: boolean; + + @Column('boolean', { + default: true, + }) + public objectStorageS3ForcePathStyle: boolean; + + @Column('boolean', { + default: false, + }) + public enableIpLogging: boolean; + + @Column('boolean', { + default: true, + }) + public enableActiveEmailValidation: boolean; +} diff --git a/packages/backend/src/models/entities/ModerationLog.ts b/packages/backend/src/models/entities/ModerationLog.ts new file mode 100644 index 000000000000..c99e55078269 --- /dev/null +++ b/packages/backend/src/models/entities/ModerationLog.ts @@ -0,0 +1,32 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +export class ModerationLog { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the ModerationLog.', + }) + public createdAt: Date; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column('varchar', { + length: 128, + }) + public type: string; + + @Column('jsonb') + public info: Record; +} diff --git a/packages/backend/src/models/entities/MutedNote.ts b/packages/backend/src/models/entities/MutedNote.ts new file mode 100644 index 000000000000..96a4fa8e3336 --- /dev/null +++ b/packages/backend/src/models/entities/MutedNote.ts @@ -0,0 +1,48 @@ +import { Entity, Index, JoinColumn, Column, ManyToOne, PrimaryColumn } from 'typeorm'; +import { Note } from './note.js'; +import { User } from './user.js'; +import { id } from '../id.js'; +import { mutedNoteReasons } from '../../types.js'; + +@Entity() +@Index(['noteId', 'userId'], { unique: true }) +export class MutedNote { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column({ + ...id(), + comment: 'The note ID.', + }) + public noteId: Note['id']; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public note: Note | null; + + @Index() + @Column({ + ...id(), + comment: 'The user ID.', + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + /** + * ミュートされた理由。 + */ + @Index() + @Column('enum', { + enum: mutedNoteReasons, + comment: 'The reason of the MutedNote.', + }) + public reason: typeof mutedNoteReasons[number]; +} diff --git a/packages/backend/src/models/entities/Muting.ts b/packages/backend/src/models/entities/Muting.ts new file mode 100644 index 000000000000..8f9e69063adc --- /dev/null +++ b/packages/backend/src/models/entities/Muting.ts @@ -0,0 +1,48 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +@Index(['muterId', 'muteeId'], { unique: true }) +export class Muting { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the Muting.', + }) + public createdAt: Date; + + @Index() + @Column('timestamp with time zone', { + nullable: true, + }) + public expiresAt: Date | null; + + @Index() + @Column({ + ...id(), + comment: 'The mutee user ID.', + }) + public muteeId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public mutee: User | null; + + @Index() + @Column({ + ...id(), + comment: 'The muter user ID.', + }) + public muterId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public muter: User | null; +} diff --git a/packages/backend/src/models/entities/Note.ts b/packages/backend/src/models/entities/Note.ts new file mode 100644 index 000000000000..0ffeb85f69a8 --- /dev/null +++ b/packages/backend/src/models/entities/Note.ts @@ -0,0 +1,242 @@ +import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { DriveFile } from './drive-file.js'; +import { id } from '../id.js'; +import { noteVisibilities } from '../../types.js'; +import { Channel } from './channel.js'; + +@Entity() +@Index('IDX_NOTE_TAGS', { synchronize: false }) +@Index('IDX_NOTE_MENTIONS', { synchronize: false }) +@Index('IDX_NOTE_VISIBLE_USER_IDS', { synchronize: false }) +export class Note { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the Note.', + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + nullable: true, + comment: 'The ID of reply target.', + }) + public replyId: Note['id'] | null; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public reply: Note | null; + + @Index() + @Column({ + ...id(), + nullable: true, + comment: 'The ID of renote target.', + }) + public renoteId: Note['id'] | null; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public renote: Note | null; + + @Index() + @Column('varchar', { + length: 256, nullable: true, + }) + public threadId: string | null; + + @Column('text', { + nullable: true, + }) + public text: string | null; + + @Column('varchar', { + length: 256, nullable: true, + }) + public name: string | null; + + @Column('varchar', { + length: 512, nullable: true, + }) + public cw: string | null; + + @Index() + @Column({ + ...id(), + comment: 'The ID of author.', + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column('boolean', { + default: false, + }) + public localOnly: boolean; + + @Column('smallint', { + default: 0, + }) + public renoteCount: number; + + @Column('smallint', { + default: 0, + }) + public repliesCount: number; + + @Column('jsonb', { + default: {}, + }) + public reactions: Record; + + /** + * public ... 公開 + * home ... ホームタイムライン(ユーザーページのタイムライン含む)のみに流す + * followers ... フォロワーのみ + * specified ... visibleUserIds で指定したユーザーのみ + */ + @Column('enum', { enum: noteVisibilities }) + public visibility: typeof noteVisibilities[number]; + + @Index({ unique: true }) + @Column('varchar', { + length: 512, nullable: true, + comment: 'The URI of a note. it will be null when the note is local.', + }) + public uri: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: 'The human readable url of a note. it will be null when the note is local.', + }) + public url: string | null; + + @Column('integer', { + default: 0, select: false, + }) + public score: number; + + @Index() + @Column({ + ...id(), + array: true, default: '{}', + }) + public fileIds: DriveFile['id'][]; + + @Index() + @Column('varchar', { + length: 256, array: true, default: '{}', + }) + public attachedFileTypes: string[]; + + @Index() + @Column({ + ...id(), + array: true, default: '{}', + }) + public visibleUserIds: User['id'][]; + + @Index() + @Column({ + ...id(), + array: true, default: '{}', + }) + public mentions: User['id'][]; + + @Column('text', { + default: '[]', + }) + public mentionedRemoteUsers: string; + + @Column('varchar', { + length: 128, array: true, default: '{}', + }) + public emojis: string[]; + + @Index() + @Column('varchar', { + length: 128, array: true, default: '{}', + }) + public tags: string[]; + + @Column('boolean', { + default: false, + }) + public hasPoll: boolean; + + @Index() + @Column({ + ...id(), + nullable: true, + comment: 'The ID of source channel.', + }) + public channelId: Channel['id'] | null; + + @ManyToOne(type => Channel, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public channel: Channel | null; + + //#region Denormalized fields + @Index() + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]', + }) + public userHost: string | null; + + @Column({ + ...id(), + nullable: true, + comment: '[Denormalized]', + }) + public replyUserId: User['id'] | null; + + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]', + }) + public replyUserHost: string | null; + + @Column({ + ...id(), + nullable: true, + comment: '[Denormalized]', + }) + public renoteUserId: User['id'] | null; + + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]', + }) + public renoteUserHost: string | null; + //#endregion + + constructor(data: Partial) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} + +export type IMentionedRemoteUsers = { + uri: string; + url?: string; + username: string; + host: string; +}[]; diff --git a/packages/backend/src/models/entities/NoteFavorite.ts b/packages/backend/src/models/entities/NoteFavorite.ts new file mode 100644 index 000000000000..fe065b77a8eb --- /dev/null +++ b/packages/backend/src/models/entities/NoteFavorite.ts @@ -0,0 +1,35 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { Note } from './note.js'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +@Index(['userId', 'noteId'], { unique: true }) +export class NoteFavorite { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the NoteFavorite.', + }) + public createdAt: Date; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column(id()) + public noteId: Note['id']; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public note: Note | null; +} diff --git a/packages/backend/src/models/entities/NoteReaction.ts b/packages/backend/src/models/entities/NoteReaction.ts new file mode 100644 index 000000000000..d7bc60989813 --- /dev/null +++ b/packages/backend/src/models/entities/NoteReaction.ts @@ -0,0 +1,44 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { Note } from './note.js'; +import { id } from '../id.js'; + +@Entity() +@Index(['userId', 'noteId'], { unique: true }) +export class NoteReaction { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the NoteReaction.', + }) + public createdAt: Date; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user?: User | null; + + @Index() + @Column(id()) + public noteId: Note['id']; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public note?: Note | null; + + // TODO: 対象noteのuserIdを非正規化したい(「受け取ったリアクション一覧」のようなものを(JOIN無しで)実装したいため) + + @Column('varchar', { + length: 260, + }) + public reaction: string; +} diff --git a/packages/backend/src/models/entities/NoteThreadMuting.ts b/packages/backend/src/models/entities/NoteThreadMuting.ts new file mode 100644 index 000000000000..8c5f7bbab426 --- /dev/null +++ b/packages/backend/src/models/entities/NoteThreadMuting.ts @@ -0,0 +1,33 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { Note } from './note.js'; +import { id } from '../id.js'; + +@Entity() +@Index(['userId', 'threadId'], { unique: true }) +export class NoteThreadMuting { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Index() + @Column('varchar', { + length: 256, + }) + public threadId: string; +} diff --git a/packages/backend/src/models/entities/NoteUnread.ts b/packages/backend/src/models/entities/NoteUnread.ts new file mode 100644 index 000000000000..a7acf254d388 --- /dev/null +++ b/packages/backend/src/models/entities/NoteUnread.ts @@ -0,0 +1,63 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { Note } from './note.js'; +import { id } from '../id.js'; +import { Channel } from './channel.js'; + +@Entity() +@Index(['userId', 'noteId'], { unique: true }) +export class NoteUnread { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Index() + @Column(id()) + public noteId: Note['id']; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public note: Note | null; + + /** + * メンションか否か + */ + @Index() + @Column('boolean') + public isMentioned: boolean; + + /** + * ダイレクト投稿か否か + */ + @Index() + @Column('boolean') + public isSpecified: boolean; + + //#region Denormalized fields + @Index() + @Column({ + ...id(), + comment: '[Denormalized]', + }) + public noteUserId: User['id']; + + @Index() + @Column({ + ...id(), + nullable: true, + comment: '[Denormalized]', + }) + public noteChannelId: Channel['id'] | null; + //#endregion +} diff --git a/packages/backend/src/models/entities/Notification.ts b/packages/backend/src/models/entities/Notification.ts new file mode 100644 index 000000000000..db3dba363292 --- /dev/null +++ b/packages/backend/src/models/entities/Notification.ts @@ -0,0 +1,173 @@ +import { Entity, Index, JoinColumn, ManyToOne, Column, PrimaryColumn } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; +import { Note } from './note.js'; +import { FollowRequest } from './follow-request.js'; +import { UserGroupInvitation } from './user-group-invitation.js'; +import { AccessToken } from './access-token.js'; +import { notificationTypes } from '@/types.js'; + +@Entity() +export class Notification { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the Notification.', + }) + public createdAt: Date; + + /** + * 通知の受信者 + */ + @Index() + @Column({ + ...id(), + comment: 'The ID of recipient user of the Notification.', + }) + public notifieeId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public notifiee: User | null; + + /** + * 通知の送信者(initiator) + */ + @Index() + @Column({ + ...id(), + nullable: true, + comment: 'The ID of sender user of the Notification.', + }) + public notifierId: User['id'] | null; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public notifier: User | null; + + /** + * 通知の種類。 + * follow - フォローされた + * mention - 投稿で自分が言及された + * reply - (自分または自分がWatchしている)投稿が返信された + * renote - (自分または自分がWatchしている)投稿がRenoteされた + * quote - (自分または自分がWatchしている)投稿が引用Renoteされた + * reaction - (自分または自分がWatchしている)投稿にリアクションされた + * pollVote - (自分または自分がWatchしている)投稿のアンケートに投票された + * pollEnded - 自分のアンケートもしくは自分が投票したアンケートが終了した + * receiveFollowRequest - フォローリクエストされた + * followRequestAccepted - 自分の送ったフォローリクエストが承認された + * groupInvited - グループに招待された + * app - アプリ通知 + */ + @Index() + @Column('enum', { + enum: notificationTypes, + comment: 'The type of the Notification.', + }) + public type: typeof notificationTypes[number]; + + /** + * 通知が読まれたかどうか + */ + @Index() + @Column('boolean', { + default: false, + comment: 'Whether the Notification is read.', + }) + public isRead: boolean; + + @Column({ + ...id(), + nullable: true, + }) + public noteId: Note['id'] | null; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public note: Note | null; + + @Column({ + ...id(), + nullable: true, + }) + public followRequestId: FollowRequest['id'] | null; + + @ManyToOne(type => FollowRequest, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public followRequest: FollowRequest | null; + + @Column({ + ...id(), + nullable: true, + }) + public userGroupInvitationId: UserGroupInvitation['id'] | null; + + @ManyToOne(type => UserGroupInvitation, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public userGroupInvitation: UserGroupInvitation | null; + + @Column('varchar', { + length: 128, nullable: true, + }) + public reaction: string | null; + + @Column('integer', { + nullable: true, + }) + public choice: number | null; + + /** + * アプリ通知のbody + */ + @Column('varchar', { + length: 2048, nullable: true, + }) + public customBody: string | null; + + /** + * アプリ通知のheader + * (省略時はアプリ名で表示されることを期待) + */ + @Column('varchar', { + length: 256, nullable: true, + }) + public customHeader: string | null; + + /** + * アプリ通知のicon(URL) + * (省略時はアプリアイコンで表示されることを期待) + */ + @Column('varchar', { + length: 1024, nullable: true, + }) + public customIcon: string | null; + + /** + * アプリ通知のアプリ(のトークン) + */ + @Index() + @Column({ + ...id(), + nullable: true, + }) + public appAccessTokenId: AccessToken['id'] | null; + + @ManyToOne(type => AccessToken, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public appAccessToken: AccessToken | null; +} diff --git a/packages/backend/src/models/entities/Page.ts b/packages/backend/src/models/entities/Page.ts new file mode 100644 index 000000000000..baad3a36fa46 --- /dev/null +++ b/packages/backend/src/models/entities/Page.ts @@ -0,0 +1,121 @@ +import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; +import { DriveFile } from './drive-file.js'; + +@Entity() +@Index(['userId', 'name'], { unique: true }) +export class Page { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the Page.', + }) + public createdAt: Date; + + @Index() + @Column('timestamp with time zone', { + comment: 'The updated date of the Page.', + }) + public updatedAt: Date; + + @Column('varchar', { + length: 256, + }) + public title: string; + + @Index() + @Column('varchar', { + length: 256, + }) + public name: string; + + @Column('varchar', { + length: 256, nullable: true, + }) + public summary: string | null; + + @Column('boolean') + public alignCenter: boolean; + + @Column('boolean', { + default: false, + }) + public hideTitleWhenPinned: boolean; + + @Column('varchar', { + length: 32, + }) + public font: string; + + @Index() + @Column({ + ...id(), + comment: 'The ID of author.', + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column({ + ...id(), + nullable: true, + }) + public eyeCatchingImageId: DriveFile['id'] | null; + + @ManyToOne(type => DriveFile, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public eyeCatchingImage: DriveFile | null; + + @Column('jsonb', { + default: [], + }) + public content: Record[]; + + @Column('jsonb', { + default: [], + }) + public variables: Record[]; + + @Column('varchar', { + length: 16384, + default: '', + }) + public script: string; + + /** + * public ... 公開 + * followers ... フォロワーのみ + * specified ... visibleUserIds で指定したユーザーのみ + */ + @Column('enum', { enum: ['public', 'followers', 'specified'] }) + public visibility: 'public' | 'followers' | 'specified'; + + @Index() + @Column({ + ...id(), + array: true, default: '{}', + }) + public visibleUserIds: User['id'][]; + + @Column('integer', { + default: 0, + }) + public likedCount: number; + + constructor(data: Partial) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} diff --git a/packages/backend/src/models/entities/PageLike.ts b/packages/backend/src/models/entities/PageLike.ts new file mode 100644 index 000000000000..17f4ebf520ee --- /dev/null +++ b/packages/backend/src/models/entities/PageLike.ts @@ -0,0 +1,33 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; +import { Page } from './page.js'; + +@Entity() +@Index(['userId', 'pageId'], { unique: true }) +export class PageLike { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone') + public createdAt: Date; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column(id()) + public pageId: Page['id']; + + @ManyToOne(type => Page, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public page: Page | null; +} diff --git a/packages/backend/src/models/entities/PasswordResetRequest.ts b/packages/backend/src/models/entities/PasswordResetRequest.ts new file mode 100644 index 000000000000..05e62cc5ab83 --- /dev/null +++ b/packages/backend/src/models/entities/PasswordResetRequest.ts @@ -0,0 +1,30 @@ +import { PrimaryColumn, Entity, Index, Column, ManyToOne, JoinColumn } from 'typeorm'; +import { id } from '../id.js'; +import { User } from './user.js'; + +@Entity() +export class PasswordResetRequest { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone') + public createdAt: Date; + + @Index({ unique: true }) + @Column('varchar', { + length: 256, + }) + public token: string; + + @Index() + @Column({ + ...id(), + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; +} diff --git a/packages/backend/src/models/entities/Poll.ts b/packages/backend/src/models/entities/Poll.ts new file mode 100644 index 000000000000..83d0873cc50a --- /dev/null +++ b/packages/backend/src/models/entities/Poll.ts @@ -0,0 +1,72 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm'; +import { id } from '../id.js'; +import { Note } from './note.js'; +import { User } from './user.js'; +import { noteVisibilities } from '../../types.js'; + +@Entity() +export class Poll { + @PrimaryColumn(id()) + public noteId: Note['id']; + + @OneToOne(type => Note, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public note: Note | null; + + @Column('timestamp with time zone', { + nullable: true, + }) + public expiresAt: Date | null; + + @Column('boolean') + public multiple: boolean; + + @Column('varchar', { + length: 128, array: true, default: '{}', + }) + public choices: string[]; + + @Column('integer', { + array: true, + }) + public votes: number[]; + + //#region Denormalized fields + @Column('enum', { + enum: noteVisibilities, + comment: '[Denormalized]', + }) + public noteVisibility: typeof noteVisibilities[number]; + + @Index() + @Column({ + ...id(), + comment: '[Denormalized]', + }) + public userId: User['id']; + + @Index() + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]', + }) + public userHost: string | null; + //#endregion + + constructor(data: Partial) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} + +export type IPoll = { + choices: string[]; + votes?: number[]; + multiple: boolean; + expiresAt: Date | null; +}; diff --git a/packages/backend/src/models/entities/PollVote.ts b/packages/backend/src/models/entities/PollVote.ts new file mode 100644 index 000000000000..fca1cd009944 --- /dev/null +++ b/packages/backend/src/models/entities/PollVote.ts @@ -0,0 +1,40 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { Note } from './note.js'; +import { id } from '../id.js'; + +@Entity() +@Index(['userId', 'noteId', 'choice'], { unique: true }) +export class PollVote { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the PollVote.', + }) + public createdAt: Date; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Index() + @Column(id()) + public noteId: Note['id']; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public note: Note | null; + + @Column('integer') + public choice: number; +} diff --git a/packages/backend/src/models/entities/PromoNote.ts b/packages/backend/src/models/entities/PromoNote.ts new file mode 100644 index 000000000000..d110b81e93ec --- /dev/null +++ b/packages/backend/src/models/entities/PromoNote.ts @@ -0,0 +1,28 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm'; +import { Note } from './note.js'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +export class PromoNote { + @PrimaryColumn(id()) + public noteId: Note['id']; + + @OneToOne(type => Note, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public note: Note | null; + + @Column('timestamp with time zone') + public expiresAt: Date; + + //#region Denormalized fields + @Index() + @Column({ + ...id(), + comment: '[Denormalized]', + }) + public userId: User['id']; + //#endregion +} diff --git a/packages/backend/src/models/entities/PromoRead.ts b/packages/backend/src/models/entities/PromoRead.ts new file mode 100644 index 000000000000..a63b79cd1ebe --- /dev/null +++ b/packages/backend/src/models/entities/PromoRead.ts @@ -0,0 +1,35 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { Note } from './note.js'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +@Index(['userId', 'noteId'], { unique: true }) +export class PromoRead { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the PromoRead.', + }) + public createdAt: Date; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column(id()) + public noteId: Note['id']; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public note: Note | null; +} diff --git a/packages/backend/src/models/entities/RegistrationTickets.ts b/packages/backend/src/models/entities/RegistrationTickets.ts new file mode 100644 index 000000000000..139e40f85e4c --- /dev/null +++ b/packages/backend/src/models/entities/RegistrationTickets.ts @@ -0,0 +1,17 @@ +import { PrimaryColumn, Entity, Index, Column } from 'typeorm'; +import { id } from '../id.js'; + +@Entity() +export class RegistrationTicket { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone') + public createdAt: Date; + + @Index({ unique: true }) + @Column('varchar', { + length: 64, + }) + public code: string; +} diff --git a/packages/backend/src/models/entities/RegistryItem.ts b/packages/backend/src/models/entities/RegistryItem.ts new file mode 100644 index 000000000000..283796df9166 --- /dev/null +++ b/packages/backend/src/models/entities/RegistryItem.ts @@ -0,0 +1,58 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; + +// TODO: 同じdomain、同じscope、同じkeyのレコードは二つ以上存在しないように制約付けたい +@Entity() +export class RegistryItem { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the RegistryItem.', + }) + public createdAt: Date; + + @Column('timestamp with time zone', { + comment: 'The updated date of the RegistryItem.', + }) + public updatedAt: Date; + + @Index() + @Column({ + ...id(), + comment: 'The owner ID.', + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column('varchar', { + length: 1024, + comment: 'The key of the RegistryItem.', + }) + public key: string; + + @Column('jsonb', { + default: {}, nullable: true, + comment: 'The value of the RegistryItem.', + }) + public value: any | null; + + @Index() + @Column('varchar', { + length: 1024, array: true, default: '{}', + }) + public scope: string[]; + + // サードパーティアプリに開放するときのためのカラム + @Index() + @Column('varchar', { + length: 512, nullable: true, + }) + public domain: string | null; +} diff --git a/packages/backend/src/models/entities/Relay.ts b/packages/backend/src/models/entities/Relay.ts new file mode 100644 index 000000000000..94d192957411 --- /dev/null +++ b/packages/backend/src/models/entities/Relay.ts @@ -0,0 +1,19 @@ +import { PrimaryColumn, Entity, Index, Column } from 'typeorm'; +import { id } from '../id.js'; + +@Entity() +export class Relay { + @PrimaryColumn(id()) + public id: string; + + @Index({ unique: true }) + @Column('varchar', { + length: 512, nullable: false, + }) + public inbox: string; + + @Column('enum', { + enum: ['requesting', 'accepted', 'rejected'], + }) + public status: 'requesting' | 'accepted' | 'rejected'; +} diff --git a/packages/backend/src/models/entities/Signin.ts b/packages/backend/src/models/entities/Signin.ts new file mode 100644 index 000000000000..ba81f45e4995 --- /dev/null +++ b/packages/backend/src/models/entities/Signin.ts @@ -0,0 +1,35 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +export class Signin { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the Signin.', + }) + public createdAt: Date; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column('varchar', { + length: 128, + }) + public ip: string; + + @Column('jsonb') + public headers: Record; + + @Column('boolean') + public success: boolean; +} diff --git a/packages/backend/src/models/entities/SwSubscription.ts b/packages/backend/src/models/entities/SwSubscription.ts new file mode 100644 index 000000000000..59144d348b61 --- /dev/null +++ b/packages/backend/src/models/entities/SwSubscription.ts @@ -0,0 +1,37 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +export class SwSubscription { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone') + public createdAt: Date; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column('varchar', { + length: 512, + }) + public endpoint: string; + + @Column('varchar', { + length: 256, + }) + public auth: string; + + @Column('varchar', { + length: 128, + }) + public publickey: string; +} diff --git a/packages/backend/src/models/entities/UsedUsername.ts b/packages/backend/src/models/entities/UsedUsername.ts new file mode 100644 index 000000000000..eb90bef6ca1f --- /dev/null +++ b/packages/backend/src/models/entities/UsedUsername.ts @@ -0,0 +1,20 @@ +import { PrimaryColumn, Entity, Column } from 'typeorm'; + +@Entity() +export class UsedUsername { + @PrimaryColumn('varchar', { + length: 128, + }) + public username: string; + + @Column('timestamp with time zone') + public createdAt: Date; + + constructor(data: Partial) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} diff --git a/packages/backend/src/models/entities/User.ts b/packages/backend/src/models/entities/User.ts new file mode 100644 index 000000000000..76f8e7935e35 --- /dev/null +++ b/packages/backend/src/models/entities/User.ts @@ -0,0 +1,255 @@ +import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm'; +import { id } from '../id.js'; +import { DriveFile } from './drive-file.js'; + +@Entity() +@Index(['usernameLower', 'host'], { unique: true }) +export class User { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the User.', + }) + public createdAt: Date; + + @Index() + @Column('timestamp with time zone', { + nullable: true, + comment: 'The updated date of the User.', + }) + public updatedAt: Date | null; + + @Column('timestamp with time zone', { + nullable: true, + }) + public lastFetchedAt: Date | null; + + @Index() + @Column('timestamp with time zone', { + nullable: true, + }) + public lastActiveDate: Date | null; + + @Column('boolean', { + default: false, + }) + public hideOnlineStatus: boolean; + + @Column('varchar', { + length: 128, + comment: 'The username of the User.', + }) + public username: string; + + @Index() + @Column('varchar', { + length: 128, select: false, + comment: 'The username (lowercased) of the User.', + }) + public usernameLower: string; + + @Column('varchar', { + length: 128, nullable: true, + comment: 'The name of the User.', + }) + public name: string | null; + + @Column('integer', { + default: 0, + comment: 'The count of followers.', + }) + public followersCount: number; + + @Column('integer', { + default: 0, + comment: 'The count of following.', + }) + public followingCount: number; + + @Column('integer', { + default: 0, + comment: 'The count of notes.', + }) + public notesCount: number; + + @Column({ + ...id(), + nullable: true, + comment: 'The ID of avatar DriveFile.', + }) + public avatarId: DriveFile['id'] | null; + + @OneToOne(type => DriveFile, { + onDelete: 'SET NULL', + }) + @JoinColumn() + public avatar: DriveFile | null; + + @Column({ + ...id(), + nullable: true, + comment: 'The ID of banner DriveFile.', + }) + public bannerId: DriveFile['id'] | null; + + @OneToOne(type => DriveFile, { + onDelete: 'SET NULL', + }) + @JoinColumn() + public banner: DriveFile | null; + + @Index() + @Column('varchar', { + length: 128, array: true, default: '{}', + }) + public tags: string[]; + + @Column('boolean', { + default: false, + comment: 'Whether the User is suspended.', + }) + public isSuspended: boolean; + + @Column('boolean', { + default: false, + comment: 'Whether the User is silenced.', + }) + public isSilenced: boolean; + + @Column('boolean', { + default: false, + comment: 'Whether the User is locked.', + }) + public isLocked: boolean; + + @Column('boolean', { + default: false, + comment: 'Whether the User is a bot.', + }) + public isBot: boolean; + + @Column('boolean', { + default: false, + comment: 'Whether the User is a cat.', + }) + public isCat: boolean; + + @Column('boolean', { + default: false, + comment: 'Whether the User is the admin.', + }) + public isAdmin: boolean; + + @Column('boolean', { + default: false, + comment: 'Whether the User is a moderator.', + }) + public isModerator: boolean; + + @Index() + @Column('boolean', { + default: true, + comment: 'Whether the User is explorable.', + }) + public isExplorable: boolean; + + // アカウントが削除されたかどうかのフラグだが、完全に削除される際は物理削除なので実質削除されるまでの「削除が進行しているかどうか」のフラグ + @Column('boolean', { + default: false, + comment: 'Whether the User is deleted.', + }) + public isDeleted: boolean; + + @Column('varchar', { + length: 128, array: true, default: '{}', + }) + public emojis: string[]; + + @Index() + @Column('varchar', { + length: 128, nullable: true, + comment: 'The host of the User. It will be null if the origin of the user is local.', + }) + public host: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: 'The inbox URL of the User. It will be null if the origin of the user is local.', + }) + public inbox: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: 'The sharedInbox URL of the User. It will be null if the origin of the user is local.', + }) + public sharedInbox: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: 'The featured URL of the User. It will be null if the origin of the user is local.', + }) + public featured: string | null; + + @Index() + @Column('varchar', { + length: 512, nullable: true, + comment: 'The URI of the User. It will be null if the origin of the user is local.', + }) + public uri: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: 'The URI of the user Follower Collection. It will be null if the origin of the user is local.', + }) + public followersUri: string | null; + + @Column('boolean', { + default: false, + comment: 'Whether to show users replying to other users in the timeline.', + }) + public showTimelineReplies: boolean; + + @Index({ unique: true }) + @Column('char', { + length: 16, nullable: true, unique: true, + comment: 'The native access token of the User. It will be null if the origin of the user is local.', + }) + public token: string | null; + + @Column('integer', { + nullable: true, + comment: 'Overrides user drive capacity limit', + }) + public driveCapacityOverrideMb: number | null; + + constructor(data: Partial) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} + +export interface ILocalUser extends User { + host: null; +} + +export interface IRemoteUser extends User { + host: string; +} + +export type CacheableLocalUser = ILocalUser; + +export type CacheableRemoteUser = IRemoteUser; + +export type CacheableUser = CacheableLocalUser | CacheableRemoteUser; + +export const localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const; +export const passwordSchema = { type: 'string', minLength: 1 } as const; +export const nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const; +export const descriptionSchema = { type: 'string', minLength: 1, maxLength: 500 } as const; +export const locationSchema = { type: 'string', minLength: 1, maxLength: 50 } as const; +export const birthdaySchema = { type: 'string', pattern: /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.toString().slice(1, -1) } as const; diff --git a/packages/backend/src/models/entities/UserGroup.ts b/packages/backend/src/models/entities/UserGroup.ts new file mode 100644 index 000000000000..8d5de1d926d1 --- /dev/null +++ b/packages/backend/src/models/entities/UserGroup.ts @@ -0,0 +1,46 @@ +import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +export class UserGroup { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column('timestamp with time zone', { + comment: 'The created date of the UserGroup.', + }) + public createdAt: Date; + + @Column('varchar', { + length: 256, + }) + public name: string; + + @Index() + @Column({ + ...id(), + comment: 'The ID of owner.', + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column('boolean', { + default: false, + }) + public isPrivate: boolean; + + constructor(data: Partial) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} diff --git a/packages/backend/src/models/entities/UserGroupInvitation.ts b/packages/backend/src/models/entities/UserGroupInvitation.ts new file mode 100644 index 000000000000..10f357049fa8 --- /dev/null +++ b/packages/backend/src/models/entities/UserGroupInvitation.ts @@ -0,0 +1,42 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { UserGroup } from './user-group.js'; +import { id } from '../id.js'; + +@Entity() +@Index(['userId', 'userGroupId'], { unique: true }) +export class UserGroupInvitation { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the UserGroupInvitation.', + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + comment: 'The user ID.', + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Index() + @Column({ + ...id(), + comment: 'The group ID.', + }) + public userGroupId: UserGroup['id']; + + @ManyToOne(type => UserGroup, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public userGroup: UserGroup | null; +} diff --git a/packages/backend/src/models/entities/UserGroupJoining.ts b/packages/backend/src/models/entities/UserGroupJoining.ts new file mode 100644 index 000000000000..62a814218a3d --- /dev/null +++ b/packages/backend/src/models/entities/UserGroupJoining.ts @@ -0,0 +1,42 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { UserGroup } from './user-group.js'; +import { id } from '../id.js'; + +@Entity() +@Index(['userId', 'userGroupId'], { unique: true }) +export class UserGroupJoining { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the UserGroupJoining.', + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + comment: 'The user ID.', + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Index() + @Column({ + ...id(), + comment: 'The group ID.', + }) + public userGroupId: UserGroup['id']; + + @ManyToOne(type => UserGroup, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public userGroup: UserGroup | null; +} diff --git a/packages/backend/src/models/entities/UserIp.ts b/packages/backend/src/models/entities/UserIp.ts new file mode 100644 index 000000000000..543e9e7289e8 --- /dev/null +++ b/packages/backend/src/models/entities/UserIp.ts @@ -0,0 +1,24 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; +import { id } from '../id.js'; +import { Note } from './note.js'; +import { User } from './user.js'; + +@Entity() +@Index(['userId', 'ip'], { unique: true }) +export class UserIp { + @PrimaryGeneratedColumn() + public id: string; + + @Column('timestamp with time zone', { + }) + public createdAt: Date; + + @Index() + @Column(id()) + public userId: User['id']; + + @Column('varchar', { + length: 128, + }) + public ip: string; +} diff --git a/packages/backend/src/models/entities/UserKeypair.ts b/packages/backend/src/models/entities/UserKeypair.ts new file mode 100644 index 000000000000..85fa06297795 --- /dev/null +++ b/packages/backend/src/models/entities/UserKeypair.ts @@ -0,0 +1,33 @@ +import { PrimaryColumn, Entity, JoinColumn, Column, OneToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +export class UserKeypair { + @PrimaryColumn(id()) + public userId: User['id']; + + @OneToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column('varchar', { + length: 4096, + }) + public publicKey: string; + + @Column('varchar', { + length: 4096, + }) + public privateKey: string; + + constructor(data: Partial) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} diff --git a/packages/backend/src/models/entities/UserList.ts b/packages/backend/src/models/entities/UserList.ts new file mode 100644 index 000000000000..ca69394e93c8 --- /dev/null +++ b/packages/backend/src/models/entities/UserList.ts @@ -0,0 +1,33 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +export class UserList { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the UserList.', + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + comment: 'The owner ID.', + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column('varchar', { + length: 128, + comment: 'The name of the UserList.', + }) + public name: string; +} diff --git a/packages/backend/src/models/entities/UserListJoining.ts b/packages/backend/src/models/entities/UserListJoining.ts new file mode 100644 index 000000000000..12f28c414977 --- /dev/null +++ b/packages/backend/src/models/entities/UserListJoining.ts @@ -0,0 +1,42 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { UserList } from './user-list.js'; +import { id } from '../id.js'; + +@Entity() +@Index(['userId', 'userListId'], { unique: true }) +export class UserListJoining { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the UserListJoining.', + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + comment: 'The user ID.', + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Index() + @Column({ + ...id(), + comment: 'The list ID.', + }) + public userListId: UserList['id']; + + @ManyToOne(type => UserList, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public userList: UserList | null; +} diff --git a/packages/backend/src/models/entities/UserNotePining.ts b/packages/backend/src/models/entities/UserNotePining.ts new file mode 100644 index 000000000000..c91ab7fdd865 --- /dev/null +++ b/packages/backend/src/models/entities/UserNotePining.ts @@ -0,0 +1,35 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { Note } from './note.js'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +@Index(['userId', 'noteId'], { unique: true }) +export class UserNotePining { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the UserNotePinings.', + }) + public createdAt: Date; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column(id()) + public noteId: Note['id']; + + @ManyToOne(type => Note, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public note: Note | null; +} diff --git a/packages/backend/src/models/entities/UserPending.ts b/packages/backend/src/models/entities/UserPending.ts new file mode 100644 index 000000000000..763794884161 --- /dev/null +++ b/packages/backend/src/models/entities/UserPending.ts @@ -0,0 +1,32 @@ +import { PrimaryColumn, Entity, Index, Column } from 'typeorm'; +import { id } from '../id.js'; + +@Entity() +export class UserPending { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone') + public createdAt: Date; + + @Index({ unique: true }) + @Column('varchar', { + length: 128, + }) + public code: string; + + @Column('varchar', { + length: 128, + }) + public username: string; + + @Column('varchar', { + length: 128, + }) + public email: string; + + @Column('varchar', { + length: 128, + }) + public password: string; +} diff --git a/packages/backend/src/models/entities/UserProfile.ts b/packages/backend/src/models/entities/UserProfile.ts new file mode 100644 index 000000000000..3654b0a99460 --- /dev/null +++ b/packages/backend/src/models/entities/UserProfile.ts @@ -0,0 +1,232 @@ +import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm'; +import { ffVisibility, notificationTypes } from '@/types.js'; +import { id } from '../id.js'; +import { User } from './user.js'; +import { Page } from './page.js'; + +// TODO: このテーブルで管理している情報すべてレジストリで管理するようにしても良いかも +// ただ、「emailVerified が true なユーザーを find する」のようなクエリは書けなくなるからウーン +@Entity() +export class UserProfile { + @PrimaryColumn(id()) + public userId: User['id']; + + @OneToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column('varchar', { + length: 128, nullable: true, + comment: 'The location of the User.', + }) + public location: string | null; + + @Column('char', { + length: 10, nullable: true, + comment: 'The birthday (YYYY-MM-DD) of the User.', + }) + public birthday: string | null; + + @Column('varchar', { + length: 2048, nullable: true, + comment: 'The description (bio) of the User.', + }) + public description: string | null; + + @Column('jsonb', { + default: [], + }) + public fields: { + name: string; + value: string; + }[]; + + @Column('varchar', { + length: 32, nullable: true, + }) + public lang: string | null; + + @Column('varchar', { + length: 512, nullable: true, + comment: 'Remote URL of the user.', + }) + public url: string | null; + + @Column('varchar', { + length: 128, nullable: true, + comment: 'The email address of the User.', + }) + public email: string | null; + + @Column('varchar', { + length: 128, nullable: true, + }) + public emailVerifyCode: string | null; + + @Column('boolean', { + default: false, + }) + public emailVerified: boolean; + + @Column('jsonb', { + default: ['follow', 'receiveFollowRequest', 'groupInvited'], + }) + public emailNotificationTypes: string[]; + + @Column('boolean', { + default: false, + }) + public publicReactions: boolean; + + @Column('enum', { + enum: ffVisibility, + default: 'public', + }) + public ffVisibility: typeof ffVisibility[number]; + + @Column('varchar', { + length: 128, nullable: true, + }) + public twoFactorTempSecret: string | null; + + @Column('varchar', { + length: 128, nullable: true, + }) + public twoFactorSecret: string | null; + + @Column('boolean', { + default: false, + }) + public twoFactorEnabled: boolean; + + @Column('boolean', { + default: false, + }) + public securityKeysAvailable: boolean; + + @Column('boolean', { + default: false, + }) + public usePasswordLessLogin: boolean; + + @Column('varchar', { + length: 128, nullable: true, + comment: 'The password hash of the User. It will be null if the origin of the user is local.', + }) + public password: string | null; + + @Column('varchar', { + length: 8192, default: '', + }) + public moderationNote: string | null; + + // TODO: そのうち消す + @Column('jsonb', { + default: {}, + comment: 'The client-specific data of the User.', + }) + public clientData: Record; + + // TODO: そのうち消す + @Column('jsonb', { + default: {}, + comment: 'The room data of the User.', + }) + public room: Record; + + @Column('boolean', { + default: false, + }) + public autoAcceptFollowed: boolean; + + @Column('boolean', { + default: false, + comment: 'Whether reject index by crawler.', + }) + public noCrawle: boolean; + + @Column('boolean', { + default: false, + }) + public alwaysMarkNsfw: boolean; + + @Column('boolean', { + default: false, + }) + public autoSensitive: boolean; + + @Column('boolean', { + default: false, + }) + public carefulBot: boolean; + + @Column('boolean', { + default: true, + }) + public injectFeaturedNote: boolean; + + @Column('boolean', { + default: true, + }) + public receiveAnnouncementEmail: boolean; + + @Column({ + ...id(), + nullable: true, + }) + public pinnedPageId: Page['id'] | null; + + @OneToOne(type => Page, { + onDelete: 'SET NULL', + }) + @JoinColumn() + public pinnedPage: Page | null; + + @Column('jsonb', { + default: {}, + }) + public integrations: Record; + + @Index() + @Column('boolean', { + default: false, select: false, + }) + public enableWordMute: boolean; + + @Column('jsonb', { + default: [], + }) + public mutedWords: string[][]; + + @Column('jsonb', { + default: [], + comment: 'List of instances muted by the user.', + }) + public mutedInstances: string[]; + + @Column('enum', { + enum: notificationTypes, + array: true, + default: [], + }) + public mutingNotificationTypes: typeof notificationTypes[number][]; + + //#region Denormalized fields + @Index() + @Column('varchar', { + length: 128, nullable: true, + comment: '[Denormalized]', + }) + public userHost: string | null; + //#endregion + + constructor(data: Partial) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} diff --git a/packages/backend/src/models/entities/UserPublickey.ts b/packages/backend/src/models/entities/UserPublickey.ts new file mode 100644 index 000000000000..31ed60de8228 --- /dev/null +++ b/packages/backend/src/models/entities/UserPublickey.ts @@ -0,0 +1,34 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +export class UserPublickey { + @PrimaryColumn(id()) + public userId: User['id']; + + @OneToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Index({ unique: true }) + @Column('varchar', { + length: 256, + }) + public keyId: string; + + @Column('varchar', { + length: 4096, + }) + public keyPem: string; + + constructor(data: Partial) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} diff --git a/packages/backend/src/models/entities/UserSecurityKey.ts b/packages/backend/src/models/entities/UserSecurityKey.ts new file mode 100644 index 000000000000..c4f2a852e228 --- /dev/null +++ b/packages/backend/src/models/entities/UserSecurityKey.ts @@ -0,0 +1,48 @@ +import { PrimaryColumn, Entity, JoinColumn, Column, ManyToOne, Index } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; + +@Entity() +export class UserSecurityKey { + @PrimaryColumn('varchar', { + comment: 'Variable-length id given to navigator.credentials.get()', + }) + public id: string; + + @Index() + @Column(id()) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Index() + @Column('varchar', { + comment: + 'Variable-length public key used to verify attestations (hex-encoded).', + }) + public publicKey: string; + + @Column('timestamp with time zone', { + comment: + 'The date of the last time the UserSecurityKey was successfully validated.', + }) + public lastUsed: Date; + + @Column('varchar', { + comment: 'User-defined name for this key', + length: 30, + }) + public name: string; + + constructor(data: Partial) { + if (data == null) return; + + for (const [k, v] of Object.entries(data)) { + (this as any)[k] = v; + } + } +} diff --git a/packages/backend/src/models/entities/Webhook.ts b/packages/backend/src/models/entities/Webhook.ts new file mode 100644 index 000000000000..56b411f87965 --- /dev/null +++ b/packages/backend/src/models/entities/Webhook.ts @@ -0,0 +1,73 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { User } from './user.js'; +import { id } from '../id.js'; + +export const webhookEventTypes = ['mention', 'unfollow', 'follow', 'followed', 'note', 'reply', 'renote', 'reaction'] as const; + +@Entity() +export class Webhook { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the Antenna.', + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + comment: 'The owner ID.', + }) + public userId: User['id']; + + @ManyToOne(type => User, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user: User | null; + + @Column('varchar', { + length: 128, + comment: 'The name of the Antenna.', + }) + public name: string; + + @Index() + @Column('varchar', { + length: 128, array: true, default: '{}', + }) + public on: (typeof webhookEventTypes)[number][]; + + @Column('varchar', { + length: 1024, + }) + public url: string; + + @Column('varchar', { + length: 1024, + }) + public secret: string; + + @Index() + @Column('boolean', { + default: true, + }) + public active: boolean; + + /** + * 直近のリクエスト送信日時 + */ + @Column('timestamp with time zone', { + nullable: true, + }) + public latestSentAt: Date | null; + + /** + * 直近のリクエスト送信時のHTTPステータスコード + */ + @Column('integer', { + nullable: true, + }) + public latestStatus: number | null; +} From f78a33e9f9512492c946c1f9cc5459823798f688 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 04:22:39 +0900 Subject: [PATCH 133/180] wip --- packages/backend/src/app.module.ts | 4 +- packages/backend/src/server/ServerModule.ts | 53 ++ .../backend/src/server/api/EndpointsModule.ts | 634 +++++++++++++++++ .../src/server/api/endpoints.module.ts | 640 ------------------ packages/backend/src/services/CoreModule.ts | 51 ++ 5 files changed, 740 insertions(+), 642 deletions(-) create mode 100644 packages/backend/src/server/ServerModule.ts create mode 100644 packages/backend/src/server/api/EndpointsModule.ts delete mode 100644 packages/backend/src/server/api/endpoints.module.ts diff --git a/packages/backend/src/app.module.ts b/packages/backend/src/app.module.ts index 3512e3b5969e..22fa5cec4cb7 100644 --- a/packages/backend/src/app.module.ts +++ b/packages/backend/src/app.module.ts @@ -1,18 +1,18 @@ import { Module } from '@nestjs/common'; -import { EndpointsModule } from '@/server/api/endpoints.module.js'; import { QueueModule } from '@/queue/queue.module.js'; import { CoreModule } from './services/CoreModule.js'; import { DI } from './di-symbols.js'; import { loadConfig } from './config.js'; import { db } from './db/postgre'; import { RepositoryModule } from './RepositoryModule.js'; +import { ServerModule } from './server/ServerModule.js'; @Module({ imports: [ RepositoryModule, CoreModule, - EndpointsModule, QueueModule, + ServerModule, ], providers: [{ provide: DI.config, diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts new file mode 100644 index 000000000000..3e956e545630 --- /dev/null +++ b/packages/backend/src/server/ServerModule.ts @@ -0,0 +1,53 @@ +import { Module } from '@nestjs/common'; +import { EndpointsModule } from '@/server/api/EndpointsModule.js'; +import { CoreModule } from '@/services/CoreModule.js'; +import { ApiCallService } from './api/ApiCallService.js'; +import { FileServerService } from './FileServerService.js'; +import { MediaProxyServerService } from './MediaProxyServerService.js'; +import { NodeinfoServerService } from './NodeinfoServerService.js'; +import { ServerService } from './ServerService.js'; +import { WellKnownServerService } from './WellKnownServerService.js'; +import { GetterService } from './api/common/GetterService.js'; +import { DiscordServerService } from './api/integration/DiscordServerService.js'; +import { GithubServerService } from './api/integration/GithubServerService.js'; +import { TwitterServerService } from './api/integration/TwitterServerService.js'; +import { ChannelsService } from './api/stream/ChannelsService.js'; +import { ActivityPubServerService } from './ActivityPubServerService.js'; +import { ApiLoggerService } from './api/ApiLoggerService.js'; +import { ApiServerService } from './api/ApiServerService.js'; +import { AuthenticateService } from './api/AuthenticateService.js'; +import { RateLimiterService } from './api/RateLimiterService.js'; +import { SigninApiService } from './api/SigninApiService.js'; +import { SigninService } from './api/SigninService.js'; +import { SignupApiService } from './api/SignupApiService.js'; +import { StreamingApiServerService } from './api/StreamingApiServerService.js'; + +@Module({ + imports: [ + EndpointsModule, + CoreModule, + ], + providers: [ + ActivityPubServerService, + FileServerService, + MediaProxyServerService, + NodeinfoServerService, + ServerService, + WellKnownServerService, + GetterService, + DiscordServerService, + GithubServerService, + TwitterServerService, + ChannelsService, + ApiCallService, + ApiLoggerService, + ApiServerService, + AuthenticateService, + RateLimiterService, + SigninApiService, + SigninService, + SignupApiService, + StreamingApiServerService, + ], +}) +export class ServerModule {} diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts new file mode 100644 index 000000000000..35ecd41e0cd8 --- /dev/null +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -0,0 +1,634 @@ +import { Module } from '@nestjs/common'; + +import * as ep___admin_meta from './endpoints/admin/meta.js'; +import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js'; +import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js'; +import * as ep___admin_accounts_delete from './endpoints/admin/accounts/delete.js'; +import * as ep___admin_ad_create from './endpoints/admin/ad/create.js'; +import * as ep___admin_ad_delete from './endpoints/admin/ad/delete.js'; +import * as ep___admin_ad_list from './endpoints/admin/ad/list.js'; +import * as ep___admin_ad_update from './endpoints/admin/ad/update.js'; +import * as ep___admin_announcements_create from './endpoints/admin/announcements/create.js'; +import * as ep___admin_announcements_delete from './endpoints/admin/announcements/delete.js'; +import * as ep___admin_announcements_list from './endpoints/admin/announcements/list.js'; +import * as ep___admin_announcements_update from './endpoints/admin/announcements/update.js'; +import * as ep___admin_deleteAllFilesOfAUser from './endpoints/admin/delete-all-files-of-a-user.js'; +import * as ep___admin_drive_cleanRemoteFiles from './endpoints/admin/drive/clean-remote-files.js'; +import * as ep___admin_drive_cleanup from './endpoints/admin/drive/cleanup.js'; +import * as ep___admin_drive_files from './endpoints/admin/drive/files.js'; +import * as ep___admin_drive_showFile from './endpoints/admin/drive/show-file.js'; +import * as ep___admin_emoji_addAliasesBulk from './endpoints/admin/emoji/add-aliases-bulk.js'; +import * as ep___admin_emoji_add from './endpoints/admin/emoji/add.js'; +import * as ep___admin_emoji_copy from './endpoints/admin/emoji/copy.js'; +import * as ep___admin_emoji_deleteBulk from './endpoints/admin/emoji/delete-bulk.js'; +import * as ep___admin_emoji_delete from './endpoints/admin/emoji/delete.js'; +import * as ep___admin_emoji_importZip from './endpoints/admin/emoji/import-zip.js'; +import * as ep___admin_emoji_listRemote from './endpoints/admin/emoji/list-remote.js'; +import * as ep___admin_emoji_list from './endpoints/admin/emoji/list.js'; +import * as ep___admin_emoji_removeAliasesBulk from './endpoints/admin/emoji/remove-aliases-bulk.js'; +import * as ep___admin_emoji_setAliasesBulk from './endpoints/admin/emoji/set-aliases-bulk.js'; +import * as ep___admin_emoji_setCategoryBulk from './endpoints/admin/emoji/set-category-bulk.js'; +import * as ep___admin_emoji_update from './endpoints/admin/emoji/update.js'; +import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federation/delete-all-files.js'; +import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js'; +import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js'; +import * as ep___admin_federation_updateInstance from './endpoints/admin/federation/update-instance.js'; +import * as ep___admin_getIndexStats from './endpoints/admin/get-index-stats.js'; +import * as ep___admin_getTableStats from './endpoints/admin/get-table-stats.js'; +import * as ep___admin_getUserIps from './endpoints/admin/get-user-ips.js'; +import * as ep___admin_invite from './endpoints/admin/invite.js'; +import * as ep___admin_moderators_add from './endpoints/admin/moderators/add.js'; +import * as ep___admin_moderators_remove from './endpoints/admin/moderators/remove.js'; +import * as ep___admin_promo_create from './endpoints/admin/promo/create.js'; +import * as ep___admin_queue_clear from './endpoints/admin/queue/clear.js'; +import * as ep___admin_queue_deliverDelayed from './endpoints/admin/queue/deliver-delayed.js'; +import * as ep___admin_queue_inboxDelayed from './endpoints/admin/queue/inbox-delayed.js'; +import * as ep___admin_queue_stats from './endpoints/admin/queue/stats.js'; +import * as ep___admin_relays_add from './endpoints/admin/relays/add.js'; +import * as ep___admin_relays_list from './endpoints/admin/relays/list.js'; +import * as ep___admin_relays_remove from './endpoints/admin/relays/remove.js'; +import * as ep___admin_resetPassword from './endpoints/admin/reset-password.js'; +import * as ep___admin_resolveAbuseUserReport from './endpoints/admin/resolve-abuse-user-report.js'; +import * as ep___admin_sendEmail from './endpoints/admin/send-email.js'; +import * as ep___admin_serverInfo from './endpoints/admin/server-info.js'; +import * as ep___admin_showModerationLogs from './endpoints/admin/show-moderation-logs.js'; +import * as ep___admin_showUser from './endpoints/admin/show-user.js'; +import * as ep___admin_showUsers from './endpoints/admin/show-users.js'; +import * as ep___admin_silenceUser from './endpoints/admin/silence-user.js'; +import * as ep___admin_suspendUser from './endpoints/admin/suspend-user.js'; +import * as ep___admin_unsilenceUser from './endpoints/admin/unsilence-user.js'; +import * as ep___admin_unsuspendUser from './endpoints/admin/unsuspend-user.js'; +import * as ep___admin_updateMeta from './endpoints/admin/update-meta.js'; +import * as ep___admin_deleteAccount from './endpoints/admin/delete-account.js'; +import * as ep___admin_updateUserNote from './endpoints/admin/update-user-note.js'; +import * as ep___announcements from './endpoints/announcements.js'; +import * as ep___antennas_create from './endpoints/antennas/create.js'; +import * as ep___antennas_delete from './endpoints/antennas/delete.js'; +import * as ep___antennas_list from './endpoints/antennas/list.js'; +import * as ep___antennas_notes from './endpoints/antennas/notes.js'; +import * as ep___antennas_show from './endpoints/antennas/show.js'; +import * as ep___antennas_update from './endpoints/antennas/update.js'; +import * as ep___ap_get from './endpoints/ap/get.js'; +import * as ep___ap_show from './endpoints/ap/show.js'; +import * as ep___app_create from './endpoints/app/create.js'; +import * as ep___app_show from './endpoints/app/show.js'; +import * as ep___auth_accept from './endpoints/auth/accept.js'; +import * as ep___auth_session_generate from './endpoints/auth/session/generate.js'; +import * as ep___auth_session_show from './endpoints/auth/session/show.js'; +import * as ep___auth_session_userkey from './endpoints/auth/session/userkey.js'; +import * as ep___blocking_create from './endpoints/blocking/create.js'; +import * as ep___blocking_delete from './endpoints/blocking/delete.js'; +import * as ep___blocking_list from './endpoints/blocking/list.js'; +import * as ep___channels_create from './endpoints/channels/create.js'; +import * as ep___channels_featured from './endpoints/channels/featured.js'; +import * as ep___channels_follow from './endpoints/channels/follow.js'; +import * as ep___channels_followed from './endpoints/channels/followed.js'; +import * as ep___channels_owned from './endpoints/channels/owned.js'; +import * as ep___channels_show from './endpoints/channels/show.js'; +import * as ep___channels_timeline from './endpoints/channels/timeline.js'; +import * as ep___channels_unfollow from './endpoints/channels/unfollow.js'; +import * as ep___channels_update from './endpoints/channels/update.js'; +import * as ep___charts_activeUsers from './endpoints/charts/active-users.js'; +import * as ep___charts_apRequest from './endpoints/charts/ap-request.js'; +import * as ep___charts_drive from './endpoints/charts/drive.js'; +import * as ep___charts_federation from './endpoints/charts/federation.js'; +import * as ep___charts_hashtag from './endpoints/charts/hashtag.js'; +import * as ep___charts_instance from './endpoints/charts/instance.js'; +import * as ep___charts_notes from './endpoints/charts/notes.js'; +import * as ep___charts_user_drive from './endpoints/charts/user/drive.js'; +import * as ep___charts_user_following from './endpoints/charts/user/following.js'; +import * as ep___charts_user_notes from './endpoints/charts/user/notes.js'; +import * as ep___charts_user_reactions from './endpoints/charts/user/reactions.js'; +import * as ep___charts_users from './endpoints/charts/users.js'; +import * as ep___clips_addNote from './endpoints/clips/add-note.js'; +import * as ep___clips_removeNote from './endpoints/clips/remove-note.js'; +import * as ep___clips_create from './endpoints/clips/create.js'; +import * as ep___clips_delete from './endpoints/clips/delete.js'; +import * as ep___clips_list from './endpoints/clips/list.js'; +import * as ep___clips_notes from './endpoints/clips/notes.js'; +import * as ep___clips_show from './endpoints/clips/show.js'; +import * as ep___clips_update from './endpoints/clips/update.js'; +import * as ep___drive from './endpoints/drive.js'; +import * as ep___drive_files from './endpoints/drive/files.js'; +import * as ep___drive_files_attachedNotes from './endpoints/drive/files/attached-notes.js'; +import * as ep___drive_files_checkExistence from './endpoints/drive/files/check-existence.js'; +import * as ep___drive_files_create from './endpoints/drive/files/create.js'; +import * as ep___drive_files_delete from './endpoints/drive/files/delete.js'; +import * as ep___drive_files_findByHash from './endpoints/drive/files/find-by-hash.js'; +import * as ep___drive_files_find from './endpoints/drive/files/find.js'; +import * as ep___drive_files_show from './endpoints/drive/files/show.js'; +import * as ep___drive_files_update from './endpoints/drive/files/update.js'; +import * as ep___drive_files_uploadFromUrl from './endpoints/drive/files/upload-from-url.js'; +import * as ep___drive_folders from './endpoints/drive/folders.js'; +import * as ep___drive_folders_create from './endpoints/drive/folders/create.js'; +import * as ep___drive_folders_delete from './endpoints/drive/folders/delete.js'; +import * as ep___drive_folders_find from './endpoints/drive/folders/find.js'; +import * as ep___drive_folders_show from './endpoints/drive/folders/show.js'; +import * as ep___drive_folders_update from './endpoints/drive/folders/update.js'; +import * as ep___drive_stream from './endpoints/drive/stream.js'; +import * as ep___emailAddress_available from './endpoints/email-address/available.js'; +import * as ep___endpoint from './endpoints/endpoint.js'; +import * as ep___endpoints from './endpoints/endpoints.js'; +import * as ep___exportCustomEmojis from './endpoints/export-custom-emojis.js'; +import * as ep___federation_followers from './endpoints/federation/followers.js'; +import * as ep___federation_following from './endpoints/federation/following.js'; +import * as ep___federation_instances from './endpoints/federation/instances.js'; +import * as ep___federation_showInstance from './endpoints/federation/show-instance.js'; +import * as ep___federation_updateRemoteUser from './endpoints/federation/update-remote-user.js'; +import * as ep___federation_users from './endpoints/federation/users.js'; +import * as ep___federation_stats from './endpoints/federation/stats.js'; +import * as ep___following_create from './endpoints/following/create.js'; +import * as ep___following_delete from './endpoints/following/delete.js'; +import * as ep___following_invalidate from './endpoints/following/invalidate.js'; +import * as ep___following_requests_accept from './endpoints/following/requests/accept.js'; +import * as ep___following_requests_cancel from './endpoints/following/requests/cancel.js'; +import * as ep___following_requests_list from './endpoints/following/requests/list.js'; +import * as ep___following_requests_reject from './endpoints/following/requests/reject.js'; +import * as ep___gallery_featured from './endpoints/gallery/featured.js'; +import * as ep___gallery_popular from './endpoints/gallery/popular.js'; +import * as ep___gallery_posts from './endpoints/gallery/posts.js'; +import * as ep___gallery_posts_create from './endpoints/gallery/posts/create.js'; +import * as ep___gallery_posts_delete from './endpoints/gallery/posts/delete.js'; +import * as ep___gallery_posts_like from './endpoints/gallery/posts/like.js'; +import * as ep___gallery_posts_show from './endpoints/gallery/posts/show.js'; +import * as ep___gallery_posts_unlike from './endpoints/gallery/posts/unlike.js'; +import * as ep___gallery_posts_update from './endpoints/gallery/posts/update.js'; +import * as ep___getOnlineUsersCount from './endpoints/get-online-users-count.js'; +import * as ep___hashtags_list from './endpoints/hashtags/list.js'; +import * as ep___hashtags_search from './endpoints/hashtags/search.js'; +import * as ep___hashtags_show from './endpoints/hashtags/show.js'; +import * as ep___hashtags_trend from './endpoints/hashtags/trend.js'; +import * as ep___hashtags_users from './endpoints/hashtags/users.js'; +import * as ep___i from './endpoints/i.js'; +import * as ep___i_2fa_done from './endpoints/i/2fa/done.js'; +import * as ep___i_2fa_keyDone from './endpoints/i/2fa/key-done.js'; +import * as ep___i_2fa_passwordLess from './endpoints/i/2fa/password-less.js'; +import * as ep___i_2fa_registerKey from './endpoints/i/2fa/register-key.js'; +import * as ep___i_2fa_register from './endpoints/i/2fa/register.js'; +import * as ep___i_2fa_removeKey from './endpoints/i/2fa/remove-key.js'; +import * as ep___i_2fa_unregister from './endpoints/i/2fa/unregister.js'; +import * as ep___i_apps from './endpoints/i/apps.js'; +import * as ep___i_authorizedApps from './endpoints/i/authorized-apps.js'; +import * as ep___i_changePassword from './endpoints/i/change-password.js'; +import * as ep___i_deleteAccount from './endpoints/i/delete-account.js'; +import * as ep___i_exportBlocking from './endpoints/i/export-blocking.js'; +import * as ep___i_exportFollowing from './endpoints/i/export-following.js'; +import * as ep___i_exportMute from './endpoints/i/export-mute.js'; +import * as ep___i_exportNotes from './endpoints/i/export-notes.js'; +import * as ep___i_exportUserLists from './endpoints/i/export-user-lists.js'; +import * as ep___i_favorites from './endpoints/i/favorites.js'; +import * as ep___i_gallery_likes from './endpoints/i/gallery/likes.js'; +import * as ep___i_gallery_posts from './endpoints/i/gallery/posts.js'; +import * as ep___i_getWordMutedNotesCount from './endpoints/i/get-word-muted-notes-count.js'; +import * as ep___i_importBlocking from './endpoints/i/import-blocking.js'; +import * as ep___i_importFollowing from './endpoints/i/import-following.js'; +import * as ep___i_importMuting from './endpoints/i/import-muting.js'; +import * as ep___i_importUserLists from './endpoints/i/import-user-lists.js'; +import * as ep___i_notifications from './endpoints/i/notifications.js'; +import * as ep___i_pageLikes from './endpoints/i/page-likes.js'; +import * as ep___i_pages from './endpoints/i/pages.js'; +import * as ep___i_pin from './endpoints/i/pin.js'; +import * as ep___i_readAllMessagingMessages from './endpoints/i/read-all-messaging-messages.js'; +import * as ep___i_readAllUnreadNotes from './endpoints/i/read-all-unread-notes.js'; +import * as ep___i_readAnnouncement from './endpoints/i/read-announcement.js'; +import * as ep___i_regenerateToken from './endpoints/i/regenerate-token.js'; +import * as ep___i_registry_getAll from './endpoints/i/registry/get-all.js'; +import * as ep___i_registry_getDetail from './endpoints/i/registry/get-detail.js'; +import * as ep___i_registry_get from './endpoints/i/registry/get.js'; +import * as ep___i_registry_keysWithType from './endpoints/i/registry/keys-with-type.js'; +import * as ep___i_registry_keys from './endpoints/i/registry/keys.js'; +import * as ep___i_registry_remove from './endpoints/i/registry/remove.js'; +import * as ep___i_registry_scopes from './endpoints/i/registry/scopes.js'; +import * as ep___i_registry_set from './endpoints/i/registry/set.js'; +import * as ep___i_revokeToken from './endpoints/i/revoke-token.js'; +import * as ep___i_signinHistory from './endpoints/i/signin-history.js'; +import * as ep___i_unpin from './endpoints/i/unpin.js'; +import * as ep___i_updateEmail from './endpoints/i/update-email.js'; +import * as ep___i_update from './endpoints/i/update.js'; +import * as ep___i_userGroupInvites from './endpoints/i/user-group-invites.js'; +import * as ep___i_webhooks_create from './endpoints/i/webhooks/create.js'; +import * as ep___i_webhooks_show from './endpoints/i/webhooks/show.js'; +import * as ep___i_webhooks_list from './endpoints/i/webhooks/list.js'; +import * as ep___i_webhooks_update from './endpoints/i/webhooks/update.js'; +import * as ep___i_webhooks_delete from './endpoints/i/webhooks/delete.js'; +import * as ep___messaging_history from './endpoints/messaging/history.js'; +import * as ep___messaging_messages from './endpoints/messaging/messages.js'; +import * as ep___messaging_messages_create from './endpoints/messaging/messages/create.js'; +import * as ep___messaging_messages_delete from './endpoints/messaging/messages/delete.js'; +import * as ep___messaging_messages_read from './endpoints/messaging/messages/read.js'; +import * as ep___meta from './endpoints/meta.js'; +import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; +import * as ep___mute_create from './endpoints/mute/create.js'; +import * as ep___mute_delete from './endpoints/mute/delete.js'; +import * as ep___mute_list from './endpoints/mute/list.js'; +import * as ep___my_apps from './endpoints/my/apps.js'; +import * as ep___notes from './endpoints/notes.js'; +import * as ep___notes_children from './endpoints/notes/children.js'; +import * as ep___notes_clips from './endpoints/notes/clips.js'; +import * as ep___notes_conversation from './endpoints/notes/conversation.js'; +import * as ep___notes_create from './endpoints/notes/create.js'; +import * as ep___notes_delete from './endpoints/notes/delete.js'; +import * as ep___notes_favorites_create from './endpoints/notes/favorites/create.js'; +import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js'; +import * as ep___notes_featured from './endpoints/notes/featured.js'; +import * as ep___notes_globalTimeline from './endpoints/notes/global-timeline.js'; +import * as ep___notes_hybridTimeline from './endpoints/notes/hybrid-timeline.js'; +import * as ep___notes_localTimeline from './endpoints/notes/local-timeline.js'; +import * as ep___notes_mentions from './endpoints/notes/mentions.js'; +import * as ep___notes_polls_recommendation from './endpoints/notes/polls/recommendation.js'; +import * as ep___notes_polls_vote from './endpoints/notes/polls/vote.js'; +import * as ep___notes_reactions from './endpoints/notes/reactions.js'; +import * as ep___notes_reactions_create from './endpoints/notes/reactions/create.js'; +import * as ep___notes_reactions_delete from './endpoints/notes/reactions/delete.js'; +import * as ep___notes_renotes from './endpoints/notes/renotes.js'; +import * as ep___notes_replies from './endpoints/notes/replies.js'; +import * as ep___notes_searchByTag from './endpoints/notes/search-by-tag.js'; +import * as ep___notes_search from './endpoints/notes/search.js'; +import * as ep___notes_show from './endpoints/notes/show.js'; +import * as ep___notes_state from './endpoints/notes/state.js'; +import * as ep___notes_threadMuting_create from './endpoints/notes/thread-muting/create.js'; +import * as ep___notes_threadMuting_delete from './endpoints/notes/thread-muting/delete.js'; +import * as ep___notes_timeline from './endpoints/notes/timeline.js'; +import * as ep___notes_translate from './endpoints/notes/translate.js'; +import * as ep___notes_unrenote from './endpoints/notes/unrenote.js'; +import * as ep___notes_userListTimeline from './endpoints/notes/user-list-timeline.js'; +import * as ep___notifications_create from './endpoints/notifications/create.js'; +import * as ep___notifications_markAllAsRead from './endpoints/notifications/mark-all-as-read.js'; +import * as ep___notifications_read from './endpoints/notifications/read.js'; +import * as ep___pagePush from './endpoints/page-push.js'; +import * as ep___pages_create from './endpoints/pages/create.js'; +import * as ep___pages_delete from './endpoints/pages/delete.js'; +import * as ep___pages_featured from './endpoints/pages/featured.js'; +import * as ep___pages_like from './endpoints/pages/like.js'; +import * as ep___pages_show from './endpoints/pages/show.js'; +import * as ep___pages_unlike from './endpoints/pages/unlike.js'; +import * as ep___pages_update from './endpoints/pages/update.js'; +import * as ep___ping from './endpoints/ping.js'; +import * as ep___pinnedUsers from './endpoints/pinned-users.js'; +import * as ep___promo_read from './endpoints/promo/read.js'; +import * as ep___requestResetPassword from './endpoints/request-reset-password.js'; +import * as ep___resetDb from './endpoints/reset-db.js'; +import * as ep___resetPassword from './endpoints/reset-password.js'; +import * as ep___serverInfo from './endpoints/server-info.js'; +import * as ep___stats from './endpoints/stats.js'; +import * as ep___sw_register from './endpoints/sw/register.js'; +import * as ep___sw_unregister from './endpoints/sw/unregister.js'; +import * as ep___test from './endpoints/test.js'; +import * as ep___username_available from './endpoints/username/available.js'; +import * as ep___users from './endpoints/users.js'; +import * as ep___users_clips from './endpoints/users/clips.js'; +import * as ep___users_followers from './endpoints/users/followers.js'; +import * as ep___users_following from './endpoints/users/following.js'; +import * as ep___users_gallery_posts from './endpoints/users/gallery/posts.js'; +import * as ep___users_getFrequentlyRepliedUsers from './endpoints/users/get-frequently-replied-users.js'; +import * as ep___users_groups_create from './endpoints/users/groups/create.js'; +import * as ep___users_groups_delete from './endpoints/users/groups/delete.js'; +import * as ep___users_groups_invitations_accept from './endpoints/users/groups/invitations/accept.js'; +import * as ep___users_groups_invitations_reject from './endpoints/users/groups/invitations/reject.js'; +import * as ep___users_groups_invite from './endpoints/users/groups/invite.js'; +import * as ep___users_groups_joined from './endpoints/users/groups/joined.js'; +import * as ep___users_groups_leave from './endpoints/users/groups/leave.js'; +import * as ep___users_groups_owned from './endpoints/users/groups/owned.js'; +import * as ep___users_groups_pull from './endpoints/users/groups/pull.js'; +import * as ep___users_groups_show from './endpoints/users/groups/show.js'; +import * as ep___users_groups_transfer from './endpoints/users/groups/transfer.js'; +import * as ep___users_groups_update from './endpoints/users/groups/update.js'; +import * as ep___users_lists_create from './endpoints/users/lists/create.js'; +import * as ep___users_lists_delete from './endpoints/users/lists/delete.js'; +import * as ep___users_lists_list from './endpoints/users/lists/list.js'; +import * as ep___users_lists_pull from './endpoints/users/lists/pull.js'; +import * as ep___users_lists_push from './endpoints/users/lists/push.js'; +import * as ep___users_lists_show from './endpoints/users/lists/show.js'; +import * as ep___users_lists_update from './endpoints/users/lists/update.js'; +import * as ep___users_notes from './endpoints/users/notes.js'; +import * as ep___users_pages from './endpoints/users/pages.js'; +import * as ep___users_reactions from './endpoints/users/reactions.js'; +import * as ep___users_recommendation from './endpoints/users/recommendation.js'; +import * as ep___users_relation from './endpoints/users/relation.js'; +import * as ep___users_reportAbuse from './endpoints/users/report-abuse.js'; +import * as ep___users_searchByUsernameAndHost from './endpoints/users/search-by-username-and-host.js'; +import * as ep___users_search from './endpoints/users/search.js'; +import * as ep___users_show from './endpoints/users/show.js'; +import * as ep___users_stats from './endpoints/users/stats.js'; +import * as ep___fetchRss from './endpoints/fetch-rss.js'; +import * as ep___admin_driveCapOverride from './endpoints/admin/drive-capacity-override.js'; + +@Module({ + imports: [ + ], + providers: [ + { provide: 'ep:admin/meta', useClass: ep___admin_meta.default }, + { provide: 'ep:admin/abuse-user-reports', useClass: ep___admin_abuseUserReports.default }, + { provide: 'ep:admin/accounts/create', useClass: ep___admin_accounts_create.default }, + { provide: 'ep:admin/accounts/delete', useClass: ep___admin_accounts_delete.default }, + { provide: 'ep:admin/ad/create', useClass: ep___admin_ad_create.default }, + { provide: 'ep:admin/ad/delete', useClass: ep___admin_ad_delete.default }, + { provide: 'ep:admin/ad/list', useClass: ep___admin_ad_list.default }, + { provide: 'ep:admin/ad/update', useClass: ep___admin_ad_update.default }, + { provide: 'ep:admin/announcements/create', useClass: ep___admin_announcements_create.default }, + { provide: 'ep:admin/announcements/delete', useClass: ep___admin_announcements_delete.default }, + { provide: 'ep:admin/announcements/list', useClass: ep___admin_announcements_list.default }, + { provide: 'ep:admin/announcements/update', useClass: ep___admin_announcements_update.default }, + { provide: 'ep:admin/delete-all-files-of-a-user', useClass: ep___admin_deleteAllFilesOfAUser.default }, + { provide: 'ep:admin/drive/clean-remote-files', useClass: ep___admin_drive_cleanRemoteFiles.default }, + { provide: 'ep:admin/drive/cleanup', useClass: ep___admin_drive_cleanup.default }, + { provide: 'ep:admin/drive/files', useClass: ep___admin_drive_files.default }, + { provide: 'ep:admin/drive/show-file', useClass: ep___admin_drive_showFile.default }, + { provide: 'ep:admin/emoji/add-aliases-bulk', useClass: ep___admin_emoji_addAliasesBulk.default }, + { provide: 'ep:admin/emoji/add', useClass: ep___admin_emoji_add.default }, + { provide: 'ep:admin/emoji/copy', useClass: ep___admin_emoji_copy.default }, + { provide: 'ep:admin/emoji/delete-bulk', useClass: ep___admin_emoji_deleteBulk.default }, + { provide: 'ep:admin/emoji/delete', useClass: ep___admin_emoji_delete.default }, + { provide: 'ep:admin/emoji/import-zip', useClass: ep___admin_emoji_importZip.default }, + { provide: 'ep:admin/emoji/list-remote', useClass: ep___admin_emoji_listRemote.default }, + { provide: 'ep:admin/emoji/list', useClass: ep___admin_emoji_list.default }, + { provide: 'ep:admin/emoji/remove-aliases-bulk', useClass: ep___admin_emoji_removeAliasesBulk.default }, + { provide: 'ep:admin/emoji/set-aliases-bulk', useClass: ep___admin_emoji_setAliasesBulk.default }, + { provide: 'ep:admin/emoji/set-category-bulk', useClass: ep___admin_emoji_setCategoryBulk.default }, + { provide: 'ep:admin/emoji/update', useClass: ep___admin_emoji_update.default }, + { provide: 'ep:admin/federation/delete-all-files', useClass: ep___admin_federation_deleteAllFiles.default }, + { provide: 'ep:admin/federation/refresh-remote-instance-metadata', useClass: ep___admin_federation_refreshRemoteInstanceMetadata.default }, + { provide: 'ep:admin/federation/remove-all-following', useClass: ep___admin_federation_removeAllFollowing.default }, + { provide: 'ep:admin/federation/update-instance', useClass: ep___admin_federation_updateInstance.default }, + { provide: 'ep:admin/get-index-stats', useClass: ep___admin_getIndexStats.default }, + { provide: 'ep:admin/get-table-stats', useClass: ep___admin_getTableStats.default }, + { provide: 'ep:admin/get-user-ips', useClass: ep___admin_getUserIps.default }, + { provide: 'ep:admin/invite', useClass: ep___admin_invite.default }, + { provide: 'ep:admin/moderators/add', useClass: ep___admin_moderators_add.default }, + { provide: 'ep:admin/moderators/remove', useClass: ep___admin_moderators_remove.default }, + { provide: 'ep:admin/promo/create', useClass: ep___admin_promo_create.default }, + { provide: 'ep:admin/queue/clear', useClass: ep___admin_queue_clear.default }, + { provide: 'ep:admin/queue/deliver-delayed', useClass: ep___admin_queue_deliverDelayed.default }, + { provide: 'ep:admin/queue/inbox-delayed', useClass: ep___admin_queue_inboxDelayed.default }, + { provide: 'ep:admin/queue/stats', useClass: ep___admin_queue_stats.default }, + { provide: 'ep:admin/relays/add', useClass: ep___admin_relays_add.default }, + { provide: 'ep:admin/relays/list', useClass: ep___admin_relays_list.default }, + { provide: 'ep:admin/relays/remove', useClass: ep___admin_relays_remove.default }, + { provide: 'ep:admin/reset-password', useClass: ep___admin_resetPassword.default }, + { provide: 'ep:admin/resolve-abuse-user-report', useClass: ep___admin_resolveAbuseUserReport.default }, + { provide: 'ep:admin/send-email', useClass: ep___admin_sendEmail.default }, + { provide: 'ep:admin/server-info', useClass: ep___admin_serverInfo.default }, + { provide: 'ep:admin/show-moderation-logs', useClass: ep___admin_showModerationLogs.default }, + { provide: 'ep:admin/show-user', useClass: ep___admin_showUser.default }, + { provide: 'ep:admin/show-users', useClass: ep___admin_showUsers.default }, + { provide: 'ep:admin/silence-user', useClass: ep___admin_silenceUser.default }, + { provide: 'ep:admin/suspend-user', useClass: ep___admin_suspendUser.default }, + { provide: 'ep:admin/unsilence-user', useClass: ep___admin_unsilenceUser.default }, + { provide: 'ep:admin/unsuspend-user', useClass: ep___admin_unsuspendUser.default }, + { provide: 'ep:admin/update-meta', useClass: ep___admin_updateMeta.default }, + { provide: 'ep:admin/delete-account', useClass: ep___admin_deleteAccount.default }, + { provide: 'ep:admin/update-user-note', useClass: ep___admin_updateUserNote.default }, + { provide: 'ep:announcements', useClass: ep___announcements.default }, + { provide: 'ep:antennas/create', useClass: ep___antennas_create.default }, + { provide: 'ep:antennas/delete', useClass: ep___antennas_delete.default }, + { provide: 'ep:antennas/list', useClass: ep___antennas_list.default }, + { provide: 'ep:antennas/notes', useClass: ep___antennas_notes.default }, + { provide: 'ep:antennas/show', useClass: ep___antennas_show.default }, + { provide: 'ep:antennas/update', useClass: ep___antennas_update.default }, + { provide: 'ep:ap/get', useClass: ep___ap_get.default }, + { provide: 'ep:ap/show', useClass: ep___ap_show.default }, + { provide: 'ep:app/create', useClass: ep___app_create.default }, + { provide: 'ep:app/show', useClass: ep___app_show.default }, + { provide: 'ep:auth/accept', useClass: ep___auth_accept.default }, + { provide: 'ep:auth/session/generate', useClass: ep___auth_session_generate.default }, + { provide: 'ep:auth/session/show', useClass: ep___auth_session_show.default }, + { provide: 'ep:auth/session/userkey', useClass: ep___auth_session_userkey.default }, + { provide: 'ep:blocking/create', useClass: ep___blocking_create.default }, + { provide: 'ep:blocking/delete', useClass: ep___blocking_delete.default }, + { provide: 'ep:blocking/list', useClass: ep___blocking_list.default }, + { provide: 'ep:channels/create', useClass: ep___channels_create.default }, + { provide: 'ep:channels/featured', useClass: ep___channels_featured.default }, + { provide: 'ep:channels/follow', useClass: ep___channels_follow.default }, + { provide: 'ep:channels/followed', useClass: ep___channels_followed.default }, + { provide: 'ep:channels/owned', useClass: ep___channels_owned.default }, + { provide: 'ep:channels/show', useClass: ep___channels_show.default }, + { provide: 'ep:channels/timeline', useClass: ep___channels_timeline.default }, + { provide: 'ep:channels/unfollow', useClass: ep___channels_unfollow.default }, + { provide: 'ep:channels/update', useClass: ep___channels_update.default }, + { provide: 'ep:charts/active-users', useClass: ep___charts_activeUsers.default }, + { provide: 'ep:charts/ap-request', useClass: ep___charts_apRequest.default }, + { provide: 'ep:charts/drive', useClass: ep___charts_drive.default }, + { provide: 'ep:charts/federation', useClass: ep___charts_federation.default }, + { provide: 'ep:charts/hashtag', useClass: ep___charts_hashtag.default }, + { provide: 'ep:charts/instance', useClass: ep___charts_instance.default }, + { provide: 'ep:charts/notes', useClass: ep___charts_notes.default }, + { provide: 'ep:charts/user/drive', useClass: ep___charts_user_drive.default }, + { provide: 'ep:charts/user/following', useClass: ep___charts_user_following.default }, + { provide: 'ep:charts/user/notes', useClass: ep___charts_user_notes.default }, + { provide: 'ep:charts/user/reactions', useClass: ep___charts_user_reactions.default }, + { provide: 'ep:charts/users', useClass: ep___charts_users.default }, + { provide: 'ep:clips/add-note', useClass: ep___clips_addNote.default }, + { provide: 'ep:clips/remove-note', useClass: ep___clips_removeNote.default }, + { provide: 'ep:clips/create', useClass: ep___clips_create.default }, + { provide: 'ep:clips/delete', useClass: ep___clips_delete.default }, + { provide: 'ep:clips/list', useClass: ep___clips_list.default }, + { provide: 'ep:clips/notes', useClass: ep___clips_notes.default }, + { provide: 'ep:clips/show', useClass: ep___clips_show.default }, + { provide: 'ep:clips/update', useClass: ep___clips_update.default }, + { provide: 'ep:drive', useClass: ep___drive.default }, + { provide: 'ep:drive/files', useClass: ep___drive_files.default }, + { provide: 'ep:drive/files/attached-notes', useClass: ep___drive_files_attachedNotes.default }, + { provide: 'ep:drive/files/check-existence', useClass: ep___drive_files_checkExistence.default }, + { provide: 'ep:drive/files/create', useClass: ep___drive_files_create.default }, + { provide: 'ep:drive/files/delete', useClass: ep___drive_files_delete.default }, + { provide: 'ep:drive/files/find-by-hash', useClass: ep___drive_files_findByHash.default }, + { provide: 'ep:drive/files/find', useClass: ep___drive_files_find.default }, + { provide: 'ep:drive/files/show', useClass: ep___drive_files_show.default }, + { provide: 'ep:drive/files/update', useClass: ep___drive_files_update.default }, + { provide: 'ep:drive/files/upload-from-url', useClass: ep___drive_files_uploadFromUrl.default }, + { provide: 'ep:drive/folders', useClass: ep___drive_folders.default }, + { provide: 'ep:drive/folders/create', useClass: ep___drive_folders_create.default }, + { provide: 'ep:drive/folders/delete', useClass: ep___drive_folders_delete.default }, + { provide: 'ep:drive/folders/find', useClass: ep___drive_folders_find.default }, + { provide: 'ep:drive/folders/show', useClass: ep___drive_folders_show.default }, + { provide: 'ep:drive/folders/update', useClass: ep___drive_folders_update.default }, + { provide: 'ep:drive/stream', useClass: ep___drive_stream.default }, + { provide: 'ep:email-address/available', useClass: ep___emailAddress_available.default }, + { provide: 'ep:endpoint', useClass: ep___endpoint.default }, + { provide: 'ep:endpoints', useClass: ep___endpoints.default }, + { provide: 'ep:export-custom-emojis', useClass: ep___exportCustomEmojis.default }, + { provide: 'ep:federation/followers', useClass: ep___federation_followers.default }, + { provide: 'ep:federation/following', useClass: ep___federation_following.default }, + { provide: 'ep:federation/instances', useClass: ep___federation_instances.default }, + { provide: 'ep:federation/show-instance', useClass: ep___federation_showInstance.default }, + { provide: 'ep:federation/update-remote-user', useClass: ep___federation_updateRemoteUser.default }, + { provide: 'ep:federation/users', useClass: ep___federation_users.default }, + { provide: 'ep:federation/stats', useClass: ep___federation_stats.default }, + { provide: 'ep:following/create', useClass: ep___following_create.default }, + { provide: 'ep:following/delete', useClass: ep___following_delete.default }, + { provide: 'ep:following/invalidate', useClass: ep___following_invalidate.default }, + { provide: 'ep:following/requests/accept', useClass: ep___following_requests_accept.default }, + { provide: 'ep:following/requests/cancel', useClass: ep___following_requests_cancel.default }, + { provide: 'ep:following/requests/list', useClass: ep___following_requests_list.default }, + { provide: 'ep:following/requests/reject', useClass: ep___following_requests_reject.default }, + { provide: 'ep:gallery/featured', useClass: ep___gallery_featured.default }, + { provide: 'ep:gallery/popular', useClass: ep___gallery_popular.default }, + { provide: 'ep:gallery/posts', useClass: ep___gallery_posts.default }, + { provide: 'ep:gallery/posts/create', useClass: ep___gallery_posts_create.default }, + { provide: 'ep:gallery/posts/delete', useClass: ep___gallery_posts_delete.default }, + { provide: 'ep:gallery/posts/like', useClass: ep___gallery_posts_like.default }, + { provide: 'ep:gallery/posts/show', useClass: ep___gallery_posts_show.default }, + { provide: 'ep:gallery/posts/unlike', useClass: ep___gallery_posts_unlike.default }, + { provide: 'ep:gallery/posts/update', useClass: ep___gallery_posts_update.default }, + { provide: 'ep:get-online-users-count', useClass: ep___getOnlineUsersCount.default }, + { provide: 'ep:hashtags/list', useClass: ep___hashtags_list.default }, + { provide: 'ep:hashtags/search', useClass: ep___hashtags_search.default }, + { provide: 'ep:hashtags/show', useClass: ep___hashtags_show.default }, + { provide: 'ep:hashtags/trend', useClass: ep___hashtags_trend.default }, + { provide: 'ep:hashtags/users', useClass: ep___hashtags_users.default }, + { provide: 'ep:i', useClass: ep___i.default }, + { provide: 'ep:i/2fa/done', useClass: ep___i_2fa_done.default }, + { provide: 'ep:i/2fa/key-done', useClass: ep___i_2fa_keyDone.default }, + { provide: 'ep:i/2fa/password-less', useClass: ep___i_2fa_passwordLess.default }, + { provide: 'ep:i/2fa/register-key', useClass: ep___i_2fa_registerKey.default }, + { provide: 'ep:i/2fa/register', useClass: ep___i_2fa_register.default }, + { provide: 'ep:i/2fa/remove-key', useClass: ep___i_2fa_removeKey.default }, + { provide: 'ep:i/2fa/unregister', useClass: ep___i_2fa_unregister.default }, + { provide: 'ep:i/apps', useClass: ep___i_apps.default }, + { provide: 'ep:i/authorized-apps', useClass: ep___i_authorizedApps.default }, + { provide: 'ep:i/change-password', useClass: ep___i_changePassword.default }, + { provide: 'ep:i/delete-account', useClass: ep___i_deleteAccount.default }, + { provide: 'ep:i/export-blocking', useClass: ep___i_exportBlocking.default }, + { provide: 'ep:i/export-following', useClass: ep___i_exportFollowing.default }, + { provide: 'ep:i/export-mute', useClass: ep___i_exportMute.default }, + { provide: 'ep:i/export-notes', useClass: ep___i_exportNotes.default }, + { provide: 'ep:i/export-user-lists', useClass: ep___i_exportUserLists.default }, + { provide: 'ep:i/favorites', useClass: ep___i_favorites.default }, + { provide: 'ep:i/gallery/likes', useClass: ep___i_gallery_likes.default }, + { provide: 'ep:i/gallery/posts', useClass: ep___i_gallery_posts.default }, + { provide: 'ep:i/get-word-muted-notes-count', useClass: ep___i_getWordMutedNotesCount.default }, + { provide: 'ep:i/import-blocking', useClass: ep___i_importBlocking.default }, + { provide: 'ep:i/import-following', useClass: ep___i_importFollowing.default }, + { provide: 'ep:i/import-muting', useClass: ep___i_importMuting.default }, + { provide: 'ep:i/import-user-lists', useClass: ep___i_importUserLists.default }, + { provide: 'ep:i/notifications', useClass: ep___i_notifications.default }, + { provide: 'ep:i/page-likes', useClass: ep___i_pageLikes.default }, + { provide: 'ep:i/pages', useClass: ep___i_pages.default }, + { provide: 'ep:i/pin', useClass: ep___i_pin.default }, + { provide: 'ep:i/read-all-messaging-messages', useClass: ep___i_readAllMessagingMessages.default }, + { provide: 'ep:i/read-all-unread-notes', useClass: ep___i_readAllUnreadNotes.default }, + { provide: 'ep:i/read-announcement', useClass: ep___i_readAnnouncement.default }, + { provide: 'ep:i/regenerate-token', useClass: ep___i_regenerateToken.default }, + { provide: 'ep:i/registry/get-all', useClass: ep___i_registry_getAll.default }, + { provide: 'ep:i/registry/get-detail', useClass: ep___i_registry_getDetail.default }, + { provide: 'ep:i/registry/get', useClass: ep___i_registry_get.default }, + { provide: 'ep:i/registry/keys-with-type', useClass: ep___i_registry_keysWithType.default }, + { provide: 'ep:i/registry/keys', useClass: ep___i_registry_keys.default }, + { provide: 'ep:i/registry/remove', useClass: ep___i_registry_remove.default }, + { provide: 'ep:i/registry/scopes', useClass: ep___i_registry_scopes.default }, + { provide: 'ep:i/registry/set', useClass: ep___i_registry_set.default }, + { provide: 'ep:i/revoke-token', useClass: ep___i_revokeToken.default }, + { provide: 'ep:i/signin-history', useClass: ep___i_signinHistory.default }, + { provide: 'ep:i/unpin', useClass: ep___i_unpin.default }, + { provide: 'ep:i/update-email', useClass: ep___i_updateEmail.default }, + { provide: 'ep:i/update', useClass: ep___i_update.default }, + { provide: 'ep:i/user-group-invites', useClass: ep___i_userGroupInvites.default }, + { provide: 'ep:i/webhooks/create', useClass: ep___i_webhooks_create.default }, + { provide: 'ep:i/webhooks/list', useClass: ep___i_webhooks_list.default }, + { provide: 'ep:i/webhooks/show', useClass: ep___i_webhooks_show.default }, + { provide: 'ep:i/webhooks/update', useClass: ep___i_webhooks_update.default }, + { provide: 'ep:i/webhooks/delete', useClass: ep___i_webhooks_delete.default }, + { provide: 'ep:messaging/history', useClass: ep___messaging_history.default }, + { provide: 'ep:messaging/messages', useClass: ep___messaging_messages.default }, + { provide: 'ep:messaging/messages/create', useClass: ep___messaging_messages_create.default }, + { provide: 'ep:messaging/messages/delete', useClass: ep___messaging_messages_delete.default }, + { provide: 'ep:messaging/messages/read', useClass: ep___messaging_messages_read.default }, + { provide: 'ep:meta', useClass: ep___meta.default }, + { provide: 'ep:miauth/gen-token', useClass: ep___miauth_genToken.default }, + { provide: 'ep:mute/create', useClass: ep___mute_create.default }, + { provide: 'ep:mute/delete', useClass: ep___mute_delete.default }, + { provide: 'ep:mute/list', useClass: ep___mute_list.default }, + { provide: 'ep:my/apps', useClass: ep___my_apps.default }, + { provide: 'ep:notes', useClass: ep___notes.default }, + { provide: 'ep:notes/children', useClass: ep___notes_children.default }, + { provide: 'ep:notes/clips', useClass: ep___notes_clips.default }, + { provide: 'ep:notes/conversation', useClass: ep___notes_conversation.default }, + { provide: 'ep:notes/create', useClass: ep___notes_create.default }, + { provide: 'ep:notes/delete', useClass: ep___notes_delete.default }, + { provide: 'ep:notes/favorites/create', useClass: ep___notes_favorites_create.default }, + { provide: 'ep:notes/favorites/delete', useClass: ep___notes_favorites_delete.default }, + { provide: 'ep:notes/featured', useClass: ep___notes_featured.default }, + { provide: 'ep:notes/global-timeline', useClass: ep___notes_globalTimeline.default }, + { provide: 'ep:notes/hybrid-timeline', useClass: ep___notes_hybridTimeline.default }, + { provide: 'ep:notes/local-timeline', useClass: ep___notes_localTimeline.default }, + { provide: 'ep:notes/mentions', useClass: ep___notes_mentions.default }, + { provide: 'ep:notes/polls/recommendation', useClass: ep___notes_polls_recommendation.default }, + { provide: 'ep:notes/polls/vote', useClass: ep___notes_polls_vote.default }, + { provide: 'ep:notes/reactions', useClass: ep___notes_reactions.default }, + { provide: 'ep:notes/reactions/create', useClass: ep___notes_reactions_create.default }, + { provide: 'ep:notes/reactions/delete', useClass: ep___notes_reactions_delete.default }, + { provide: 'ep:notes/renotes', useClass: ep___notes_renotes.default }, + { provide: 'ep:notes/replies', useClass: ep___notes_replies.default }, + { provide: 'ep:notes/search-by-tag', useClass: ep___notes_searchByTag.default }, + { provide: 'ep:notes/search', useClass: ep___notes_search.default }, + { provide: 'ep:notes/show', useClass: ep___notes_show.default }, + { provide: 'ep:notes/state', useClass: ep___notes_state.default }, + { provide: 'ep:notes/thread-muting/create', useClass: ep___notes_threadMuting_create.default }, + { provide: 'ep:notes/thread-muting/delete', useClass: ep___notes_threadMuting_delete.default }, + { provide: 'ep:notes/timeline', useClass: ep___notes_timeline.default }, + { provide: 'ep:notes/translate', useClass: ep___notes_translate.default }, + { provide: 'ep:notes/unrenote', useClass: ep___notes_unrenote.default }, + { provide: 'ep:notes/user-list-timeline', useClass: ep___notes_userListTimeline.default }, + { provide: 'ep:notifications/create', useClass: ep___notifications_create.default }, + { provide: 'ep:notifications/mark-all-as-read', useClass: ep___notifications_markAllAsRead.default }, + { provide: 'ep:notifications/read', useClass: ep___notifications_read.default }, + { provide: 'ep:page-push', useClass: ep___pagePush.default }, + { provide: 'ep:pages/create', useClass: ep___pages_create.default }, + { provide: 'ep:pages/delete', useClass: ep___pages_delete.default }, + { provide: 'ep:pages/featured', useClass: ep___pages_featured.default }, + { provide: 'ep:pages/like', useClass: ep___pages_like.default }, + { provide: 'ep:pages/show', useClass: ep___pages_show.default }, + { provide: 'ep:pages/unlike', useClass: ep___pages_unlike.default }, + { provide: 'ep:pages/update', useClass: ep___pages_update.default }, + { provide: 'ep:ping', useClass: ep___ping.default }, + { provide: 'ep:pinned-users', useClass: ep___pinnedUsers.default }, + { provide: 'ep:promo/read', useClass: ep___promo_read.default }, + { provide: 'ep:request-reset-password', useClass: ep___requestResetPassword.default }, + { provide: 'ep:reset-db', useClass: ep___resetDb.default }, + { provide: 'ep:reset-password', useClass: ep___resetPassword.default }, + { provide: 'ep:server-info', useClass: ep___serverInfo.default }, + { provide: 'ep:stats', useClass: ep___stats.default }, + { provide: 'ep:sw/register', useClass: ep___sw_register.default }, + { provide: 'ep:sw/unregister', useClass: ep___sw_unregister.default }, + { provide: 'ep:test', useClass: ep___test.default }, + { provide: 'ep:username/available', useClass: ep___username_available.default }, + { provide: 'ep:users', useClass: ep___users.default }, + { provide: 'ep:users/clips', useClass: ep___users_clips.default }, + { provide: 'ep:users/followers', useClass: ep___users_followers.default }, + { provide: 'ep:users/following', useClass: ep___users_following.default }, + { provide: 'ep:users/gallery/posts', useClass: ep___users_gallery_posts.default }, + { provide: 'ep:users/get-frequently-replied-users', useClass: ep___users_getFrequentlyRepliedUsers.default }, + { provide: 'ep:users/groups/create', useClass: ep___users_groups_create.default }, + { provide: 'ep:users/groups/delete', useClass: ep___users_groups_delete.default }, + { provide: 'ep:users/groups/invitations/accept', useClass: ep___users_groups_invitations_accept.default }, + { provide: 'ep:users/groups/invitations/reject', useClass: ep___users_groups_invitations_reject.default }, + { provide: 'ep:users/groups/invite', useClass: ep___users_groups_invite.default }, + { provide: 'ep:users/groups/joined', useClass: ep___users_groups_joined.default }, + { provide: 'ep:users/groups/leave', useClass: ep___users_groups_leave.default }, + { provide: 'ep:users/groups/owned', useClass: ep___users_groups_owned.default }, + { provide: 'ep:users/groups/pull', useClass: ep___users_groups_pull.default }, + { provide: 'ep:users/groups/show', useClass: ep___users_groups_show.default }, + { provide: 'ep:users/groups/transfer', useClass: ep___users_groups_transfer.default }, + { provide: 'ep:users/groups/update', useClass: ep___users_groups_update.default }, + { provide: 'ep:users/lists/create', useClass: ep___users_lists_create.default }, + { provide: 'ep:users/lists/delete', useClass: ep___users_lists_delete.default }, + { provide: 'ep:users/lists/list', useClass: ep___users_lists_list.default }, + { provide: 'ep:users/lists/pull', useClass: ep___users_lists_pull.default }, + { provide: 'ep:users/lists/push', useClass: ep___users_lists_push.default }, + { provide: 'ep:users/lists/show', useClass: ep___users_lists_show.default }, + { provide: 'ep:users/lists/update', useClass: ep___users_lists_update.default }, + { provide: 'ep:users/notes', useClass: ep___users_notes.default }, + { provide: 'ep:users/pages', useClass: ep___users_pages.default }, + { provide: 'ep:users/reactions', useClass: ep___users_reactions.default }, + { provide: 'ep:users/recommendation', useClass: ep___users_recommendation.default }, + { provide: 'ep:users/relation', useClass: ep___users_relation.default }, + { provide: 'ep:users/report-abuse', useClass: ep___users_reportAbuse.default }, + { provide: 'ep:users/search-by-username-and-host', useClass: ep___users_searchByUsernameAndHost.default }, + { provide: 'ep:users/search', useClass: ep___users_search.default }, + { provide: 'ep:users/show', useClass: ep___users_show.default }, + { provide: 'ep:users/stats', useClass: ep___users_stats.default }, + { provide: 'ep:admin/drive-capacity-override', useClass: ep___admin_driveCapOverride.default }, + { provide: 'ep:fetch-rss', useClass: ep___fetchRss.default }, + ], +}) +export class EndpointsModule {} diff --git a/packages/backend/src/server/api/endpoints.module.ts b/packages/backend/src/server/api/endpoints.module.ts deleted file mode 100644 index 6fb21e6e6e5e..000000000000 --- a/packages/backend/src/server/api/endpoints.module.ts +++ /dev/null @@ -1,640 +0,0 @@ -import { Module } from '@nestjs/common'; - -import * as ep___admin_meta from './endpoints/admin/meta.js'; -import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js'; -import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js'; -import * as ep___admin_accounts_delete from './endpoints/admin/accounts/delete.js'; -import * as ep___admin_ad_create from './endpoints/admin/ad/create.js'; -import * as ep___admin_ad_delete from './endpoints/admin/ad/delete.js'; -import * as ep___admin_ad_list from './endpoints/admin/ad/list.js'; -import * as ep___admin_ad_update from './endpoints/admin/ad/update.js'; -import * as ep___admin_announcements_create from './endpoints/admin/announcements/create.js'; -import * as ep___admin_announcements_delete from './endpoints/admin/announcements/delete.js'; -import * as ep___admin_announcements_list from './endpoints/admin/announcements/list.js'; -import * as ep___admin_announcements_update from './endpoints/admin/announcements/update.js'; -import * as ep___admin_deleteAllFilesOfAUser from './endpoints/admin/delete-all-files-of-a-user.js'; -import * as ep___admin_drive_cleanRemoteFiles from './endpoints/admin/drive/clean-remote-files.js'; -import * as ep___admin_drive_cleanup from './endpoints/admin/drive/cleanup.js'; -import * as ep___admin_drive_files from './endpoints/admin/drive/files.js'; -import * as ep___admin_drive_showFile from './endpoints/admin/drive/show-file.js'; -import * as ep___admin_emoji_addAliasesBulk from './endpoints/admin/emoji/add-aliases-bulk.js'; -import * as ep___admin_emoji_add from './endpoints/admin/emoji/add.js'; -import * as ep___admin_emoji_copy from './endpoints/admin/emoji/copy.js'; -import * as ep___admin_emoji_deleteBulk from './endpoints/admin/emoji/delete-bulk.js'; -import * as ep___admin_emoji_delete from './endpoints/admin/emoji/delete.js'; -import * as ep___admin_emoji_importZip from './endpoints/admin/emoji/import-zip.js'; -import * as ep___admin_emoji_listRemote from './endpoints/admin/emoji/list-remote.js'; -import * as ep___admin_emoji_list from './endpoints/admin/emoji/list.js'; -import * as ep___admin_emoji_removeAliasesBulk from './endpoints/admin/emoji/remove-aliases-bulk.js'; -import * as ep___admin_emoji_setAliasesBulk from './endpoints/admin/emoji/set-aliases-bulk.js'; -import * as ep___admin_emoji_setCategoryBulk from './endpoints/admin/emoji/set-category-bulk.js'; -import * as ep___admin_emoji_update from './endpoints/admin/emoji/update.js'; -import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federation/delete-all-files.js'; -import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js'; -import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js'; -import * as ep___admin_federation_updateInstance from './endpoints/admin/federation/update-instance.js'; -import * as ep___admin_getIndexStats from './endpoints/admin/get-index-stats.js'; -import * as ep___admin_getTableStats from './endpoints/admin/get-table-stats.js'; -import * as ep___admin_getUserIps from './endpoints/admin/get-user-ips.js'; -import * as ep___admin_invite from './endpoints/admin/invite.js'; -import * as ep___admin_moderators_add from './endpoints/admin/moderators/add.js'; -import * as ep___admin_moderators_remove from './endpoints/admin/moderators/remove.js'; -import * as ep___admin_promo_create from './endpoints/admin/promo/create.js'; -import * as ep___admin_queue_clear from './endpoints/admin/queue/clear.js'; -import * as ep___admin_queue_deliverDelayed from './endpoints/admin/queue/deliver-delayed.js'; -import * as ep___admin_queue_inboxDelayed from './endpoints/admin/queue/inbox-delayed.js'; -import * as ep___admin_queue_stats from './endpoints/admin/queue/stats.js'; -import * as ep___admin_relays_add from './endpoints/admin/relays/add.js'; -import * as ep___admin_relays_list from './endpoints/admin/relays/list.js'; -import * as ep___admin_relays_remove from './endpoints/admin/relays/remove.js'; -import * as ep___admin_resetPassword from './endpoints/admin/reset-password.js'; -import * as ep___admin_resolveAbuseUserReport from './endpoints/admin/resolve-abuse-user-report.js'; -import * as ep___admin_sendEmail from './endpoints/admin/send-email.js'; -import * as ep___admin_serverInfo from './endpoints/admin/server-info.js'; -import * as ep___admin_showModerationLogs from './endpoints/admin/show-moderation-logs.js'; -import * as ep___admin_showUser from './endpoints/admin/show-user.js'; -import * as ep___admin_showUsers from './endpoints/admin/show-users.js'; -import * as ep___admin_silenceUser from './endpoints/admin/silence-user.js'; -import * as ep___admin_suspendUser from './endpoints/admin/suspend-user.js'; -import * as ep___admin_unsilenceUser from './endpoints/admin/unsilence-user.js'; -import * as ep___admin_unsuspendUser from './endpoints/admin/unsuspend-user.js'; -import * as ep___admin_updateMeta from './endpoints/admin/update-meta.js'; -import * as ep___admin_vacuum from './endpoints/admin/vacuum.js'; -import * as ep___admin_deleteAccount from './endpoints/admin/delete-account.js'; -import * as ep___admin_updateUserNote from './endpoints/admin/update-user-note.js'; -import * as ep___announcements from './endpoints/announcements.js'; -import * as ep___antennas_create from './endpoints/antennas/create.js'; -import * as ep___antennas_delete from './endpoints/antennas/delete.js'; -import * as ep___antennas_list from './endpoints/antennas/list.js'; -import * as ep___antennas_notes from './endpoints/antennas/notes.js'; -import * as ep___antennas_show from './endpoints/antennas/show.js'; -import * as ep___antennas_update from './endpoints/antennas/update.js'; -import * as ep___ap_get from './endpoints/ap/get.js'; -import * as ep___ap_show from './endpoints/ap/show.js'; -import * as ep___app_create from './endpoints/app/create.js'; -import * as ep___app_show from './endpoints/app/show.js'; -import * as ep___auth_accept from './endpoints/auth/accept.js'; -import * as ep___auth_session_generate from './endpoints/auth/session/generate.js'; -import * as ep___auth_session_show from './endpoints/auth/session/show.js'; -import * as ep___auth_session_userkey from './endpoints/auth/session/userkey.js'; -import * as ep___blocking_create from './endpoints/blocking/create.js'; -import * as ep___blocking_delete from './endpoints/blocking/delete.js'; -import * as ep___blocking_list from './endpoints/blocking/list.js'; -import * as ep___channels_create from './endpoints/channels/create.js'; -import * as ep___channels_featured from './endpoints/channels/featured.js'; -import * as ep___channels_follow from './endpoints/channels/follow.js'; -import * as ep___channels_followed from './endpoints/channels/followed.js'; -import * as ep___channels_owned from './endpoints/channels/owned.js'; -import * as ep___channels_show from './endpoints/channels/show.js'; -import * as ep___channels_timeline from './endpoints/channels/timeline.js'; -import * as ep___channels_unfollow from './endpoints/channels/unfollow.js'; -import * as ep___channels_update from './endpoints/channels/update.js'; -import * as ep___charts_activeUsers from './endpoints/charts/active-users.js'; -import * as ep___charts_apRequest from './endpoints/charts/ap-request.js'; -import * as ep___charts_drive from './endpoints/charts/drive.js'; -import * as ep___charts_federation from './endpoints/charts/federation.js'; -import * as ep___charts_hashtag from './endpoints/charts/hashtag.js'; -import * as ep___charts_instance from './endpoints/charts/instance.js'; -import * as ep___charts_notes from './endpoints/charts/notes.js'; -import * as ep___charts_user_drive from './endpoints/charts/user/drive.js'; -import * as ep___charts_user_following from './endpoints/charts/user/following.js'; -import * as ep___charts_user_notes from './endpoints/charts/user/notes.js'; -import * as ep___charts_user_reactions from './endpoints/charts/user/reactions.js'; -import * as ep___charts_users from './endpoints/charts/users.js'; -import * as ep___clips_addNote from './endpoints/clips/add-note.js'; -import * as ep___clips_removeNote from './endpoints/clips/remove-note.js'; -import * as ep___clips_create from './endpoints/clips/create.js'; -import * as ep___clips_delete from './endpoints/clips/delete.js'; -import * as ep___clips_list from './endpoints/clips/list.js'; -import * as ep___clips_notes from './endpoints/clips/notes.js'; -import * as ep___clips_show from './endpoints/clips/show.js'; -import * as ep___clips_update from './endpoints/clips/update.js'; -import * as ep___drive from './endpoints/drive.js'; -import * as ep___drive_files from './endpoints/drive/files.js'; -import * as ep___drive_files_attachedNotes from './endpoints/drive/files/attached-notes.js'; -import * as ep___drive_files_checkExistence from './endpoints/drive/files/check-existence.js'; -import * as ep___drive_files_create from './endpoints/drive/files/create.js'; -import * as ep___drive_files_delete from './endpoints/drive/files/delete.js'; -import * as ep___drive_files_findByHash from './endpoints/drive/files/find-by-hash.js'; -import * as ep___drive_files_find from './endpoints/drive/files/find.js'; -import * as ep___drive_files_show from './endpoints/drive/files/show.js'; -import * as ep___drive_files_update from './endpoints/drive/files/update.js'; -import * as ep___drive_files_uploadFromUrl from './endpoints/drive/files/upload-from-url.js'; -import * as ep___drive_folders from './endpoints/drive/folders.js'; -import * as ep___drive_folders_create from './endpoints/drive/folders/create.js'; -import * as ep___drive_folders_delete from './endpoints/drive/folders/delete.js'; -import * as ep___drive_folders_find from './endpoints/drive/folders/find.js'; -import * as ep___drive_folders_show from './endpoints/drive/folders/show.js'; -import * as ep___drive_folders_update from './endpoints/drive/folders/update.js'; -import * as ep___drive_stream from './endpoints/drive/stream.js'; -import * as ep___emailAddress_available from './endpoints/email-address/available.js'; -import * as ep___endpoint from './endpoints/endpoint.js'; -import * as ep___endpoints from './endpoints/endpoints.js'; -import * as ep___exportCustomEmojis from './endpoints/export-custom-emojis.js'; -import * as ep___federation_followers from './endpoints/federation/followers.js'; -import * as ep___federation_following from './endpoints/federation/following.js'; -import * as ep___federation_instances from './endpoints/federation/instances.js'; -import * as ep___federation_showInstance from './endpoints/federation/show-instance.js'; -import * as ep___federation_updateRemoteUser from './endpoints/federation/update-remote-user.js'; -import * as ep___federation_users from './endpoints/federation/users.js'; -import * as ep___federation_stats from './endpoints/federation/stats.js'; -import * as ep___following_create from './endpoints/following/create.js'; -import * as ep___following_delete from './endpoints/following/delete.js'; -import * as ep___following_invalidate from './endpoints/following/invalidate.js'; -import * as ep___following_requests_accept from './endpoints/following/requests/accept.js'; -import * as ep___following_requests_cancel from './endpoints/following/requests/cancel.js'; -import * as ep___following_requests_list from './endpoints/following/requests/list.js'; -import * as ep___following_requests_reject from './endpoints/following/requests/reject.js'; -import * as ep___gallery_featured from './endpoints/gallery/featured.js'; -import * as ep___gallery_popular from './endpoints/gallery/popular.js'; -import * as ep___gallery_posts from './endpoints/gallery/posts.js'; -import * as ep___gallery_posts_create from './endpoints/gallery/posts/create.js'; -import * as ep___gallery_posts_delete from './endpoints/gallery/posts/delete.js'; -import * as ep___gallery_posts_like from './endpoints/gallery/posts/like.js'; -import * as ep___gallery_posts_show from './endpoints/gallery/posts/show.js'; -import * as ep___gallery_posts_unlike from './endpoints/gallery/posts/unlike.js'; -import * as ep___gallery_posts_update from './endpoints/gallery/posts/update.js'; -import * as ep___getOnlineUsersCount from './endpoints/get-online-users-count.js'; -import * as ep___hashtags_list from './endpoints/hashtags/list.js'; -import * as ep___hashtags_search from './endpoints/hashtags/search.js'; -import * as ep___hashtags_show from './endpoints/hashtags/show.js'; -import * as ep___hashtags_trend from './endpoints/hashtags/trend.js'; -import * as ep___hashtags_users from './endpoints/hashtags/users.js'; -import * as ep___i from './endpoints/i.js'; -import * as ep___i_2fa_done from './endpoints/i/2fa/done.js'; -import * as ep___i_2fa_keyDone from './endpoints/i/2fa/key-done.js'; -import * as ep___i_2fa_passwordLess from './endpoints/i/2fa/password-less.js'; -import * as ep___i_2fa_registerKey from './endpoints/i/2fa/register-key.js'; -import * as ep___i_2fa_register from './endpoints/i/2fa/register.js'; -import * as ep___i_2fa_removeKey from './endpoints/i/2fa/remove-key.js'; -import * as ep___i_2fa_unregister from './endpoints/i/2fa/unregister.js'; -import * as ep___i_apps from './endpoints/i/apps.js'; -import * as ep___i_authorizedApps from './endpoints/i/authorized-apps.js'; -import * as ep___i_changePassword from './endpoints/i/change-password.js'; -import * as ep___i_deleteAccount from './endpoints/i/delete-account.js'; -import * as ep___i_exportBlocking from './endpoints/i/export-blocking.js'; -import * as ep___i_exportFollowing from './endpoints/i/export-following.js'; -import * as ep___i_exportMute from './endpoints/i/export-mute.js'; -import * as ep___i_exportNotes from './endpoints/i/export-notes.js'; -import * as ep___i_exportUserLists from './endpoints/i/export-user-lists.js'; -import * as ep___i_favorites from './endpoints/i/favorites.js'; -import * as ep___i_gallery_likes from './endpoints/i/gallery/likes.js'; -import * as ep___i_gallery_posts from './endpoints/i/gallery/posts.js'; -import * as ep___i_getWordMutedNotesCount from './endpoints/i/get-word-muted-notes-count.js'; -import * as ep___i_importBlocking from './endpoints/i/import-blocking.js'; -import * as ep___i_importFollowing from './endpoints/i/import-following.js'; -import * as ep___i_importMuting from './endpoints/i/import-muting.js'; -import * as ep___i_importUserLists from './endpoints/i/import-user-lists.js'; -import * as ep___i_notifications from './endpoints/i/notifications.js'; -import * as ep___i_pageLikes from './endpoints/i/page-likes.js'; -import * as ep___i_pages from './endpoints/i/pages.js'; -import * as ep___i_pin from './endpoints/i/pin.js'; -import * as ep___i_readAllMessagingMessages from './endpoints/i/read-all-messaging-messages.js'; -import * as ep___i_readAllUnreadNotes from './endpoints/i/read-all-unread-notes.js'; -import * as ep___i_readAnnouncement from './endpoints/i/read-announcement.js'; -import * as ep___i_regenerateToken from './endpoints/i/regenerate-token.js'; -import * as ep___i_registry_getAll from './endpoints/i/registry/get-all.js'; -import * as ep___i_registry_getDetail from './endpoints/i/registry/get-detail.js'; -import * as ep___i_registry_get from './endpoints/i/registry/get.js'; -import * as ep___i_registry_keysWithType from './endpoints/i/registry/keys-with-type.js'; -import * as ep___i_registry_keys from './endpoints/i/registry/keys.js'; -import * as ep___i_registry_remove from './endpoints/i/registry/remove.js'; -import * as ep___i_registry_scopes from './endpoints/i/registry/scopes.js'; -import * as ep___i_registry_set from './endpoints/i/registry/set.js'; -import * as ep___i_revokeToken from './endpoints/i/revoke-token.js'; -import * as ep___i_signinHistory from './endpoints/i/signin-history.js'; -import * as ep___i_unpin from './endpoints/i/unpin.js'; -import * as ep___i_updateEmail from './endpoints/i/update-email.js'; -import * as ep___i_update from './endpoints/i/update.js'; -import * as ep___i_userGroupInvites from './endpoints/i/user-group-invites.js'; -import * as ep___i_webhooks_create from './endpoints/i/webhooks/create.js'; -import * as ep___i_webhooks_show from './endpoints/i/webhooks/show.js'; -import * as ep___i_webhooks_list from './endpoints/i/webhooks/list.js'; -import * as ep___i_webhooks_update from './endpoints/i/webhooks/update.js'; -import * as ep___i_webhooks_delete from './endpoints/i/webhooks/delete.js'; -import * as ep___messaging_history from './endpoints/messaging/history.js'; -import * as ep___messaging_messages from './endpoints/messaging/messages.js'; -import * as ep___messaging_messages_create from './endpoints/messaging/messages/create.js'; -import * as ep___messaging_messages_delete from './endpoints/messaging/messages/delete.js'; -import * as ep___messaging_messages_read from './endpoints/messaging/messages/read.js'; -import * as ep___meta from './endpoints/meta.js'; -import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; -import * as ep___mute_create from './endpoints/mute/create.js'; -import * as ep___mute_delete from './endpoints/mute/delete.js'; -import * as ep___mute_list from './endpoints/mute/list.js'; -import * as ep___my_apps from './endpoints/my/apps.js'; -import * as ep___notes from './endpoints/notes.js'; -import * as ep___notes_children from './endpoints/notes/children.js'; -import * as ep___notes_clips from './endpoints/notes/clips.js'; -import * as ep___notes_conversation from './endpoints/notes/conversation.js'; -import * as ep___notes_create from './endpoints/notes/create.js'; -import * as ep___notes_delete from './endpoints/notes/delete.js'; -import * as ep___notes_favorites_create from './endpoints/notes/favorites/create.js'; -import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js'; -import * as ep___notes_featured from './endpoints/notes/featured.js'; -import * as ep___notes_globalTimeline from './endpoints/notes/global-timeline.js'; -import * as ep___notes_hybridTimeline from './endpoints/notes/hybrid-timeline.js'; -import * as ep___notes_localTimeline from './endpoints/notes/local-timeline.js'; -import * as ep___notes_mentions from './endpoints/notes/mentions.js'; -import * as ep___notes_polls_recommendation from './endpoints/notes/polls/recommendation.js'; -import * as ep___notes_polls_vote from './endpoints/notes/polls/vote.js'; -import * as ep___notes_reactions from './endpoints/notes/reactions.js'; -import * as ep___notes_reactions_create from './endpoints/notes/reactions/create.js'; -import * as ep___notes_reactions_delete from './endpoints/notes/reactions/delete.js'; -import * as ep___notes_renotes from './endpoints/notes/renotes.js'; -import * as ep___notes_replies from './endpoints/notes/replies.js'; -import * as ep___notes_searchByTag from './endpoints/notes/search-by-tag.js'; -import * as ep___notes_search from './endpoints/notes/search.js'; -import * as ep___notes_show from './endpoints/notes/show.js'; -import * as ep___notes_state from './endpoints/notes/state.js'; -import * as ep___notes_threadMuting_create from './endpoints/notes/thread-muting/create.js'; -import * as ep___notes_threadMuting_delete from './endpoints/notes/thread-muting/delete.js'; -import * as ep___notes_timeline from './endpoints/notes/timeline.js'; -import * as ep___notes_translate from './endpoints/notes/translate.js'; -import * as ep___notes_unrenote from './endpoints/notes/unrenote.js'; -import * as ep___notes_userListTimeline from './endpoints/notes/user-list-timeline.js'; -import * as ep___notes_watching_create from './endpoints/notes/watching/create.js'; -import * as ep___notes_watching_delete from './endpoints/notes/watching/delete.js'; -import * as ep___notifications_create from './endpoints/notifications/create.js'; -import * as ep___notifications_markAllAsRead from './endpoints/notifications/mark-all-as-read.js'; -import * as ep___notifications_read from './endpoints/notifications/read.js'; -import * as ep___pagePush from './endpoints/page-push.js'; -import * as ep___pages_create from './endpoints/pages/create.js'; -import * as ep___pages_delete from './endpoints/pages/delete.js'; -import * as ep___pages_featured from './endpoints/pages/featured.js'; -import * as ep___pages_like from './endpoints/pages/like.js'; -import * as ep___pages_show from './endpoints/pages/show.js'; -import * as ep___pages_unlike from './endpoints/pages/unlike.js'; -import * as ep___pages_update from './endpoints/pages/update.js'; -import * as ep___ping from './endpoints/ping.js'; -import * as ep___pinnedUsers from './endpoints/pinned-users.js'; -import * as ep___promo_read from './endpoints/promo/read.js'; -import * as ep___requestResetPassword from './endpoints/request-reset-password.js'; -import * as ep___resetDb from './endpoints/reset-db.js'; -import * as ep___resetPassword from './endpoints/reset-password.js'; -import * as ep___serverInfo from './endpoints/server-info.js'; -import * as ep___stats from './endpoints/stats.js'; -import * as ep___sw_register from './endpoints/sw/register.js'; -import * as ep___sw_unregister from './endpoints/sw/unregister.js'; -import * as ep___test from './endpoints/test.js'; -import * as ep___username_available from './endpoints/username/available.js'; -import * as ep___users from './endpoints/users.js'; -import * as ep___users_clips from './endpoints/users/clips.js'; -import * as ep___users_followers from './endpoints/users/followers.js'; -import * as ep___users_following from './endpoints/users/following.js'; -import * as ep___users_gallery_posts from './endpoints/users/gallery/posts.js'; -import * as ep___users_getFrequentlyRepliedUsers from './endpoints/users/get-frequently-replied-users.js'; -import * as ep___users_groups_create from './endpoints/users/groups/create.js'; -import * as ep___users_groups_delete from './endpoints/users/groups/delete.js'; -import * as ep___users_groups_invitations_accept from './endpoints/users/groups/invitations/accept.js'; -import * as ep___users_groups_invitations_reject from './endpoints/users/groups/invitations/reject.js'; -import * as ep___users_groups_invite from './endpoints/users/groups/invite.js'; -import * as ep___users_groups_joined from './endpoints/users/groups/joined.js'; -import * as ep___users_groups_leave from './endpoints/users/groups/leave.js'; -import * as ep___users_groups_owned from './endpoints/users/groups/owned.js'; -import * as ep___users_groups_pull from './endpoints/users/groups/pull.js'; -import * as ep___users_groups_show from './endpoints/users/groups/show.js'; -import * as ep___users_groups_transfer from './endpoints/users/groups/transfer.js'; -import * as ep___users_groups_update from './endpoints/users/groups/update.js'; -import * as ep___users_lists_create from './endpoints/users/lists/create.js'; -import * as ep___users_lists_delete from './endpoints/users/lists/delete.js'; -import * as ep___users_lists_list from './endpoints/users/lists/list.js'; -import * as ep___users_lists_pull from './endpoints/users/lists/pull.js'; -import * as ep___users_lists_push from './endpoints/users/lists/push.js'; -import * as ep___users_lists_show from './endpoints/users/lists/show.js'; -import * as ep___users_lists_update from './endpoints/users/lists/update.js'; -import * as ep___users_notes from './endpoints/users/notes.js'; -import * as ep___users_pages from './endpoints/users/pages.js'; -import * as ep___users_reactions from './endpoints/users/reactions.js'; -import * as ep___users_recommendation from './endpoints/users/recommendation.js'; -import * as ep___users_relation from './endpoints/users/relation.js'; -import * as ep___users_reportAbuse from './endpoints/users/report-abuse.js'; -import * as ep___users_searchByUsernameAndHost from './endpoints/users/search-by-username-and-host.js'; -import * as ep___users_search from './endpoints/users/search.js'; -import * as ep___users_show from './endpoints/users/show.js'; -import * as ep___users_stats from './endpoints/users/stats.js'; -import * as ep___fetchRss from './endpoints/fetch-rss.js'; -import * as ep___admin_driveCapOverride from './endpoints/admin/drive-capacity-override.js'; - -@Module({ - imports: [ - ], - providers: [ - { provide: 'ep:admin/meta', useClass: ep___admin_meta.default }, - { provide: 'ep:admin/abuse-user-reports', useClass: ep___admin_abuseUserReports.default }, - { provide: 'ep:admin/accounts/create', useClass: ep___admin_accounts_create.default }, - { provide: 'ep:admin/accounts/delete', useClass: ep___admin_accounts_delete.default }, - { provide: 'ep:admin/ad/create', useClass: ep___admin_ad_create.default }, - { provide: 'ep:admin/ad/delete', useClass: ep___admin_ad_delete.default }, - { provide: 'ep:admin/ad/list', useClass: ep___admin_ad_list.default }, - { provide: 'ep:admin/ad/update', useClass: ep___admin_ad_update.default }, - { provide: 'ep:admin/announcements/create', useClass: ep___admin_announcements_create.default }, - { provide: 'ep:admin/announcements/delete', useClass: ep___admin_announcements_delete.default }, - { provide: 'ep:admin/announcements/list', useClass: ep___admin_announcements_list.default }, - { provide: 'ep:admin/announcements/update', useClass: ep___admin_announcements_update.default }, - { provide: 'ep:admin/delete-all-files-of-a-user', useClass: ep___admin_deleteAllFilesOfAUser.default }, - { provide: 'ep:admin/drive/clean-remote-files', useClass: ep___admin_drive_cleanRemoteFiles.default }, - { provide: 'ep:admin/drive/cleanup', useClass: ep___admin_drive_cleanup.default }, - { provide: 'ep:admin/drive/files', useClass: ep___admin_drive_files.default }, - { provide: 'ep:admin/drive/show-file', useClass: ep___admin_drive_showFile.default }, - { provide: 'ep:admin/emoji/add-aliases-bulk', useClass: ep___admin_emoji_addAliasesBulk.default }, - { provide: 'ep:admin/emoji/add', useClass: ep___admin_emoji_add.default }, - { provide: 'ep:admin/emoji/copy', useClass: ep___admin_emoji_copy.default }, - { provide: 'ep:admin/emoji/delete-bulk', useClass: ep___admin_emoji_deleteBulk.default }, - { provide: 'ep:admin/emoji/delete', useClass: ep___admin_emoji_delete.default }, - { provide: 'ep:admin/emoji/import-zip', useClass: ep___admin_emoji_importZip.default }, - { provide: 'ep:admin/emoji/list-remote', useClass: ep___admin_emoji_listRemote.default }, - { provide: 'ep:admin/emoji/list', useClass: ep___admin_emoji_list.default }, - { provide: 'ep:admin/emoji/remove-aliases-bulk', useClass: ep___admin_emoji_removeAliasesBulk.default }, - { provide: 'ep:admin/emoji/set-aliases-bulk', useClass: ep___admin_emoji_setAliasesBulk.default }, - { provide: 'ep:admin/emoji/set-category-bulk', useClass: ep___admin_emoji_setCategoryBulk.default }, - { provide: 'ep:admin/emoji/update', useClass: ep___admin_emoji_update.default }, - { provide: 'ep:admin/federation/delete-all-files', useClass: ep___admin_federation_deleteAllFiles.default }, - { provide: 'ep:admin/federation/refresh-remote-instance-metadata', useClass: ep___admin_federation_refreshRemoteInstanceMetadata.default }, - { provide: 'ep:admin/federation/remove-all-following', useClass: ep___admin_federation_removeAllFollowing.default }, - { provide: 'ep:admin/federation/update-instance', useClass: ep___admin_federation_updateInstance.default }, - { provide: 'ep:admin/get-index-stats', useClass: ep___admin_getIndexStats.default }, - { provide: 'ep:admin/get-table-stats', useClass: ep___admin_getTableStats.default }, - { provide: 'ep:admin/get-user-ips', useClass: ep___admin_getUserIps.default }, - { provide: 'ep:admin/invite', useClass: ep___admin_invite.default }, - { provide: 'ep:admin/moderators/add', useClass: ep___admin_moderators_add.default }, - { provide: 'ep:admin/moderators/remove', useClass: ep___admin_moderators_remove.default }, - { provide: 'ep:admin/promo/create', useClass: ep___admin_promo_create.default }, - { provide: 'ep:admin/queue/clear', useClass: ep___admin_queue_clear.default }, - { provide: 'ep:admin/queue/deliver-delayed', useClass: ep___admin_queue_deliverDelayed.default }, - { provide: 'ep:admin/queue/inbox-delayed', useClass: ep___admin_queue_inboxDelayed.default }, - { provide: 'ep:admin/queue/stats', useClass: ep___admin_queue_stats.default }, - { provide: 'ep:admin/relays/add', useClass: ep___admin_relays_add.default }, - { provide: 'ep:admin/relays/list', useClass: ep___admin_relays_list.default }, - { provide: 'ep:admin/relays/remove', useClass: ep___admin_relays_remove.default }, - { provide: 'ep:admin/reset-password', useClass: ep___admin_resetPassword.default }, - { provide: 'ep:admin/resolve-abuse-user-report', useClass: ep___admin_resolveAbuseUserReport.default }, - { provide: 'ep:admin/send-email', useClass: ep___admin_sendEmail.default }, - { provide: 'ep:admin/server-info', useClass: ep___admin_serverInfo.default }, - { provide: 'ep:admin/show-moderation-logs', useClass: ep___admin_showModerationLogs.default }, - { provide: 'ep:admin/show-user', useClass: ep___admin_showUser.default }, - { provide: 'ep:admin/show-users', useClass: ep___admin_showUsers.default }, - { provide: 'ep:admin/silence-user', useClass: ep___admin_silenceUser.default }, - { provide: 'ep:admin/suspend-user', useClass: ep___admin_suspendUser.default }, - { provide: 'ep:admin/unsilence-user', useClass: ep___admin_unsilenceUser.default }, - { provide: 'ep:admin/unsuspend-user', useClass: ep___admin_unsuspendUser.default }, - { provide: 'ep:admin/update-meta', useClass: ep___admin_updateMeta.default }, - { provide: 'ep:admin/vacuum', useClass: ep___admin_vacuum.default }, - { provide: 'ep:admin/delete-account', useClass: ep___admin_deleteAccount.default }, - { provide: 'ep:admin/update-user-note', useClass: ep___admin_updateUserNote.default }, - { provide: 'ep:announcements', useClass: ep___announcements.default }, - { provide: 'ep:antennas/create', useClass: ep___antennas_create.default }, - { provide: 'ep:antennas/delete', useClass: ep___antennas_delete.default }, - { provide: 'ep:antennas/list', useClass: ep___antennas_list.default }, - { provide: 'ep:antennas/notes', useClass: ep___antennas_notes.default }, - { provide: 'ep:antennas/show', useClass: ep___antennas_show.default }, - { provide: 'ep:antennas/update', useClass: ep___antennas_update.default }, - { provide: 'ep:ap/get', useClass: ep___ap_get.default }, - { provide: 'ep:ap/show', useClass: ep___ap_show.default }, - { provide: 'ep:app/create', useClass: ep___app_create.default }, - { provide: 'ep:app/show', useClass: ep___app_show.default }, - { provide: 'ep:auth/accept', useClass: ep___auth_accept.default }, - { provide: 'ep:auth/session/generate', useClass: ep___auth_session_generate.default }, - { provide: 'ep:auth/session/show', useClass: ep___auth_session_show.default }, - { provide: 'ep:auth/session/userkey', useClass: ep___auth_session_userkey.default }, - { provide: 'ep:blocking/create', useClass: ep___blocking_create.default }, - { provide: 'ep:blocking/delete', useClass: ep___blocking_delete.default }, - { provide: 'ep:blocking/list', useClass: ep___blocking_list.default }, - { provide: 'ep:channels/create', useClass: ep___channels_create.default }, - { provide: 'ep:channels/featured', useClass: ep___channels_featured.default }, - { provide: 'ep:channels/follow', useClass: ep___channels_follow.default }, - { provide: 'ep:channels/followed', useClass: ep___channels_followed.default }, - { provide: 'ep:channels/owned', useClass: ep___channels_owned.default }, - { provide: 'ep:channels/show', useClass: ep___channels_show.default }, - { provide: 'ep:channels/timeline', useClass: ep___channels_timeline.default }, - { provide: 'ep:channels/unfollow', useClass: ep___channels_unfollow.default }, - { provide: 'ep:channels/update', useClass: ep___channels_update.default }, - { provide: 'ep:charts/active-users', useClass: ep___charts_activeUsers.default }, - { provide: 'ep:charts/ap-request', useClass: ep___charts_apRequest.default }, - { provide: 'ep:charts/drive', useClass: ep___charts_drive.default }, - { provide: 'ep:charts/federation', useClass: ep___charts_federation.default }, - { provide: 'ep:charts/hashtag', useClass: ep___charts_hashtag.default }, - { provide: 'ep:charts/instance', useClass: ep___charts_instance.default }, - { provide: 'ep:charts/notes', useClass: ep___charts_notes.default }, - { provide: 'ep:charts/user/drive', useClass: ep___charts_user_drive.default }, - { provide: 'ep:charts/user/following', useClass: ep___charts_user_following.default }, - { provide: 'ep:charts/user/notes', useClass: ep___charts_user_notes.default }, - { provide: 'ep:charts/user/reactions', useClass: ep___charts_user_reactions.default }, - { provide: 'ep:charts/users', useClass: ep___charts_users.default }, - { provide: 'ep:clips/add-note', useClass: ep___clips_addNote.default }, - { provide: 'ep:clips/remove-note', useClass: ep___clips_removeNote.default }, - { provide: 'ep:clips/create', useClass: ep___clips_create.default }, - { provide: 'ep:clips/delete', useClass: ep___clips_delete.default }, - { provide: 'ep:clips/list', useClass: ep___clips_list.default }, - { provide: 'ep:clips/notes', useClass: ep___clips_notes.default }, - { provide: 'ep:clips/show', useClass: ep___clips_show.default }, - { provide: 'ep:clips/update', useClass: ep___clips_update.default }, - { provide: 'ep:drive', useClass: ep___drive.default }, - { provide: 'ep:drive/files', useClass: ep___drive_files.default }, - { provide: 'ep:drive/files/attached-notes', useClass: ep___drive_files_attachedNotes.default }, - { provide: 'ep:drive/files/check-existence', useClass: ep___drive_files_checkExistence.default }, - { provide: 'ep:drive/files/create', useClass: ep___drive_files_create.default }, - { provide: 'ep:drive/files/delete', useClass: ep___drive_files_delete.default }, - { provide: 'ep:drive/files/find-by-hash', useClass: ep___drive_files_findByHash.default }, - { provide: 'ep:drive/files/find', useClass: ep___drive_files_find.default }, - { provide: 'ep:drive/files/show', useClass: ep___drive_files_show.default }, - { provide: 'ep:drive/files/update', useClass: ep___drive_files_update.default }, - { provide: 'ep:drive/files/upload-from-url', useClass: ep___drive_files_uploadFromUrl.default }, - { provide: 'ep:drive/folders', useClass: ep___drive_folders.default }, - { provide: 'ep:drive/folders/create', useClass: ep___drive_folders_create.default }, - { provide: 'ep:drive/folders/delete', useClass: ep___drive_folders_delete.default }, - { provide: 'ep:drive/folders/find', useClass: ep___drive_folders_find.default }, - { provide: 'ep:drive/folders/show', useClass: ep___drive_folders_show.default }, - { provide: 'ep:drive/folders/update', useClass: ep___drive_folders_update.default }, - { provide: 'ep:drive/stream', useClass: ep___drive_stream.default }, - { provide: 'ep:email-address/available', useClass: ep___emailAddress_available.default }, - { provide: 'ep:endpoint', useClass: ep___endpoint.default }, - { provide: 'ep:endpoints', useClass: ep___endpoints.default }, - { provide: 'ep:export-custom-emojis', useClass: ep___exportCustomEmojis.default }, - { provide: 'ep:federation/followers', useClass: ep___federation_followers.default }, - { provide: 'ep:federation/following', useClass: ep___federation_following.default }, - { provide: 'ep:federation/instances', useClass: ep___federation_instances.default }, - { provide: 'ep:federation/show-instance', useClass: ep___federation_showInstance.default }, - { provide: 'ep:federation/update-remote-user', useClass: ep___federation_updateRemoteUser.default }, - { provide: 'ep:federation/users', useClass: ep___federation_users.default }, - { provide: 'ep:federation/stats', useClass: ep___federation_stats.default }, - { provide: 'ep:following/create', useClass: ep___following_create.default }, - { provide: 'ep:following/delete', useClass: ep___following_delete.default }, - { provide: 'ep:following/invalidate', useClass: ep___following_invalidate.default }, - { provide: 'ep:following/requests/accept', useClass: ep___following_requests_accept.default }, - { provide: 'ep:following/requests/cancel', useClass: ep___following_requests_cancel.default }, - { provide: 'ep:following/requests/list', useClass: ep___following_requests_list.default }, - { provide: 'ep:following/requests/reject', useClass: ep___following_requests_reject.default }, - { provide: 'ep:gallery/featured', useClass: ep___gallery_featured.default }, - { provide: 'ep:gallery/popular', useClass: ep___gallery_popular.default }, - { provide: 'ep:gallery/posts', useClass: ep___gallery_posts.default }, - { provide: 'ep:gallery/posts/create', useClass: ep___gallery_posts_create.default }, - { provide: 'ep:gallery/posts/delete', useClass: ep___gallery_posts_delete.default }, - { provide: 'ep:gallery/posts/like', useClass: ep___gallery_posts_like.default }, - { provide: 'ep:gallery/posts/show', useClass: ep___gallery_posts_show.default }, - { provide: 'ep:gallery/posts/unlike', useClass: ep___gallery_posts_unlike.default }, - { provide: 'ep:gallery/posts/update', useClass: ep___gallery_posts_update.default }, - { provide: 'ep:get-online-users-count', useClass: ep___getOnlineUsersCount.default }, - { provide: 'ep:hashtags/list', useClass: ep___hashtags_list.default }, - { provide: 'ep:hashtags/search', useClass: ep___hashtags_search.default }, - { provide: 'ep:hashtags/show', useClass: ep___hashtags_show.default }, - { provide: 'ep:hashtags/trend', useClass: ep___hashtags_trend.default }, - { provide: 'ep:hashtags/users', useClass: ep___hashtags_users.default }, - { provide: 'ep:i', useClass: ep___i.default }, - { provide: 'ep:i/2fa/done', useClass: ep___i_2fa_done.default }, - { provide: 'ep:i/2fa/key-done', useClass: ep___i_2fa_keyDone.default }, - { provide: 'ep:i/2fa/password-less', useClass: ep___i_2fa_passwordLess.default }, - { provide: 'ep:i/2fa/register-key', useClass: ep___i_2fa_registerKey.default }, - { provide: 'ep:i/2fa/register', useClass: ep___i_2fa_register.default }, - { provide: 'ep:i/2fa/remove-key', useClass: ep___i_2fa_removeKey.default }, - { provide: 'ep:i/2fa/unregister', useClass: ep___i_2fa_unregister.default }, - { provide: 'ep:i/apps', useClass: ep___i_apps.default }, - { provide: 'ep:i/authorized-apps', useClass: ep___i_authorizedApps.default }, - { provide: 'ep:i/change-password', useClass: ep___i_changePassword.default }, - { provide: 'ep:i/delete-account', useClass: ep___i_deleteAccount.default }, - { provide: 'ep:i/export-blocking', useClass: ep___i_exportBlocking.default }, - { provide: 'ep:i/export-following', useClass: ep___i_exportFollowing.default }, - { provide: 'ep:i/export-mute', useClass: ep___i_exportMute.default }, - { provide: 'ep:i/export-notes', useClass: ep___i_exportNotes.default }, - { provide: 'ep:i/export-user-lists', useClass: ep___i_exportUserLists.default }, - { provide: 'ep:i/favorites', useClass: ep___i_favorites.default }, - { provide: 'ep:i/gallery/likes', useClass: ep___i_gallery_likes.default }, - { provide: 'ep:i/gallery/posts', useClass: ep___i_gallery_posts.default }, - { provide: 'ep:i/get-word-muted-notes-count', useClass: ep___i_getWordMutedNotesCount.default }, - { provide: 'ep:i/import-blocking', useClass: ep___i_importBlocking.default }, - { provide: 'ep:i/import-following', useClass: ep___i_importFollowing.default }, - { provide: 'ep:i/import-muting', useClass: ep___i_importMuting.default }, - { provide: 'ep:i/import-user-lists', useClass: ep___i_importUserLists.default }, - { provide: 'ep:i/notifications', useClass: ep___i_notifications.default }, - { provide: 'ep:i/page-likes', useClass: ep___i_pageLikes.default }, - { provide: 'ep:i/pages', useClass: ep___i_pages.default }, - { provide: 'ep:i/pin', useClass: ep___i_pin.default }, - { provide: 'ep:i/read-all-messaging-messages', useClass: ep___i_readAllMessagingMessages.default }, - { provide: 'ep:i/read-all-unread-notes', useClass: ep___i_readAllUnreadNotes.default }, - { provide: 'ep:i/read-announcement', useClass: ep___i_readAnnouncement.default }, - { provide: 'ep:i/regenerate-token', useClass: ep___i_regenerateToken.default }, - { provide: 'ep:i/registry/get-all', useClass: ep___i_registry_getAll.default }, - { provide: 'ep:i/registry/get-detail', useClass: ep___i_registry_getDetail.default }, - { provide: 'ep:i/registry/get', useClass: ep___i_registry_get.default }, - { provide: 'ep:i/registry/keys-with-type', useClass: ep___i_registry_keysWithType.default }, - { provide: 'ep:i/registry/keys', useClass: ep___i_registry_keys.default }, - { provide: 'ep:i/registry/remove', useClass: ep___i_registry_remove.default }, - { provide: 'ep:i/registry/scopes', useClass: ep___i_registry_scopes.default }, - { provide: 'ep:i/registry/set', useClass: ep___i_registry_set.default }, - { provide: 'ep:i/revoke-token', useClass: ep___i_revokeToken.default }, - { provide: 'ep:i/signin-history', useClass: ep___i_signinHistory.default }, - { provide: 'ep:i/unpin', useClass: ep___i_unpin.default }, - { provide: 'ep:i/update-email', useClass: ep___i_updateEmail.default }, - { provide: 'ep:i/update', useClass: ep___i_update.default }, - { provide: 'ep:i/user-group-invites', useClass: ep___i_userGroupInvites.default }, - { provide: 'ep:i/webhooks/create', useClass: ep___i_webhooks_create.default }, - { provide: 'ep:i/webhooks/list', useClass: ep___i_webhooks_list.default }, - { provide: 'ep:i/webhooks/show', useClass: ep___i_webhooks_show.default }, - { provide: 'ep:i/webhooks/update', useClass: ep___i_webhooks_update.default }, - { provide: 'ep:i/webhooks/delete', useClass: ep___i_webhooks_delete.default }, - { provide: 'ep:messaging/history', useClass: ep___messaging_history.default }, - { provide: 'ep:messaging/messages', useClass: ep___messaging_messages.default }, - { provide: 'ep:messaging/messages/create', useClass: ep___messaging_messages_create.default }, - { provide: 'ep:messaging/messages/delete', useClass: ep___messaging_messages_delete.default }, - { provide: 'ep:messaging/messages/read', useClass: ep___messaging_messages_read.default }, - { provide: 'ep:meta', useClass: ep___meta.default }, - { provide: 'ep:miauth/gen-token', useClass: ep___miauth_genToken.default }, - { provide: 'ep:mute/create', useClass: ep___mute_create.default }, - { provide: 'ep:mute/delete', useClass: ep___mute_delete.default }, - { provide: 'ep:mute/list', useClass: ep___mute_list.default }, - { provide: 'ep:my/apps', useClass: ep___my_apps.default }, - { provide: 'ep:notes', useClass: ep___notes.default }, - { provide: 'ep:notes/children', useClass: ep___notes_children.default }, - { provide: 'ep:notes/clips', useClass: ep___notes_clips.default }, - { provide: 'ep:notes/conversation', useClass: ep___notes_conversation.default }, - { provide: 'ep:notes/create', useClass: ep___notes_create.default }, - { provide: 'ep:notes/delete', useClass: ep___notes_delete.default }, - { provide: 'ep:notes/favorites/create', useClass: ep___notes_favorites_create.default }, - { provide: 'ep:notes/favorites/delete', useClass: ep___notes_favorites_delete.default }, - { provide: 'ep:notes/featured', useClass: ep___notes_featured.default }, - { provide: 'ep:notes/global-timeline', useClass: ep___notes_globalTimeline.default }, - { provide: 'ep:notes/hybrid-timeline', useClass: ep___notes_hybridTimeline.default }, - { provide: 'ep:notes/local-timeline', useClass: ep___notes_localTimeline.default }, - { provide: 'ep:notes/mentions', useClass: ep___notes_mentions.default }, - { provide: 'ep:notes/polls/recommendation', useClass: ep___notes_polls_recommendation.default }, - { provide: 'ep:notes/polls/vote', useClass: ep___notes_polls_vote.default }, - { provide: 'ep:notes/reactions', useClass: ep___notes_reactions.default }, - { provide: 'ep:notes/reactions/create', useClass: ep___notes_reactions_create.default }, - { provide: 'ep:notes/reactions/delete', useClass: ep___notes_reactions_delete.default }, - { provide: 'ep:notes/renotes', useClass: ep___notes_renotes.default }, - { provide: 'ep:notes/replies', useClass: ep___notes_replies.default }, - { provide: 'ep:notes/search-by-tag', useClass: ep___notes_searchByTag.default }, - { provide: 'ep:notes/search', useClass: ep___notes_search.default }, - { provide: 'ep:notes/show', useClass: ep___notes_show.default }, - { provide: 'ep:notes/state', useClass: ep___notes_state.default }, - { provide: 'ep:notes/thread-muting/create', useClass: ep___notes_threadMuting_create.default }, - { provide: 'ep:notes/thread-muting/delete', useClass: ep___notes_threadMuting_delete.default }, - { provide: 'ep:notes/timeline', useClass: ep___notes_timeline.default }, - { provide: 'ep:notes/translate', useClass: ep___notes_translate.default }, - { provide: 'ep:notes/unrenote', useClass: ep___notes_unrenote.default }, - { provide: 'ep:notes/user-list-timeline', useClass: ep___notes_userListTimeline.default }, - { provide: 'ep:notes/watching/create', useClass: ep___notes_watching_create.default }, - { provide: 'ep:notes/watching/delete', useClass: ep___notes_watching_delete.default }, - { provide: 'ep:notifications/create', useClass: ep___notifications_create.default }, - { provide: 'ep:notifications/mark-all-as-read', useClass: ep___notifications_markAllAsRead.default }, - { provide: 'ep:notifications/read', useClass: ep___notifications_read.default }, - { provide: 'ep:page-push', useClass: ep___pagePush.default }, - { provide: 'ep:pages/create', useClass: ep___pages_create.default }, - { provide: 'ep:pages/delete', useClass: ep___pages_delete.default }, - { provide: 'ep:pages/featured', useClass: ep___pages_featured.default }, - { provide: 'ep:pages/like', useClass: ep___pages_like.default }, - { provide: 'ep:pages/show', useClass: ep___pages_show.default }, - { provide: 'ep:pages/unlike', useClass: ep___pages_unlike.default }, - { provide: 'ep:pages/update', useClass: ep___pages_update.default }, - { provide: 'ep:ping', useClass: ep___ping.default }, - { provide: 'ep:pinned-users', useClass: ep___pinnedUsers.default }, - { provide: 'ep:promo/read', useClass: ep___promo_read.default }, - { provide: 'ep:request-reset-password', useClass: ep___requestResetPassword.default }, - { provide: 'ep:reset-db', useClass: ep___resetDb.default }, - { provide: 'ep:reset-password', useClass: ep___resetPassword.default }, - { provide: 'ep:server-info', useClass: ep___serverInfo.default }, - { provide: 'ep:stats', useClass: ep___stats.default }, - { provide: 'ep:sw/register', useClass: ep___sw_register.default }, - { provide: 'ep:sw/unregister', useClass: ep___sw_unregister.default }, - { provide: 'ep:test', useClass: ep___test.default }, - { provide: 'ep:username/available', useClass: ep___username_available.default }, - { provide: 'ep:users', useClass: ep___users.default }, - { provide: 'ep:users/clips', useClass: ep___users_clips.default }, - { provide: 'ep:users/followers', useClass: ep___users_followers.default }, - { provide: 'ep:users/following', useClass: ep___users_following.default }, - { provide: 'ep:users/gallery/posts', useClass: ep___users_gallery_posts.default }, - { provide: 'ep:users/get-frequently-replied-users', useClass: ep___users_getFrequentlyRepliedUsers.default }, - { provide: 'ep:users/groups/create', useClass: ep___users_groups_create.default }, - { provide: 'ep:users/groups/delete', useClass: ep___users_groups_delete.default }, - { provide: 'ep:users/groups/invitations/accept', useClass: ep___users_groups_invitations_accept.default }, - { provide: 'ep:users/groups/invitations/reject', useClass: ep___users_groups_invitations_reject.default }, - { provide: 'ep:users/groups/invite', useClass: ep___users_groups_invite.default }, - { provide: 'ep:users/groups/joined', useClass: ep___users_groups_joined.default }, - { provide: 'ep:users/groups/leave', useClass: ep___users_groups_leave.default }, - { provide: 'ep:users/groups/owned', useClass: ep___users_groups_owned.default }, - { provide: 'ep:users/groups/pull', useClass: ep___users_groups_pull.default }, - { provide: 'ep:users/groups/show', useClass: ep___users_groups_show.default }, - { provide: 'ep:users/groups/transfer', useClass: ep___users_groups_transfer.default }, - { provide: 'ep:users/groups/update', useClass: ep___users_groups_update.default }, - { provide: 'ep:users/lists/create', useClass: ep___users_lists_create.default }, - { provide: 'ep:users/lists/delete', useClass: ep___users_lists_delete.default }, - { provide: 'ep:users/lists/list', useClass: ep___users_lists_list.default }, - { provide: 'ep:users/lists/pull', useClass: ep___users_lists_pull.default }, - { provide: 'ep:users/lists/push', useClass: ep___users_lists_push.default }, - { provide: 'ep:users/lists/show', useClass: ep___users_lists_show.default }, - { provide: 'ep:users/lists/update', useClass: ep___users_lists_update.default }, - { provide: 'ep:users/notes', useClass: ep___users_notes.default }, - { provide: 'ep:users/pages', useClass: ep___users_pages.default }, - { provide: 'ep:users/reactions', useClass: ep___users_reactions.default }, - { provide: 'ep:users/recommendation', useClass: ep___users_recommendation.default }, - { provide: 'ep:users/relation', useClass: ep___users_relation.default }, - { provide: 'ep:users/report-abuse', useClass: ep___users_reportAbuse.default }, - { provide: 'ep:users/search-by-username-and-host', useClass: ep___users_searchByUsernameAndHost.default }, - { provide: 'ep:users/search', useClass: ep___users_search.default }, - { provide: 'ep:users/show', useClass: ep___users_show.default }, - { provide: 'ep:users/stats', useClass: ep___users_stats.default }, - { provide: 'ep:admin/drive-capacity-override', useClass: ep___admin_driveCapOverride.default }, - { provide: 'ep:fetch-rss', useClass: ep___fetchRss.default }, - ], - }) -export class EndpointsModule {} diff --git a/packages/backend/src/services/CoreModule.ts b/packages/backend/src/services/CoreModule.ts index 0e013e0876f7..0eaf939bde70 100644 --- a/packages/backend/src/services/CoreModule.ts +++ b/packages/backend/src/services/CoreModule.ts @@ -104,5 +104,56 @@ import { EntityModule } from './entities/EntityModule.js'; VideoProcessingService, WebhookService, ], + exports: [ + ChartsModule, + EntityModule, + AccountUpdateService, + AiService, + AntennaService, + AppLockService, + CaptchaService, + CreateNotificationService, + CreateSystemUserService, + CustomEmojiService, + DeleteAccountService, + DownloadService, + DriveService, + EmailService, + FederatedInstanceService, + FetchInstanceMetadataService, + GlobalEventService, + HashtagService, + HttpRequestService, + IdService, + ImageProcessingService, + InstanceActorService, + InternalStorageService, + MessagingService, + MetaService, + MfmService, + ModerationLogService, + NoteCreateService, + NoteDeleteService, + NotePiningService, + NoteReadService, + NotificationService, + PollService, + PushNotificationService, + QueryService, + ReactionService, + RelayService, + S3Service, + SignupService, + TwoFactorAuthenticationService, + UserBlockingService, + UserCacheService, + UserFollowingService, + UserKeypairStoreService, + UserListService, + UserMutingService, + UserSuspendService, + VideoProcessingService, + WebhookService, + ], }) export class CoreModule {} From 737cdef8fd3711b82606c93af3941264d856c274 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 04:25:30 +0900 Subject: [PATCH 134/180] Update app.module.ts --- packages/backend/src/app.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/app.module.ts b/packages/backend/src/app.module.ts index 22fa5cec4cb7..4805957bdcc3 100644 --- a/packages/backend/src/app.module.ts +++ b/packages/backend/src/app.module.ts @@ -3,7 +3,7 @@ import { QueueModule } from '@/queue/queue.module.js'; import { CoreModule } from './services/CoreModule.js'; import { DI } from './di-symbols.js'; import { loadConfig } from './config.js'; -import { db } from './db/postgre'; +import { db } from './db/postgre.js'; import { RepositoryModule } from './RepositoryModule.js'; import { ServerModule } from './server/ServerModule.js'; From ec2b08b2517162fccfee2d50c3821857bb2f2920 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 04:30:20 +0900 Subject: [PATCH 135/180] wip --- .../backend/src/misc/fetch-proxy-account.ts | 9 -------- packages/backend/src/services/CoreModule.ts | 5 ++++- .../src/services/ProxyAccountService.ts | 21 +++++++++++++++++++ .../backend/src/services/UserListService.ts | 5 +++-- .../backend/src/services/UserMutingService.ts | 6 +++--- .../src/services/UserSuspendService.ts | 17 ++++++++------- .../src/services/VideoProcessingService.ts | 5 +++-- 7 files changed, 43 insertions(+), 25 deletions(-) delete mode 100644 packages/backend/src/misc/fetch-proxy-account.ts create mode 100644 packages/backend/src/services/ProxyAccountService.ts diff --git a/packages/backend/src/misc/fetch-proxy-account.ts b/packages/backend/src/misc/fetch-proxy-account.ts deleted file mode 100644 index 24af320fe6e2..000000000000 --- a/packages/backend/src/misc/fetch-proxy-account.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { fetchMeta } from './fetch-meta.js'; -import { ILocalUser } from '@/models/entities/User.js'; -import { Users } from '@/models/index.js'; - -export async function fetchProxyAccount(): Promise { - const meta = await fetchMeta(); - if (meta.proxyAccountId == null) return null; - return await Users.findOneByOrFail({ id: meta.proxyAccountId }) as ILocalUser; -} diff --git a/packages/backend/src/services/CoreModule.ts b/packages/backend/src/services/CoreModule.ts index 0eaf939bde70..76e782ce7bdd 100644 --- a/packages/backend/src/services/CoreModule.ts +++ b/packages/backend/src/services/CoreModule.ts @@ -1,6 +1,7 @@ import { Module } from '@nestjs/common'; import { DI } from '../di-symbols.js'; import { ChartsModule } from './chart/ChartsModule.js'; +import { EntityModule } from './entities/EntityModule.js'; import { AccountUpdateService } from './AccountUpdateService.js'; import { AiService } from './AiService.js'; import { AntennaService } from './AntennaService.js'; @@ -48,7 +49,7 @@ import { UserMutingService } from './UserMutingService.js'; import { UserSuspendService } from './UserSuspendService.js'; import { VideoProcessingService } from './VideoProcessingService.js'; import { WebhookService } from './WebhookService.js'; -import { EntityModule } from './entities/EntityModule.js'; +import { ProxyAccountService } from './ProxyAccountService.js'; @Module({ imports: [ @@ -87,6 +88,7 @@ import { EntityModule } from './entities/EntityModule.js'; NoteReadService, NotificationService, PollService, + ProxyAccountService, PushNotificationService, QueryService, ReactionService, @@ -138,6 +140,7 @@ import { EntityModule } from './entities/EntityModule.js'; NoteReadService, NotificationService, PollService, + ProxyAccountService, PushNotificationService, QueryService, ReactionService, diff --git a/packages/backend/src/services/ProxyAccountService.ts b/packages/backend/src/services/ProxyAccountService.ts new file mode 100644 index 000000000000..8c4093e2602e --- /dev/null +++ b/packages/backend/src/services/ProxyAccountService.ts @@ -0,0 +1,21 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { Users } from '@/models/index.js'; +import type { ILocalUser, User } from '@/models/entities/User.js'; +import { MetaService } from './MetaService.js'; + +@Injectable() +export class ProxyAccountService { + constructor( + @Inject('usersRepository') + private usersRepository: typeof Users, + + private metaService: MetaService, + ) { + } + + public async fetch(): Promise { + const meta = await this.metaService.fetch(); + if (meta.proxyAccountId == null) return null; + return await this.usersRepository.findOneByOrFail({ id: meta.proxyAccountId }) as ILocalUser; + } +} diff --git a/packages/backend/src/services/UserListService.ts b/packages/backend/src/services/UserListService.ts index 99212ccb54aa..b5ada69634f7 100644 --- a/packages/backend/src/services/UserListService.ts +++ b/packages/backend/src/services/UserListService.ts @@ -1,6 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserListJoinings , Users } from '@/models/index.js'; - import type { User } from '@/models/entities/User.js'; import type { UserList } from '@/models/entities/UserList.js'; import type { UserListJoining } from '@/models/entities/UserListJoining.js'; @@ -8,6 +7,7 @@ import { IdService } from '@/services/IdService.js'; import { UserFollowingService } from '@/services/UserFollowingService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { UserEntityService } from './entities/UserEntityService.js'; +import { ProxyAccountService } from './ProxyAccountService.js'; @Injectable() export class UserListService { @@ -22,6 +22,7 @@ export class UserListService { private idService: IdService, private userFollowingService: UserFollowingService, private globalEventServie: GlobalEventService, + private proxyAccountService: ProxyAccountService, ) { } @@ -37,7 +38,7 @@ export class UserListService { // このインスタンス内にこのリモートユーザーをフォローしているユーザーがいなくても投稿を受け取るためにダミーのユーザーがフォローしたということにする if (this.userEntityService.isRemoteUser(target)) { - const proxy = await fetchProxyAccount(); + const proxy = await this.proxyAccountService.fetch(); if (proxy) { this.userFollowingService.follow(proxy, target); } diff --git a/packages/backend/src/services/UserMutingService.ts b/packages/backend/src/services/UserMutingService.ts index 84060cd37ebc..057fee0bd641 100644 --- a/packages/backend/src/services/UserMutingService.ts +++ b/packages/backend/src/services/UserMutingService.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users , Mutings } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; -import type { QueueService } from '@/queue/queue.service.js'; -import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import { IdService } from '@/services/IdService.js'; +import { QueueService } from '@/queue/queue.service.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; import type { User } from '@/models/entities/User.js'; @Injectable() diff --git a/packages/backend/src/services/UserSuspendService.ts b/packages/backend/src/services/UserSuspendService.ts index 5336f9476d32..0bde8b4ad225 100644 --- a/packages/backend/src/services/UserSuspendService.ts +++ b/packages/backend/src/services/UserSuspendService.ts @@ -2,13 +2,12 @@ import { Inject, Injectable } from '@nestjs/common'; import { Not, IsNull } from 'typeorm'; import type { Followings , Users } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; -import type { QueueService } from '@/queue/queue.service.js'; -import renderDelete from '@/services/remote/activitypub/renderer/delete.js'; -import renderUndo from '@/services/remote/activitypub/renderer/undo.js'; -import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; -import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import { QueueService } from '@/queue/queue.service.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; +import { ApRendererService } from './remote/activitypub/ApRendererService.js'; +import { UserEntityService } from './entities/UserEntityService.js'; @Injectable() export class UserSuspendService { @@ -22,8 +21,10 @@ export class UserSuspendService { @Inject('followingsRepository') private followingsRepository: typeof Followings, + private userEntityService: UserEntityService, private queueService: QueueService, private globalEventService: GlobalEventService, + private apRendererService: ApRendererService, ) { } @@ -32,7 +33,7 @@ export class UserSuspendService { if (this.userEntityService.isLocalUser(user)) { // 知り得る全SharedInboxにDelete配信 - const content = renderActivity(renderDelete(`${this.config.url}/users/${user.id}`, user)); + const content = this.apRendererService.renderActivity(this.apRendererService.renderDelete(`${this.config.url}/users/${user.id}`, user)); const queue: string[] = []; @@ -61,7 +62,7 @@ export class UserSuspendService { if (this.userEntityService.isLocalUser(user)) { // 知り得る全SharedInboxにUndo Delete配信 - const content = renderActivity(renderUndo(renderDelete(`${this.config.url}/users/${user.id}`, user), user)); + const content = this.apRendererService.renderActivity(this.apRendererService.renderUndo(this.apRendererService.renderDelete(`${this.config.url}/users/${user.id}`, user), user)); const queue: string[] = []; diff --git a/packages/backend/src/services/VideoProcessingService.ts b/packages/backend/src/services/VideoProcessingService.ts index d943c2f50da7..9391099c9dcf 100644 --- a/packages/backend/src/services/VideoProcessingService.ts +++ b/packages/backend/src/services/VideoProcessingService.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import FFmpeg from 'fluent-ffmpeg'; import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; -import type { IImage, ImageProcessingService } from '@/services/ImageProcessingService.js'; +import { Config } from '@/config.js'; +import { ImageProcessingService } from '@/services/ImageProcessingService.js'; +import type { IImage } from '@/services/ImageProcessingService.js'; import { createTempDir } from '@/misc/create-temp'; @Injectable() From f14914e49e50ae07fe612559f36b9c15053a0777 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 04:50:07 +0900 Subject: [PATCH 136/180] wip --- .../backend/src/misc/check-hit-antenna.ts | 99 ---------------- .../src/services/AccountUpdateService.ts | 14 ++- packages/backend/src/services/AiService.ts | 2 +- .../backend/src/services/AntennaService.ts | 111 +++++++++++++++++- .../src/services/CreateNotificationService.ts | 4 +- .../src/services/CreateSystemUserService.ts | 1 - .../src/services/DeleteAccountService.ts | 6 +- .../services/FetchInstanceMetadataService.ts | 4 +- .../src/services/HttpRequestService.ts | 2 +- packages/backend/src/services/IdService.ts | 2 +- .../src/services/InstanceActorService.ts | 2 +- .../src/services/InternalStorageService.ts | 2 +- .../backend/src/services/MessagingService.ts | 2 +- packages/backend/src/services/MetaService.ts | 3 - packages/backend/src/services/MfmService.ts | 2 +- .../src/services/ModerationLogService.ts | 2 +- .../backend/src/services/NoteCreateService.ts | 5 +- .../backend/src/services/NoteDeleteService.ts | 44 +++---- .../backend/src/services/NotePiningService.ts | 14 ++- .../backend/src/services/NoteReadService.ts | 47 ++++++-- packages/backend/src/services/PollService.ts | 13 +- .../backend/src/services/ReactionService.ts | 24 ++-- packages/backend/src/services/RelayService.ts | 8 +- packages/backend/src/services/S3Service.ts | 2 +- .../backend/src/services/SignupService.ts | 1 + .../src/services/UserBlockingService.ts | 1 - .../src/services/UserFollowingService.ts | 2 +- .../src/services/UserKeypairStoreService.ts | 1 - .../backend/src/services/UserMutingService.ts | 1 - 29 files changed, 231 insertions(+), 190 deletions(-) delete mode 100644 packages/backend/src/misc/check-hit-antenna.ts diff --git a/packages/backend/src/misc/check-hit-antenna.ts b/packages/backend/src/misc/check-hit-antenna.ts deleted file mode 100644 index 5269009ebff5..000000000000 --- a/packages/backend/src/misc/check-hit-antenna.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { Antenna } from '@/models/entities/Antenna.js'; -import { Note } from '@/models/entities/Note.js'; -import { User } from '@/models/entities/User.js'; -import { UserListJoinings, UserGroupJoinings, Blockings } from '@/models/index.js'; -import { getFullApAccount } from './convert-host.js'; -import * as Acct from '@/misc/acct.js'; -import { Packed } from './schema.js'; -import { Cache } from './cache.js'; - -const blockingCache = new Cache(1000 * 60 * 5); - -// NOTE: フォローしているユーザーのノート、リストのユーザーのノート、グループのユーザーのノート指定はパフォーマンス上の理由で無効になっている - -/** - * noteUserFollowers / antennaUserFollowing はどちらか一方が指定されていればよい - */ -export async function checkHitAntenna(antenna: Antenna, note: (Note | Packed<'Note'>), noteUser: { id: User['id']; username: string; host: string | null; }, noteUserFollowers?: User['id'][], antennaUserFollowing?: User['id'][]): Promise { - if (note.visibility === 'specified') return false; - - // アンテナ作成者がノート作成者にブロックされていたらスキップ - const blockings = await blockingCache.fetch(noteUser.id, () => Blockings.findBy({ blockerId: noteUser.id }).then(res => res.map(x => x.blockeeId))); - if (blockings.some(blocking => blocking === antenna.userId)) return false; - - if (note.visibility === 'followers') { - if (noteUserFollowers && !noteUserFollowers.includes(antenna.userId)) return false; - if (antennaUserFollowing && !antennaUserFollowing.includes(note.userId)) return false; - } - - if (!antenna.withReplies && note.replyId != null) return false; - - if (antenna.src === 'home') { - if (noteUserFollowers && !noteUserFollowers.includes(antenna.userId)) return false; - if (antennaUserFollowing && !antennaUserFollowing.includes(note.userId)) return false; - } else if (antenna.src === 'list') { - const listUsers = (await UserListJoinings.findBy({ - userListId: antenna.userListId!, - })).map(x => x.userId); - - if (!listUsers.includes(note.userId)) return false; - } else if (antenna.src === 'group') { - const joining = await UserGroupJoinings.findOneByOrFail({ id: antenna.userGroupJoiningId! }); - - const groupUsers = (await UserGroupJoinings.findBy({ - userGroupId: joining.userGroupId, - })).map(x => x.userId); - - if (!groupUsers.includes(note.userId)) return false; - } else if (antenna.src === 'users') { - const accts = antenna.users.map(x => { - const { username, host } = Acct.parse(x); - return getFullApAccount(username, host).toLowerCase(); - }); - if (!accts.includes(getFullApAccount(noteUser.username, noteUser.host).toLowerCase())) return false; - } - - const keywords = antenna.keywords - // Clean up - .map(xs => xs.filter(x => x !== '')) - .filter(xs => xs.length > 0); - - if (keywords.length > 0) { - if (note.text == null) return false; - - const matched = keywords.some(and => - and.every(keyword => - antenna.caseSensitive - ? note.text!.includes(keyword) - : note.text!.toLowerCase().includes(keyword.toLowerCase()) - )); - - if (!matched) return false; - } - - const excludeKeywords = antenna.excludeKeywords - // Clean up - .map(xs => xs.filter(x => x !== '')) - .filter(xs => xs.length > 0); - - if (excludeKeywords.length > 0) { - if (note.text == null) return false; - - const matched = excludeKeywords.some(and => - and.every(keyword => - antenna.caseSensitive - ? note.text!.includes(keyword) - : note.text!.toLowerCase().includes(keyword.toLowerCase()) - )); - - if (matched) return false; - } - - if (antenna.withFile) { - if (note.fileIds && note.fileIds.length === 0) return false; - } - - // TODO: eval expression - - return true; -} diff --git a/packages/backend/src/services/AccountUpdateService.ts b/packages/backend/src/services/AccountUpdateService.ts index d69e76064b5e..f4694f78e985 100644 --- a/packages/backend/src/services/AccountUpdateService.ts +++ b/packages/backend/src/services/AccountUpdateService.ts @@ -1,11 +1,12 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { Users } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import type { Users } from '@/models/index.js'; +import { Config } from '@/config.js'; import type { User } from '@/models/entities/User.js'; -import type { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; -import type { RelayService } from '@/services/RelayService.js'; -import type { ApDeliverManagerService } from '@/services/remote/activitypub/ApDeliverManagerService.js'; +import { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; +import { RelayService } from '@/services/RelayService.js'; +import { ApDeliverManagerService } from '@/services/remote/activitypub/ApDeliverManagerService.js'; +import { UserEntityService } from './entities/UserEntityService.js'; @Injectable() export class AccountUpdateService { @@ -16,6 +17,7 @@ export class AccountUpdateService { @Inject('usersRepository') private usersRepository: typeof Users, + private userEntityService: UserEntityService, private apRendererService: ApRendererService, private apDeliverManagerService: ApDeliverManagerService, private relayService: RelayService, @@ -27,7 +29,7 @@ export class AccountUpdateService { if (user == null) throw new Error('user not found'); // フォロワーがリモートユーザーかつ投稿者がローカルユーザーならUpdateを配信 - if (Users.isLocalUser(user)) { + if (this.userEntityService.isLocalUser(user)) { const content = this.apRendererService.renderActivity(this.apRendererService.renderUpdate(await this.apRendererService.renderPerson(user), user)); this.apDeliverManagerService.deliverToFollowers(user, content); this.relayService.deliverToRelays(user, content); diff --git a/packages/backend/src/services/AiService.ts b/packages/backend/src/services/AiService.ts index ac66d312ae73..1cfc3382a987 100644 --- a/packages/backend/src/services/AiService.ts +++ b/packages/backend/src/services/AiService.ts @@ -4,7 +4,7 @@ import { dirname } from 'node:path'; import { Inject, Injectable } from '@nestjs/common'; import * as nsfw from 'nsfwjs'; import si from 'systeminformation'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; const _filename = fileURLToPath(import.meta.url); diff --git a/packages/backend/src/services/AntennaService.ts b/packages/backend/src/services/AntennaService.ts index 959ec6162d1b..0304a1b0aa37 100644 --- a/packages/backend/src/services/AntennaService.ts +++ b/packages/backend/src/services/AntennaService.ts @@ -1,27 +1,43 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { AntennaNotes, Mutings, Notes, Users } from '@/models/index.js'; +import type { UserGroupJoinings, UserListJoinings , AntennaNotes, Mutings, Notes, Users , Blockings } from '@/models/index.js'; import type { Antenna } from '@/models/entities/Antenna.js'; import type { Note } from '@/models/entities/Note.js'; import type { User } from '@/models/entities/User.js'; -import type { IdService } from '@/services/IdService.js'; +import { IdService } from '@/services/IdService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; -import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import * as Acct from '@/misc/acct.js'; +import { Cache } from '@/misc/cache.js'; +import type { Packed } from '@/misc/schema.js'; +import { getFullApAccount } from '@/misc/convert-host'; @Injectable() export class AntennaService { + #blockingCache: Cache; + constructor( @Inject('mutingsRepository') private mutingsRepository: typeof Mutings, + @Inject('blockingsRepository') + private blockingsRepository: typeof Blockings, + @Inject('notesRepository') private notesRepository: typeof Notes, @Inject('antennaNotesRepository') private antennaNotesRepository: typeof AntennaNotes, + @Inject('userGroupJoiningsRepository') + private userGroupJoiningsRepository: typeof UserGroupJoinings, + + @Inject('userListJoiningsRepository') + private userListJoiningsRepository: typeof UserListJoinings, + private idService: IdService, private globalEventServie: GlobalEventService, ) { + this.#blockingCache = new Cache(1000 * 60 * 5); } public async addNoteToAntenna(antenna: Antenna, note: Note, noteUser: { id: User['id']; }): Promise { @@ -70,4 +86,93 @@ export class AntennaService { }, 2000); } } + + // NOTE: フォローしているユーザーのノート、リストのユーザーのノート、グループのユーザーのノート指定はパフォーマンス上の理由で無効になっている + + /** + * noteUserFollowers / antennaUserFollowing はどちらか一方が指定されていればよい + */ + public async checkHitAntenna(antenna: Antenna, note: (Note | Packed<'Note'>), noteUser: { id: User['id']; username: string; host: string | null; }, noteUserFollowers?: User['id'][], antennaUserFollowing?: User['id'][]): Promise { + if (note.visibility === 'specified') return false; + + // アンテナ作成者がノート作成者にブロックされていたらスキップ + const blockings = await this.#blockingCache.fetch(noteUser.id, () => this.blockingsRepository.findBy({ blockerId: noteUser.id }).then(res => res.map(x => x.blockeeId))); + if (blockings.some(blocking => blocking === antenna.userId)) return false; + + if (note.visibility === 'followers') { + if (noteUserFollowers && !noteUserFollowers.includes(antenna.userId)) return false; + if (antennaUserFollowing && !antennaUserFollowing.includes(note.userId)) return false; + } + + if (!antenna.withReplies && note.replyId != null) return false; + + if (antenna.src === 'home') { + if (noteUserFollowers && !noteUserFollowers.includes(antenna.userId)) return false; + if (antennaUserFollowing && !antennaUserFollowing.includes(note.userId)) return false; + } else if (antenna.src === 'list') { + const listUsers = (await this.userListJoiningsRepository.findBy({ + userListId: antenna.userListId!, + })).map(x => x.userId); + + if (!listUsers.includes(note.userId)) return false; + } else if (antenna.src === 'group') { + const joining = await this.userGroupJoiningsRepository.findOneByOrFail({ id: antenna.userGroupJoiningId! }); + + const groupUsers = (await this.userGroupJoiningsRepository.findBy({ + userGroupId: joining.userGroupId, + })).map(x => x.userId); + + if (!groupUsers.includes(note.userId)) return false; + } else if (antenna.src === 'users') { + const accts = antenna.users.map(x => { + const { username, host } = Acct.parse(x); + return getFullApAccount(username, host).toLowerCase(); + }); + if (!accts.includes(getFullApAccount(noteUser.username, noteUser.host).toLowerCase())) return false; + } + + const keywords = antenna.keywords + // Clean up + .map(xs => xs.filter(x => x !== '')) + .filter(xs => xs.length > 0); + + if (keywords.length > 0) { + if (note.text == null) return false; + + const matched = keywords.some(and => + and.every(keyword => + antenna.caseSensitive + ? note.text!.includes(keyword) + : note.text!.toLowerCase().includes(keyword.toLowerCase()), + )); + + if (!matched) return false; + } + + const excludeKeywords = antenna.excludeKeywords + // Clean up + .map(xs => xs.filter(x => x !== '')) + .filter(xs => xs.length > 0); + + if (excludeKeywords.length > 0) { + if (note.text == null) return false; + + const matched = excludeKeywords.some(and => + and.every(keyword => + antenna.caseSensitive + ? note.text!.includes(keyword) + : note.text!.toLowerCase().includes(keyword.toLowerCase()), + )); + + if (matched) return false; + } + + if (antenna.withFile) { + if (note.fileIds && note.fileIds.length === 0) return false; + } + + // TODO: eval expression + + return true; + } } diff --git a/packages/backend/src/services/CreateNotificationService.ts b/packages/backend/src/services/CreateNotificationService.ts index 225f5a6f4dcc..6f7e981f0264 100644 --- a/packages/backend/src/services/CreateNotificationService.ts +++ b/packages/backend/src/services/CreateNotificationService.ts @@ -5,6 +5,7 @@ import type { Notification } from '@/models/entities/Notification.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { IdService } from '@/services/IdService.js'; import { NotificationEntityService } from './entities/NotificationEntityService.js'; +import { PushNotificationService } from './PushNotificationService.js'; @Injectable() export class CreateNotificationService { @@ -24,6 +25,7 @@ export class CreateNotificationService { private notificationEntityService: NotificationEntityService, private idService: IdService, private globalEventServie: GlobalEventService, + private pushNotificationService: PushNotificationService, ) { } @@ -73,7 +75,7 @@ export class CreateNotificationService { //#endregion this.globalEventServie.publishMainStream(notifieeId, 'unreadNotification', packed); - pushNotification(notifieeId, 'notification', packed); + this.pushNotificationService.pushNotification(notifieeId, 'notification', packed); if (type === 'follow') this.#emailNotificationFollow(notifieeId, await this.usersRepository.findOneByOrFail({ id: data.notifierId! })); if (type === 'receiveFollowRequest') this.#emailNotificationReceiveFollowRequest(notifieeId, await this.usersRepository.findOneByOrFail({ id: data.notifierId! })); diff --git a/packages/backend/src/services/CreateSystemUserService.ts b/packages/backend/src/services/CreateSystemUserService.ts index f1bc3f488a27..873f6a0b8f5c 100644 --- a/packages/backend/src/services/CreateSystemUserService.ts +++ b/packages/backend/src/services/CreateSystemUserService.ts @@ -1,5 +1,4 @@ import { Inject, Injectable } from '@nestjs/common'; - import bcrypt from 'bcryptjs'; import { v4 as uuid } from 'uuid'; import { IsNull , DataSource } from 'typeorm'; diff --git a/packages/backend/src/services/DeleteAccountService.ts b/packages/backend/src/services/DeleteAccountService.ts index 203bec375e5d..35bd44eda288 100644 --- a/packages/backend/src/services/DeleteAccountService.ts +++ b/packages/backend/src/services/DeleteAccountService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users } from '@/models/index.js'; -import type { QueueService } from '@/queue/queue.service.js'; -import type { UserSuspendService } from '@/services/UserSuspendService.js'; -import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import { QueueService } from '@/queue/queue.service.js'; +import { UserSuspendService } from '@/services/UserSuspendService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; @Injectable() export class DeleteAccountService { diff --git a/packages/backend/src/services/FetchInstanceMetadataService.ts b/packages/backend/src/services/FetchInstanceMetadataService.ts index 3515a29344cd..2233c75d9fa8 100644 --- a/packages/backend/src/services/FetchInstanceMetadataService.ts +++ b/packages/backend/src/services/FetchInstanceMetadataService.ts @@ -5,9 +5,9 @@ import fetch from 'node-fetch'; import tinycolor from 'tinycolor2'; import type { Instance } from '@/models/entities/Instance.js'; import type { Instances } from '@/models/index.js'; -import type { AppLockService } from '@/services/AppLockService.js'; +import { AppLockService } from '@/services/AppLockService.js'; import Logger from '@/logger.js'; -import type { HttpRequestService } from './HttpRequestService.js'; +import { HttpRequestService } from './HttpRequestService.js'; import type { DOMWindow } from 'jsdom'; const logger = new Logger('metadata', 'cyan'); diff --git a/packages/backend/src/services/HttpRequestService.ts b/packages/backend/src/services/HttpRequestService.ts index ca2d87b4a38e..a6f0a000a133 100644 --- a/packages/backend/src/services/HttpRequestService.ts +++ b/packages/backend/src/services/HttpRequestService.ts @@ -5,7 +5,7 @@ import fetch from 'node-fetch'; import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent'; import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { Config } from '@/config/types'; +import { Config } from '@/config.js'; import { StatusError } from '@/misc/status-error.js'; import type { Response } from 'node-fetch'; import type { URL } from 'node:url'; diff --git a/packages/backend/src/services/IdService.ts b/packages/backend/src/services/IdService.ts index 8e39930462e1..b3b0d63627ff 100644 --- a/packages/backend/src/services/IdService.ts +++ b/packages/backend/src/services/IdService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { ulid } from 'ulid'; import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import { genAid } from '@/misc/id/aid.js'; import { genMeid } from '@/misc/id/meid.js'; import { genMeidg } from '@/misc/id/meidg.js'; diff --git a/packages/backend/src/services/InstanceActorService.ts b/packages/backend/src/services/InstanceActorService.ts index a2bb46425644..d4b2b29bf8ca 100644 --- a/packages/backend/src/services/InstanceActorService.ts +++ b/packages/backend/src/services/InstanceActorService.ts @@ -4,7 +4,7 @@ import { IsNull } from 'typeorm'; import type { ILocalUser } from '@/models/entities/User.js'; import type { Users } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; -import type { CreateSystemUserService } from './CreateSystemUserService.js'; +import { CreateSystemUserService } from './CreateSystemUserService.js'; const ACTOR_USERNAME = 'instance.actor' as const; diff --git a/packages/backend/src/services/InternalStorageService.ts b/packages/backend/src/services/InternalStorageService.ts index 426c27c05b3a..af8a641b36cb 100644 --- a/packages/backend/src/services/InternalStorageService.ts +++ b/packages/backend/src/services/InternalStorageService.ts @@ -4,7 +4,7 @@ import { fileURLToPath } from 'node:url'; import { dirname } from 'node:path'; import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); diff --git a/packages/backend/src/services/MessagingService.ts b/packages/backend/src/services/MessagingService.ts index 13422633e3af..311b5acf7d38 100644 --- a/packages/backend/src/services/MessagingService.ts +++ b/packages/backend/src/services/MessagingService.ts @@ -6,7 +6,7 @@ import { Config } from '@/config.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; import type { Note } from '@/models/entities/Note.js'; -import type { User, CacheableUser } from '@/models/entities/User.js'; +import type { User, CacheableUser, IRemoteUser } from '@/models/entities/User.js'; import type { UserGroup } from '@/models/entities/UserGroup.js'; import { QueueService } from '@/queue/queue.service.js'; import { toArray } from '@/prelude/array'; diff --git a/packages/backend/src/services/MetaService.ts b/packages/backend/src/services/MetaService.ts index 599c480aada5..33e4c68cda0b 100644 --- a/packages/backend/src/services/MetaService.ts +++ b/packages/backend/src/services/MetaService.ts @@ -13,9 +13,6 @@ export class MetaService implements OnApplicationShutdown { constructor( @Inject(DI.db) private db: DataSource, - - @Inject('usersRepository') - private usersRepository: typeof Users, ) { if (process.env.NODE_ENV !== 'test') { this.#intervalId = setInterval(() => { diff --git a/packages/backend/src/services/MfmService.ts b/packages/backend/src/services/MfmService.ts index 8a1fd8fb8fff..9d136f919ad9 100644 --- a/packages/backend/src/services/MfmService.ts +++ b/packages/backend/src/services/MfmService.ts @@ -4,7 +4,7 @@ import * as parse5 from 'parse5'; import { JSDOM } from 'jsdom'; import { DI } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import { intersperse } from '@/prelude/array.js'; import type { IMentionedRemoteUsers } from '@/models/entities/Note.js'; import * as TreeAdapter from '../../node_modules/parse5/dist/tree-adapters/default.js'; diff --git a/packages/backend/src/services/ModerationLogService.ts b/packages/backend/src/services/ModerationLogService.ts index 06904f4ca080..32e24aba7dfb 100644 --- a/packages/backend/src/services/ModerationLogService.ts +++ b/packages/backend/src/services/ModerationLogService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { ModerationLogs } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; -import type { IdService } from '@/services/IdService.js'; +import { IdService } from '@/services/IdService.js'; @Injectable() export class ModerationLogService { diff --git a/packages/backend/src/services/NoteCreateService.ts b/packages/backend/src/services/NoteCreateService.ts index 31316f2fb134..160eaaab3455 100644 --- a/packages/backend/src/services/NoteCreateService.ts +++ b/packages/backend/src/services/NoteCreateService.ts @@ -7,7 +7,7 @@ import { extractHashtags } from '@/misc/extract-hashtags.js'; import type { IMentionedRemoteUsers } from '@/models/entities/Note.js'; import { Note } from '@/models/entities/Note.js'; import type { Notes , Users } from '@/models/index.js'; -import { Mutings, NoteWatchings, Instances, UserProfiles, MutedNotes, Channels, ChannelFollowings, NoteThreadMutings } from '@/models/index.js'; +import { Mutings, Instances, UserProfiles, MutedNotes, Channels, ChannelFollowings, NoteThreadMutings } from '@/models/index.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { App } from '@/models/entities/App.js'; import { concat } from '@/prelude/array.js'; @@ -16,7 +16,6 @@ import type { User, ILocalUser, IRemoteUser } from '@/models/entities/User.js'; import type { IPoll } from '@/models/entities/Poll.js'; import { Poll } from '@/models/entities/Poll.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; -import { checkHitAntenna } from '@/misc/check-hit-antenna.js'; import { checkWordMute } from '@/misc/check-word-mute.js'; import { countSameRenotes } from '@/misc/count-same-renotes.js'; import type { Channel } from '@/models/entities/Channel.js'; @@ -430,7 +429,7 @@ export class NoteCreateService { // Antenna for (const antenna of (await getAntennas())) { - checkHitAntenna(antenna, note, user).then(hit => { + this.antennaService.checkHitAntenna(antenna, note, user).then(hit => { if (hit) { this.antennaService.addNoteToAntenna(antenna, note, user); } diff --git a/packages/backend/src/services/NoteDeleteService.ts b/packages/backend/src/services/NoteDeleteService.ts index 72e2e71e0f16..9d3897dd4c01 100644 --- a/packages/backend/src/services/NoteDeleteService.ts +++ b/packages/backend/src/services/NoteDeleteService.ts @@ -1,24 +1,21 @@ import { Brackets, In } from 'typeorm'; import { Injectable, Inject } from '@nestjs/common'; -import renderDelete from '@/services/remote/activitypub/renderer/delete.js'; -import renderAnnounce from '@/services/remote/activitypub/renderer/announce.js'; -import renderUndo from '@/services/remote/activitypub/renderer/undo.js'; -import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; -import renderTombstone from '@/services/remote/activitypub/renderer/tombstone.js'; import type { User, ILocalUser, IRemoteUser } from '@/models/entities/User.js'; import type { Note, IMentionedRemoteUsers } from '@/models/entities/Note.js'; import type { Notes } from '@/models/index.js'; import { Users, Instances } from '@/models/index.js'; -import { deliverToFollowers, deliverToUser } from '@/services/remote/activitypub/deliver-manager.js'; import { countSameRenotes } from '@/misc/count-same-renotes.js'; -import type { RelayService } from '@/services/RelayService.js'; -import type { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; +import { RelayService } from '@/services/RelayService.js'; +import { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; -import type NotesChart from '@/services/chart/charts/notes.js'; -import type PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; -import type InstanceChart from '@/services/chart/charts/instance.js'; -import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import { Config } from '@/config.js'; +import NotesChart from '@/services/chart/charts/notes.js'; +import PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; +import InstanceChart from '@/services/chart/charts/instance.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { ApRendererService } from './remote/activitypub/ApRendererService.js'; +import { ApDeliverManagerService } from './remote/activitypub/ApDeliverManagerService.js'; +import { UserEntityService } from './entities/UserEntityService.js'; @Injectable() export class NoteDeleteService { @@ -29,9 +26,12 @@ export class NoteDeleteService { @Inject('notesRepository') private notesRepository: typeof Notes, + private userEntityService: UserEntityService, private globalEventServie: GlobalEventService, private relayService: RelayService, private federatedInstanceService: FederatedInstanceService, + private apRendererService: ApRendererService, + private apDeliverManagerService: ApDeliverManagerService, private notesChart: NotesChart, private perUserNotesChart: PerUserNotesChart, private instanceChart: InstanceChart, @@ -61,7 +61,7 @@ export class NoteDeleteService { }); //#region ローカルの投稿なら削除アクティビティを配送 - if (Users.isLocalUser(user) && !note.localOnly) { + if (this.userEntityService.isLocalUser(user) && !note.localOnly) { let renote: Note | null = null; // if deletd note is renote @@ -71,9 +71,9 @@ export class NoteDeleteService { }); } - const content = renderActivity(renote - ? renderUndo(renderAnnounce(renote.uri || `${this.config.url}/notes/${renote.id}`, note), user) - : renderDelete(renderTombstone(`${this.config.url}/notes/${note.id}`), user)); + const content = this.apRendererService.renderActivity(renote + ? this.apRendererService.renderUndo(this.apRendererService.renderAnnounce(renote.uri || `${this.config.url}/notes/${renote.id}`, note), user) + : this.apRendererService.renderDelete(this.apRendererService.renderTombstone(`${this.config.url}/notes/${note.id}`), user)); this.#deliverToConcerned(user, note, content); } @@ -82,8 +82,8 @@ export class NoteDeleteService { const cascadingNotes = (await this.#findCascadingNotes(note)).filter(note => !note.localOnly); // filter out local-only notes for (const cascadingNote of cascadingNotes) { if (!cascadingNote.user) continue; - if (!Users.isLocalUser(cascadingNote.user)) continue; - const content = renderActivity(renderDelete(renderTombstone(`${this.config.url}/notes/${cascadingNote.id}`), cascadingNote.user)); + if (!this.userEntityService.isLocalUser(cascadingNote.user)) continue; + const content = this.apRendererService.renderActivity(this.apRendererService.renderDelete(this.apRendererService.renderTombstone(`${this.config.url}/notes/${cascadingNote.id}`), cascadingNote.user)); this.#deliverToConcerned(cascadingNote.user, cascadingNote, content); } //#endregion @@ -92,7 +92,7 @@ export class NoteDeleteService { this.notesChart.update(note, false); this.perUserNotesChart.update(user, note, false); - if (Users.isRemoteUser(user)) { + if (this.userEntityService.isRemoteUser(user)) { this.federatedInstanceService.registerOrFetchInstanceDoc(user.host).then(i => { Instances.decrement({ id: i.id }, 'notesCount', 1); this.instanceChart.updateNote(i.host, note, false); @@ -154,11 +154,11 @@ export class NoteDeleteService { } async #deliverToConcerned(user: { id: ILocalUser['id']; host: null; }, note: Note, content: any) { - deliverToFollowers(user, content); + this.apDeliverManagerService.deliverToFollowers(user, content); this.relayService.deliverToRelays(user, content); const remoteUsers = await this.#getMentionedRemoteUsers(note); for (const remoteUser of remoteUsers) { - deliverToUser(user, content, remoteUser); + this.apDeliverManagerService.deliverToUser(user, content, remoteUser); } } } diff --git a/packages/backend/src/services/NotePiningService.ts b/packages/backend/src/services/NotePiningService.ts index c3fcad76a202..16db1939415b 100644 --- a/packages/backend/src/services/NotePiningService.ts +++ b/packages/backend/src/services/NotePiningService.ts @@ -9,6 +9,9 @@ import { IdService } from '@/services/IdService.js'; import type { UserNotePining } from '@/models/entities/UserNotePining.js'; import { RelayService } from '@/services/RelayService.js'; import { Config } from '@/config.js'; +import { UserEntityService } from './entities/UserEntityService.js'; +import { ApDeliverManagerService } from './remote/activitypub/ApDeliverManagerService.js'; +import { ApRendererService } from './remote/activitypub/ApRendererService.js'; @Injectable() export class NotePiningService { @@ -25,8 +28,11 @@ export class NotePiningService { @Inject('userNotePiningsRepository') private userNotePiningsRepository: typeof UserNotePinings, + private userEntityService: UserEntityService, private idService: IdService, private relayService: RelayService, + private apDeliverManagerService: ApDeliverManagerService, + private apRendererService: ApRendererService, ) { } @@ -65,7 +71,7 @@ export class NotePiningService { // Deliver to remote followers if (this.userEntityService.isLocalUser(user)) { - deliverPinnedChange(user.id, note.id, true); + this.deliverPinnedChange(user.id, note.id, true); } } @@ -92,7 +98,7 @@ export class NotePiningService { // Deliver to remote followers if (this.userEntityService.isLocalUser(user)) { - deliverPinnedChange(user.id, noteId, false); + this.deliverPinnedChange(user.id, noteId, false); } } @@ -104,9 +110,9 @@ export class NotePiningService { const target = `${this.config.url}/users/${user.id}/collections/featured`; const item = `${this.config.url}/notes/${noteId}`; - const content = renderActivity(isAddition ? renderAdd(user, target, item) : renderRemove(user, target, item)); + const content = this.apRendererService.renderActivity(isAddition ? this.apRendererService.renderAdd(user, target, item) : this.apRendererService.renderRemove(user, target, item)); - deliverToFollowers(user, content); + this.apDeliverManagerService.deliverToFollowers(user, content); this.relayService.deliverToRelays(user, content); } } diff --git a/packages/backend/src/services/NoteReadService.ts b/packages/backend/src/services/NoteReadService.ts index a0650d117d40..235cea68d0a1 100644 --- a/packages/backend/src/services/NoteReadService.ts +++ b/packages/backend/src/services/NoteReadService.ts @@ -1,13 +1,18 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, IsNull, Not } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { NoteUnreads , Users } from '@/models/index.js'; +import type { AntennaNotes, ChannelFollowings, Followings, Mutings, NoteThreadMutings , NoteUnreads , Users } from '@/models/index.js'; + import type { User } from '@/models/entities/User.js'; import type { Channel } from '@/models/entities/Channel.js'; import type { Packed } from '@/misc/schema.js'; import type { Note } from '@/models/entities/Note.js'; -import type { IdService } from '@/services/IdService.js'; -import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import { IdService } from '@/services/IdService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { getAntennas } from '@/misc/antenna-cache.js'; +import { UserEntityService } from './entities/UserEntityService.js'; +import { NotificationService } from './NotificationService.js'; +import { AntennaService } from './AntennaService.js'; @Injectable() export class NoteReadService { @@ -18,8 +23,26 @@ export class NoteReadService { @Inject('noteUnreadsRepository') private noteUnreadsRepository: typeof NoteUnreads, + @Inject('mutingsRepository') + private mutingsRepository: typeof Mutings, + + @Inject('noteThreadMutingsRepository') + private noteThreadMutingsRepository: typeof NoteThreadMutings, + + @Inject('followingsRepository') + private followingsRepository: typeof Followings, + + @Inject('channelFollowingsRepository') + private channelFollowingsRepository: typeof ChannelFollowings, + + @Inject('antennaNotesRepository') + private antennaNotesRepository: typeof AntennaNotes, + + private userEntityService: UserEntityService, private idService: IdService, private globalEventServie: GlobalEventService, + private notificationService: NotificationService, + private antennaService: AntennaService, ) { } @@ -30,14 +53,14 @@ export class NoteReadService { }): Promise { //#region ミュートしているなら無視 // TODO: 現在の仕様ではChannelにミュートは適用されないのでよしなにケアする - const mute = await Mutings.findBy({ + const mute = await this.mutingsRepository.findBy({ muterId: userId, }); if (mute.map(m => m.muteeId).includes(note.userId)) return; //#endregion // スレッドミュート - const threadMute = await NoteThreadMutings.findOneBy({ + const threadMute = await this.noteThreadMutingsRepository.findOneBy({ userId: userId, threadId: note.threadId || note.id, }); @@ -81,13 +104,13 @@ export class NoteReadService { followingChannels: Set; }, ): Promise { - const following = info?.following ? info.following : new Set((await Followings.find({ + const following = info?.following ? info.following : new Set((await this.followingsRepository.find({ where: { followerId: userId, }, select: ['followeeId'], })).map(x => x.followeeId)); - const followingChannels = info?.followingChannels ? info.followingChannels : new Set((await ChannelFollowings.find({ + const followingChannels = info?.followingChannels ? info.followingChannels : new Set((await this.channelFollowingsRepository.find({ where: { followerId: userId, }, @@ -113,7 +136,7 @@ export class NoteReadService { if (note.user != null) { // たぶんnullになることは無いはずだけど一応 for (const antenna of myAntennas) { - if (await checkHitAntenna(antenna, note, note.user, undefined, Array.from(following))) { + if (await this.antennaService.checkHitAntenna(antenna, note, note.user, undefined, Array.from(following))) { readAntennaNotes.push(note); } } @@ -159,13 +182,13 @@ export class NoteReadService { } }); - readNotificationByQuery(userId, { + this.notificationService.readNotificationByQuery(userId, { noteId: In([...readMentions.map(n => n.id), ...readSpecifiedNotes.map(n => n.id)]), }); } if (readAntennaNotes.length > 0) { - await AntennaNotes.update({ + await this.antennaNotesRepository.update({ antennaId: In(myAntennas.map(a => a.id)), noteId: In(readAntennaNotes.map(n => n.id)), }, { @@ -174,7 +197,7 @@ export class NoteReadService { // TODO: まとめてクエリしたい for (const antenna of myAntennas) { - const count = await AntennaNotes.countBy({ + const count = await this.antennaNotesRepository.countBy({ antennaId: antenna.id, read: false, }); @@ -184,7 +207,7 @@ export class NoteReadService { } } - this.usersRepository.getHasUnreadAntenna(userId).then(unread => { + this.userEntityService.getHasUnreadAntenna(userId).then(unread => { if (!unread) { this.globalEventServie.publishMainStream(userId, 'readAllAntennas'); } diff --git a/packages/backend/src/services/PollService.ts b/packages/backend/src/services/PollService.ts index fe668b5af78e..59b4fdf1d953 100644 --- a/packages/backend/src/services/PollService.ts +++ b/packages/backend/src/services/PollService.ts @@ -9,9 +9,9 @@ import type { CacheableUser } from '@/models/entities/User.js'; import { IdService } from '@/services/IdService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { CreateNotificationService } from '@/services/CreateNotificationService.js'; -import renderUpdate from '@/services/remote/activitypub/renderer/update.js'; -import { renderActivity } from '@/services/remote/activitypub/renderer/index.js'; -import renderNote from '@/services/remote/activitypub/renderer/note.js'; +import { ApRendererService } from './remote/activitypub/ApRendererService.js'; +import { UserEntityService } from './entities/UserEntityService.js'; +import { ApDeliverManagerService } from './remote/activitypub/ApDeliverManagerService.js'; @Injectable() export class PollService { @@ -31,10 +31,13 @@ export class PollService { @Inject('blockingsRepository') private blockingsRepository: typeof Blockings, + private userEntityService: UserEntityService, private idService: IdService, private relayService: RelayService, private globalEventServie: GlobalEventService, private createNotificationService: CreateNotificationService, + private apRendererService: ApRendererService, + private apDeliverManagerService: ApDeliverManagerService, ) { } @@ -105,8 +108,8 @@ export class PollService { if (user == null) throw new Error('note not found'); if (this.userEntityService.isLocalUser(user)) { - const content = renderActivity(renderUpdate(await renderNote(note, false), user)); - deliverToFollowers(user, content); + const content = this.apRendererService.renderActivity(this.apRendererService.renderUpdate(await this.apRendererService.renderNote(note, false), user)); + this.apDeliverManagerService.deliverToFollowers(user, content); this.relayService.deliverToRelays(user, content); } } diff --git a/packages/backend/src/services/ReactionService.ts b/packages/backend/src/services/ReactionService.ts index 859efb59d3c1..f78f86580834 100644 --- a/packages/backend/src/services/ReactionService.ts +++ b/packages/backend/src/services/ReactionService.ts @@ -2,7 +2,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { Blockings, Emojis, NoteReactions , Users , Notes } from '@/models/index.js'; - import { IdentifiableError } from '@/misc/identifiable-error.js'; import type { IRemoteUser, User } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; @@ -11,9 +10,12 @@ import type { NoteReaction } from '@/models/entities/NoteReaction.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { CreateNotificationService } from '@/services/CreateNotificationService.js'; -import DeliverManager from '@/services/remote/activitypub/deliver-manager.js'; import PerUserReactionsChart from '@/services/chart/charts/per-user-reactions.js'; -import { toDbReaction } from '@/misc/reaction-lib.js'; +import { decodeReaction, toDbReaction } from '@/misc/reaction-lib.js'; +import { ApDeliverManagerService } from './remote/activitypub/ApDeliverManagerService.js'; +import { NoteEntityService } from './entities/NoteEntityService.js'; +import { UserEntityService } from './entities/UserEntityService.js'; +import { ApRendererService } from './remote/activitypub/ApRendererService.js'; @Injectable() export class ReactionService { @@ -33,8 +35,12 @@ export class ReactionService { @Inject('emojisRepository') private emojisRepository: typeof Emojis, + private userEntityService: UserEntityService, + private noteEntityService: NoteEntityService, private idService: IdService, private globalEventServie: GlobalEventService, + private apRendererService: ApRendererService, + private apDeliverManagerService: ApDeliverManagerService, private createNotificationService: CreateNotificationService, private perUserReactionsChart: PerUserReactionsChart, ) { @@ -53,7 +59,7 @@ export class ReactionService { } // check visibility - if (!await this.notesRepository.isVisibleForMe(note, user.id)) { + if (!await this.noteEntityService.isVisibleForMe(note, user.id)) { throw new IdentifiableError('68e9d2d1-48bf-42c2-b90a-b20e09fd3d48', 'Note not accessible for you.'); } @@ -80,7 +86,7 @@ export class ReactionService { if (exists.reaction !== reaction) { // 別のリアクションがすでにされていたら置き換える - await deleteReaction(user, note); + await this.delete(user, note); await this.noteReactionsRepository.insert(record); } else { // 同じリアクションがすでにされていたらエラー @@ -134,8 +140,8 @@ export class ReactionService { //#region 配信 if (this.userEntityService.isLocalUser(user) && !note.localOnly) { - const content = renderActivity(await renderLike(record, note)); - const dm = new DeliverManager(user, content); + const content = this.apRendererService.renderActivity(await this.apRendererService.renderLike(record, note)); + const dm = this.apDeliverManagerService.createDeliverManager(user, content); if (note.userHost !== null) { const reactee = await this.usersRepository.findOneBy({ id: note.userId }); dm.addDirectRecipe(reactee as IRemoteUser); @@ -191,8 +197,8 @@ export class ReactionService { //#region 配信 if (this.userEntityService.isLocalUser(user) && !note.localOnly) { - const content = renderActivity(renderUndo(await renderLike(exist, note), user)); - const dm = new DeliverManager(user, content); + const content = this.apRendererService.renderActivity(this.apRendererService.renderUndo(await this.apRendererService.renderLike(exist, note), user)); + const dm = this.apDeliverManagerService.createDeliverManager(user, content); if (note.userHost !== null) { const reactee = await this.usersRepository.findOneBy({ id: note.userId }); dm.addDirectRecipe(reactee as IRemoteUser); diff --git a/packages/backend/src/services/RelayService.ts b/packages/backend/src/services/RelayService.ts index 3bb472d29ff2..6c71e0011ac9 100644 --- a/packages/backend/src/services/RelayService.ts +++ b/packages/backend/src/services/RelayService.ts @@ -2,12 +2,12 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import type { ILocalUser, User } from '@/models/entities/User.js'; import type { Relays, Users } from '@/models/index.js'; -import type { IdService } from '@/services/IdService.js'; +import { IdService } from '@/services/IdService.js'; import { Cache } from '@/misc/cache.js'; import type { Relay } from '@/models/entities/Relay.js'; -import type { QueueService } from '@/queue/queue.service.js'; -import type { CreateSystemUserService } from '@/services/CreateSystemUserService.js'; -import type { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; +import { QueueService } from '@/queue/queue.service.js'; +import { CreateSystemUserService } from '@/services/CreateSystemUserService.js'; +import { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; const ACTOR_USERNAME = 'relay.actor' as const; diff --git a/packages/backend/src/services/S3Service.ts b/packages/backend/src/services/S3Service.ts index 7709f086d84a..0cbb70217b22 100644 --- a/packages/backend/src/services/S3Service.ts +++ b/packages/backend/src/services/S3Service.ts @@ -4,7 +4,7 @@ import S3 from 'aws-sdk/clients/s3.js'; import { DI } from '@/di-symbols.js'; import { Config } from '@/config.js'; import type { Meta } from '@/models/entities/Meta.js'; -import { HttpRequestService } from '../HttpRequestService.js'; +import { HttpRequestService } from './HttpRequestService.js'; @Injectable() export class S3Service { diff --git a/packages/backend/src/services/SignupService.ts b/packages/backend/src/services/SignupService.ts index 3da864c02447..dfa93247a55d 100644 --- a/packages/backend/src/services/SignupService.ts +++ b/packages/backend/src/services/SignupService.ts @@ -12,6 +12,7 @@ import { IdService } from '@/services/IdService.js'; import { toPunyNullable } from '@/misc/convert-host'; import { UserKeypair } from '@/models/entities/UserKeypair.js'; import { UsedUsername } from '@/models/entities/UsedUsername.js'; +import generateUserToken from '@/misc/generate-native-user-token.js'; import UsersChart from './chart/charts/users.js'; import { UserEntityService } from './entities/UserEntityService.js'; diff --git a/packages/backend/src/services/UserBlockingService.ts b/packages/backend/src/services/UserBlockingService.ts index 7ce648c89336..0b816d92d1c9 100644 --- a/packages/backend/src/services/UserBlockingService.ts +++ b/packages/backend/src/services/UserBlockingService.ts @@ -1,7 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import type { FollowRequests , Followings , UserLists , UserListJoinings , Users , Blockings } from '@/models/index.js'; - import { IdService } from '@/services/IdService.js'; import type { CacheableUser, User } from '@/models/entities/User.js'; import type { Blocking } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/services/UserFollowingService.ts b/packages/backend/src/services/UserFollowingService.ts index c602ea853db9..69a3f568ba40 100644 --- a/packages/backend/src/services/UserFollowingService.ts +++ b/packages/backend/src/services/UserFollowingService.ts @@ -12,7 +12,7 @@ import InstanceChart from '@/services/chart/charts/instance.js'; import { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; import { WebhookService } from '@/services/WebhookService.js'; import { CreateNotificationService } from '@/services/CreateNotificationService.js'; -import Logger from './logger.js'; +import Logger from '../logger.js'; import { UserEntityService } from './entities/UserEntityService.js'; import { ApRendererService } from './remote/activitypub/ApRendererService.js'; diff --git a/packages/backend/src/services/UserKeypairStoreService.ts b/packages/backend/src/services/UserKeypairStoreService.ts index a4859acdcbb6..a732ace8d4d7 100644 --- a/packages/backend/src/services/UserKeypairStoreService.ts +++ b/packages/backend/src/services/UserKeypairStoreService.ts @@ -1,5 +1,4 @@ import { Inject, Injectable } from '@nestjs/common'; - import type { User } from '@/models/entities/User.js'; import type { UserKeypairs } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; diff --git a/packages/backend/src/services/UserMutingService.ts b/packages/backend/src/services/UserMutingService.ts index 057fee0bd641..3d3c1a33a9ab 100644 --- a/packages/backend/src/services/UserMutingService.ts +++ b/packages/backend/src/services/UserMutingService.ts @@ -1,4 +1,3 @@ - import { Inject, Injectable } from '@nestjs/common'; import type { Users , Mutings } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; From b01906ff781424cd242c3476ee8a91cc3f2ec7a4 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 04:51:30 +0900 Subject: [PATCH 137/180] Update index.ts --- packages/backend/src/boot/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/boot/index.ts b/packages/backend/src/boot/index.ts index c3d059225654..f67b09b1cc55 100644 --- a/packages/backend/src/boot/index.ts +++ b/packages/backend/src/boot/index.ts @@ -2,7 +2,7 @@ import cluster from 'node:cluster'; import chalk from 'chalk'; import Xev from 'xev'; -import Logger from '@/services/logger.js'; +import Logger from '@/logger.js'; import { envOption } from '../env.js'; // for typeorm From 4cfd0d94e14f641589c7f8161e51eda8fbb183b6 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 04:53:11 +0900 Subject: [PATCH 138/180] wip --- packages/backend/src/boot/master.ts | 8 ++++---- packages/backend/src/boot/worker.ts | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts index 48f2a6263029..3d741798c2d1 100644 --- a/packages/backend/src/boot/master.ts +++ b/packages/backend/src/boot/master.ts @@ -6,8 +6,8 @@ import cluster from 'node:cluster'; import chalk from 'chalk'; import chalkTemplate from 'chalk-template'; import semver from 'semver'; -import Logger from '@/services/logger.js'; -import loadConfig from '@/config.js'; +import Logger from '@/logger.js'; +import { loadConfig } from '@/config.js'; import type { Config } from '@/config.js'; import { lessThan } from '@/prelude/array.js'; import { showMachineInfo } from '@/misc/show-machine-info.js'; @@ -135,9 +135,9 @@ async function connectDb(): Promise { await initDb(); const v = await db.query('SHOW server_version').then(x => x[0].server_version); dbLogger.succ(`Connected: v${v}`); - } catch (e) { + } catch (err) { dbLogger.error('Cannot connect', null, true); - dbLogger.error(e); + dbLogger.error(err); process.exit(1); } } diff --git a/packages/backend/src/boot/worker.ts b/packages/backend/src/boot/worker.ts index 687d53ce4d5d..f72db535150d 100644 --- a/packages/backend/src/boot/worker.ts +++ b/packages/backend/src/boot/worker.ts @@ -2,8 +2,8 @@ import cluster from 'node:cluster'; import { NestFactory } from '@nestjs/core'; import { envOption } from '@/env.js'; import { ChartManagementService } from '@/services/chart/ChartManagementService.js'; +import { ServerService } from '@/server/ServerService.js'; import { initDb } from '../db/postgre.js'; -import createServer from '../server/index.js'; import initializeQueue from '../queue/index.js'; import { AppModule } from '../app.module.js'; @@ -16,7 +16,8 @@ export async function workerMain() { const app = await NestFactory.createApplicationContext(AppModule); // start server - await createServer(app); + const serverService = app.get(ServerService); + serverService.launch(); // start job queue if (!envOption.onlyServer) { From 2e793b0820a49c6fb53b16d4c1ab17556bbc7950 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 04:59:33 +0900 Subject: [PATCH 139/180] wip --- .../backend/src/models/entities/AbuseUserReport.ts | 4 ++-- packages/backend/src/models/entities/AccessToken.ts | 4 ++-- .../backend/src/models/entities/AnnouncementRead.ts | 4 ++-- packages/backend/src/models/entities/Antenna.ts | 6 +++--- packages/backend/src/models/entities/AntennaNote.ts | 4 ++-- packages/backend/src/models/entities/App.ts | 2 +- .../src/models/entities/AttestationChallenge.ts | 2 +- packages/backend/src/models/entities/AuthSession.ts | 4 ++-- packages/backend/src/models/entities/Blocking.ts | 2 +- packages/backend/src/models/entities/Channel.ts | 4 ++-- .../backend/src/models/entities/ChannelFollowing.ts | 4 ++-- .../backend/src/models/entities/ChannelNotePining.ts | 4 ++-- packages/backend/src/models/entities/Clip.ts | 2 +- packages/backend/src/models/entities/ClipNote.ts | 4 ++-- packages/backend/src/models/entities/DriveFile.ts | 4 ++-- packages/backend/src/models/entities/DriveFolder.ts | 2 +- packages/backend/src/models/entities/FollowRequest.ts | 2 +- packages/backend/src/models/entities/Following.ts | 2 +- packages/backend/src/models/entities/GalleryLike.ts | 4 ++-- packages/backend/src/models/entities/GalleryPost.ts | 4 ++-- packages/backend/src/models/entities/Hashtag.ts | 2 +- .../backend/src/models/entities/MessagingMessage.ts | 6 +++--- packages/backend/src/models/entities/Meta.ts | 4 ++-- packages/backend/src/models/entities/ModerationLog.ts | 2 +- packages/backend/src/models/entities/MutedNote.ts | 4 ++-- packages/backend/src/models/entities/Muting.ts | 2 +- packages/backend/src/models/entities/Note.ts | 6 +++--- packages/backend/src/models/entities/NoteFavorite.ts | 4 ++-- packages/backend/src/models/entities/NoteReaction.ts | 4 ++-- .../backend/src/models/entities/NoteThreadMuting.ts | 4 ++-- packages/backend/src/models/entities/NoteUnread.ts | 6 +++--- packages/backend/src/models/entities/Notification.ts | 10 +++++----- packages/backend/src/models/entities/Page.ts | 4 ++-- packages/backend/src/models/entities/PageLike.ts | 4 ++-- .../src/models/entities/PasswordResetRequest.ts | 2 +- packages/backend/src/models/entities/Poll.ts | 4 ++-- packages/backend/src/models/entities/PollVote.ts | 4 ++-- packages/backend/src/models/entities/PromoNote.ts | 4 ++-- packages/backend/src/models/entities/PromoRead.ts | 4 ++-- packages/backend/src/models/entities/RegistryItem.ts | 2 +- packages/backend/src/models/entities/Signin.ts | 2 +- packages/backend/src/models/entities/SwSubscription.ts | 2 +- packages/backend/src/models/entities/User.ts | 2 +- packages/backend/src/models/entities/UserGroup.ts | 2 +- .../backend/src/models/entities/UserGroupInvitation.ts | 4 ++-- .../backend/src/models/entities/UserGroupJoining.ts | 4 ++-- packages/backend/src/models/entities/UserIp.ts | 4 ++-- packages/backend/src/models/entities/UserKeypair.ts | 2 +- packages/backend/src/models/entities/UserList.ts | 2 +- .../backend/src/models/entities/UserListJoining.ts | 4 ++-- packages/backend/src/models/entities/UserNotePining.ts | 4 ++-- packages/backend/src/models/entities/UserProfile.ts | 4 ++-- packages/backend/src/models/entities/UserPublickey.ts | 2 +- .../backend/src/models/entities/UserSecurityKey.ts | 2 +- packages/backend/src/models/entities/Webhook.ts | 2 +- 55 files changed, 96 insertions(+), 96 deletions(-) diff --git a/packages/backend/src/models/entities/AbuseUserReport.ts b/packages/backend/src/models/entities/AbuseUserReport.ts index 6ac5635528de..07305cf23a1d 100644 --- a/packages/backend/src/models/entities/AbuseUserReport.ts +++ b/packages/backend/src/models/entities/AbuseUserReport.ts @@ -1,6 +1,6 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; +import { User } from './User.js'; @Entity() export class AbuseUserReport { @@ -52,7 +52,7 @@ export class AbuseUserReport { public resolved: boolean; @Column('boolean', { - default: false + default: false, }) public forwarded: boolean; diff --git a/packages/backend/src/models/entities/AccessToken.ts b/packages/backend/src/models/entities/AccessToken.ts index c6e2141a46dc..8e987ffeefb3 100644 --- a/packages/backend/src/models/entities/AccessToken.ts +++ b/packages/backend/src/models/entities/AccessToken.ts @@ -1,7 +1,7 @@ import { Entity, PrimaryColumn, Index, Column, ManyToOne, JoinColumn } from 'typeorm'; -import { User } from './user.js'; -import { App } from './app.js'; import { id } from '../id.js'; +import { User } from './User.js'; +import { App } from './App.js'; @Entity() export class AccessToken { diff --git a/packages/backend/src/models/entities/AnnouncementRead.ts b/packages/backend/src/models/entities/AnnouncementRead.ts index e4d256a8642e..72cf6888005d 100644 --- a/packages/backend/src/models/entities/AnnouncementRead.ts +++ b/packages/backend/src/models/entities/AnnouncementRead.ts @@ -1,7 +1,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { Announcement } from './announcement.js'; import { id } from '../id.js'; +import { User } from './User.js'; +import { Announcement } from './Announcement.js'; @Entity() @Index(['userId', 'announcementId'], { unique: true }) diff --git a/packages/backend/src/models/entities/Antenna.ts b/packages/backend/src/models/entities/Antenna.ts index 6c8bb13e5067..860fd9cf5544 100644 --- a/packages/backend/src/models/entities/Antenna.ts +++ b/packages/backend/src/models/entities/Antenna.ts @@ -1,8 +1,8 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; -import { UserList } from './user-list.js'; -import { UserGroupJoining } from './user-group-joining.js'; +import { User } from './User.js'; +import { UserList } from './UserList.js'; +import { UserGroupJoining } from './UserGroupJoining.js'; @Entity() export class Antenna { diff --git a/packages/backend/src/models/entities/AntennaNote.ts b/packages/backend/src/models/entities/AntennaNote.ts index fcca493fe071..5524a89367d3 100644 --- a/packages/backend/src/models/entities/AntennaNote.ts +++ b/packages/backend/src/models/entities/AntennaNote.ts @@ -1,7 +1,7 @@ import { Entity, Index, JoinColumn, Column, ManyToOne, PrimaryColumn } from 'typeorm'; -import { Note } from './note.js'; -import { Antenna } from './antenna.js'; import { id } from '../id.js'; +import { Note } from './Note.js'; +import { Antenna } from './Antenna.js'; @Entity() @Index(['noteId', 'antennaId'], { unique: true }) diff --git a/packages/backend/src/models/entities/App.ts b/packages/backend/src/models/entities/App.ts index 46c11548a527..3a1ea7732e1c 100644 --- a/packages/backend/src/models/entities/App.ts +++ b/packages/backend/src/models/entities/App.ts @@ -1,6 +1,6 @@ import { Entity, PrimaryColumn, Column, Index, ManyToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; +import { User } from './User.js'; @Entity() export class App { diff --git a/packages/backend/src/models/entities/AttestationChallenge.ts b/packages/backend/src/models/entities/AttestationChallenge.ts index c40df23293bb..4795642657fc 100644 --- a/packages/backend/src/models/entities/AttestationChallenge.ts +++ b/packages/backend/src/models/entities/AttestationChallenge.ts @@ -1,6 +1,6 @@ import { PrimaryColumn, Entity, JoinColumn, Column, ManyToOne, Index } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; +import { User } from './User.js'; @Entity() export class AttestationChallenge { diff --git a/packages/backend/src/models/entities/AuthSession.ts b/packages/backend/src/models/entities/AuthSession.ts index 295d1b486c4b..6b2f50e8d674 100644 --- a/packages/backend/src/models/entities/AuthSession.ts +++ b/packages/backend/src/models/entities/AuthSession.ts @@ -1,7 +1,7 @@ import { Entity, PrimaryColumn, Index, Column, ManyToOne, JoinColumn } from 'typeorm'; -import { User } from './user.js'; -import { App } from './app.js'; import { id } from '../id.js'; +import { User } from './User.js'; +import { App } from './App.js'; @Entity() export class AuthSession { diff --git a/packages/backend/src/models/entities/Blocking.ts b/packages/backend/src/models/entities/Blocking.ts index 4ac73a00b5dd..9892ff308e53 100644 --- a/packages/backend/src/models/entities/Blocking.ts +++ b/packages/backend/src/models/entities/Blocking.ts @@ -1,6 +1,6 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; +import { User } from './User.js'; @Entity() @Index(['blockerId', 'blockeeId'], { unique: true }) diff --git a/packages/backend/src/models/entities/Channel.ts b/packages/backend/src/models/entities/Channel.ts index abf6668bd285..a6e32d54f7c4 100644 --- a/packages/backend/src/models/entities/Channel.ts +++ b/packages/backend/src/models/entities/Channel.ts @@ -1,7 +1,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; -import { DriveFile } from './drive-file.js'; +import { User } from './User.js'; +import { DriveFile } from './DriveFile.js'; @Entity() export class Channel { diff --git a/packages/backend/src/models/entities/ChannelFollowing.ts b/packages/backend/src/models/entities/ChannelFollowing.ts index 029dd6cf1a07..c65c38b67db5 100644 --- a/packages/backend/src/models/entities/ChannelFollowing.ts +++ b/packages/backend/src/models/entities/ChannelFollowing.ts @@ -1,7 +1,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; -import { Channel } from './channel.js'; +import { User } from './User.js'; +import { Channel } from './Channel.js'; @Entity() @Index(['followerId', 'followeeId'], { unique: true }) diff --git a/packages/backend/src/models/entities/ChannelNotePining.ts b/packages/backend/src/models/entities/ChannelNotePining.ts index 23be3b69d46b..ab5796626af9 100644 --- a/packages/backend/src/models/entities/ChannelNotePining.ts +++ b/packages/backend/src/models/entities/ChannelNotePining.ts @@ -1,7 +1,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { Note } from './note.js'; -import { Channel } from './channel.js'; import { id } from '../id.js'; +import { Note } from './Note.js'; +import { Channel } from './Channel.js'; @Entity() @Index(['channelId', 'noteId'], { unique: true }) diff --git a/packages/backend/src/models/entities/Clip.ts b/packages/backend/src/models/entities/Clip.ts index 1386684c32c9..57a310ac03bf 100644 --- a/packages/backend/src/models/entities/Clip.ts +++ b/packages/backend/src/models/entities/Clip.ts @@ -1,6 +1,6 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; +import { User } from './User.js'; @Entity() export class Clip { diff --git a/packages/backend/src/models/entities/ClipNote.ts b/packages/backend/src/models/entities/ClipNote.ts index 6f3688550830..bc9ef4b8743e 100644 --- a/packages/backend/src/models/entities/ClipNote.ts +++ b/packages/backend/src/models/entities/ClipNote.ts @@ -1,7 +1,7 @@ import { Entity, Index, JoinColumn, Column, ManyToOne, PrimaryColumn } from 'typeorm'; -import { Note } from './note.js'; -import { Clip } from './clip.js'; import { id } from '../id.js'; +import { Note } from './Note.js'; +import { Clip } from './Clip.js'; @Entity() @Index(['noteId', 'clipId'], { unique: true }) diff --git a/packages/backend/src/models/entities/DriveFile.ts b/packages/backend/src/models/entities/DriveFile.ts index d410b1d429dc..7b9670fb924d 100644 --- a/packages/backend/src/models/entities/DriveFile.ts +++ b/packages/backend/src/models/entities/DriveFile.ts @@ -1,7 +1,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; import { id } from '../id.js'; -import { User } from './user.js'; -import { DriveFolder } from './drive-folder.js'; +import { User } from './User.js'; +import { DriveFolder } from './DriveFolder.js'; @Entity() @Index(['userId', 'folderId', 'id']) diff --git a/packages/backend/src/models/entities/DriveFolder.ts b/packages/backend/src/models/entities/DriveFolder.ts index d4022c6ebc14..2a73a0875d36 100644 --- a/packages/backend/src/models/entities/DriveFolder.ts +++ b/packages/backend/src/models/entities/DriveFolder.ts @@ -1,6 +1,6 @@ import { JoinColumn, ManyToOne, Entity, PrimaryColumn, Index, Column } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; +import { User } from './User.js'; @Entity() export class DriveFolder { diff --git a/packages/backend/src/models/entities/FollowRequest.ts b/packages/backend/src/models/entities/FollowRequest.ts index 89946f6d35df..0988e7e50470 100644 --- a/packages/backend/src/models/entities/FollowRequest.ts +++ b/packages/backend/src/models/entities/FollowRequest.ts @@ -1,6 +1,6 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; +import { User } from './User.js'; @Entity() @Index(['followerId', 'followeeId'], { unique: true }) diff --git a/packages/backend/src/models/entities/Following.ts b/packages/backend/src/models/entities/Following.ts index b283ca7e8af8..112afd7e6e5f 100644 --- a/packages/backend/src/models/entities/Following.ts +++ b/packages/backend/src/models/entities/Following.ts @@ -1,6 +1,6 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; +import { User } from './User.js'; @Entity() @Index(['followerId', 'followeeId'], { unique: true }) diff --git a/packages/backend/src/models/entities/GalleryLike.ts b/packages/backend/src/models/entities/GalleryLike.ts index 4ce166d194fc..cc54b528e9d7 100644 --- a/packages/backend/src/models/entities/GalleryLike.ts +++ b/packages/backend/src/models/entities/GalleryLike.ts @@ -1,7 +1,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; -import { GalleryPost } from './gallery-post.js'; +import { User } from './User.js'; +import { GalleryPost } from './GalleryPost.js'; @Entity() @Index(['userId', 'postId'], { unique: true }) diff --git a/packages/backend/src/models/entities/GalleryPost.ts b/packages/backend/src/models/entities/GalleryPost.ts index 774cb946e970..36e879afa780 100644 --- a/packages/backend/src/models/entities/GalleryPost.ts +++ b/packages/backend/src/models/entities/GalleryPost.ts @@ -1,7 +1,7 @@ import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; -import { DriveFile } from './drive-file.js'; +import { User } from './User.js'; +import type { DriveFile } from './DriveFile.js'; @Entity() export class GalleryPost { diff --git a/packages/backend/src/models/entities/Hashtag.ts b/packages/backend/src/models/entities/Hashtag.ts index 6bd991f6299a..2d6bfaa045d4 100644 --- a/packages/backend/src/models/entities/Hashtag.ts +++ b/packages/backend/src/models/entities/Hashtag.ts @@ -1,6 +1,6 @@ import { Entity, PrimaryColumn, Index, Column } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; +import type { User } from './User.js'; @Entity() export class Hashtag { diff --git a/packages/backend/src/models/entities/MessagingMessage.ts b/packages/backend/src/models/entities/MessagingMessage.ts index 099fb7aa013b..69fc9815d40a 100644 --- a/packages/backend/src/models/entities/MessagingMessage.ts +++ b/packages/backend/src/models/entities/MessagingMessage.ts @@ -1,8 +1,8 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { DriveFile } from './drive-file.js'; import { id } from '../id.js'; -import { UserGroup } from './user-group.js'; +import { User } from './User.js'; +import { DriveFile } from './DriveFile.js'; +import { UserGroup } from './UserGroup.js'; @Entity() export class MessagingMessage { diff --git a/packages/backend/src/models/entities/Meta.ts b/packages/backend/src/models/entities/Meta.ts index d33ff2519e69..f528b7ac0802 100644 --- a/packages/backend/src/models/entities/Meta.ts +++ b/packages/backend/src/models/entities/Meta.ts @@ -1,7 +1,7 @@ import { Entity, Column, PrimaryColumn, ManyToOne, JoinColumn } from 'typeorm'; import { id } from '../id.js'; -import { User } from './user.js'; -import { Clip } from './clip.js'; +import { User } from './User.js'; +import type { Clip } from './Clip.js'; @Entity() export class Meta { diff --git a/packages/backend/src/models/entities/ModerationLog.ts b/packages/backend/src/models/entities/ModerationLog.ts index c99e55078269..ab6a226cf7ce 100644 --- a/packages/backend/src/models/entities/ModerationLog.ts +++ b/packages/backend/src/models/entities/ModerationLog.ts @@ -1,6 +1,6 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; +import { User } from './User.js'; @Entity() export class ModerationLog { diff --git a/packages/backend/src/models/entities/MutedNote.ts b/packages/backend/src/models/entities/MutedNote.ts index 96a4fa8e3336..78347d891737 100644 --- a/packages/backend/src/models/entities/MutedNote.ts +++ b/packages/backend/src/models/entities/MutedNote.ts @@ -1,8 +1,8 @@ import { Entity, Index, JoinColumn, Column, ManyToOne, PrimaryColumn } from 'typeorm'; -import { Note } from './note.js'; -import { User } from './user.js'; import { id } from '../id.js'; import { mutedNoteReasons } from '../../types.js'; +import { Note } from './Note.js'; +import { User } from './User.js'; @Entity() @Index(['noteId', 'userId'], { unique: true }) diff --git a/packages/backend/src/models/entities/Muting.ts b/packages/backend/src/models/entities/Muting.ts index 8f9e69063adc..bf5498b96aac 100644 --- a/packages/backend/src/models/entities/Muting.ts +++ b/packages/backend/src/models/entities/Muting.ts @@ -1,6 +1,6 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; +import { User } from './User.js'; @Entity() @Index(['muterId', 'muteeId'], { unique: true }) diff --git a/packages/backend/src/models/entities/Note.ts b/packages/backend/src/models/entities/Note.ts index 0ffeb85f69a8..f1a94bd9ce95 100644 --- a/packages/backend/src/models/entities/Note.ts +++ b/packages/backend/src/models/entities/Note.ts @@ -1,9 +1,9 @@ import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { DriveFile } from './drive-file.js'; import { id } from '../id.js'; import { noteVisibilities } from '../../types.js'; -import { Channel } from './channel.js'; +import { User } from './User.js'; +import { Channel } from './Channel.js'; +import type { DriveFile } from './DriveFile.js'; @Entity() @Index('IDX_NOTE_TAGS', { synchronize: false }) diff --git a/packages/backend/src/models/entities/NoteFavorite.ts b/packages/backend/src/models/entities/NoteFavorite.ts index fe065b77a8eb..80c97cb531f6 100644 --- a/packages/backend/src/models/entities/NoteFavorite.ts +++ b/packages/backend/src/models/entities/NoteFavorite.ts @@ -1,7 +1,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { Note } from './note.js'; -import { User } from './user.js'; import { id } from '../id.js'; +import { Note } from './Note.js'; +import { User } from './User.js'; @Entity() @Index(['userId', 'noteId'], { unique: true }) diff --git a/packages/backend/src/models/entities/NoteReaction.ts b/packages/backend/src/models/entities/NoteReaction.ts index d7bc60989813..c3c381af56ee 100644 --- a/packages/backend/src/models/entities/NoteReaction.ts +++ b/packages/backend/src/models/entities/NoteReaction.ts @@ -1,7 +1,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { Note } from './note.js'; import { id } from '../id.js'; +import { User } from './User.js'; +import { Note } from './Note.js'; @Entity() @Index(['userId', 'noteId'], { unique: true }) diff --git a/packages/backend/src/models/entities/NoteThreadMuting.ts b/packages/backend/src/models/entities/NoteThreadMuting.ts index 8c5f7bbab426..a23176b99436 100644 --- a/packages/backend/src/models/entities/NoteThreadMuting.ts +++ b/packages/backend/src/models/entities/NoteThreadMuting.ts @@ -1,7 +1,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { Note } from './note.js'; import { id } from '../id.js'; +import { User } from './User.js'; +import { Note } from './Note.js'; @Entity() @Index(['userId', 'threadId'], { unique: true }) diff --git a/packages/backend/src/models/entities/NoteUnread.ts b/packages/backend/src/models/entities/NoteUnread.ts index a7acf254d388..af91234d0f9e 100644 --- a/packages/backend/src/models/entities/NoteUnread.ts +++ b/packages/backend/src/models/entities/NoteUnread.ts @@ -1,8 +1,8 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { Note } from './note.js'; import { id } from '../id.js'; -import { Channel } from './channel.js'; +import { User } from './User.js'; +import { Note } from './Note.js'; +import type { Channel } from './Channel.js'; @Entity() @Index(['userId', 'noteId'], { unique: true }) diff --git a/packages/backend/src/models/entities/Notification.ts b/packages/backend/src/models/entities/Notification.ts index db3dba363292..6be372e69ac6 100644 --- a/packages/backend/src/models/entities/Notification.ts +++ b/packages/backend/src/models/entities/Notification.ts @@ -1,11 +1,11 @@ import { Entity, Index, JoinColumn, ManyToOne, Column, PrimaryColumn } from 'typeorm'; -import { User } from './user.js'; +import { notificationTypes } from '@/types.js'; import { id } from '../id.js'; +import { User } from './User.js'; import { Note } from './note.js'; -import { FollowRequest } from './follow-request.js'; -import { UserGroupInvitation } from './user-group-invitation.js'; -import { AccessToken } from './access-token.js'; -import { notificationTypes } from '@/types.js'; +import { FollowRequest } from './FollowRequest.js'; +import { UserGroupInvitation } from './UserGroupInvitation.js'; +import { AccessToken } from './AccessToken.js'; @Entity() export class Notification { diff --git a/packages/backend/src/models/entities/Page.ts b/packages/backend/src/models/entities/Page.ts index baad3a36fa46..6078bc1bc706 100644 --- a/packages/backend/src/models/entities/Page.ts +++ b/packages/backend/src/models/entities/Page.ts @@ -1,7 +1,7 @@ import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; -import { DriveFile } from './drive-file.js'; +import { User } from './User.js'; +import { DriveFile } from './DriveFile.js'; @Entity() @Index(['userId', 'name'], { unique: true }) diff --git a/packages/backend/src/models/entities/PageLike.ts b/packages/backend/src/models/entities/PageLike.ts index 17f4ebf520ee..f8c5943a3e12 100644 --- a/packages/backend/src/models/entities/PageLike.ts +++ b/packages/backend/src/models/entities/PageLike.ts @@ -1,7 +1,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; -import { Page } from './page.js'; +import { User } from './User.js'; +import { Page } from './Page.js'; @Entity() @Index(['userId', 'pageId'], { unique: true }) diff --git a/packages/backend/src/models/entities/PasswordResetRequest.ts b/packages/backend/src/models/entities/PasswordResetRequest.ts index 05e62cc5ab83..939fcc460f6b 100644 --- a/packages/backend/src/models/entities/PasswordResetRequest.ts +++ b/packages/backend/src/models/entities/PasswordResetRequest.ts @@ -1,6 +1,6 @@ import { PrimaryColumn, Entity, Index, Column, ManyToOne, JoinColumn } from 'typeorm'; import { id } from '../id.js'; -import { User } from './user.js'; +import { User } from './User.js'; @Entity() export class PasswordResetRequest { diff --git a/packages/backend/src/models/entities/Poll.ts b/packages/backend/src/models/entities/Poll.ts index 83d0873cc50a..6641b435eb6d 100644 --- a/packages/backend/src/models/entities/Poll.ts +++ b/packages/backend/src/models/entities/Poll.ts @@ -1,8 +1,8 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm'; import { id } from '../id.js'; -import { Note } from './note.js'; -import { User } from './user.js'; import { noteVisibilities } from '../../types.js'; +import { Note } from './Note.js'; +import type { User } from './User.js'; @Entity() export class Poll { diff --git a/packages/backend/src/models/entities/PollVote.ts b/packages/backend/src/models/entities/PollVote.ts index fca1cd009944..d447a7be8f8d 100644 --- a/packages/backend/src/models/entities/PollVote.ts +++ b/packages/backend/src/models/entities/PollVote.ts @@ -1,7 +1,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { Note } from './note.js'; import { id } from '../id.js'; +import { User } from './User.js'; +import { Note } from './Note.js'; @Entity() @Index(['userId', 'noteId', 'choice'], { unique: true }) diff --git a/packages/backend/src/models/entities/PromoNote.ts b/packages/backend/src/models/entities/PromoNote.ts index d110b81e93ec..958008338ad8 100644 --- a/packages/backend/src/models/entities/PromoNote.ts +++ b/packages/backend/src/models/entities/PromoNote.ts @@ -1,7 +1,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm'; -import { Note } from './note.js'; -import { User } from './user.js'; import { id } from '../id.js'; +import { Note } from './Note.js'; +import type { User } from './User.js'; @Entity() export class PromoNote { diff --git a/packages/backend/src/models/entities/PromoRead.ts b/packages/backend/src/models/entities/PromoRead.ts index a63b79cd1ebe..27f5d0dc1119 100644 --- a/packages/backend/src/models/entities/PromoRead.ts +++ b/packages/backend/src/models/entities/PromoRead.ts @@ -1,7 +1,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { Note } from './note.js'; -import { User } from './user.js'; import { id } from '../id.js'; +import { Note } from './Note.js'; +import { User } from './User.js'; @Entity() @Index(['userId', 'noteId'], { unique: true }) diff --git a/packages/backend/src/models/entities/RegistryItem.ts b/packages/backend/src/models/entities/RegistryItem.ts index 283796df9166..670a236ea0bc 100644 --- a/packages/backend/src/models/entities/RegistryItem.ts +++ b/packages/backend/src/models/entities/RegistryItem.ts @@ -1,6 +1,6 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; +import { User } from './User.js'; // TODO: 同じdomain、同じscope、同じkeyのレコードは二つ以上存在しないように制約付けたい @Entity() diff --git a/packages/backend/src/models/entities/Signin.ts b/packages/backend/src/models/entities/Signin.ts index ba81f45e4995..380bf028a658 100644 --- a/packages/backend/src/models/entities/Signin.ts +++ b/packages/backend/src/models/entities/Signin.ts @@ -1,6 +1,6 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; +import { User } from './User.js'; @Entity() export class Signin { diff --git a/packages/backend/src/models/entities/SwSubscription.ts b/packages/backend/src/models/entities/SwSubscription.ts index 59144d348b61..51b9786e96f8 100644 --- a/packages/backend/src/models/entities/SwSubscription.ts +++ b/packages/backend/src/models/entities/SwSubscription.ts @@ -1,6 +1,6 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; +import { User } from './User.js'; @Entity() export class SwSubscription { diff --git a/packages/backend/src/models/entities/User.ts b/packages/backend/src/models/entities/User.ts index 76f8e7935e35..73736f01504d 100644 --- a/packages/backend/src/models/entities/User.ts +++ b/packages/backend/src/models/entities/User.ts @@ -1,6 +1,6 @@ import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm'; import { id } from '../id.js'; -import { DriveFile } from './drive-file.js'; +import { DriveFile } from './DriveFile.js'; @Entity() @Index(['usernameLower', 'host'], { unique: true }) diff --git a/packages/backend/src/models/entities/UserGroup.ts b/packages/backend/src/models/entities/UserGroup.ts index 8d5de1d926d1..328a1883cb9e 100644 --- a/packages/backend/src/models/entities/UserGroup.ts +++ b/packages/backend/src/models/entities/UserGroup.ts @@ -1,6 +1,6 @@ import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; +import { User } from './User.js'; @Entity() export class UserGroup { diff --git a/packages/backend/src/models/entities/UserGroupInvitation.ts b/packages/backend/src/models/entities/UserGroupInvitation.ts index 10f357049fa8..e4aa3ccae14b 100644 --- a/packages/backend/src/models/entities/UserGroupInvitation.ts +++ b/packages/backend/src/models/entities/UserGroupInvitation.ts @@ -1,7 +1,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { UserGroup } from './user-group.js'; import { id } from '../id.js'; +import { User } from './User.js'; +import { UserGroup } from './UserGroup.js'; @Entity() @Index(['userId', 'userGroupId'], { unique: true }) diff --git a/packages/backend/src/models/entities/UserGroupJoining.ts b/packages/backend/src/models/entities/UserGroupJoining.ts index 62a814218a3d..fae724152519 100644 --- a/packages/backend/src/models/entities/UserGroupJoining.ts +++ b/packages/backend/src/models/entities/UserGroupJoining.ts @@ -1,7 +1,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { UserGroup } from './user-group.js'; import { id } from '../id.js'; +import { User } from './User.js'; +import { UserGroup } from './UserGroup.js'; @Entity() @Index(['userId', 'userGroupId'], { unique: true }) diff --git a/packages/backend/src/models/entities/UserIp.ts b/packages/backend/src/models/entities/UserIp.ts index 543e9e7289e8..e9afd40d4996 100644 --- a/packages/backend/src/models/entities/UserIp.ts +++ b/packages/backend/src/models/entities/UserIp.ts @@ -1,7 +1,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; import { id } from '../id.js'; -import { Note } from './note.js'; -import { User } from './user.js'; +import { Note } from './Note.js'; +import type { User } from './User.js'; @Entity() @Index(['userId', 'ip'], { unique: true }) diff --git a/packages/backend/src/models/entities/UserKeypair.ts b/packages/backend/src/models/entities/UserKeypair.ts index 85fa06297795..3cd02d3c4f8f 100644 --- a/packages/backend/src/models/entities/UserKeypair.ts +++ b/packages/backend/src/models/entities/UserKeypair.ts @@ -1,6 +1,6 @@ import { PrimaryColumn, Entity, JoinColumn, Column, OneToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; +import { User } from './User.js'; @Entity() export class UserKeypair { diff --git a/packages/backend/src/models/entities/UserList.ts b/packages/backend/src/models/entities/UserList.ts index ca69394e93c8..b8a4b54d4c37 100644 --- a/packages/backend/src/models/entities/UserList.ts +++ b/packages/backend/src/models/entities/UserList.ts @@ -1,6 +1,6 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; +import { User } from './User.js'; @Entity() export class UserList { diff --git a/packages/backend/src/models/entities/UserListJoining.ts b/packages/backend/src/models/entities/UserListJoining.ts index 12f28c414977..a40793a3e863 100644 --- a/packages/backend/src/models/entities/UserListJoining.ts +++ b/packages/backend/src/models/entities/UserListJoining.ts @@ -1,7 +1,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; -import { UserList } from './user-list.js'; import { id } from '../id.js'; +import { User } from './User.js'; +import { UserList } from './UserList.js'; @Entity() @Index(['userId', 'userListId'], { unique: true }) diff --git a/packages/backend/src/models/entities/UserNotePining.ts b/packages/backend/src/models/entities/UserNotePining.ts index c91ab7fdd865..fee95d4f7dec 100644 --- a/packages/backend/src/models/entities/UserNotePining.ts +++ b/packages/backend/src/models/entities/UserNotePining.ts @@ -1,7 +1,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { Note } from './note.js'; -import { User } from './user.js'; import { id } from '../id.js'; +import { Note } from './Note.js'; +import { User } from './User.js'; @Entity() @Index(['userId', 'noteId'], { unique: true }) diff --git a/packages/backend/src/models/entities/UserProfile.ts b/packages/backend/src/models/entities/UserProfile.ts index 3654b0a99460..c561da87ceb3 100644 --- a/packages/backend/src/models/entities/UserProfile.ts +++ b/packages/backend/src/models/entities/UserProfile.ts @@ -1,8 +1,8 @@ import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm'; import { ffVisibility, notificationTypes } from '@/types.js'; import { id } from '../id.js'; -import { User } from './user.js'; -import { Page } from './page.js'; +import { User } from './User.js'; +import { Page } from './Page.js'; // TODO: このテーブルで管理している情報すべてレジストリで管理するようにしても良いかも // ただ、「emailVerified が true なユーザーを find する」のようなクエリは書けなくなるからウーン diff --git a/packages/backend/src/models/entities/UserPublickey.ts b/packages/backend/src/models/entities/UserPublickey.ts index 31ed60de8228..7b505e5b4c4c 100644 --- a/packages/backend/src/models/entities/UserPublickey.ts +++ b/packages/backend/src/models/entities/UserPublickey.ts @@ -1,6 +1,6 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; +import { User } from './User.js'; @Entity() export class UserPublickey { diff --git a/packages/backend/src/models/entities/UserSecurityKey.ts b/packages/backend/src/models/entities/UserSecurityKey.ts index c4f2a852e228..947692a32be4 100644 --- a/packages/backend/src/models/entities/UserSecurityKey.ts +++ b/packages/backend/src/models/entities/UserSecurityKey.ts @@ -1,6 +1,6 @@ import { PrimaryColumn, Entity, JoinColumn, Column, ManyToOne, Index } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; +import { User } from './User.js'; @Entity() export class UserSecurityKey { diff --git a/packages/backend/src/models/entities/Webhook.ts b/packages/backend/src/models/entities/Webhook.ts index 56b411f87965..eabb604de94f 100644 --- a/packages/backend/src/models/entities/Webhook.ts +++ b/packages/backend/src/models/entities/Webhook.ts @@ -1,6 +1,6 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; -import { User } from './user.js'; import { id } from '../id.js'; +import { User } from './User.js'; export const webhookEventTypes = ['mention', 'unfollow', 'follow', 'followed', 'note', 'reply', 'renote', 'reaction'] as const; From d036c9fddbf5b73ab3f78dc8dba0f6b746962cdb Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 05:08:37 +0900 Subject: [PATCH 140/180] Update NodeinfoServerService.ts --- packages/backend/src/server/NodeinfoServerService.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index 38c17b7fb5b7..7682a94adc70 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -3,10 +3,9 @@ import Router from '@koa/router'; import { IsNull, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { Notes , Users } from '@/models/index.js'; - import { Config } from '@/config.js'; import { MetaService } from '@/services/MetaService.js'; -import { MAX_NOTE_TEXT_LENGTH } from '@/const'; +import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { Cache } from '@/misc/cache.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; From cae8b76b8f34dadf94e6a09b89ff1d435d188cfa Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 08:09:51 +0900 Subject: [PATCH 141/180] wip --- packages/backend/src/db/elasticsearch.ts | 56 ------ packages/backend/src/db/redis.ts | 6 +- packages/backend/src/misc/convert-host.ts | 26 --- packages/backend/src/misc/populate-emojis.ts | 125 ------------ packages/backend/src/misc/reaction-lib.ts | 131 ------------ .../src/models/schema/federation-instance.ts | 3 - packages/backend/src/queue/initialize.ts | 4 +- .../processors/DeliverProcessorService.ts | 7 +- .../ExportBlockingProcessorService.ts | 11 +- .../ExportFollowingProcessorService.ts | 11 +- .../ExportMutingProcessorService.ts | 11 +- .../ExportUserListsProcessorService.ts | 11 +- .../ImportBlockingProcessorService.ts | 17 +- .../ImportFollowingProcessorService.ts | 17 +- .../ImportMutingProcessorService.ts | 18 +- .../ImportUserListsProcessorService.ts | 19 +- .../queue/processors/InboxProcessorService.ts | 20 +- .../src/server/ActivityPubServerService.ts | 7 +- .../api/endpoints/admin/emoji/list-remote.ts | 7 +- .../refresh-remote-instance-metadata.ts | 5 +- .../admin/federation/update-instance.ts | 8 +- .../src/server/api/endpoints/ap/show.ts | 5 +- .../api/endpoints/federation/show-instance.ts | 5 +- .../server/api/endpoints/i/2fa/register.ts | 10 +- .../server/api/endpoints/users/followers.ts | 5 +- .../server/api/endpoints/users/following.ts | 5 +- .../src/server/api/openapi/gen-spec.ts | 190 ------------------ .../backend/src/services/AntennaService.ts | 7 +- packages/backend/src/services/CoreModule.ts | 3 + .../src/services/CustomEmojiService.ts | 126 +++++++++++- .../src/services/FederatedInstanceService.ts | 5 +- .../backend/src/services/ReactionService.ts | 140 ++++++++++++- .../backend/src/services/SignupService.ts | 5 +- .../backend/src/services/UtilityService.ts | 37 ++++ .../src/services/chart/charts/instance.ts | 15 +- .../entities/DriveFileEntityService.ts | 5 +- .../services/entities/NoteEntityService.ts | 18 +- .../entities/NoteReactionEntityService.ts | 5 +- .../entities/NotificationEntityService.ts | 9 +- .../services/entities/UserEntityService.ts | 23 ++- .../src/services/remote/ResolveUserService.ts | 18 +- .../remote/activitypub/ApAudienceService.ts | 104 ++++++++++ .../remote/activitypub/ApDbResolverService.ts | 4 +- .../remote/activitypub/ApInboxService.ts | 56 ++++-- .../remote/activitypub/ApResolverService.ts | 23 ++- .../services/remote/activitypub/audience.ts | 92 --------- .../activitypub/models/ApMentionService.ts | 8 +- .../activitypub/models/ApNoteService.ts | 41 ++-- .../activitypub/models/ApPersonService.ts | 137 +++++++------ 49 files changed, 744 insertions(+), 877 deletions(-) delete mode 100644 packages/backend/src/db/elasticsearch.ts delete mode 100644 packages/backend/src/misc/convert-host.ts delete mode 100644 packages/backend/src/misc/populate-emojis.ts delete mode 100644 packages/backend/src/misc/reaction-lib.ts delete mode 100644 packages/backend/src/server/api/openapi/gen-spec.ts create mode 100644 packages/backend/src/services/UtilityService.ts create mode 100644 packages/backend/src/services/remote/activitypub/ApAudienceService.ts delete mode 100644 packages/backend/src/services/remote/activitypub/audience.ts diff --git a/packages/backend/src/db/elasticsearch.ts b/packages/backend/src/db/elasticsearch.ts deleted file mode 100644 index d98c5d180bfe..000000000000 --- a/packages/backend/src/db/elasticsearch.ts +++ /dev/null @@ -1,56 +0,0 @@ -import * as elasticsearch from '@elastic/elasticsearch'; -import config from '@/config/index.js'; - -const index = { - settings: { - analysis: { - analyzer: { - ngram: { - tokenizer: 'ngram', - }, - }, - }, - }, - mappings: { - properties: { - text: { - type: 'text', - index: true, - analyzer: 'ngram', - }, - userId: { - type: 'keyword', - index: true, - }, - userHost: { - type: 'keyword', - index: true, - }, - }, - }, -}; - -// Init ElasticSearch connection -const client = config.elasticsearch ? new elasticsearch.Client({ - node: `${config.elasticsearch.ssl ? 'https://' : 'http://'}${config.elasticsearch.host}:${config.elasticsearch.port}`, - auth: (config.elasticsearch.user && config.elasticsearch.pass) ? { - username: config.elasticsearch.user, - password: config.elasticsearch.pass, - } : undefined, - pingTimeout: 30000, -}) : null; - -if (client) { - client.indices.exists({ - index: config.elasticsearch.index || 'misskey_note', - }).then(exist => { - if (!exist.body) { - client.indices.create({ - index: config.elasticsearch.index || 'misskey_note', - body: index, - }); - } - }); -} - -export default client; diff --git a/packages/backend/src/db/redis.ts b/packages/backend/src/db/redis.ts index 49f5bb2ba8c9..8f333719e10c 100644 --- a/packages/backend/src/db/redis.ts +++ b/packages/backend/src/db/redis.ts @@ -1,7 +1,9 @@ import Redis from 'ioredis'; -import config from '@/config/index.js'; +import { loadConfig } from '@/config.js'; export function createConnection() { + const config = loadConfig(); + return new Redis({ port: config.redis.port, host: config.redis.host, @@ -13,6 +15,6 @@ export function createConnection() { } export const subsdcriber = createConnection(); -subsdcriber.subscribe(config.host); +subsdcriber.subscribe(loadConfig().host); export const redisClient = createConnection(); diff --git a/packages/backend/src/misc/convert-host.ts b/packages/backend/src/misc/convert-host.ts deleted file mode 100644 index 7eb940a7e003..000000000000 --- a/packages/backend/src/misc/convert-host.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { URL } from 'node:url'; -import config from '@/config/index.js'; -import { toASCII } from 'punycode'; - -export function getFullApAccount(username: string, host: string | null) { - return host ? `${username}@${toPuny(host)}` : `${username}@${toPuny(config.host)}`; -} - -export function isSelfHost(host: string) { - if (host == null) return true; - return toPuny(config.host) === toPuny(host); -} - -export function extractDbHost(uri: string) { - const url = new URL(uri); - return toPuny(url.hostname); -} - -export function toPuny(host: string) { - return toASCII(host.toLowerCase()); -} - -export function toPunyNullable(host: string | null | undefined): string | null { - if (host == null) return null; - return toASCII(host.toLowerCase()); -} diff --git a/packages/backend/src/misc/populate-emojis.ts b/packages/backend/src/misc/populate-emojis.ts deleted file mode 100644 index 6c31312168f2..000000000000 --- a/packages/backend/src/misc/populate-emojis.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { In, IsNull } from 'typeorm'; -import { Emojis } from '@/models/index.js'; -import { Emoji } from '@/models/entities/Emoji.js'; -import { Note } from '@/models/entities/Note.js'; -import { Cache } from './cache.js'; -import { isSelfHost, toPunyNullable } from './convert-host.js'; -import { decodeReaction } from './reaction-lib.js'; -import config from '@/config/index.js'; -import { query } from '@/prelude/url.js'; - -const cache = new Cache(1000 * 60 * 60 * 12); - -/** - * 添付用絵文字情報 - */ -type PopulatedEmoji = { - name: string; - url: string; -}; - -function normalizeHost(src: string | undefined, noteUserHost: string | null): string | null { - // クエリに使うホスト - let host = src === '.' ? null // .はローカルホスト (ここがマッチするのはリアクションのみ) - : src === undefined ? noteUserHost // ノートなどでホスト省略表記の場合はローカルホスト (ここがリアクションにマッチすることはない) - : isSelfHost(src) ? null // 自ホスト指定 - : (src || noteUserHost); // 指定されたホスト || ノートなどの所有者のホスト (こっちがリアクションにマッチすることはない) - - host = toPunyNullable(host); - - return host; -} - -function parseEmojiStr(emojiName: string, noteUserHost: string | null) { - const match = emojiName.match(/^(\w+)(?:@([\w.-]+))?$/); - if (!match) return { name: null, host: null }; - - const name = match[1]; - - // ホスト正規化 - const host = toPunyNullable(normalizeHost(match[2], noteUserHost)); - - return { name, host }; -} - -/** - * 添付用絵文字情報を解決する - * @param emojiName ノートやユーザープロフィールに添付された、またはリアクションのカスタム絵文字名 (:は含めない, リアクションでローカルホストの場合は@.を付ける (これはdecodeReactionで可能)) - * @param noteUserHost ノートやユーザープロフィールの所有者のホスト - * @returns 絵文字情報, nullは未マッチを意味する - */ -export async function populateEmoji(emojiName: string, noteUserHost: string | null): Promise { - const { name, host } = parseEmojiStr(emojiName, noteUserHost); - if (name == null) return null; - - const queryOrNull = async () => (await Emojis.findOneBy({ - name, - host: host ?? IsNull(), - })) || null; - - const emoji = await cache.fetch(`${name} ${host}`, queryOrNull); - - if (emoji == null) return null; - - const isLocal = emoji.host == null; - const emojiUrl = emoji.publicUrl || emoji.originalUrl; // || emoji.originalUrl してるのは後方互換性のため - const url = isLocal ? emojiUrl : `${config.url}/proxy/${encodeURIComponent((new URL(emojiUrl)).pathname)}?${query({ url: emojiUrl })}`; - - return { - name: emojiName, - url, - }; -} - -/** - * 複数の添付用絵文字情報を解決する (キャシュ付き, 存在しないものは結果から除外される) - */ -export async function populateEmojis(emojiNames: string[], noteUserHost: string | null): Promise { - const emojis = await Promise.all(emojiNames.map(x => populateEmoji(x, noteUserHost))); - return emojis.filter((x): x is PopulatedEmoji => x != null); -} - -export function aggregateNoteEmojis(notes: Note[]) { - let emojis: { name: string | null; host: string | null; }[] = []; - for (const note of notes) { - emojis = emojis.concat(note.emojis - .map(e => parseEmojiStr(e, note.userHost))); - if (note.renote) { - emojis = emojis.concat(note.renote.emojis - .map(e => parseEmojiStr(e, note.renote!.userHost))); - if (note.renote.user) { - emojis = emojis.concat(note.renote.user.emojis - .map(e => parseEmojiStr(e, note.renote!.userHost))); - } - } - const customReactions = Object.keys(note.reactions).map(x => decodeReaction(x)).filter(x => x.name != null) as typeof emojis; - emojis = emojis.concat(customReactions); - if (note.user) { - emojis = emojis.concat(note.user.emojis - .map(e => parseEmojiStr(e, note.userHost))); - } - } - return emojis.filter(x => x.name != null) as { name: string; host: string | null; }[]; -} - -/** - * 与えられた絵文字のリストをデータベースから取得し、キャッシュに追加します - */ -export async function prefetchEmojis(emojis: { name: string; host: string | null; }[]): Promise { - const notCachedEmojis = emojis.filter(emoji => cache.get(`${emoji.name} ${emoji.host}`) == null); - const emojisQuery: any[] = []; - const hosts = new Set(notCachedEmojis.map(e => e.host)); - for (const host of hosts) { - emojisQuery.push({ - name: In(notCachedEmojis.filter(e => e.host === host).map(e => e.name)), - host: host ?? IsNull(), - }); - } - const _emojis = emojisQuery.length > 0 ? await Emojis.find({ - where: emojisQuery, - select: ['name', 'host', 'originalUrl', 'publicUrl'], - }) : []; - for (const emoji of _emojis) { - cache.set(`${emoji.name} ${emoji.host}`, emoji); - } -} diff --git a/packages/backend/src/misc/reaction-lib.ts b/packages/backend/src/misc/reaction-lib.ts deleted file mode 100644 index fefc2781f37f..000000000000 --- a/packages/backend/src/misc/reaction-lib.ts +++ /dev/null @@ -1,131 +0,0 @@ -/* eslint-disable key-spacing */ -import { emojiRegex } from './emoji-regex.js'; -import { fetchMeta } from './fetch-meta.js'; -import { Emojis } from '@/models/index.js'; -import { toPunyNullable } from './convert-host.js'; -import { IsNull } from 'typeorm'; - -const legacies: Record = { - 'like': '👍', - 'love': '❤', // ここに記述する場合は異体字セレクタを入れない - 'laugh': '😆', - 'hmm': '🤔', - 'surprise': '😮', - 'congrats': '🎉', - 'angry': '💢', - 'confused': '😥', - 'rip': '😇', - 'pudding': '🍮', - 'star': '⭐', -}; - -export async function getFallbackReaction(): Promise { - const meta = await fetchMeta(); - return meta.useStarForReactionFallback ? '⭐' : '👍'; -} - -export function convertLegacyReactions(reactions: Record) { - const _reactions = {} as Record; - - for (const reaction of Object.keys(reactions)) { - if (reactions[reaction] <= 0) continue; - - if (Object.keys(legacies).includes(reaction)) { - if (_reactions[legacies[reaction]]) { - _reactions[legacies[reaction]] += reactions[reaction]; - } else { - _reactions[legacies[reaction]] = reactions[reaction]; - } - } else { - if (_reactions[reaction]) { - _reactions[reaction] += reactions[reaction]; - } else { - _reactions[reaction] = reactions[reaction]; - } - } - } - - const _reactions2 = {} as Record; - - for (const reaction of Object.keys(_reactions)) { - _reactions2[decodeReaction(reaction).reaction] = _reactions[reaction]; - } - - return _reactions2; -} - -export async function toDbReaction(reaction?: string | null, reacterHost?: string | null): Promise { - if (reaction == null) return await getFallbackReaction(); - - reacterHost = toPunyNullable(reacterHost); - - // 文字列タイプのリアクションを絵文字に変換 - if (Object.keys(legacies).includes(reaction)) return legacies[reaction]; - - // Unicode絵文字 - const match = emojiRegex.exec(reaction); - if (match) { - // 合字を含む1つの絵文字 - const unicode = match[0]; - - // 異体字セレクタ除去 - return unicode.match('\u200d') ? unicode : unicode.replace(/\ufe0f/g, ''); - } - - const custom = reaction.match(/^:([\w+-]+)(?:@\.)?:$/); - if (custom) { - const name = custom[1]; - const emoji = await Emojis.findOneBy({ - host: reacterHost ?? IsNull(), - name, - }); - - if (emoji) return reacterHost ? `:${name}@${reacterHost}:` : `:${name}:`; - } - - return await getFallbackReaction(); -} - -type DecodedReaction = { - /** - * リアクション名 (Unicode Emoji or ':name@hostname' or ':name@.') - */ - reaction: string; - - /** - * name (カスタム絵文字の場合name, Emojiクエリに使う) - */ - name?: string; - - /** - * host (カスタム絵文字の場合host, Emojiクエリに使う) - */ - host?: string | null; -}; - -export function decodeReaction(str: string): DecodedReaction { - const custom = str.match(/^:([\w+-]+)(?:@([\w.-]+))?:$/); - - if (custom) { - const name = custom[1]; - const host = custom[2] || null; - - return { - reaction: `:${name}@${host || '.'}:`, // ローカル分は@以降を省略するのではなく.にする - name, - host, - }; - } - - return { - reaction: str, - name: undefined, - host: undefined, - }; -} - -export function convertLegacyReaction(reaction: string): string { - reaction = decodeReaction(reaction).reaction; - if (Object.keys(legacies).includes(reaction)) return legacies[reaction]; - return reaction; -} diff --git a/packages/backend/src/models/schema/federation-instance.ts b/packages/backend/src/models/schema/federation-instance.ts index 93327304f35c..c57b3fec19e5 100644 --- a/packages/backend/src/models/schema/federation-instance.ts +++ b/packages/backend/src/models/schema/federation-instance.ts @@ -1,5 +1,3 @@ -import config from '@/config/index.js'; - export const packedFederationInstanceSchema = { type: 'object', properties: { @@ -64,7 +62,6 @@ export const packedFederationInstanceSchema = { softwareVersion: { type: 'string', optional: false, nullable: true, - example: config.version, }, openRegistrations: { type: 'boolean', diff --git a/packages/backend/src/queue/initialize.ts b/packages/backend/src/queue/initialize.ts index eef4080af300..cefe8e62c1e5 100644 --- a/packages/backend/src/queue/initialize.ts +++ b/packages/backend/src/queue/initialize.ts @@ -1,7 +1,9 @@ import Bull from 'bull'; -import config from '@/config/index.js'; +import { loadConfig } from '@/config.js'; export function initialize(name: string, limitPerSec = -1) { + const config = loadConfig(); + return new Bull(name, { redis: { port: config.redis.port, diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts index 842bd3b6ef6e..08d0366b37d5 100644 --- a/packages/backend/src/queue/processors/DeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts @@ -5,7 +5,6 @@ import type { DriveFiles , Instances } from '@/models/index.js'; import { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { MetaService } from '@/services/MetaService.js'; -import { toPuny } from '@/misc/convert-host.js'; import { ApRequestService } from '@/services/remote/activitypub/ApRequestService.js'; import { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; import { FetchInstanceMetadataService } from '@/services/FetchInstanceMetadataService.js'; @@ -15,6 +14,7 @@ import InstanceChart from '@/services/chart/charts/instance.js'; import ApRequestChart from '@/services/chart/charts/ap-request.js'; import FederationChart from '@/services/chart/charts/federation.js'; import { StatusError } from '@/misc/status-error.js'; +import { UtilityService } from '@/services/UtilityService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DeliverJobData } from '../types.js'; @@ -36,6 +36,7 @@ export class DeliverProcessorService { private driveFilesRepository: typeof DriveFiles, private metaService: MetaService, + private utilityService: UtilityService, private federatedInstanceService: FederatedInstanceService, private fetchInstanceMetadataService: FetchInstanceMetadataService, private apRequestService: ApRequestService, @@ -54,7 +55,7 @@ export class DeliverProcessorService { // ブロックしてたら中断 const meta = await this.metaService.fetch(); - if (meta.blockedHosts.includes(toPuny(host))) { + if (meta.blockedHosts.includes(this.utilityService.toPuny(host))) { return 'skip (blocked)'; } @@ -68,7 +69,7 @@ export class DeliverProcessorService { }); this.#suspendedHostsCache.set(null, suspendedHosts); } - if (suspendedHosts.map(x => x.host).includes(toPuny(host))) { + if (suspendedHosts.map(x => x.host).includes(this.utilityService.toPuny(host))) { return 'skip (suspended)'; } diff --git a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts index 47f795252af1..e81839fd5712 100644 --- a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts @@ -4,14 +4,14 @@ import { MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI } from '@/di-symbols.js'; import type { DriveFiles, UserProfiles , Notes , Users , Blockings } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import type { DriveService } from '@/services/DriveService.js'; -import { getFullApAccount } from '@/misc/convert-host.js'; +import { DriveService } from '@/services/DriveService.js'; import { createTemp } from '@/misc/create-temp.js'; +import { UtilityService } from '@/services/UtilityService.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserJobData } from '../types.js'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; @Injectable() export class ExportBlockingProcessorService { @@ -27,6 +27,7 @@ export class ExportBlockingProcessorService { @Inject('blockingsRepository') private blockingsRepository: typeof Blockings, + private utilityService: UtilityService, private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { @@ -78,7 +79,7 @@ export class ExportBlockingProcessorService { exportedCount++; continue; } - const content = getFullApAccount(u.username, u.host); + const content = this.utilityService.getFullApAccount(u.username, u.host); await new Promise((res, rej) => { stream.write(content + '\n', err => { if (err) { diff --git a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts index ee80c927677b..10681933fd8e 100644 --- a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts @@ -5,15 +5,15 @@ import { format as dateFormat } from 'date-fns'; import { DI } from '@/di-symbols.js'; import { Users } from '@/models/index.js'; import type { Followings, Mutings } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import type { DriveService } from '@/services/DriveService.js'; -import { getFullApAccount } from '@/misc/convert-host.js'; +import { DriveService } from '@/services/DriveService.js'; import { createTemp } from '@/misc/create-temp.js'; import type { Following } from '@/models/entities/Following.js'; +import { UtilityService } from '@/services/UtilityService.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserJobData } from '../types.js'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; @Injectable() export class ExportFollowingProcessorService { @@ -32,6 +32,7 @@ export class ExportFollowingProcessorService { @Inject('mutingsRepository') private mutingsRepository: typeof Mutings, + private utilityService: UtilityService, private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { @@ -90,7 +91,7 @@ export class ExportFollowingProcessorService { continue; } - const content = getFullApAccount(u.username, u.host); + const content = this.utilityService.getFullApAccount(u.username, u.host); await new Promise((res, rej) => { stream.write(content + '\n', err => { if (err) { diff --git a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts index 6d652f17208b..d7add3ddc573 100644 --- a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts @@ -4,14 +4,14 @@ import { IsNull, MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI } from '@/di-symbols.js'; import type { Mutings, Users , Blockings } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import type { DriveService } from '@/services/DriveService.js'; -import { getFullApAccount } from '@/misc/convert-host.js'; +import { DriveService } from '@/services/DriveService.js'; import { createTemp } from '@/misc/create-temp.js'; +import { UtilityService } from '@/services/UtilityService.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserJobData } from '../types.js'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; @Injectable() export class ExportMutingProcessorService { @@ -30,6 +30,7 @@ export class ExportMutingProcessorService { @Inject('mutingsRepository') private mutingsRepository: typeof Mutings, + private utilityService: UtilityService, private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { @@ -82,7 +83,7 @@ export class ExportMutingProcessorService { exportedCount++; continue; } - const content = getFullApAccount(u.username, u.host); + const content = this.utilityService.getFullApAccount(u.username, u.host); await new Promise((res, rej) => { stream.write(content + '\n', err => { if (err) { diff --git a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts index 6e7626f80e3f..b724ec4cc8df 100644 --- a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts @@ -4,14 +4,14 @@ import { In, IsNull, MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI } from '@/di-symbols.js'; import type { UserListJoinings, UserLists, Users } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import type { DriveService } from '@/services/DriveService.js'; +import { DriveService } from '@/services/DriveService.js'; import { createTemp } from '@/misc/create-temp.js'; -import { getFullApAccount } from '@/misc/convert-host.js'; +import { UtilityService } from '@/services/UtilityService.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserJobData } from '../types.js'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; @Injectable() export class ExportUserListsProcessorService { @@ -30,6 +30,7 @@ export class ExportUserListsProcessorService { @Inject('userListJoiningsRepository') private userListJoiningsRepository: typeof UserListJoinings, + private utilityService: UtilityService, private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { @@ -64,7 +65,7 @@ export class ExportUserListsProcessorService { }); for (const u of users) { - const acct = getFullApAccount(u.username, u.host); + const acct = this.utilityService.getFullApAccount(u.username, u.host); const content = `${list.name},${acct}`; await new Promise((res, rej) => { stream.write(content + '\n', err => { diff --git a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts index 921630ce4c5d..4a2ef315a65d 100644 --- a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts @@ -3,16 +3,16 @@ import { IsNull, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import { Users } from '@/models/index.js'; import type { Blockings , DriveFiles } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import { isSelfHost, toPuny } from '@/misc/convert-host.js'; import * as Acct from '@/misc/acct.js'; -import type { ResolveUserService } from '@/services/remote/ResolveUserService.js'; -import type { UserBlockingService } from '@/services/UserBlockingService.js'; -import type { DownloadService } from '@/services/DownloadService.js'; +import { ResolveUserService } from '@/services/remote/ResolveUserService.js'; +import { UserBlockingService } from '@/services/UserBlockingService.js'; +import { DownloadService } from '@/services/DownloadService.js'; +import { UtilityService } from '@/services/UtilityService.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserImportJobData } from '../types.js'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; @Injectable() export class ImportBlockingProcessorService { @@ -31,6 +31,7 @@ export class ImportBlockingProcessorService { @Inject('driveFilesRepository') private driveFilesRepository: typeof DriveFiles, + private utilityService: UtilityService, private userBlockingService: UserBlockingService, private resolveUserService: ResolveUserService, private downloadService: DownloadService, @@ -67,11 +68,11 @@ export class ImportBlockingProcessorService { const acct = line.split(',')[0].trim(); const { username, host } = Acct.parse(acct); - let target = isSelfHost(host!) ? await this.usersRepository.findOneBy({ + let target = this.utilityService.isSelfHost(host!) ? await this.usersRepository.findOneBy({ host: IsNull(), usernameLower: username.toLowerCase(), }) : await Users.findOneBy({ - host: toPuny(host!), + host: this.utilityService.toPuny(host!), usernameLower: username.toLowerCase(), }); diff --git a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts index fa4f56741f53..e5c58235613a 100644 --- a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts @@ -3,16 +3,16 @@ import { IsNull, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import { Users } from '@/models/index.js'; import type { DriveFiles } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import { isSelfHost, toPuny } from '@/misc/convert-host.js'; import * as Acct from '@/misc/acct.js'; -import type { ResolveUserService } from '@/services/remote/ResolveUserService.js'; -import type { DownloadService } from '@/services/DownloadService.js'; -import type { UserFollowingService } from '@/services/UserFollowingService.js'; +import { ResolveUserService } from '@/services/remote/ResolveUserService.js'; +import { DownloadService } from '@/services/DownloadService.js'; +import { UserFollowingService } from '@/services/UserFollowingService.js'; +import { UtilityService } from '@/services/UtilityService.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserImportJobData } from '../types.js'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; @Injectable() export class ImportFollowingProcessorService { @@ -28,6 +28,7 @@ export class ImportFollowingProcessorService { @Inject('driveFilesRepository') private driveFilesRepository: typeof DriveFiles, + private utilityService: UtilityService, private userFollowingService: UserFollowingService, private resolveUserService: ResolveUserService, private downloadService: DownloadService, @@ -64,11 +65,11 @@ export class ImportFollowingProcessorService { const acct = line.split(',')[0].trim(); const { username, host } = Acct.parse(acct); - let target = isSelfHost(host!) ? await Users.findOneBy({ + let target = this.utilityService.isSelfHost(host!) ? await Users.findOneBy({ host: IsNull(), usernameLower: username.toLowerCase(), }) : await Users.findOneBy({ - host: toPuny(host!), + host: this.utilityService.toPuny(host!), usernameLower: username.toLowerCase(), }); diff --git a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts index 37c93a335fd1..48c1bd5b2fe0 100644 --- a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts @@ -3,18 +3,17 @@ import { IsNull, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { DriveFiles } from '@/models/index.js'; import { Users } from '@/models/index.js'; - -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import { isSelfHost, toPuny } from '@/misc/convert-host.js'; import * as Acct from '@/misc/acct.js'; -import type { ResolveUserService } from '@/services/remote/ResolveUserService.js'; -import type { DownloadService } from '@/services/DownloadService.js'; +import { ResolveUserService } from '@/services/remote/ResolveUserService.js'; +import { DownloadService } from '@/services/DownloadService.js'; import type { UserFollowingService } from '@/services/UserFollowingService.js'; -import type { UserMutingService } from '@/services/UserMutingService.js'; +import { UserMutingService } from '@/services/UserMutingService.js'; +import { UtilityService } from '@/services/UtilityService.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserImportJobData } from '../types.js'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; @Injectable() export class ImportMutingProcessorService { @@ -30,6 +29,7 @@ export class ImportMutingProcessorService { @Inject('driveFilesRepository') private driveFilesRepository: typeof DriveFiles, + private utilityService: UtilityService, private userMutingService: UserMutingService, private resolveUserService: ResolveUserService, private downloadService: DownloadService, @@ -66,11 +66,11 @@ export class ImportMutingProcessorService { const acct = line.split(',')[0].trim(); const { username, host } = Acct.parse(acct); - let target = isSelfHost(host!) ? await Users.findOneBy({ + let target = this.utilityService.isSelfHost(host!) ? await Users.findOneBy({ host: IsNull(), usernameLower: username.toLowerCase(), }) : await Users.findOneBy({ - host: toPuny(host!), + host: this.utilityService.toPuny(host!), usernameLower: username.toLowerCase(), }); diff --git a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts index 813abb3a352a..7b6f2e5dd5fa 100644 --- a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts @@ -3,17 +3,17 @@ import { IsNull, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { DriveFiles , UserListJoinings , UserLists } from '@/models/index.js'; import { Users } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import { isSelfHost, toPuny } from '@/misc/convert-host.js'; import * as Acct from '@/misc/acct.js'; -import type { ResolveUserService } from '@/services/remote/ResolveUserService.js'; -import type { DownloadService } from '@/services/DownloadService.js'; -import type { UserListService } from '@/services/UserListService.js'; -import type { IdService } from '@/services/IdService.js'; +import { ResolveUserService } from '@/services/remote/ResolveUserService.js'; +import { DownloadService } from '@/services/DownloadService.js'; +import { UserListService } from '@/services/UserListService.js'; +import { IdService } from '@/services/IdService.js'; +import { UtilityService } from '@/services/UtilityService.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserImportJobData } from '../types.js'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; @Injectable() export class ImportUserListsProcessorService { @@ -35,6 +35,7 @@ export class ImportUserListsProcessorService { @Inject('userListJoiningsRepository') private userListJoiningsRepository: typeof UserListJoinings, + private utilityService: UtilityService, private idService: IdService, private userListService: UserListService, private resolveUserService: ResolveUserService, @@ -86,11 +87,11 @@ export class ImportUserListsProcessorService { }).then(x => this.userListsRepository.findOneByOrFail(x.identifiers[0])); } - let target = isSelfHost(host!) ? await Users.findOneBy({ + let target = this.utilityService.isSelfHost(host!) ? await Users.findOneBy({ host: IsNull(), usernameLower: username.toLowerCase(), }) : await Users.findOneBy({ - host: toPuny(host!), + host: this.utilityService.toPuny(host!), usernameLower: username.toLowerCase(), }); diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index f74bf50013c5..e7a7619936bc 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -8,7 +8,6 @@ import type { DriveFiles } from '@/models/index.js'; import { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { MetaService } from '@/services/MetaService.js'; -import { extractDbHost, toPuny } from '@/misc/convert-host.js'; import { ApRequestService } from '@/services/remote/activitypub/ApRequestService.js'; import { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; import { FetchInstanceMetadataService } from '@/services/FetchInstanceMetadataService.js'; @@ -23,6 +22,9 @@ import type { CacheableRemoteUser } from '@/models/entities/User.js'; import type { UserPublickey } from '@/models/entities/UserPublickey.js'; import { ApDbResolverService } from '@/services/remote/activitypub/ApDbResolverService.js'; import { StatusError } from '@/misc/status-error.js'; +import { UtilityService } from '@/services/UtilityService.js'; +import { ApPersonService } from '@/services/remote/activitypub/models/ApPersonService.js'; +import perform from '@/services/remote/activitypub/perform.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DeliverJobData, InboxJobData } from '../types.js'; @@ -42,13 +44,15 @@ export class InboxProcessorService { @Inject('driveFilesRepository') private driveFilesRepository: typeof DriveFiles, + private utilityService: UtilityService, private metaService: MetaService, private federatedInstanceService: FederatedInstanceService, private fetchInstanceMetadataService: FetchInstanceMetadataService, private apRequestService: ApRequestService, + private apPersonService: ApPersonService, + private apDbResolverService: ApDbResolverService, private instanceChart: InstanceChart, private apRequestChart: ApRequestChart, - private apDbResolverService: ApDbResolverService, private federationChart: FederationChart, private queueLoggerService: QueueLoggerService, ) { @@ -65,7 +69,7 @@ export class InboxProcessorService { this.#logger.debug(JSON.stringify(info, null, 2)); //#endregion - const host = toPuny(new URL(signature.keyId).hostname); + const host = this.utilityService.toPuny(new URL(signature.keyId).hostname); // ブロックしてたら中断 const meta = await this.metaService.fetch(); @@ -124,11 +128,11 @@ export class InboxProcessorService { // みたいになっててUserを引っ張れば公開キーも入ることを期待する if (activity.signature.creator) { const candicate = activity.signature.creator.replace(/#.*/, ''); - await resolvePerson(candicate).catch(() => null); + await this.apPersonService.resolvePerson(candicate).catch(() => null); } // keyIdからLD-Signatureのユーザーを取得 - authUser = await dbResolver.getAuthUserFromKeyId(activity.signature.creator); + authUser = await this.apDbResolverService.getAuthUserFromKeyId(activity.signature.creator); if (authUser == null) { return 'skip: LD-Signatureのユーザーが取得できませんでした'; } @@ -150,7 +154,7 @@ export class InboxProcessorService { } // ブロックしてたら中断 - const ldHost = extractDbHost(authUser.user.uri); + const ldHost = this.utilityService.extractDbHost(authUser.user.uri); if (meta.blockedHosts.includes(ldHost)) { return `Blocked request: ${ldHost}`; } @@ -161,8 +165,8 @@ export class InboxProcessorService { // activity.idがあればホストが署名者のホストであることを確認する if (typeof activity.id === 'string') { - const signerHost = extractDbHost(authUser.user.uri!); - const activityIdHost = extractDbHost(activity.id); + const signerHost = this.utilityService.extractDbHost(authUser.user.uri!); + const activityIdHost = this.utilityService.extractDbHost(activity.id); if (signerHost !== activityIdHost) { return `skip: signerHost(${signerHost}) !== activity.id host(${activityIdHost}`; } diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index b2f5bb679f13..0e1e7c395df8 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -8,7 +8,6 @@ import { Followings , Notes } from '@/models/index.js'; import type { Emojis, NoteReactions , UserProfiles , UserNotePinings , Users } from '@/models/index.js'; import * as url from '@/prelude/url.js'; import { Config } from '@/config.js'; -import { isSelfHost } from '@/misc/convert-host.js'; import { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; import { QueueService } from '@/queue/queue.service.js'; import type { ILocalUser, User } from '@/models/entities/User.js'; @@ -17,6 +16,8 @@ import type { Following } from '@/models/entities/Following.js'; import { countIf } from '@/prelude/array.js'; import type { Note } from '@/models/entities/Note.js'; import { QueryService } from '@/services/QueryService.js'; +import { UtilityService } from '@/services/UtilityService.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; import type { FindOptionsWhere } from 'typeorm'; const ACTIVITY_JSON = 'application/activity+json; charset=utf-8'; @@ -49,6 +50,8 @@ export class ActivityPubServerService { @Inject('followingsRepository') private followingsRepository: typeof Followings, + private utilityService: UtilityService, + private userEntityService: UserEntityService, private apRendererService: ApRendererService, private queueService: QueueService, private userKeypairStoreService: UserKeypairStoreService, @@ -417,7 +420,7 @@ export class ActivityPubServerService { // リモートだったらリダイレクト if (note.userHost != null) { - if (note.uri == null || isSelfHost(note.userHost)) { + if (note.uri == null || this.utilityService.isSelfHost(note.userHost)) { ctx.status = 500; return; } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts index e801e61e6ada..249f2335c0d8 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Emojis } from '@/models/index.js'; -import { toPuny } from '@/misc/convert-host.js'; import { QueryService } from '@/services/QueryService.js'; +import { UtilityService } from '@/services/UtilityService.js'; +import { EmojiEntityService } from '@/services/entities/EmojiEntityService.js'; export const meta = { tags: ['admin'], @@ -76,7 +77,9 @@ export default class extends Endpoint { @Inject('emojisRepository') private emojisRepository: typeof Emojis, + private utilityService: UtilityService, private queryService: QueryService, + private emojiEntityService: EmojiEntityService, ) { super(meta, paramDef, async (ps, me) => { const q = this.queryService.makePaginationQuery(this.emojisRepository.createQueryBuilder('emoji'), ps.sinceId, ps.untilId); @@ -84,7 +87,7 @@ export default class extends Endpoint { if (ps.host == null) { q.andWhere('emoji.host IS NOT NULL'); } else { - q.andWhere('emoji.host = :host', { host: toPuny(ps.host) }); + q.andWhere('emoji.host = :host', { host: this.utilityService.toPuny(ps.host) }); } if (ps.query) { diff --git a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts index 6b62d1bf67a0..e85c925a4d22 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Instances } from '@/models/index.js'; -import { toPuny } from '@/misc/convert-host.js'; import { FetchInstanceMetadataService } from '@/services/FetchInstanceMetadataService.js'; +import { UtilityService } from '@/services/UtilityService.js'; export const meta = { tags: ['admin'], @@ -26,10 +26,11 @@ export default class extends Endpoint { @Inject('instancesRepository') private instancesRepository: typeof Instances, + private utilityService: UtilityService, private fetchInstanceMetadataService: FetchInstanceMetadataService, ) { super(meta, paramDef, async (ps, me) => { - const instance = await this.instancesRepository.findOneBy({ host: toPuny(ps.host) }); + const instance = await this.instancesRepository.findOneBy({ host: this.utilityService.toPuny(ps.host) }); if (instance == null) { throw new Error('instance not found'); diff --git a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts index e7aea70ff69f..9c4a7aa3fac3 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Instances } from '@/models/index.js'; -import { toPuny } from '@/misc/convert-host.js'; +import { UtilityService } from '@/services/UtilityService.js'; export const meta = { tags: ['admin'], @@ -25,15 +25,17 @@ export default class extends Endpoint { constructor( @Inject('instancesRepository') private instancesRepository: typeof Instances, + + private utilityService: UtilityService, ) { super(meta, paramDef, async (ps, me) => { - const instance = await this.instancesRepository.findOneBy({ host: toPuny(ps.host) }); + const instance = await this.instancesRepository.findOneBy({ host: this.utilityService.toPuny(ps.host) }); if (instance == null) { throw new Error('instance not found'); } - this.instancesRepository.update({ host: toPuny(ps.host) }, { + this.instancesRepository.update({ host: this.utilityService.toPuny(ps.host) }, { isSuspended: ps.isSuspended, }); }); diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index c4e9c23be246..46933726cc23 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -1,7 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { extractDbHost } from '@/misc/convert-host.js'; import type { Users , Notes } from '@/models/index.js'; import type { Note } from '@/models/entities/Note.js'; import type { CacheableLocalUser, User } from '@/models/entities/User.js'; @@ -14,6 +13,7 @@ import { ApPersonService } from '@/services/remote/activitypub/models/ApPersonSe import { ApNoteService } from '@/services/remote/activitypub/models/ApNoteService.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { UtilityService } from '@/services/UtilityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -89,6 +89,7 @@ export default class extends Endpoint { @Inject('notesRepository') private notesRepository: typeof Notes, + private utilityService: UtilityService, private userEntityService: UserEntityService, private noteEntityService: NoteEntityService, private metaService: MetaService, @@ -113,7 +114,7 @@ export default class extends Endpoint { async #fetchAny(uri: string, me: CacheableLocalUser | null | undefined): Promise | null> { // ブロックしてたら中断 const fetchedMeta = await this.metaService.fetch(); - if (fetchedMeta.blockedHosts.includes(extractDbHost(uri))) return null; + if (fetchedMeta.blockedHosts.includes(this.utilityService.extractDbHost(uri))) return null; let local = await this.#mergePack(me, ...await Promise.all([ this.apDbResolverService.getUserFromApId(uri), diff --git a/packages/backend/src/server/api/endpoints/federation/show-instance.ts b/packages/backend/src/server/api/endpoints/federation/show-instance.ts index 2a4099da3e18..8b9d170e33f9 100644 --- a/packages/backend/src/server/api/endpoints/federation/show-instance.ts +++ b/packages/backend/src/server/api/endpoints/federation/show-instance.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Instances } from '@/models/index.js'; -import { toPuny } from '@/misc/convert-host.js'; import { InstanceEntityService } from '@/services/entities/InstanceEntityService.js'; +import { UtilityService } from '@/services/UtilityService.js'; export const meta = { tags: ['federation'], @@ -34,11 +34,12 @@ export default class extends Endpoint { @Inject('instancesRepository') private instancesRepository: typeof Instances, + private utilityService: UtilityService, private instanceEntityService: InstanceEntityService, ) { super(meta, paramDef, async (ps, me) => { const instance = await this.instancesRepository - .findOneBy({ host: toPuny(ps.host) }); + .findOneBy({ host: this.utilityService.toPuny(ps.host) }); return instance ? await this.instanceEntityService.pack(instance) : null; }); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register.ts b/packages/backend/src/server/api/endpoints/i/2fa/register.ts index 76844c03c5f8..d09f8eb81cb2 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts @@ -2,9 +2,10 @@ import bcrypt from 'bcryptjs'; import * as speakeasy from 'speakeasy'; import * as QRCode from 'qrcode'; import { Inject, Injectable } from '@nestjs/common'; -import config from '@/config/index.js'; import type { UserProfiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; +import { Config } from '@/config.js'; export const meta = { requireCredential: true, @@ -24,6 +25,9 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( + @Inject(DI.config) + private config: Config, + @Inject('userProfilesRepository') private userProfilesRepository: typeof UserProfiles, ) { @@ -51,7 +55,7 @@ export default class extends Endpoint { secret: secret.base32, encoding: 'base32', label: me.username, - issuer: config.host, + issuer: this.config.host, }); const dataUrl = await QRCode.toDataURL(url); @@ -60,7 +64,7 @@ export default class extends Endpoint { url, secret: secret.base32, label: me.username, - issuer: config.host, + issuer: this.config.host, }; }); } diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index b36e13d26831..771f8044f877 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -1,10 +1,10 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { Users , Followings, UserProfiles } from '@/models/index.js'; -import { toPunyNullable } from '@/misc/convert-host.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { FollowingEntityService } from '@/services/entities/FollowingEntityService.js'; +import { UtilityService } from '@/services/UtilityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -80,13 +80,14 @@ export default class extends Endpoint { @Inject('followingsRepository') private followingsRepository: typeof Followings, + private utilityService: UtilityService, private followingEntityService: FollowingEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy(ps.userId != null ? { id: ps.userId } - : { usernameLower: ps.username!.toLowerCase(), host: toPunyNullable(ps.host) ?? IsNull() }); + : { usernameLower: ps.username!.toLowerCase(), host: this.utilityService.toPunyNullable(ps.host) ?? IsNull() }); if (user == null) { throw new ApiError(meta.errors.noSuchUser); diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index 968c195a2abd..62e4318a81e8 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -1,10 +1,10 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { Users , Followings , UserProfiles } from '@/models/index.js'; -import { toPunyNullable } from '@/misc/convert-host.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { FollowingEntityService } from '@/services/entities/FollowingEntityService.js'; +import { UtilityService } from '@/services/UtilityService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -80,13 +80,14 @@ export default class extends Endpoint { @Inject('followingsRepository') private followingsRepository: typeof Followings, + private utilityService: UtilityService, private followingEntityService: FollowingEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { const user = await this.usersRepository.findOneBy(ps.userId != null ? { id: ps.userId } - : { usernameLower: ps.username!.toLowerCase(), host: toPunyNullable(ps.host) ?? IsNull() }); + : { usernameLower: ps.username!.toLowerCase(), host: this.utilityService.toPunyNullable(ps.host) ?? IsNull() }); if (user == null) { throw new ApiError(meta.errors.noSuchUser); diff --git a/packages/backend/src/server/api/openapi/gen-spec.ts b/packages/backend/src/server/api/openapi/gen-spec.ts deleted file mode 100644 index 68fa81404134..000000000000 --- a/packages/backend/src/server/api/openapi/gen-spec.ts +++ /dev/null @@ -1,190 +0,0 @@ -import endpoints from '../endpoints.js'; -import config from '@/config/index.js'; -import { errors as basicErrors } from './errors.js'; -import { schemas, convertSchemaToOpenApiSchema } from './schemas.js'; - -export function genOpenapiSpec() { - const spec = { - openapi: '3.0.0', - - info: { - version: 'v1', - title: 'Misskey API', - 'x-logo': { url: '/static-assets/api-doc.png' }, - }, - - externalDocs: { - description: 'Repository', - url: 'https://github.com/misskey-dev/misskey', - }, - - servers: [{ - url: config.apiUrl, - }], - - paths: {} as any, - - components: { - schemas: schemas, - - securitySchemes: { - ApiKeyAuth: { - type: 'apiKey', - in: 'body', - name: 'i', - }, - }, - }, - }; - - for (const endpoint of endpoints.filter(ep => !ep.meta.secure)) { - const errors = {} as any; - - if (endpoint.meta.errors) { - for (const e of Object.values(endpoint.meta.errors)) { - errors[e.code] = { - value: { - error: e, - }, - }; - } - } - - const resSchema = endpoint.meta.res ? convertSchemaToOpenApiSchema(endpoint.meta.res) : {}; - - let desc = (endpoint.meta.description ? endpoint.meta.description : 'No description provided.') + '\n\n'; - desc += `**Credential required**: *${endpoint.meta.requireCredential ? 'Yes' : 'No'}*`; - if (endpoint.meta.kind) { - const kind = endpoint.meta.kind; - desc += ` / **Permission**: *${kind}*`; - } - - const requestType = endpoint.meta.requireFile ? 'multipart/form-data' : 'application/json'; - const schema = endpoint.params; - - if (endpoint.meta.requireFile) { - schema.properties.file = { - type: 'string', - format: 'binary', - description: 'The file contents.', - }; - schema.required.push('file'); - } - - const info = { - operationId: endpoint.name, - summary: endpoint.name, - description: desc, - externalDocs: { - description: 'Source code', - url: `https://github.com/misskey-dev/misskey/blob/develop/packages/backend/src/server/api/endpoints/${endpoint.name}.ts`, - }, - ...(endpoint.meta.tags ? { - tags: [endpoint.meta.tags[0]], - } : {}), - ...(endpoint.meta.requireCredential ? { - security: [{ - ApiKeyAuth: [], - }], - } : {}), - requestBody: { - required: true, - content: { - [requestType]: { - schema, - }, - }, - }, - responses: { - ...(endpoint.meta.res ? { - '200': { - description: 'OK (with results)', - content: { - 'application/json': { - schema: resSchema, - }, - }, - }, - } : { - '204': { - description: 'OK (without any results)', - }, - }), - '400': { - description: 'Client error', - content: { - 'application/json': { - schema: { - $ref: '#/components/schemas/Error', - }, - examples: { ...errors, ...basicErrors['400'] }, - }, - }, - }, - '401': { - description: 'Authentication error', - content: { - 'application/json': { - schema: { - $ref: '#/components/schemas/Error', - }, - examples: basicErrors['401'], - }, - }, - }, - '403': { - description: 'Forbidden error', - content: { - 'application/json': { - schema: { - $ref: '#/components/schemas/Error', - }, - examples: basicErrors['403'], - }, - }, - }, - '418': { - description: 'I\'m Ai', - content: { - 'application/json': { - schema: { - $ref: '#/components/schemas/Error', - }, - examples: basicErrors['418'], - }, - }, - }, - ...(endpoint.meta.limit ? { - '429': { - description: 'To many requests', - content: { - 'application/json': { - schema: { - $ref: '#/components/schemas/Error', - }, - examples: basicErrors['429'], - }, - }, - }, - } : {}), - '500': { - description: 'Internal server error', - content: { - 'application/json': { - schema: { - $ref: '#/components/schemas/Error', - }, - examples: basicErrors['500'], - }, - }, - }, - }, - }; - - spec.paths['/' + endpoint.name] = { - post: info, - }; - } - - return spec; -} diff --git a/packages/backend/src/services/AntennaService.ts b/packages/backend/src/services/AntennaService.ts index 0304a1b0aa37..523b76a50a06 100644 --- a/packages/backend/src/services/AntennaService.ts +++ b/packages/backend/src/services/AntennaService.ts @@ -9,7 +9,7 @@ import { GlobalEventService } from '@/services/GlobalEventService.js'; import * as Acct from '@/misc/acct.js'; import { Cache } from '@/misc/cache.js'; import type { Packed } from '@/misc/schema.js'; -import { getFullApAccount } from '@/misc/convert-host'; +import { UtilityService } from './UtilityService.js'; @Injectable() export class AntennaService { @@ -34,6 +34,7 @@ export class AntennaService { @Inject('userListJoiningsRepository') private userListJoiningsRepository: typeof UserListJoinings, + private utilityService: UtilityService, private idService: IdService, private globalEventServie: GlobalEventService, ) { @@ -126,9 +127,9 @@ export class AntennaService { } else if (antenna.src === 'users') { const accts = antenna.users.map(x => { const { username, host } = Acct.parse(x); - return getFullApAccount(username, host).toLowerCase(); + return this.utilityService.getFullApAccount(username, host).toLowerCase(); }); - if (!accts.includes(getFullApAccount(noteUser.username, noteUser.host).toLowerCase())) return false; + if (!accts.includes(this.utilityService.getFullApAccount(noteUser.username, noteUser.host).toLowerCase())) return false; } const keywords = antenna.keywords diff --git a/packages/backend/src/services/CoreModule.ts b/packages/backend/src/services/CoreModule.ts index 76e782ce7bdd..a8dffe0a8676 100644 --- a/packages/backend/src/services/CoreModule.ts +++ b/packages/backend/src/services/CoreModule.ts @@ -50,6 +50,7 @@ import { UserSuspendService } from './UserSuspendService.js'; import { VideoProcessingService } from './VideoProcessingService.js'; import { WebhookService } from './WebhookService.js'; import { ProxyAccountService } from './ProxyAccountService.js'; +import { UtilityService } from './UtilityService.js'; @Module({ imports: [ @@ -105,6 +106,7 @@ import { ProxyAccountService } from './ProxyAccountService.js'; UserSuspendService, VideoProcessingService, WebhookService, + UtilityService, ], exports: [ ChartsModule, @@ -157,6 +159,7 @@ import { ProxyAccountService } from './ProxyAccountService.js'; UserSuspendService, VideoProcessingService, WebhookService, + UtilityService, ], }) export class CoreModule {} diff --git a/packages/backend/src/services/CustomEmojiService.ts b/packages/backend/src/services/CustomEmojiService.ts index 12b7f7e88c3b..6b42cbb7182c 100644 --- a/packages/backend/src/services/CustomEmojiService.ts +++ b/packages/backend/src/services/CustomEmojiService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DataSource } from 'typeorm'; +import { DataSource, In, IsNull } from 'typeorm'; import { Emojis } from '@/models/index.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; @@ -7,9 +7,24 @@ import { Config } from '@/config.js'; import { IdService } from '@/services/IdService.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { Emoji } from '@/models/entities/Emoji.js'; +import { Cache } from '@/misc/cache.js'; +import { query } from '@/prelude/url'; +import type { Note } from '@/models/entities/Note.js'; +import { UtilityService } from './UtilityService.js'; +import { ReactionService } from './ReactionService.js'; + +/** + * 添付用絵文字情報 + */ +type PopulatedEmoji = { + name: string; + url: string; +}; @Injectable() export class CustomEmojiService { + #cache: Cache; + constructor( @Inject(DI.config) private config: Config, @@ -22,7 +37,10 @@ export class CustomEmojiService { private idService: IdService, private globalEventServie: GlobalEventService, + private utilityService: UtilityService, + private reactionService: ReactionService, ) { + this.#cache = new Cache(1000 * 60 * 60 * 12); } public async add(data: { @@ -48,4 +66,110 @@ export class CustomEmojiService { return emoji; } + + #normalizeHost(src: string | undefined, noteUserHost: string | null): string | null { + // クエリに使うホスト + let host = src === '.' ? null // .はローカルホスト (ここがマッチするのはリアクションのみ) + : src === undefined ? noteUserHost // ノートなどでホスト省略表記の場合はローカルホスト (ここがリアクションにマッチすることはない) + : this.utilityService.isSelfHost(src) ? null // 自ホスト指定 + : (src || noteUserHost); // 指定されたホスト || ノートなどの所有者のホスト (こっちがリアクションにマッチすることはない) + + host = this.utilityService.toPunyNullable(host); + + return host; + } + + #parseEmojiStr(emojiName: string, noteUserHost: string | null) { + const match = emojiName.match(/^(\w+)(?:@([\w.-]+))?$/); + if (!match) return { name: null, host: null }; + + const name = match[1]; + + // ホスト正規化 + const host = this.utilityService.toPunyNullable(this.#normalizeHost(match[2], noteUserHost)); + + return { name, host }; + } + + /** + * 添付用絵文字情報を解決する + * @param emojiName ノートやユーザープロフィールに添付された、またはリアクションのカスタム絵文字名 (:は含めない, リアクションでローカルホストの場合は@.を付ける (これはdecodeReactionで可能)) + * @param noteUserHost ノートやユーザープロフィールの所有者のホスト + * @returns 絵文字情報, nullは未マッチを意味する + */ + public async populateEmoji(emojiName: string, noteUserHost: string | null): Promise { + const { name, host } = this.#parseEmojiStr(emojiName, noteUserHost); + if (name == null) return null; + + const queryOrNull = async () => (await Emojis.findOneBy({ + name, + host: host ?? IsNull(), + })) || null; + + const emoji = await this.#cache.fetch(`${name} ${host}`, queryOrNull); + + if (emoji == null) return null; + + const isLocal = emoji.host == null; + const emojiUrl = emoji.publicUrl || emoji.originalUrl; // || emoji.originalUrl してるのは後方互換性のため + const url = isLocal ? emojiUrl : `${this.config.url}/proxy/${encodeURIComponent((new URL(emojiUrl)).pathname)}?${query({ url: emojiUrl })}`; + + return { + name: emojiName, + url, + }; + } + + /** + * 複数の添付用絵文字情報を解決する (キャシュ付き, 存在しないものは結果から除外される) + */ + public async populateEmojis(emojiNames: string[], noteUserHost: string | null): Promise { + const emojis = await Promise.all(emojiNames.map(x => this.populateEmoji(x, noteUserHost))); + return emojis.filter((x): x is PopulatedEmoji => x != null); + } + + public aggregateNoteEmojis(notes: Note[]) { + let emojis: { name: string | null; host: string | null; }[] = []; + for (const note of notes) { + emojis = emojis.concat(note.emojis + .map(e => this.#parseEmojiStr(e, note.userHost))); + if (note.renote) { + emojis = emojis.concat(note.renote.emojis + .map(e => this.#parseEmojiStr(e, note.renote!.userHost))); + if (note.renote.user) { + emojis = emojis.concat(note.renote.user.emojis + .map(e => this.#parseEmojiStr(e, note.renote!.userHost))); + } + } + const customReactions = Object.keys(note.reactions).map(x => this.reactionService.decodeReaction(x)).filter(x => x.name != null) as typeof emojis; + emojis = emojis.concat(customReactions); + if (note.user) { + emojis = emojis.concat(note.user.emojis + .map(e => this.#parseEmojiStr(e, note.userHost))); + } + } + return emojis.filter(x => x.name != null) as { name: string; host: string | null; }[]; + } + + /** + * 与えられた絵文字のリストをデータベースから取得し、キャッシュに追加します + */ + public async prefetchEmojis(emojis: { name: string; host: string | null; }[]): Promise { + const notCachedEmojis = emojis.filter(emoji => this.#cache.get(`${emoji.name} ${emoji.host}`) == null); + const emojisQuery: any[] = []; + const hosts = new Set(notCachedEmojis.map(e => e.host)); + for (const host of hosts) { + emojisQuery.push({ + name: In(notCachedEmojis.filter(e => e.host === host).map(e => e.name)), + host: host ?? IsNull(), + }); + } + const _emojis = emojisQuery.length > 0 ? await Emojis.find({ + where: emojisQuery, + select: ['name', 'host', 'originalUrl', 'publicUrl'], + }) : []; + for (const emoji of _emojis) { + this.#cache.set(`${emoji.name} ${emoji.host}`, emoji); + } + } } diff --git a/packages/backend/src/services/FederatedInstanceService.ts b/packages/backend/src/services/FederatedInstanceService.ts index 09d069048a93..ce6659c0ba34 100644 --- a/packages/backend/src/services/FederatedInstanceService.ts +++ b/packages/backend/src/services/FederatedInstanceService.ts @@ -3,7 +3,7 @@ import type { Instances } from '@/models/index.js'; import type { Instance } from '@/models/entities/Instance.js'; import { Cache } from '@/misc/cache.js'; import { IdService } from '@/services/IdService.js'; -import { toPuny } from '@/misc/convert-host.js'; +import { UtilityService } from './UtilityService.js'; @Injectable() export class FederatedInstanceService { @@ -13,13 +13,14 @@ export class FederatedInstanceService { @Inject('instancesRepository') private instancesRepository: typeof Instances, + private utilityService: UtilityService, private idService: IdService, ) { this.#cache = new Cache(1000 * 60 * 60); } public async registerOrFetchInstanceDoc(host: string): Promise { - host = toPuny(host); + host = this.utilityService.toPuny(host); const cached = this.#cache.get(host); if (cached) return cached; diff --git a/packages/backend/src/services/ReactionService.ts b/packages/backend/src/services/ReactionService.ts index f78f86580834..26df110790bc 100644 --- a/packages/backend/src/services/ReactionService.ts +++ b/packages/backend/src/services/ReactionService.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { Blockings, Emojis, NoteReactions , Users , Notes } from '@/models/index.js'; +import { Emojis } from '@/models/index.js'; +import type { Blockings, NoteReactions , Users , Notes } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import type { IRemoteUser, User } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; @@ -11,11 +12,44 @@ import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js import { GlobalEventService } from '@/services/GlobalEventService.js'; import { CreateNotificationService } from '@/services/CreateNotificationService.js'; import PerUserReactionsChart from '@/services/chart/charts/per-user-reactions.js'; -import { decodeReaction, toDbReaction } from '@/misc/reaction-lib.js'; +import { emojiRegex } from '@/misc/emoji-regex.js'; import { ApDeliverManagerService } from './remote/activitypub/ApDeliverManagerService.js'; import { NoteEntityService } from './entities/NoteEntityService.js'; import { UserEntityService } from './entities/UserEntityService.js'; import { ApRendererService } from './remote/activitypub/ApRendererService.js'; +import { MetaService } from './MetaService.js'; +import { UtilityService } from './UtilityService.js'; + +const legacies: Record = { + 'like': '👍', + 'love': '❤', // ここに記述する場合は異体字セレクタを入れない + 'laugh': '😆', + 'hmm': '🤔', + 'surprise': '😮', + 'congrats': '🎉', + 'angry': '💢', + 'confused': '😥', + 'rip': '😇', + 'pudding': '🍮', + 'star': '⭐', +}; + +type DecodedReaction = { + /** + * リアクション名 (Unicode Emoji or ':name@hostname' or ':name@.') + */ + reaction: string; + + /** + * name (カスタム絵文字の場合name, Emojiクエリに使う) + */ + name?: string; + + /** + * host (カスタム絵文字の場合host, Emojiクエリに使う) + */ + host?: string | null; +}; @Injectable() export class ReactionService { @@ -35,6 +69,8 @@ export class ReactionService { @Inject('emojisRepository') private emojisRepository: typeof Emojis, + private utilityService: UtilityService, + private metaService: MetaService, private userEntityService: UserEntityService, private noteEntityService: NoteEntityService, private idService: IdService, @@ -64,7 +100,7 @@ export class ReactionService { } // TODO: cache - reaction = await toDbReaction(reaction, user.host); + reaction = await this.toDbReaction(reaction, user.host); const record: NoteReaction = { id: this.idService.genId(), @@ -110,7 +146,7 @@ export class ReactionService { this.perUserReactionsChart.update(user, note); // カスタム絵文字リアクションだったら絵文字情報も送る - const decodedReaction = decodeReaction(reaction); + const decodedReaction = this.decodeReaction(reaction); const emoji = await this.emojisRepository.findOne({ where: { @@ -191,7 +227,7 @@ export class ReactionService { this.notesRepository.decrement({ id: note.id }, 'score', 1); this.globalEventServie.publishNoteStream(note.id, 'unreacted', { - reaction: decodeReaction(exist.reaction).reaction, + reaction: this.decodeReaction(exist.reaction).reaction, userId: user.id, }); @@ -208,4 +244,98 @@ export class ReactionService { } //#endregion } + + public async getFallbackReaction(): Promise { + const meta = await this.metaService.fetch(); + return meta.useStarForReactionFallback ? '⭐' : '👍'; + } + + public convertLegacyReactions(reactions: Record) { + const _reactions = {} as Record; + + for (const reaction of Object.keys(reactions)) { + if (reactions[reaction] <= 0) continue; + + if (Object.keys(legacies).includes(reaction)) { + if (_reactions[legacies[reaction]]) { + _reactions[legacies[reaction]] += reactions[reaction]; + } else { + _reactions[legacies[reaction]] = reactions[reaction]; + } + } else { + if (_reactions[reaction]) { + _reactions[reaction] += reactions[reaction]; + } else { + _reactions[reaction] = reactions[reaction]; + } + } + } + + const _reactions2 = {} as Record; + + for (const reaction of Object.keys(_reactions)) { + _reactions2[this.decodeReaction(reaction).reaction] = _reactions[reaction]; + } + + return _reactions2; + } + + public async toDbReaction(reaction?: string | null, reacterHost?: string | null): Promise { + if (reaction == null) return await this.getFallbackReaction(); + + reacterHost = this.utilityService.toPunyNullable(reacterHost); + + // 文字列タイプのリアクションを絵文字に変換 + if (Object.keys(legacies).includes(reaction)) return legacies[reaction]; + + // Unicode絵文字 + const match = emojiRegex.exec(reaction); + if (match) { + // 合字を含む1つの絵文字 + const unicode = match[0]; + + // 異体字セレクタ除去 + return unicode.match('\u200d') ? unicode : unicode.replace(/\ufe0f/g, ''); + } + + const custom = reaction.match(/^:([\w+-]+)(?:@\.)?:$/); + if (custom) { + const name = custom[1]; + const emoji = await Emojis.findOneBy({ + host: reacterHost ?? IsNull(), + name, + }); + + if (emoji) return reacterHost ? `:${name}@${reacterHost}:` : `:${name}:`; + } + + return await this.getFallbackReaction(); + } + + public decodeReaction(str: string): DecodedReaction { + const custom = str.match(/^:([\w+-]+)(?:@([\w.-]+))?:$/); + + if (custom) { + const name = custom[1]; + const host = custom[2] || null; + + return { + reaction: `:${name}@${host || '.'}:`, // ローカル分は@以降を省略するのではなく.にする + name, + host, + }; + } + + return { + reaction: str, + name: undefined, + host: undefined, + }; + } + + public convertLegacyReaction(reaction: string): string { + reaction = this.decodeReaction(reaction).reaction; + if (Object.keys(legacies).includes(reaction)) return legacies[reaction]; + return reaction; + } } diff --git a/packages/backend/src/services/SignupService.ts b/packages/backend/src/services/SignupService.ts index dfa93247a55d..7a42be9b57d7 100644 --- a/packages/backend/src/services/SignupService.ts +++ b/packages/backend/src/services/SignupService.ts @@ -9,12 +9,12 @@ import { Config } from '@/config.js'; import { User } from '@/models/entities/User.js'; import { UserProfile } from '@/models/entities/UserProfile.js'; import { IdService } from '@/services/IdService.js'; -import { toPunyNullable } from '@/misc/convert-host'; import { UserKeypair } from '@/models/entities/UserKeypair.js'; import { UsedUsername } from '@/models/entities/UsedUsername.js'; import generateUserToken from '@/misc/generate-native-user-token.js'; import UsersChart from './chart/charts/users.js'; import { UserEntityService } from './entities/UserEntityService.js'; +import { UtilityService } from './UtilityService.js'; @Injectable() export class SignupService { @@ -31,6 +31,7 @@ export class SignupService { @Inject('usedUsernamesRepository') private usedUsernamesRepository: typeof UsedUsernames, + private utilityService: UtilityService, private userEntityService: UserEntityService, private idService: IdService, private usersChart: UsersChart, @@ -108,7 +109,7 @@ export class SignupService { createdAt: new Date(), username: username, usernameLower: username.toLowerCase(), - host: toPunyNullable(host), + host: this.utilityService.toPunyNullable(host), token: secret, isAdmin: (await Users.countBy({ host: IsNull(), diff --git a/packages/backend/src/services/UtilityService.ts b/packages/backend/src/services/UtilityService.ts new file mode 100644 index 000000000000..ba03dfc0693c --- /dev/null +++ b/packages/backend/src/services/UtilityService.ts @@ -0,0 +1,37 @@ +import { URL } from 'node:url'; +import { toASCII } from 'punycode'; +import { Inject, Injectable } from '@nestjs/common'; +import { DI } from '@/di-symbols.js'; +import { Config } from '@/config.js'; + +@Injectable() +export class UtilityService { + constructor( + @Inject(DI.config) + private config: Config, + ) { + } + + public getFullApAccount(username: string, host: string | null): string { + return host ? `${username}@${this.toPuny(host)}` : `${username}@${this.toPuny(this.config.host)}`; + } + + public isSelfHost(host: string | null): boolean { + if (host == null) return true; + return this.toPuny(this.config.host) === this.toPuny(host); + } + + public extractDbHost(uri: string): string { + const url = new URL(uri); + return this.toPuny(url.hostname); + } + + public toPuny(host: string): string { + return toASCII(host.toLowerCase()); + } + + public toPunyNullable(host: string | null | undefined): string | null { + if (host == null) return null; + return toASCII(host.toLowerCase()); + } +} diff --git a/packages/backend/src/services/chart/charts/instance.ts b/packages/backend/src/services/chart/charts/instance.ts index 7c721fc9c229..98bbd7e5246d 100644 --- a/packages/backend/src/services/chart/charts/instance.ts +++ b/packages/backend/src/services/chart/charts/instance.ts @@ -3,9 +3,9 @@ import { DataSource } from 'typeorm'; import { DriveFiles, Followings, Users, Notes } from '@/models/index.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { Note } from '@/models/entities/Note.js'; -import { toPuny } from '@/misc/convert-host.js'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; +import { UtilityService } from '@/services/UtilityService.js'; import Chart from '../core.js'; import { name, schema } from './entities/instance.js'; import type { KVs } from '../core.js'; @@ -20,6 +20,7 @@ export default class InstanceChart extends Chart { @Inject(DI.db) private db: DataSource, + private utilityService: UtilityService, private appLockService: AppLockService, ) { super(db, appLockService.getChartInsertLock, name, schema, true); @@ -56,21 +57,21 @@ export default class InstanceChart extends Chart { public async requestReceived(host: string): Promise { await this.commit({ 'requests.received': 1, - }, toPuny(host)); + }, this.utilityService.toPuny(host)); } public async requestSent(host: string, isSucceeded: boolean): Promise { await this.commit({ 'requests.succeeded': isSucceeded ? 1 : 0, 'requests.failed': isSucceeded ? 0 : 1, - }, toPuny(host)); + }, this.utilityService.toPuny(host)); } public async newUser(host: string): Promise { await this.commit({ 'users.total': 1, 'users.inc': 1, - }, toPuny(host)); + }, this.utilityService.toPuny(host)); } public async updateNote(host: string, note: Note, isAdditional: boolean): Promise { @@ -82,7 +83,7 @@ export default class InstanceChart extends Chart { 'notes.diffs.renote': note.renoteId != null ? (isAdditional ? 1 : -1) : 0, 'notes.diffs.reply': note.replyId != null ? (isAdditional ? 1 : -1) : 0, 'notes.diffs.withFile': note.fileIds.length > 0 ? (isAdditional ? 1 : -1) : 0, - }, toPuny(host)); + }, this.utilityService.toPuny(host)); } public async updateFollowing(host: string, isAdditional: boolean): Promise { @@ -90,7 +91,7 @@ export default class InstanceChart extends Chart { 'following.total': isAdditional ? 1 : -1, 'following.inc': isAdditional ? 1 : 0, 'following.dec': isAdditional ? 0 : 1, - }, toPuny(host)); + }, this.utilityService.toPuny(host)); } public async updateFollowers(host: string, isAdditional: boolean): Promise { @@ -98,7 +99,7 @@ export default class InstanceChart extends Chart { 'followers.total': isAdditional ? 1 : -1, 'followers.inc': isAdditional ? 1 : 0, 'followers.dec': isAdditional ? 0 : 1, - }, toPuny(host)); + }, this.utilityService.toPuny(host)); } public async updateDrive(file: DriveFile, isAdditional: boolean): Promise { diff --git a/packages/backend/src/services/entities/DriveFileEntityService.ts b/packages/backend/src/services/entities/DriveFileEntityService.ts index c49d605be993..467269728300 100644 --- a/packages/backend/src/services/entities/DriveFileEntityService.ts +++ b/packages/backend/src/services/entities/DriveFileEntityService.ts @@ -9,7 +9,7 @@ import { awaitAll } from '@/prelude/await-all.js'; import type { User } from '@/models/entities/User.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import { appendQuery, query } from '@/prelude/url.js'; -import { toPuny } from '@/misc/convert-host.js'; +import { UtilityService } from '../UtilityService.js'; import { UserEntityService } from './UserEntityService.js'; import { DriveFolderEntityService } from './DriveFolderEntityService.js'; @@ -38,6 +38,7 @@ export class DriveFileEntityService { @Inject(forwardRef(() => UserEntityService)) private userEntityService: UserEntityService, + private utilityService: UtilityService, private driveFolderEntityService: DriveFolderEntityService, ) { } @@ -106,7 +107,7 @@ export class DriveFileEntityService { public async calcDriveUsageOfHost(host: string): Promise { const { sum } = await this.driveFilesRepository .createQueryBuilder('file') - .where('file.userHost = :host', { host: toPuny(host) }) + .where('file.userHost = :host', { host: this.utilityService.toPuny(host) }) .andWhere('file.isLink = FALSE') .select('SUM(file.size)', 'sum') .getRawOne(); diff --git a/packages/backend/src/services/entities/NoteEntityService.ts b/packages/backend/src/services/entities/NoteEntityService.ts index 3a96a04fefb2..725fdd409878 100644 --- a/packages/backend/src/services/entities/NoteEntityService.ts +++ b/packages/backend/src/services/entities/NoteEntityService.ts @@ -7,11 +7,11 @@ import { Config } from '@/config.js'; import type { Packed } from '@/misc/schema.js'; import { nyaize } from '@/misc/nyaize.js'; import { awaitAll } from '@/prelude/await-all.js'; -import { convertLegacyReaction, convertLegacyReactions, decodeReaction } from '@/misc/reaction-lib.js'; import type { User } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; import type { NoteReaction } from '@/models/entities/NoteReaction.js'; -import { aggregateNoteEmojis, populateEmojis, prefetchEmojis } from '@/misc/populate-emojis.js'; +import { CustomEmojiService } from '../CustomEmojiService.js'; +import { ReactionService } from '../ReactionService.js'; import { UserEntityService } from './UserEntityService.js'; import { DriveFileEntityService } from './DriveFileEntityService.js'; @@ -50,6 +50,8 @@ export class NoteEntityService { private userEntityService: UserEntityService, private driveFileEntityService: DriveFileEntityService, + private customEmojiService: CustomEmojiService, + private reactionService: ReactionService, ) { } @@ -157,7 +159,7 @@ export class NoteEntityService { if (_hint_?.myReactions) { const reaction = _hint_.myReactions.get(note.id); if (reaction) { - return convertLegacyReaction(reaction.reaction); + return this.reactionService.convertLegacyReaction(reaction.reaction); } else if (reaction === null) { return undefined; } @@ -170,7 +172,7 @@ export class NoteEntityService { }); if (reaction) { - return convertLegacyReaction(reaction.reaction); + return this.reactionService.convertLegacyReaction(reaction.reaction); } return undefined; @@ -261,7 +263,7 @@ export class NoteEntityService { : await this.channelsRepository.findOneBy({ id: note.channelId }) : null; - const reactionEmojiNames = Object.keys(note.reactions).filter(x => x.startsWith(':')).map(x => decodeReaction(x).reaction).map(x => x.replace(/:/g, '')); + const reactionEmojiNames = Object.keys(note.reactions).filter(x => x.startsWith(':')).map(x => this.reactionService.decodeReaction(x).reaction).map(x => x.replace(/:/g, '')); const packed: Packed<'Note'> = await awaitAll({ id: note.id, @@ -277,9 +279,9 @@ export class NoteEntityService { visibleUserIds: note.visibility === 'specified' ? note.visibleUserIds : undefined, renoteCount: note.renoteCount, repliesCount: note.repliesCount, - reactions: convertLegacyReactions(note.reactions), + reactions: this.reactionService.convertLegacyReactions(note.reactions), tags: note.tags.length > 0 ? note.tags : undefined, - emojis: populateEmojis(note.emojis.concat(reactionEmojiNames), host), + emojis: this.customEmojiService.populateEmojis(note.emojis.concat(reactionEmojiNames), host), fileIds: note.fileIds, files: this.driveFileEntityService.packMany(note.fileIds), replyId: note.replyId, @@ -355,7 +357,7 @@ export class NoteEntityService { } } - await prefetchEmojis(aggregateNoteEmojis(notes)); + await this.customEmojiService.prefetchEmojis(this.customEmojiService.aggregateNoteEmojis(notes)); return await Promise.all(notes.map(n => this.pack(n, me, { ...options, diff --git a/packages/backend/src/services/entities/NoteReactionEntityService.ts b/packages/backend/src/services/entities/NoteReactionEntityService.ts index c1d7d00a98ea..23566f24ced0 100644 --- a/packages/backend/src/services/entities/NoteReactionEntityService.ts +++ b/packages/backend/src/services/entities/NoteReactionEntityService.ts @@ -6,7 +6,7 @@ import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { NoteReaction } from '@/models/entities/NoteReaction.js'; -import { convertLegacyReaction } from '@/misc/reaction-lib.js'; +import { ReactionService } from '../ReactionService.js'; import { UserEntityService } from './UserEntityService.js'; import { NoteEntityService } from './NoteEntityService.js'; @@ -18,6 +18,7 @@ export class NoteReactionEntityService { private userEntityService: UserEntityService, private noteEntityService: NoteEntityService, + private reactionService: ReactionService, ) { } @@ -38,7 +39,7 @@ export class NoteReactionEntityService { id: reaction.id, createdAt: reaction.createdAt.toISOString(), user: await this.userEntityService.pack(reaction.user ?? reaction.userId, me), - type: convertLegacyReaction(reaction.reaction), + type: this.reactionService.convertLegacyReaction(reaction.reaction), ...(opts.withNote ? { note: await this.noteEntityService.pack(reaction.note ?? reaction.noteId, me), } : {}), diff --git a/packages/backend/src/services/entities/NotificationEntityService.ts b/packages/backend/src/services/entities/NotificationEntityService.ts index 797c21fc62de..2dcb324cab6e 100644 --- a/packages/backend/src/services/entities/NotificationEntityService.ts +++ b/packages/backend/src/services/entities/NotificationEntityService.ts @@ -4,12 +4,13 @@ import { DI } from '@/di-symbols.js'; import type { AccessTokens, NoteReactions , Notifications } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Notification } from '@/models/entities/Notification.js'; -import { aggregateNoteEmojis, prefetchEmojis } from '@/misc/populate-emojis.js'; import type { NoteReaction } from '@/models/entities/NoteReaction.js'; import type { Note } from '@/models/entities/Note.js'; import type { Packed } from '@/misc/schema.js'; +import { CustomEmojiService } from '../CustomEmojiService.js'; import { UserEntityService } from './UserEntityService.js'; import { NoteEntityService } from './NoteEntityService.js'; +import { UserGroupInvitationEntityService } from './UserGroupInvitationEntityService.js'; @Injectable() export class NotificationEntityService { @@ -25,6 +26,8 @@ export class NotificationEntityService { private userEntityService: UserEntityService, private noteEntityService: NoteEntityService, + private userGroupInvitationEntityService: UserGroupInvitationEntityService, + private customEmojiService: CustomEmojiService, ) { } @@ -91,7 +94,7 @@ export class NotificationEntityService { }), } : {}), ...(notification.type === 'groupInvited' ? { - invitation: UserGroupInvitations.pack(notification.userGroupInvitationId!), + invitation: this.userGroupInvitationEntityService.pack(notification.userGroupInvitationId!), } : {}), ...(notification.type === 'app' ? { body: notification.customBody, @@ -121,7 +124,7 @@ export class NotificationEntityService { myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) || null); } - await prefetchEmojis(aggregateNoteEmojis(notes)); + await this.customEmojiService.prefetchEmojis(this.customEmojiService.aggregateNoteEmojis(notes)); return await Promise.all(notifications.map(x => this.pack(x, { _hintForEachNotes_: { diff --git a/packages/backend/src/services/entities/UserEntityService.ts b/packages/backend/src/services/entities/UserEntityService.ts index d07a3278a4bb..fbf13de74ec1 100644 --- a/packages/backend/src/services/entities/UserEntityService.ts +++ b/packages/backend/src/services/entities/UserEntityService.ts @@ -2,20 +2,21 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import { EntityRepository, Repository, In, Not } from 'typeorm'; import Ajv from 'ajv'; import { DI } from '@/di-symbols.js'; -import { Pages } from '@/models/index.js'; -import type { AntennaNotes, Instances, MessagingMessages, UserSecurityKeys , Blockings, Mutings , Followings, FollowRequests, Users, DriveFiles, NoteUnreads, ChannelFollowings, Notifications, UserNotePinings, UserProfiles , AnnouncementReads, Announcements, UserGroupJoinings } from '@/models/index.js'; +import type { Pages , AntennaNotes, Instances, MessagingMessages, UserSecurityKeys , Blockings, Mutings , Followings, FollowRequests, Users, DriveFiles, NoteUnreads, ChannelFollowings, Notifications, UserNotePinings, UserProfiles , AnnouncementReads, Announcements, UserGroupJoinings } from '@/models/index.js'; import { Config } from '@/config.js'; import type { Packed } from '@/misc/schema.js'; import type { Promiseable } from '@/prelude/await-all.js'; import { awaitAll } from '@/prelude/await-all.js'; -import { populateEmojis } from '@/misc/populate-emojis.js'; import { getAntennas } from '@/misc/antenna-cache.js'; import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js'; import { Cache } from '@/misc/cache.js'; import type { Instance } from '@/models/entities/Instance.js'; import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/entities/User.js'; +import { CustomEmojiService } from '../CustomEmojiService.js'; import { NoteEntityService } from './NoteEntityService.js'; +import { DriveFileEntityService } from './DriveFileEntityService.js'; +import { PageEntityService } from './PageEntityService.js'; type IsUserDetailed = Detailed extends true ? Packed<'UserDetailed'> : Packed<'UserLite'>; type IsMeAndIsUserDetailed = @@ -107,6 +108,10 @@ export class UserEntityService { // 循環参照のため / for circular dependency @Inject(forwardRef(() => NoteEntityService)) private noteEntityService: NoteEntityService, + + private driveFileEntityService: DriveFileEntityService, + private pageEntityService: PageEntityService, + private customEmojiService: CustomEmojiService, ) { this.#userInstanceCache = new Cache(1000 * 60 * 60 * 3); } @@ -280,10 +285,10 @@ export class UserEntityService { public async getAvatarUrl(user: User): Promise { if (user.avatar) { - return this.driveFilesRepository.getPublicUrl(user.avatar, true) || this.getIdenticonUrl(user.id); + return this.driveFileEntityService.getPublicUrl(user.avatar, true) || this.getIdenticonUrl(user.id); } else if (user.avatarId) { const avatar = await this.driveFilesRepository.findOneByOrFail({ id: user.avatarId }); - return this.driveFilesRepository.getPublicUrl(avatar, true) || this.getIdenticonUrl(user.id); + return this.driveFileEntityService.getPublicUrl(avatar, true) || this.getIdenticonUrl(user.id); } else { return this.getIdenticonUrl(user.id); } @@ -291,7 +296,7 @@ export class UserEntityService { public getAvatarUrlSync(user: User): string { if (user.avatar) { - return this.driveFilesRepository.getPublicUrl(user.avatar, true) || this.getIdenticonUrl(user.id); + return this.driveFileEntityService.getPublicUrl(user.avatar, true) || this.getIdenticonUrl(user.id); } else { return this.getIdenticonUrl(user.id); } @@ -376,7 +381,7 @@ export class UserEntityService { faviconUrl: instance.faviconUrl, themeColor: instance.themeColor, } : undefined) : undefined, - emojis: populateEmojis(user.emojis, user.host), + emojis: this.customEmojiService.populateEmojis(user.emojis, user.host), onlineStatus: this.getOnlineStatus(user), driveCapacityOverrideMb: user.driveCapacityOverrideMb, @@ -386,7 +391,7 @@ export class UserEntityService { createdAt: user.createdAt.toISOString(), updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null, lastFetchedAt: user.lastFetchedAt ? user.lastFetchedAt.toISOString() : null, - bannerUrl: user.banner ? this.driveFilesRepository.getPublicUrl(user.banner, false) : null, + bannerUrl: user.banner ? this.driveFileEntityService.getPublicUrl(user.banner, false) : null, bannerBlurhash: user.banner?.blurhash || null, bannerColor: null, // 後方互換性のため isLocked: user.isLocked, @@ -405,7 +410,7 @@ export class UserEntityService { detail: true, }), pinnedPageId: profile!.pinnedPageId, - pinnedPage: profile!.pinnedPageId ? Pages.pack(profile!.pinnedPageId, me) : null, + pinnedPage: profile!.pinnedPageId ? this.pageEntityService.pack(profile!.pinnedPageId, me) : null, publicReactions: profile!.publicReactions, ffVisibility: profile!.ffVisibility, twoFactorEnabled: profile!.twoFactorEnabled, diff --git a/packages/backend/src/services/remote/ResolveUserService.ts b/packages/backend/src/services/remote/ResolveUserService.ts index 7c50d5b1863d..d266419a331e 100644 --- a/packages/backend/src/services/remote/ResolveUserService.ts +++ b/packages/backend/src/services/remote/ResolveUserService.ts @@ -5,12 +5,12 @@ import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; import type { IRemoteUser, User } from '@/models/entities/User.js'; -import type { Config } from '@/config.js'; -import { toPuny } from '@/misc/convert-host.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import { createPerson, updatePerson } from './activitypub/models/person.js'; -import type { WebfingerService } from './WebfingerService.js'; -import type { RemoteLoggerService } from './RemoteLoggerService.js'; +import { UtilityService } from '../UtilityService.js'; +import { WebfingerService } from './WebfingerService.js'; +import { RemoteLoggerService } from './RemoteLoggerService.js'; +import { ApPersonService } from './activitypub/models/ApPersonService.js'; @Injectable() export class ResolveUserService { @@ -23,8 +23,10 @@ export class ResolveUserService { @Inject('usersRepository') private usersRepository: typeof Users, + private utilityService: UtilityService, private webfingerService: WebfingerService, private remoteLoggerService: RemoteLoggerService, + private apPersonService: ApPersonService, ) { this.#logger = this.remoteLoggerService.logger.createSubLogger('resolve-user'); } @@ -43,7 +45,7 @@ export class ResolveUserService { }); } - host = toPuny(host); + host = this.utilityService.toPuny(host); if (this.config.host === host) { this.#logger.info(`return local user: ${usernameLower}`); @@ -64,7 +66,7 @@ export class ResolveUserService { const self = await this.#resolveSelf(acctLower); this.#logger.succ(`return new remote user: ${chalk.magenta(acctLower)}`); - return await createPerson(self.href); + return await this.apPersonService.createPerson(self.href); } // ユーザー情報が古い場合は、WebFilgerからやりなおして返す @@ -98,7 +100,7 @@ export class ResolveUserService { this.#logger.info(`uri is fine: ${acctLower}`); } - await updatePerson(self.href); + await this.apPersonService.updatePerson(self.href); this.#logger.info(`return resynced remote user: ${acctLower}`); return await this.usersRepository.findOneBy({ uri: self.href }).then(u => { diff --git a/packages/backend/src/services/remote/activitypub/ApAudienceService.ts b/packages/backend/src/services/remote/activitypub/ApAudienceService.ts new file mode 100644 index 000000000000..3e3eff88c086 --- /dev/null +++ b/packages/backend/src/services/remote/activitypub/ApAudienceService.ts @@ -0,0 +1,104 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { In } from 'typeorm'; +import promiseLimit from 'promise-limit'; +import { DI } from '@/di-symbols.js'; +import type { CacheableRemoteUser, CacheableUser } from '@/models/entities/User.js'; +import { concat, toArray, toSingle, unique } from '@/prelude/array.js'; +import { getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isPost, isRead, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js'; +import { ApPersonService } from './models/ApPersonService.js'; +import type { ApObject } from './type.js'; +import type { Resolver } from './ApResolverService.js'; + +type Visibility = 'public' | 'home' | 'followers' | 'specified'; + +type AudienceInfo = { + visibility: Visibility, + mentionedUsers: CacheableUser[], + visibleUsers: CacheableUser[], +}; + +@Injectable() +export class ApAudienceService { + constructor( + private apPersonService: ApPersonService, + ) { + } + + public async parseAudience(actor: CacheableRemoteUser, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise { + const toGroups = this.#groupingAudience(getApIds(to), actor); + const ccGroups = this.#groupingAudience(getApIds(cc), actor); + + const others = unique(concat([toGroups.other, ccGroups.other])); + + const limit = promiseLimit(2); + const mentionedUsers = (await Promise.all( + others.map(id => limit(() => this.apPersonService.resolvePerson(id, resolver).catch(() => null))), + )).filter((x): x is CacheableUser => x != null); + + if (toGroups.public.length > 0) { + return { + visibility: 'public', + mentionedUsers, + visibleUsers: [], + }; + } + + if (ccGroups.public.length > 0) { + return { + visibility: 'home', + mentionedUsers, + visibleUsers: [], + }; + } + + if (toGroups.followers.length > 0) { + return { + visibility: 'followers', + mentionedUsers, + visibleUsers: [], + }; + } + + return { + visibility: 'specified', + mentionedUsers, + visibleUsers: mentionedUsers, + }; + } + + #groupingAudience(ids: string[], actor: CacheableRemoteUser) { + const groups = { + public: [] as string[], + followers: [] as string[], + other: [] as string[], + }; + + for (const id of ids) { + if (this.#isPublic(id)) { + groups.public.push(id); + } else if (this.#isFollowers(id, actor)) { + groups.followers.push(id); + } else { + groups.other.push(id); + } + } + + groups.other = unique(groups.other); + + return groups; + } + + #isPublic(id: string) { + return [ + 'https://www.w3.org/ns/activitystreams#Public', + 'as#Public', + 'Public', + ].includes(id); + } + + #isFollowers(id: string, actor: CacheableRemoteUser) { + return ( + id === (actor.followersUri || `${actor.uri}/followers`) + ); + } +} diff --git a/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts b/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts index 43784a7e41fe..fe3f877ce9fc 100644 --- a/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts +++ b/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts @@ -11,6 +11,7 @@ import { UserCacheService } from '@/services/UserCacheService.js'; import type { Note } from '@/models/entities/Note.js'; import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; import { getApId } from './type.js'; +import { ApPersonService } from './models/ApPersonService.js'; import type { IObject } from './type.js'; export type UriParseResult = { @@ -48,6 +49,7 @@ export class ApDbResolverService { private userPublickeysRepository: typeof UserPublickeys, private userCacheService: UserCacheService, + private apPersonService: ApPersonService, ) { this.#publicKeyCache = new Cache(Infinity); this.#publicKeyByUserIdCache = new Cache(Infinity); @@ -161,7 +163,7 @@ export class ApDbResolverService { user: CacheableRemoteUser; key: UserPublickey | null; } | null> { - const user = await resolvePerson(uri) as CacheableRemoteUser; + const user = await this.apPersonService.resolvePerson(uri) as CacheableRemoteUser; if (user == null) return null; diff --git a/packages/backend/src/services/remote/activitypub/ApInboxService.ts b/packages/backend/src/services/remote/activitypub/ApInboxService.ts index ea46a7ff0f01..71f69932d3e9 100644 --- a/packages/backend/src/services/remote/activitypub/ApInboxService.ts +++ b/packages/backend/src/services/remote/activitypub/ApInboxService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { Followings, Notes , Users } from '@/models/index.js'; +import type { AbuseUserReports , Followings, FollowRequests, MessagingMessages, Notes , Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type { CacheableRemoteUser } from '@/models/entities/User.js'; import { UserFollowingService } from '@/services/UserFollowingService.js'; @@ -13,18 +13,23 @@ import { NoteDeleteService } from '@/services/NoteDeleteService.js'; import { NoteCreateService } from '@/services/NoteCreateService.js'; import { concat, toArray, toSingle, unique } from '@/prelude/array.js'; import { AppLockService } from '@/services/AppLockService.js'; -import { extractDbHost, isSelfHost } from '@/misc/convert-host.js'; import type Logger from '@/logger.js'; import { MetaService } from '@/services/MetaService.js'; import { IdService } from '@/services/IdService.js'; import { StatusError } from '@/misc/status-error.js'; -import { updatePerson } from './models/person.js'; -import { updateQuestion } from './models/question.js'; +import { UtilityService } from '@/services/UtilityService.js'; +import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { QueueService } from '@/queue/queue.service.js'; +import { MessagingService } from '@/services/MessagingService.js'; import { getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isPost, isRead, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js'; import { ApNoteService } from './models/ApNoteService.js'; import { ApLoggerService } from './ApLoggerService.js'; import { ApDbResolverService } from './ApDbResolverService.js'; import { ApResolverService } from './ApResolverService.js'; +import { ApAudienceService } from './ApAudienceService.js'; +import { ApPersonService } from './models/ApPersonService.js'; +import { ApQuestionService } from './models/ApQuestionService.js'; import type { Resolver } from './ApResolverService.js'; import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IRead, IReject, IRemove, IUndo, IUpdate } from './type.js'; @@ -45,9 +50,22 @@ export class ApInboxService { @Inject('followingsRepository') private followingsRepository: typeof Followings, + @Inject('messagingMessagesRepository') + private messagingMessagesRepository: typeof MessagingMessages, + + @Inject('abuseUserReportsRepository') + private abuseUserReportsRepository: typeof AbuseUserReports, + + @Inject('followRequestsRepository') + private followRequestsRepository: typeof FollowRequests, + + private userEntityService: UserEntityService, + private noteEntityService: NoteEntityService, + private utilityService: UtilityService, private idService: IdService, private metaService: MetaService, private userFollowingService: UserFollowingService, + private apAudienceService: ApAudienceService, private reactionService: ReactionService, private relayService: RelayService, private notePiningService: NotePiningService, @@ -59,6 +77,10 @@ export class ApInboxService { private apDbResolverService: ApDbResolverService, private apLoggerService: ApLoggerService, private apNoteService: ApNoteService, + private apPersonService: ApPersonService, + private apQuestionService: ApQuestionService, + private queueService: QueueService, + private messagingService: MessagingService, ) { this.#logger = this.apLoggerService.logger; } @@ -152,13 +174,13 @@ export class ApInboxService { async #read(actor: CacheableRemoteUser, activity: IRead): Promise { const id = await getApId(activity.object); - if (!isSelfHost(extractDbHost(id))) { + if (!this.utilityService.isSelfHost(this.utilityService.extractDbHost(id))) { return `skip: Read to foreign host (${id})`; } const messageId = id.split('/').pop(); - const message = await MessagingMessages.findOneBy({ id: messageId }); + const message = await this.messagingMessagesRepository.findOneBy({ id: messageId }); if (message == null) { return 'skip: message not found'; } @@ -167,7 +189,7 @@ export class ApInboxService { return 'skip: actor is not a message recipient'; } - await readUserMessagingMessage(message.recipientId!, message.userId, [message.id]); + await this.messagingService.readUserMessagingMessage(message.recipientId!, message.userId, [message.id]); return `ok: mark as read (${message.userId} => ${message.recipientId} ${message.id})`; } @@ -249,7 +271,7 @@ export class ApInboxService { // アナウンス先をブロックしてたら中断 const meta = await this.metaService.fetch(); - if (meta.blockedHosts.includes(extractDbHost(uri))) return; + if (meta.blockedHosts.includes(this.utilityService.extractDbHost(uri))) return; const unlock = await this.appLockService.getApLock(uri); @@ -277,11 +299,11 @@ export class ApInboxService { throw e; } - if (!await this.notesRepository.isVisibleForMe(renote, actor.id)) return 'skip: invalid actor for this activity'; + if (!await this.noteEntityService.isVisibleForMe(renote, actor.id)) return 'skip: invalid actor for this activity'; this.#logger.info(`Creating the (Re)Note: ${uri}`); - const activityAudience = await parseAudience(actor, activity.to, activity.cc); + const activityAudience = await this.apAudienceService.parseAudience(actor, activity.to, activity.cc); await this.noteCreateService.create(actor, { createdAt: activity.published ? new Date(activity.published) : null, @@ -356,7 +378,7 @@ export class ApInboxService { } if (typeof note.id === 'string') { - if (extractDbHost(actor.uri) !== extractDbHost(note.id)) { + if (this.utilityService.extractDbHost(actor.uri) !== this.utilityService.extractDbHost(note.id)) { return 'skip: host in actor.uri !== note.id'; } } @@ -434,7 +456,7 @@ export class ApInboxService { this.#logger.info('skip: already deleted'); } - const job = await createDeleteAccountJob(actor); + const job = await this.queueService.createDeleteAccountJob(actor); await this.usersRepository.update(actor.id, { isDeleted: true, @@ -459,7 +481,7 @@ export class ApInboxService { return '投稿を削除しようとしているユーザーは投稿の作成者ではありません'; } - await deleteMessage(message); + await this.messagingService.deleteMessage(message); return 'ok: message deleted'; } @@ -486,7 +508,7 @@ export class ApInboxService { }); if (users.length < 1) return 'skip'; - await AbuseUserReports.insert({ + await this.abuseUserReportsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), targetUserId: users[0].id, @@ -641,7 +663,7 @@ export class ApInboxService { return 'skip: フォロー解除しようとしているユーザーはローカルユーザーではありません'; } - const req = await FollowRequests.findOneBy({ + const req = await this.followRequestsRepository.findOneBy({ followerId: actor.id, followeeId: followee.id, }); @@ -693,10 +715,10 @@ export class ApInboxService { }); if (isActor(object)) { - await updatePerson(actor.uri!, resolver, object); + await this.apPersonService.updatePerson(actor.uri!, resolver, object); return 'ok: Person updated'; } else if (getApType(object) === 'Question') { - await updateQuestion(object).catch(e => console.log(e)); + await this.apQuestionService.updateQuestion(object).catch(err => console.error(err)); return 'ok: Question updated'; } else { return `skip: Unknown type: ${getApType(object)}`; diff --git a/packages/backend/src/services/remote/activitypub/ApResolverService.ts b/packages/backend/src/services/remote/activitypub/ApResolverService.ts index 04fea46a1dfb..7ca5eef7bce0 100644 --- a/packages/backend/src/services/remote/activitypub/ApResolverService.ts +++ b/packages/backend/src/services/remote/activitypub/ApResolverService.ts @@ -1,17 +1,17 @@ import { Inject, Injectable } from '@nestjs/common'; import type { ILocalUser } from '@/models/entities/User.js'; -import type { InstanceActorService } from '@/services/InstanceActorService.js'; -import { extractDbHost, isSelfHost } from '@/misc/convert-host.js'; +import { InstanceActorService } from '@/services/InstanceActorService.js'; import type { Notes , Polls , NoteReactions , Users } from '@/models/index.js'; -import type { Config } from '@/config.js'; -import type { MetaService } from '@/services/MetaService.js'; -import type { HttpRequestService } from '@/services/HttpRequestService.js'; +import { Config } from '@/config.js'; +import { MetaService } from '@/services/MetaService.js'; +import { HttpRequestService } from '@/services/HttpRequestService.js'; import { DI } from '@/di-symbols.js'; +import { UtilityService } from '@/services/UtilityService.js'; import { isCollectionOrOrderedCollection } from './type.js'; -import type { ApDbResolverService } from './ApDbResolverService.js'; -import type { ApRendererService } from './ApRendererService.js'; +import { ApDbResolverService } from './ApDbResolverService.js'; +import { ApRendererService } from './ApRendererService.js'; +import { ApRequestService } from './ApRequestService.js'; import type { IObject, ICollection, IOrderedCollection } from './type.js'; -import type { ApRequestService } from './ApRequestService.js'; @Injectable() export class ApResolverService { @@ -31,6 +31,7 @@ export class ApResolverService { @Inject('noteReactionsRepository') private noteReactionsRepository: typeof NoteReactions, + private utilityService: UtilityService, private instanceActorService: InstanceActorService, private metaService: MetaService, private apRequestService: ApRequestService, @@ -47,6 +48,7 @@ export class ApResolverService { this.notesRepository, this.pollsRepository, this.noteReactionsRepository, + this.utilityService, this.instanceActorService, this.metaService, this.apRequestService, @@ -67,6 +69,7 @@ export class Resolver { private notesRepository: typeof Notes, private pollsRepository: typeof Polls, private noteReactionsRepository: typeof NoteReactions, + private utilityService: UtilityService, private instanceActorService: InstanceActorService, private metaService: MetaService, private apRequestService: ApRequestService, @@ -115,8 +118,8 @@ export class Resolver { this.history.add(value); - const host = extractDbHost(value); - if (isSelfHost(host)) { + const host = this.utilityService.extractDbHost(value); + if (this.utilityService.isSelfHost(host)) { return await this.resolveLocal(value); } diff --git a/packages/backend/src/services/remote/activitypub/audience.ts b/packages/backend/src/services/remote/activitypub/audience.ts deleted file mode 100644 index 6d6395317651..000000000000 --- a/packages/backend/src/services/remote/activitypub/audience.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { ApObject, getApIds } from './type.js'; -import Resolver from './resolver.js'; -import { resolvePerson } from './models/person.js'; -import { unique, concat } from '@/prelude/array.js'; -import promiseLimit from 'promise-limit'; -import { User, CacheableRemoteUser, CacheableUser } from '@/models/entities/User.js'; - -type Visibility = 'public' | 'home' | 'followers' | 'specified'; - -type AudienceInfo = { - visibility: Visibility, - mentionedUsers: CacheableUser[], - visibleUsers: CacheableUser[], -}; - -export async function parseAudience(actor: CacheableRemoteUser, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise { - const toGroups = groupingAudience(getApIds(to), actor); - const ccGroups = groupingAudience(getApIds(cc), actor); - - const others = unique(concat([toGroups.other, ccGroups.other])); - - const limit = promiseLimit(2); - const mentionedUsers = (await Promise.all( - others.map(id => limit(() => resolvePerson(id, resolver).catch(() => null))) - )).filter((x): x is CacheableUser => x != null); - - if (toGroups.public.length > 0) { - return { - visibility: 'public', - mentionedUsers, - visibleUsers: [], - }; - } - - if (ccGroups.public.length > 0) { - return { - visibility: 'home', - mentionedUsers, - visibleUsers: [], - }; - } - - if (toGroups.followers.length > 0) { - return { - visibility: 'followers', - mentionedUsers, - visibleUsers: [], - }; - } - - return { - visibility: 'specified', - mentionedUsers, - visibleUsers: mentionedUsers, - }; -} - -function groupingAudience(ids: string[], actor: CacheableRemoteUser) { - const groups = { - public: [] as string[], - followers: [] as string[], - other: [] as string[], - }; - - for (const id of ids) { - if (isPublic(id)) { - groups.public.push(id); - } else if (isFollowers(id, actor)) { - groups.followers.push(id); - } else { - groups.other.push(id); - } - } - - groups.other = unique(groups.other); - - return groups; -} - -function isPublic(id: string) { - return [ - 'https://www.w3.org/ns/activitystreams#Public', - 'as#Public', - 'Public', - ].includes(id); -} - -function isFollowers(id: string, actor: CacheableRemoteUser) { - return ( - id === (actor.followersUri || `${actor.uri}/followers`) - ); -} diff --git a/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts b/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts index 15f170d56541..30a01ca8c28f 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts @@ -2,11 +2,12 @@ import { Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { DI } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import { toArray, unique } from '@/prelude/array.js'; import type { CacheableUser } from '@/models/entities/User.js'; import { isMention } from '../type.js'; -import type { ApResolverService } from '../ApResolverService.js'; +import { ApResolverService } from '../ApResolverService.js'; +import { ApPersonService } from './ApPersonService.js'; import type { IObject , IApMention } from '../type.js'; @Injectable() @@ -16,6 +17,7 @@ export class ApMentionService { private config: Config, private apResolverService: ApResolverService, + private apPersonService: ApPersonService, ) { } @@ -26,7 +28,7 @@ export class ApMentionService { const limit = promiseLimit(2); const mentionedUsers = (await Promise.all( - hrefs.map(x => limit(() => resolvePerson(x, resolver).catch(() => null))), + hrefs.map(x => limit(() => this.apPersonService.resolvePerson(x, resolver).catch(() => null))), )).filter((x): x is CacheableUser => x != null); return mentionedUsers; diff --git a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts index 3459373f9dc3..aa06630855f6 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts @@ -1,10 +1,10 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { DI } from '@/di-symbols.js'; -import type { Polls , Emojis, Users } from '@/models/index.js'; +import type { MessagingMessages , Polls , Emojis, Users } from '@/models/index.js'; + import { Config } from '@/config.js'; import type { CacheableRemoteUser } from '@/models/entities/User.js'; -import { extractDbHost, toPuny } from '@/misc/convert-host'; import type { Note } from '@/models/entities/Note.js'; import { toArray, toSingle, unique } from '@/prelude/array.js'; import type { Emoji } from '@/models/entities/Emoji.js'; @@ -16,12 +16,15 @@ import type Logger from '@/logger.js'; import { IdService } from '@/services/IdService.js'; import { PollService } from '@/services/PollService.js'; import { StatusError } from '@/misc/status-error.js'; +import { UtilityService } from '@/services/UtilityService.js'; +import { MessagingService } from '@/services/MessagingService.js'; import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js'; // eslint-disable-next-line @typescript-eslint/consistent-type-imports import { ApLoggerService } from '../ApLoggerService.js'; import { ApMfmService } from '../ApMfmService.js'; import { ApDbResolverService } from '../ApDbResolverService.js'; import { ApResolverService } from '../ApResolverService.js'; +import { ApAudienceService } from '../ApAudienceService.js'; import { ApPersonService } from './ApPersonService.js'; import { extractApHashtags } from './tag.js'; import { ApMentionService } from './ApMentionService.js'; @@ -44,6 +47,9 @@ export class ApNoteService { @Inject('emojisRepository') private emojisRepository: typeof Emojis, + @Inject('messagingMessagesRepository') + private messagingMessagesRepository: typeof MessagingMessages, + private idService: IdService, private apMfmService: ApMfmService, private apResolverService: ApResolverService, @@ -52,10 +58,13 @@ export class ApNoteService { @Inject(forwardRef(() => ApPersonService)) private apPersonService: ApPersonService, + private utilityService: UtilityService, + private apAudienceService: ApAudienceService, private apMentionService: ApMentionService, private apImageService: ApImageService, private apQuestionService: ApQuestionService, private metaService: MetaService, + private messagingService: MessagingService, private appLockService: AppLockService, private pollService: PollService, private noteCreateService: NoteCreateService, @@ -66,7 +75,7 @@ export class ApNoteService { } public validateNote(object: any, uri: string) { - const expectHost = extractDbHost(uri); + const expectHost = this.utilityService.extractDbHost(uri); if (object == null) { return new Error('invalid Note: object is null'); @@ -76,12 +85,12 @@ export class ApNoteService { return new Error(`invalid Note: invalid object type ${getApType(object)}`); } - if (object.id && extractDbHost(object.id) !== expectHost) { - return new Error(`invalid Note: id has different host. expected: ${expectHost}, actual: ${extractDbHost(object.id)}`); + if (object.id && this.utilityService.extractDbHost(object.id) !== expectHost) { + return new Error(`invalid Note: id has different host. expected: ${expectHost}, actual: ${this.utilityService.extractDbHost(object.id)}`); } - if (object.attributedTo && extractDbHost(getOneApId(object.attributedTo)) !== expectHost) { - return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${extractDbHost(object.attributedTo)}`); + if (object.attributedTo && this.utilityService.extractDbHost(getOneApId(object.attributedTo)) !== expectHost) { + return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${this.utilityService.extractDbHost(object.attributedTo)}`); } return null; @@ -131,7 +140,7 @@ export class ApNoteService { throw new Error('actor has been suspended'); } - const noteAudience = await parseAudience(actor, note.to, note.cc); + const noteAudience = await this.apAudienceService.parseAudience(actor, note.to, note.cc); let visibility = noteAudience.visibility; const visibleUsers = noteAudience.visibleUsers; @@ -143,7 +152,7 @@ export class ApNoteService { } } - let isTalk = note._misskey_talk && visibility === 'specified'; + let isMessaging = note._misskey_talk && visibility === 'specified'; const apMentions = await this.apMentionService.extractApMentions(note.tag); const apHashtags = await extractApHashtags(note.tag); @@ -175,9 +184,9 @@ export class ApNoteService { const uri = getApId(note.inReplyTo); if (uri.startsWith(this.config.url + '/')) { const id = uri.split('/').pop(); - const talk = await MessagingMessages.findOneBy({ id }); + const talk = await this.messagingMessagesRepository.findOneBy({ id }); if (talk) { - isTalk = true; + isMessaging = true; return null; } } @@ -249,7 +258,7 @@ export class ApNoteService { this.#logger.warn(`vote to expired poll from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`); } else if (index >= 0) { this.#logger.info(`vote from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`); - await vote(actor, reply, index); + await this.pollService.vote(actor, reply, index); // リモートフォロワーにUpdate配信 this.pollService.deliverQuestionUpdate(reply.id); @@ -271,9 +280,9 @@ export class ApNoteService { const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined); - if (isTalk) { + if (isMessaging) { for (const recipient of visibleUsers) { - await createMessage(actor, recipient, undefined, text || undefined, (files && files.length > 0) ? files[0] : null, object.id); + await this.messagingService.createMessage(actor, recipient, undefined, text || undefined, (files && files.length > 0) ? files[0] : null, object.id); return null; } } @@ -310,7 +319,7 @@ export class ApNoteService { // ブロックしてたら中断 const meta = await this.metaService.fetch(); - if (meta.blockedHosts.includes(extractDbHost(uri))) throw { statusCode: 451 }; + if (meta.blockedHosts.includes(this.utilityService.extractDbHost(uri))) throw { statusCode: 451 }; const unlock = await this.appLockService.getApLock(uri); @@ -337,7 +346,7 @@ export class ApNoteService { } public async extractEmojis(tags: IObject | IObject[], host: string): Promise { - host = toPuny(host); + host = this.utilityService.toPuny(host); if (!tags) return []; diff --git a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts index 50dd1549aed9..a77ad6432e6a 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts @@ -6,7 +6,6 @@ import type { Followings , Instances, UserProfiles, UserPublickeys, Users } from import { Config } from '@/config.js'; import type { CacheableUser, IRemoteUser } from '@/models/entities/User.js'; import { User } from '@/models/entities/User.js'; -import { toPuny } from '@/misc/convert-host.js'; import { truncate } from '@/misc/truncate.js'; import { UserCacheService } from '@/services/UserCacheService.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; @@ -27,6 +26,8 @@ import InstanceChart from '@/services/chart/charts/instance.js'; import { HashtagService } from '@/services/HashtagService.js'; import { UserNotePining } from '@/models/entities/UserNotePining.js'; import { StatusError } from '@/misc/status-error.js'; +import { UtilityService } from '@/services/UtilityService.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { getApId, getApType, getOneApHrefNullable, isActor, isCollection, isCollectionOrOrderedCollection, isPropertyValue } from '../type.js'; import { ApMfmService } from '../ApMfmService.js'; import { ApResolverService } from '../ApResolverService.js'; @@ -71,69 +72,6 @@ function addService(target: { [x: string]: any }, source: IApPropertyValue) { } } -/** - * Validate and convert to actor object - * @param x Fetched object - * @param uri Fetch target URI - */ -function validateActor(x: IObject, uri: string): IActor { - const expectHost = toPuny(new URL(uri).hostname); - - if (x == null) { - throw new Error('invalid Actor: object is null'); - } - - if (!isActor(x)) { - throw new Error(`invalid Actor type '${x.type}'`); - } - - if (!(typeof x.id === 'string' && x.id.length > 0)) { - throw new Error('invalid Actor: wrong id'); - } - - if (!(typeof x.inbox === 'string' && x.inbox.length > 0)) { - throw new Error('invalid Actor: wrong inbox'); - } - - if (!(typeof x.preferredUsername === 'string' && x.preferredUsername.length > 0 && x.preferredUsername.length <= 128 && /^\w([\w-.]*\w)?$/.test(x.preferredUsername))) { - throw new Error('invalid Actor: wrong username'); - } - - // These fields are only informational, and some AP software allows these - // fields to be very long. If they are too long, we cut them off. This way - // we can at least see these users and their activities. - if (x.name) { - if (!(typeof x.name === 'string' && x.name.length > 0)) { - throw new Error('invalid Actor: wrong name'); - } - x.name = truncate(x.name, nameLength); - } - if (x.summary) { - if (!(typeof x.summary === 'string' && x.summary.length > 0)) { - throw new Error('invalid Actor: wrong summary'); - } - x.summary = truncate(x.summary, summaryLength); - } - - const idHost = toPuny(new URL(x.id!).hostname); - if (idHost !== expectHost) { - throw new Error('invalid Actor: id has different host'); - } - - if (x.publicKey) { - if (typeof x.publicKey.id !== 'string') { - throw new Error('invalid Actor: publicKey.id is not a string'); - } - - const publicKeyIdHost = toPuny(new URL(x.publicKey.id).hostname); - if (publicKeyIdHost !== expectHost) { - throw new Error('invalid Actor: publicKey.id has different host'); - } - } - - return x; -} - @Injectable() export class ApPersonService { #logger: Logger; @@ -160,6 +98,8 @@ export class ApPersonService { @Inject('followingsRepository') private followingsRepository: typeof Followings, + private utilityService: UtilityService, + private userEntityService: UserEntityService, private idService: IdService, private globalEventService: GlobalEventService, private federatedInstanceService: FederatedInstanceService, @@ -182,6 +122,69 @@ export class ApPersonService { this.#logger = this.apLoggerService.logger; } + /** + * Validate and convert to actor object + * @param x Fetched object + * @param uri Fetch target URI + */ + #validateActor(x: IObject, uri: string): IActor { + const expectHost = this.utilityService.toPuny(new URL(uri).hostname); + + if (x == null) { + throw new Error('invalid Actor: object is null'); + } + + if (!isActor(x)) { + throw new Error(`invalid Actor type '${x.type}'`); + } + + if (!(typeof x.id === 'string' && x.id.length > 0)) { + throw new Error('invalid Actor: wrong id'); + } + + if (!(typeof x.inbox === 'string' && x.inbox.length > 0)) { + throw new Error('invalid Actor: wrong inbox'); + } + + if (!(typeof x.preferredUsername === 'string' && x.preferredUsername.length > 0 && x.preferredUsername.length <= 128 && /^\w([\w-.]*\w)?$/.test(x.preferredUsername))) { + throw new Error('invalid Actor: wrong username'); + } + + // These fields are only informational, and some AP software allows these + // fields to be very long. If they are too long, we cut them off. This way + // we can at least see these users and their activities. + if (x.name) { + if (!(typeof x.name === 'string' && x.name.length > 0)) { + throw new Error('invalid Actor: wrong name'); + } + x.name = truncate(x.name, nameLength); + } + if (x.summary) { + if (!(typeof x.summary === 'string' && x.summary.length > 0)) { + throw new Error('invalid Actor: wrong summary'); + } + x.summary = truncate(x.summary, summaryLength); + } + + const idHost = this.utilityService.toPuny(new URL(x.id!).hostname); + if (idHost !== expectHost) { + throw new Error('invalid Actor: id has different host'); + } + + if (x.publicKey) { + if (typeof x.publicKey.id !== 'string') { + throw new Error('invalid Actor: publicKey.id is not a string'); + } + + const publicKeyIdHost = this.utilityService.toPuny(new URL(x.publicKey.id).hostname); + if (publicKeyIdHost !== expectHost) { + throw new Error('invalid Actor: publicKey.id has different host'); + } + } + + return x; + } + /** * Personをフェッチします。 * @@ -227,11 +230,11 @@ export class ApPersonService { const object = await resolver.resolve(uri) as any; - const person = validateActor(object, uri); + const person = this.#validateActor(object, uri); this.#logger.info(`Creating the Person: ${person.id}`); - const host = toPuny(new URL(object.id).hostname); + const host = this.utilityService.toPuny(new URL(object.id).hostname); const { fields } = this.analyzeAttachments(person.attachment || []); @@ -385,7 +388,7 @@ export class ApPersonService { const object = hint || await resolver.resolve(uri); - const person = validateActor(object, uri); + const person = this.#validateActor(object, uri); this.#logger.info(`Updating the Person: ${person.id}`); From f9127e8686bfe86785595e416b8d3fbcd0259594 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 10:00:26 +0900 Subject: [PATCH 142/180] wip --- packages/backend/src/db/logger.ts | 3 - packages/backend/src/db/postgre.ts | 5 +- .../queue/processors/InboxProcessorService.ts | 5 +- .../src/server/WellKnownServerService.ts | 2 +- .../api/endpoints/drive/files/create.ts | 104 ++++++++++-------- .../src/server/api/endpoints/sw/register.ts | 1 - .../src/services/CustomEmojiService.ts | 2 +- .../src/services/chart/charts/federation.ts | 5 +- .../remote/activitypub/ApRendererService.ts | 21 +++- .../ld-signature.ts => LdSignatureService.ts} | 25 ++++- 10 files changed, 104 insertions(+), 69 deletions(-) delete mode 100644 packages/backend/src/db/logger.ts rename packages/backend/src/services/remote/activitypub/{misc/ld-signature.ts => LdSignatureService.ts} (85%) diff --git a/packages/backend/src/db/logger.ts b/packages/backend/src/db/logger.ts deleted file mode 100644 index 22f4c6b1b019..000000000000 --- a/packages/backend/src/db/logger.ts +++ /dev/null @@ -1,3 +0,0 @@ -import Logger from '@/services/logger.js'; - -export const dbLogger = new Logger('db'); diff --git a/packages/backend/src/db/postgre.ts b/packages/backend/src/db/postgre.ts index c61de59fb3f8..1ed685e3a213 100644 --- a/packages/backend/src/db/postgre.ts +++ b/packages/backend/src/db/postgre.ts @@ -71,10 +71,11 @@ import { Webhook } from '@/models/entities/Webhook.js'; import { Channel } from '@/models/entities/Channel.js'; import { loadConfig } from '@/config.js'; +import Logger from '@/logger.js'; import { envOption } from '../env.js'; import { redisClient } from './redis.js'; -import { dbLogger } from './logger.js'; -import type { Logger } from 'typeorm'; + +export const dbLogger = new Logger('db'); const sqlLogger = dbLogger.createSubLogger('sql', 'gray', false); diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index e7a7619936bc..c0e80bf9589e 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -16,7 +16,6 @@ import type { Instance } from '@/models/entities/Instance.js'; import InstanceChart from '@/services/chart/charts/instance.js'; import ApRequestChart from '@/services/chart/charts/ap-request.js'; import FederationChart from '@/services/chart/charts/federation.js'; -import { LdSignature } from '@/services/remote/activitypub/misc/ld-signature.js'; import { getApId } from '@/services/remote/activitypub/type.js'; import type { CacheableRemoteUser } from '@/models/entities/User.js'; import type { UserPublickey } from '@/models/entities/UserPublickey.js'; @@ -25,6 +24,7 @@ import { StatusError } from '@/misc/status-error.js'; import { UtilityService } from '@/services/UtilityService.js'; import { ApPersonService } from '@/services/remote/activitypub/models/ApPersonService.js'; import perform from '@/services/remote/activitypub/perform.js'; +import { LdSignatureService } from '@/services/remote/activitypub/LdSignatureService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DeliverJobData, InboxJobData } from '../types.js'; @@ -48,6 +48,7 @@ export class InboxProcessorService { private metaService: MetaService, private federatedInstanceService: FederatedInstanceService, private fetchInstanceMetadataService: FetchInstanceMetadataService, + private ldSignatureService: LdSignatureService, private apRequestService: ApRequestService, private apPersonService: ApPersonService, private apDbResolverService: ApDbResolverService, @@ -142,7 +143,7 @@ export class InboxProcessorService { } // LD-Signature検証 - const ldSignature = new LdSignature(); + const ldSignature = this.ldSignatureService.use(); const verified = await ldSignature.verifyRsaSignature2017(activity, authUser.key.keyPem).catch(() => false); if (!verified) { return 'skip: LD-Signatureの検証に失敗しました'; diff --git a/packages/backend/src/server/WellKnownServerService.ts b/packages/backend/src/server/WellKnownServerService.ts index a4843b866d29..f75e22c1af8f 100644 --- a/packages/backend/src/server/WellKnownServerService.ts +++ b/packages/backend/src/server/WellKnownServerService.ts @@ -4,7 +4,7 @@ import { IsNull, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { Users } from '@/models/index.js'; import { Config } from '@/config.js'; -import { escapeAttribute, escapeValue } from '@/prelude/xml'; +import { escapeAttribute, escapeValue } from '@/prelude/xml.js'; import type { User } from '@/models/entities/User.js'; import * as Acct from '@/misc/acct.js'; import { NodeinfoServerService } from './NodeinfoServerService.js'; diff --git a/packages/backend/src/server/api/endpoints/drive/files/create.ts b/packages/backend/src/server/api/endpoints/drive/files/create.ts index ccee5236e339..56a37c13d2c3 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/create.ts @@ -1,12 +1,12 @@ import ms from 'ms'; -import { addFile } from '@/services/drive/add-file.js'; -import { DriveFiles } from '@/models/index.js'; +import { Inject, Injectable } from '@nestjs/common'; +import type { DriveFiles } from '@/models/index.js'; import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; -import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { apiLogger } from '../../../logger.js'; +import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; +import { MetaService } from '@/services/MetaService.js'; +import { DriveService } from '@/services/DriveService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -65,48 +65,60 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user, _, file, cleanup, ip, headers) => { - // Get 'name' parameter - let name = ps.name || file.originalname; - if (name !== undefined && name !== null) { - name = name.trim(); - if (name.length === 0) { - name = null; - } else if (name === 'blob') { - name = null; - } else if (!DriveFiles.validateFileName(name)) { - throw new ApiError(meta.errors.invalidFileName); - } - } else { - name = null; - } +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, - const meta = await fetchMeta(); + private driveFileEntityService: DriveFileEntityService, + private metaService: MetaService, + private driveService: DriveService, + ) { + super(meta, paramDef, async (ps, me, _, file, cleanup, ip, headers) => { + // Get 'name' parameter + let name = ps.name || file.originalname; + if (name !== undefined && name !== null) { + name = name.trim(); + if (name.length === 0) { + name = null; + } else if (name === 'blob') { + name = null; + } else if (!this.driveFileEntityService.validateFileName(name)) { + throw new ApiError(meta.errors.invalidFileName); + } + } else { + name = null; + } - try { - // Create file - const driveFile = await addFile({ - user, - path: file.path, - name, - comment: ps.comment, - folderId: ps.folderId, - force: ps.force, - sensitive: ps.isSensitive, - requestIp: meta.enableIpLogging ? ip : null, - requestHeaders: meta.enableIpLogging ? headers : null, - }); - return await DriveFiles.pack(driveFile, { self: true }); - } catch (e) { - if (e instanceof Error || typeof e === 'string') { - apiLogger.error(e); - } - if (e instanceof IdentifiableError) { - if (e.id === '282f77bf-5816-4f72-9264-aa14d8261a21') throw new ApiError(meta.errors.inappropriate); - if (e.id === 'c6244ed2-a39a-4e1c-bf93-f0fbd7764fa6') throw new ApiError(meta.errors.noFreeSpace); - } - throw new ApiError(); - } finally { + const meta = await this.metaService.fetch(); + + try { + // Create file + const driveFile = await this.driveService.addFile({ + user: me, + path: file.path, + name, + comment: ps.comment, + folderId: ps.folderId, + force: ps.force, + sensitive: ps.isSensitive, + requestIp: meta.enableIpLogging ? ip : null, + requestHeaders: meta.enableIpLogging ? headers : null, + }); + return await this.driveFileEntityService.pack(driveFile, { self: true }); + } catch (err) { + if (err instanceof Error || typeof err === 'string') { + console.error(err); + } + if (err instanceof IdentifiableError) { + if (err.id === '282f77bf-5816-4f72-9264-aa14d8261a21') throw new ApiError(meta.errors.inappropriate); + if (err.id === 'c6244ed2-a39a-4e1c-bf93-f0fbd7764fa6') throw new ApiError(meta.errors.noFreeSpace); + } + throw new ApiError(); + } finally { cleanup!(); + } + }); } -}); +} diff --git a/packages/backend/src/server/api/endpoints/sw/register.ts b/packages/backend/src/server/api/endpoints/sw/register.ts index 9540e6be261f..18eb799d7305 100644 --- a/packages/backend/src/server/api/endpoints/sw/register.ts +++ b/packages/backend/src/server/api/endpoints/sw/register.ts @@ -1,5 +1,4 @@ import { Inject, Injectable } from '@nestjs/common'; -import { fetchMeta } from '@/misc/fetch-meta.js'; import { IdService } from '@/services/IdService.js'; import type { SwSubscriptions } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; diff --git a/packages/backend/src/services/CustomEmojiService.ts b/packages/backend/src/services/CustomEmojiService.ts index 6b42cbb7182c..5acd572fd13a 100644 --- a/packages/backend/src/services/CustomEmojiService.ts +++ b/packages/backend/src/services/CustomEmojiService.ts @@ -8,7 +8,7 @@ import { IdService } from '@/services/IdService.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { Emoji } from '@/models/entities/Emoji.js'; import { Cache } from '@/misc/cache.js'; -import { query } from '@/prelude/url'; +import { query } from '@/prelude/url.js'; import type { Note } from '@/models/entities/Note.js'; import { UtilityService } from './UtilityService.js'; import { ReactionService } from './ReactionService.js'; diff --git a/packages/backend/src/services/chart/charts/federation.ts b/packages/backend/src/services/chart/charts/federation.ts index a934bf9a6ba3..75123d92026b 100644 --- a/packages/backend/src/services/chart/charts/federation.ts +++ b/packages/backend/src/services/chart/charts/federation.ts @@ -1,9 +1,9 @@ import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { Followings, Instances } from '@/models/index.js'; -import { fetchMeta } from '@/misc/fetch-meta.js'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; +import { MetaService } from '@/services/MetaService.js'; import Chart from '../core.js'; import { name, schema } from './entities/federation.js'; import type { KVs } from '../core.js'; @@ -18,6 +18,7 @@ export default class FederationChart extends Chart { @Inject(DI.db) private db: DataSource, + private metaService: MetaService, private appLockService: AppLockService, ) { super(db, appLockService.getChartInsertLock, name, schema); @@ -29,7 +30,7 @@ export default class FederationChart extends Chart { } protected async tickMinor(): Promise>> { - const meta = await fetchMeta(); + const meta = await this.metaService.fetch(); const suspendedInstancesQuery = Instances.createQueryBuilder('instance') .select('instance.host') diff --git a/packages/backend/src/services/remote/activitypub/ApRendererService.ts b/packages/backend/src/services/remote/activitypub/ApRendererService.ts index b2ab76b318e2..191e7a71fb6e 100644 --- a/packages/backend/src/services/remote/activitypub/ApRendererService.ts +++ b/packages/backend/src/services/remote/activitypub/ApRendererService.ts @@ -1,9 +1,10 @@ +import { createPublicKey } from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import { In, IsNull } from 'typeorm'; import { v4 as uuid } from 'uuid'; import * as mfm from 'mfm-js'; import { DI } from '@/di-symbols.js'; -import type { Polls , DriveFiles, Emojis, Notes, Users } from '@/models/index.js'; +import type { UserProfiles , Polls , DriveFiles, Emojis, Notes, Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; import type { IMentionedRemoteUsers, Note } from '@/models/entities/Note.js'; @@ -17,7 +18,9 @@ import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; import type { PollVote } from '@/models/entities/PollVote.js'; import { UserKeypairStoreService } from '@/services/UserKeypairStoreService.js'; import { MfmService } from '@/services/MfmService.js'; -import { LdSignature } from './misc/ld-signature.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; +import { LdSignatureService } from './LdSignatureService.js'; import type { IActivity } from './type.js'; import type { IIdentifier } from './models/identifier.js'; @@ -30,6 +33,9 @@ export class ApRendererService { @Inject('usersRepository') private usersRepository: typeof Users, + @Inject('userProfilesRepository') + private userProfilesRepository: typeof UserProfiles, + @Inject('notesRepository') private notesRepository: typeof Notes, @@ -42,6 +48,9 @@ export class ApRendererService { @Inject('pollsRepository') private pollsRepository: typeof Polls, + private userEntityService: UserEntityService, +private driveFileEntityService: DriveFileEntityService, + private ldSignatureService: LdSignatureService, private userKeypairStoreService: UserKeypairStoreService, private mfmService: MfmService, ) { @@ -137,7 +146,7 @@ export class ApRendererService { return { type: 'Document', mediaType: file.type, - url: this.driveFilesRepository.getPublicUrl(file), + url: this.driveFileEntityService.getPublicUrl(file), name: file.comment, }; } @@ -213,7 +222,7 @@ export class ApRendererService { public renderImage(file: DriveFile) { return { type: 'Image', - url: this.driveFilesRepository.getPublicUrl(file), + url: this.driveFileEntityService.getPublicUrl(file), sensitive: file.isSensitive, name: file.comment, }; @@ -415,7 +424,7 @@ export class ApRendererService { const [avatar, banner, profile] = await Promise.all([ user.avatarId ? this.driveFilesRepository.findOneBy({ id: user.avatarId }) : Promise.resolve(undefined), user.bannerId ? this.driveFilesRepository.findOneBy({ id: user.bannerId }) : Promise.resolve(undefined), - UserProfiles.findOneByOrFail({ userId: user.id }), + this.userProfilesRepository.findOneByOrFail({ userId: user.id }), ]); const attachment: { @@ -625,7 +634,7 @@ export class ApRendererService { const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); - const ldSignature = new LdSignature(); + const ldSignature = this.ldSignatureService.use(); ldSignature.debug = false; activity = await ldSignature.signRsaSignature2017(activity, keypair.privateKey, `${this.config.url}/users/${user.id}#main-key`); diff --git a/packages/backend/src/services/remote/activitypub/misc/ld-signature.ts b/packages/backend/src/services/remote/activitypub/LdSignatureService.ts similarity index 85% rename from packages/backend/src/services/remote/activitypub/misc/ld-signature.ts rename to packages/backend/src/services/remote/activitypub/LdSignatureService.ts index 362a543ece4d..8c10536fb7f5 100644 --- a/packages/backend/src/services/remote/activitypub/misc/ld-signature.ts +++ b/packages/backend/src/services/remote/activitypub/LdSignatureService.ts @@ -1,17 +1,32 @@ import * as crypto from 'node:crypto'; +import { Inject, Injectable } from '@nestjs/common'; import jsonld from 'jsonld'; -import { CONTEXTS } from './contexts.js'; import fetch from 'node-fetch'; -import { httpAgent, httpsAgent } from '@/misc/fetch.js'; +import { HttpRequestService } from '@/services/HttpRequestService.js'; +import { CONTEXTS } from './misc/contexts.js'; // RsaSignature2017 based from https://github.com/transmute-industries/RsaSignature2017 -export class LdSignature { +@Injectable() +export class LdSignatureService { + constructor( + private httpRequestService: HttpRequestService, + ) { + } + + public use(): LdSignature { + return new LdSignature(this.httpRequestService); + } +} + +class LdSignature { public debug = false; public preLoad = true; public loderTimeout = 10 * 1000; - constructor() { + constructor( + private httpRequestService: HttpRequestService, + ) { } public async signRsaSignature2017(data: any, privateKey: string, creator: string, domain?: string, created?: Date): Promise { @@ -115,7 +130,7 @@ export class LdSignature { }, // TODO //timeout: this.loderTimeout, - agent: u => u.protocol === 'http:' ? httpAgent : httpsAgent, + agent: u => u.protocol === 'http:' ? this.httpRequestService.httpAgent : this.httpRequestService.httpsAgent, }).then(res => { if (!res.ok) { throw `${res.status} ${res.statusText}`; From fe126f591810a5b9763a1645a58fb4c6718be01d Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 10:05:09 +0900 Subject: [PATCH 143/180] wip --- .../queue/processors/InboxProcessorService.ts | 5 +++-- .../remote/activitypub/ApInboxService.ts | 9 +++++++++ .../src/services/remote/activitypub/perform.ts | 17 ----------------- 3 files changed, 12 insertions(+), 19 deletions(-) delete mode 100644 packages/backend/src/services/remote/activitypub/perform.ts diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index c0e80bf9589e..c5fabd46d0bc 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -23,8 +23,8 @@ import { ApDbResolverService } from '@/services/remote/activitypub/ApDbResolverS import { StatusError } from '@/misc/status-error.js'; import { UtilityService } from '@/services/UtilityService.js'; import { ApPersonService } from '@/services/remote/activitypub/models/ApPersonService.js'; -import perform from '@/services/remote/activitypub/perform.js'; import { LdSignatureService } from '@/services/remote/activitypub/LdSignatureService.js'; +import { ApInboxService } from '@/services/remote/activitypub/ApInboxService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DeliverJobData, InboxJobData } from '../types.js'; @@ -46,6 +46,7 @@ export class InboxProcessorService { private utilityService: UtilityService, private metaService: MetaService, + private apInboxService: ApInboxService, private federatedInstanceService: FederatedInstanceService, private fetchInstanceMetadataService: FetchInstanceMetadataService, private ldSignatureService: LdSignatureService, @@ -189,7 +190,7 @@ export class InboxProcessorService { }); // アクティビティを処理 - await perform(authUser.user, activity); + await this.apInboxService.performActivity(authUser.user, activity); return 'ok'; } } diff --git a/packages/backend/src/services/remote/activitypub/ApInboxService.ts b/packages/backend/src/services/remote/activitypub/ApInboxService.ts index 71f69932d3e9..3d98c4db0229 100644 --- a/packages/backend/src/services/remote/activitypub/ApInboxService.ts +++ b/packages/backend/src/services/remote/activitypub/ApInboxService.ts @@ -101,6 +101,15 @@ export class ApInboxService { } else { await this.performOneActivity(actor, activity); } + + // ついでにリモートユーザーの情報が古かったら更新しておく + if (actor.uri) { + if (actor.lastFetchedAt == null || Date.now() - actor.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) { + setImmediate(() => { + this.apPersonService.updatePerson(actor.uri!); + }); + } + } } public async performOneActivity(actor: CacheableRemoteUser, activity: IObject): Promise { diff --git a/packages/backend/src/services/remote/activitypub/perform.ts b/packages/backend/src/services/remote/activitypub/perform.ts deleted file mode 100644 index e143f56f31ef..000000000000 --- a/packages/backend/src/services/remote/activitypub/perform.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { IObject } from './type.js'; -import { CacheableRemoteUser } from '@/models/entities/User.js'; -import { performActivity } from './kernel/index.js'; -import { updatePerson } from './models/person.js'; - -export default async (actor: CacheableRemoteUser, activity: IObject): Promise => { - await performActivity(actor, activity); - - // ついでにリモートユーザーの情報が古かったら更新しておく - if (actor.uri) { - if (actor.lastFetchedAt == null || Date.now() - actor.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) { - setImmediate(() => { - updatePerson(actor.uri!); - }); - } - } -}; From 8bad44daf4b7143b93ffa99a9bc0feba4c211cca Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 10:28:03 +0900 Subject: [PATCH 144/180] wip --- packages/backend/src/misc/detect-url-mime.ts | 15 - packages/backend/src/misc/get-file-info.ts | 374 ----------------- .../backend/src/server/FileServerService.ts | 7 +- .../src/server/MediaProxyServerService.ts | 5 +- .../src/server/api/SignupApiService.ts | 3 +- .../server/api/StreamingApiServerService.ts | 3 + .../api/endpoints/email-address/available.ts | 2 +- .../backend/src/server/api/stream/index.ts | 5 +- packages/backend/src/services/CoreModule.ts | 3 + packages/backend/src/services/DriveService.ts | 8 +- .../backend/src/services/FileInfoService.ts | 382 ++++++++++++++++++ .../backend/src/services/UserCacheService.ts | 2 +- 12 files changed, 405 insertions(+), 404 deletions(-) delete mode 100644 packages/backend/src/misc/detect-url-mime.ts delete mode 100644 packages/backend/src/misc/get-file-info.ts create mode 100644 packages/backend/src/services/FileInfoService.ts diff --git a/packages/backend/src/misc/detect-url-mime.ts b/packages/backend/src/misc/detect-url-mime.ts deleted file mode 100644 index cd143cf2fb2f..000000000000 --- a/packages/backend/src/misc/detect-url-mime.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { createTemp } from './create-temp.js'; -import { downloadUrl } from './download-url.js'; -import { detectType } from './get-file-info.js'; - -export async function detectUrlMime(url: string) { - const [path, cleanup] = await createTemp(); - - try { - await downloadUrl(url, path); - const { mime } = await detectType(path); - return mime; - } finally { - cleanup(); - } -} diff --git a/packages/backend/src/misc/get-file-info.ts b/packages/backend/src/misc/get-file-info.ts deleted file mode 100644 index 1c988b2487c2..000000000000 --- a/packages/backend/src/misc/get-file-info.ts +++ /dev/null @@ -1,374 +0,0 @@ -import * as fs from 'node:fs'; -import * as crypto from 'node:crypto'; -import { join } from 'node:path'; -import * as stream from 'node:stream'; -import * as util from 'node:util'; -import { FSWatcher } from 'chokidar'; -import { fileTypeFromFile } from 'file-type'; -import FFmpeg from 'fluent-ffmpeg'; -import isSvg from 'is-svg'; -import probeImageSize from 'probe-image-size'; -import { type predictionType } from 'nsfwjs'; -import sharp from 'sharp'; -import { encode } from 'blurhash'; -import { detectSensitive } from '@/services/detect-sensitive.js'; -import { createTempDir } from './create-temp.js'; - -const pipeline = util.promisify(stream.pipeline); - -export type FileInfo = { - size: number; - md5: string; - type: { - mime: string; - ext: string | null; - }; - width?: number; - height?: number; - orientation?: number; - blurhash?: string; - sensitive: boolean; - porn: boolean; - warnings: string[]; -}; - -const TYPE_OCTET_STREAM = { - mime: 'application/octet-stream', - ext: null, -}; - -const TYPE_SVG = { - mime: 'image/svg+xml', - ext: 'svg', -}; - -/** - * Get file information - */ -export async function getFileInfo(path: string, opts: { - skipSensitiveDetection: boolean; - sensitiveThreshold?: number; - sensitiveThresholdForPorn?: number; - enableSensitiveMediaDetectionForVideos?: boolean; -}): Promise { - const warnings = [] as string[]; - - const size = await getFileSize(path); - const md5 = await calcHash(path); - - let type = await detectType(path); - - // image dimensions - let width: number | undefined; - let height: number | undefined; - let orientation: number | undefined; - - if (['image/jpeg', 'image/gif', 'image/png', 'image/apng', 'image/webp', 'image/bmp', 'image/tiff', 'image/svg+xml', 'image/vnd.adobe.photoshop'].includes(type.mime)) { - const imageSize = await detectImageSize(path).catch(e => { - warnings.push(`detectImageSize failed: ${e}`); - return undefined; - }); - - // うまく判定できない画像は octet-stream にする - if (!imageSize) { - warnings.push('cannot detect image dimensions'); - type = TYPE_OCTET_STREAM; - } else if (imageSize.wUnits === 'px') { - width = imageSize.width; - height = imageSize.height; - orientation = imageSize.orientation; - - // 制限を超えている画像は octet-stream にする - if (imageSize.width > 16383 || imageSize.height > 16383) { - warnings.push('image dimensions exceeds limits'); - type = TYPE_OCTET_STREAM; - } - } else { - warnings.push(`unsupported unit type: ${imageSize.wUnits}`); - } - } - - let blurhash: string | undefined; - - if (['image/jpeg', 'image/gif', 'image/png', 'image/apng', 'image/webp', 'image/svg+xml'].includes(type.mime)) { - blurhash = await getBlurhash(path).catch(e => { - warnings.push(`getBlurhash failed: ${e}`); - return undefined; - }); - } - - let sensitive = false; - let porn = false; - - if (!opts.skipSensitiveDetection) { - await detectSensitivity( - path, - type.mime, - opts.sensitiveThreshold ?? 0.5, - opts.sensitiveThresholdForPorn ?? 0.75, - opts.enableSensitiveMediaDetectionForVideos ?? false, - ).then(value => { - [sensitive, porn] = value; - }, error => { - warnings.push(`detectSensitivity failed: ${error}`); - }); - } - - return { - size, - md5, - type, - width, - height, - orientation, - blurhash, - sensitive, - porn, - warnings, - }; -} - -async function detectSensitivity(source: string, mime: string, sensitiveThreshold: number, sensitiveThresholdForPorn: number, analyzeVideo: boolean): Promise<[sensitive: boolean, porn: boolean]> { - let sensitive = false; - let porn = false; - - function judgePrediction(result: readonly predictionType[]): [sensitive: boolean, porn: boolean] { - let sensitive = false; - let porn = false; - - if ((result.find(x => x.className === 'Sexy')?.probability ?? 0) > sensitiveThreshold) sensitive = true; - if ((result.find(x => x.className === 'Hentai')?.probability ?? 0) > sensitiveThreshold) sensitive = true; - if ((result.find(x => x.className === 'Porn')?.probability ?? 0) > sensitiveThreshold) sensitive = true; - - if ((result.find(x => x.className === 'Porn')?.probability ?? 0) > sensitiveThresholdForPorn) porn = true; - - return [sensitive, porn]; - } - - if (['image/jpeg', 'image/png', 'image/webp'].includes(mime)) { - const result = await detectSensitive(source); - if (result) { - [sensitive, porn] = judgePrediction(result); - } - } else if (analyzeVideo && (mime === 'image/apng' || mime.startsWith('video/'))) { - const [outDir, disposeOutDir] = await createTempDir(); - try { - const command = FFmpeg() - .input(source) - .inputOptions([ - '-skip_frame', 'nokey', // 可能ならキーフレームのみを取得してほしいとする(そうなるとは限らない) - '-lowres', '3', // 元の画質でデコードする必要はないので 1/8 画質でデコードしてもよいとする(そうなるとは限らない) - ]) - .noAudio() - .videoFilters([ - { - filter: 'select', // フレームのフィルタリング - options: { - e: 'eq(pict_type,PICT_TYPE_I)', // I-Frame のみをフィルタする(VP9 とかはデコードしてみないとわからないっぽい) - }, - }, - { - filter: 'blackframe', // 暗いフレームの検出 - options: { - amount: '0', // 暗さに関わらず全てのフレームで測定値を取る - }, - }, - { - filter: 'metadata', - options: { - mode: 'select', // フレーム選択モード - key: 'lavfi.blackframe.pblack', // フレームにおける暗部の百分率(前のフィルタからのメタデータを参照する) - value: '50', - function: 'less', // 50% 未満のフレームを選択する(50% 以上暗部があるフレームだと誤検知を招くかもしれないので) - }, - }, - { - filter: 'scale', - options: { - w: 299, - h: 299, - }, - }, - ]) - .format('image2') - .output(join(outDir, '%d.png')) - .outputOptions(['-vsync', '0']); // 可変フレームレートにすることで穴埋めをさせない - const results: ReturnType[] = []; - let frameIndex = 0; - let targetIndex = 0; - let nextIndex = 1; - for await (const path of asyncIterateFrames(outDir, command)) { - try { - const index = frameIndex++; - if (index !== targetIndex) { - continue; - } - targetIndex = nextIndex; - nextIndex += index; // fibonacci sequence によってフレーム数制限を掛ける - const result = await detectSensitive(path); - if (result) { - results.push(judgePrediction(result)); - } - } finally { - fs.promises.unlink(path); - } - } - sensitive = results.filter(x => x[0]).length >= Math.ceil(results.length * sensitiveThreshold); - porn = results.filter(x => x[1]).length >= Math.ceil(results.length * sensitiveThresholdForPorn); - } finally { - disposeOutDir(); - } - } - - return [sensitive, porn]; -} - -async function* asyncIterateFrames(cwd: string, command: FFmpeg.FfmpegCommand): AsyncGenerator { - const watcher = new FSWatcher({ - cwd, - disableGlobbing: true, - }); - let finished = false; - command.once('end', () => { - finished = true; - watcher.close(); - }); - command.run(); - for (let i = 1; true; i++) { // eslint-disable-line @typescript-eslint/no-unnecessary-condition - const current = `${i}.png`; - const next = `${i + 1}.png`; - const framePath = join(cwd, current); - if (await exists(join(cwd, next))) { - yield framePath; - } else if (!finished) { // eslint-disable-line @typescript-eslint/no-unnecessary-condition - watcher.add(next); - await new Promise((resolve, reject) => { - watcher.on('add', function onAdd(path) { - if (path === next) { // 次フレームの書き出しが始まっているなら、現在フレームの書き出しは終わっている - watcher.unwatch(current); - watcher.off('add', onAdd); - resolve(); - } - }); - command.once('end', resolve); // 全てのフレームを処理し終わったなら、最終フレームである現在フレームの書き出しは終わっている - command.once('error', reject); - }); - yield framePath; - } else if (await exists(framePath)) { - yield framePath; - } else { - return; - } - } -} - -function exists(path: string): Promise { - return fs.promises.access(path).then(() => true, () => false); -} - -/** - * Detect MIME Type and extension - */ -export async function detectType(path: string): Promise<{ - mime: string; - ext: string | null; -}> { - // Check 0 byte - const fileSize = await getFileSize(path); - if (fileSize === 0) { - return TYPE_OCTET_STREAM; - } - - const type = await fileTypeFromFile(path); - - if (type) { - // XMLはSVGかもしれない - if (type.mime === 'application/xml' && await checkSvg(path)) { - return TYPE_SVG; - } - - return { - mime: type.mime, - ext: type.ext, - }; - } - - // 種類が不明でもSVGかもしれない - if (await checkSvg(path)) { - return TYPE_SVG; - } - - // それでも種類が不明なら application/octet-stream にする - return TYPE_OCTET_STREAM; -} - -/** - * Check the file is SVG or not - */ -export async function checkSvg(path: string) { - try { - const size = await getFileSize(path); - if (size > 1 * 1024 * 1024) return false; - return isSvg(fs.readFileSync(path)); - } catch { - return false; - } -} - -/** - * Get file size - */ -export async function getFileSize(path: string): Promise { - const getStat = util.promisify(fs.stat); - return (await getStat(path)).size; -} - -/** - * Calculate MD5 hash - */ -async function calcHash(path: string): Promise { - const hash = crypto.createHash('md5').setEncoding('hex'); - await pipeline(fs.createReadStream(path), hash); - return hash.read(); -} - -/** - * Detect dimensions of image - */ -async function detectImageSize(path: string): Promise<{ - width: number; - height: number; - wUnits: string; - hUnits: string; - orientation?: number; -}> { - const readable = fs.createReadStream(path); - const imageSize = await probeImageSize(readable); - readable.destroy(); - return imageSize; -} - -/** - * Calculate average color of image - */ -function getBlurhash(path: string): Promise { - return new Promise((resolve, reject) => { - sharp(path) - .raw() - .ensureAlpha() - .resize(64, 64, { fit: 'inside' }) - .toBuffer((err, buffer, { width, height }) => { - if (err) return reject(err); - - let hash; - - try { - hash = encode(new Uint8ClampedArray(buffer), width, height, 7, 7); - } catch (e) { - return reject(e); - } - - resolve(hash); - }); - }); -} diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index 30a62b35f78c..74b2ee8d615c 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -14,12 +14,12 @@ import { createTemp } from '@/misc/create-temp.js'; import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; import { StatusError } from '@/misc/status-error.js'; import Logger from '@/logger.js'; -import { detectType } from '@/misc/get-file-info.js'; import { DownloadService } from '@/services/DownloadService.js'; import { ImageProcessingService } from '@/services/ImageProcessingService.js'; import { VideoProcessingService } from '@/services/VideoProcessingService.js'; import { InternalStorageService } from '@/services/InternalStorageService.js'; import { contentDisposition } from '@/misc/content-disposition.js'; +import { FileInfoService } from '@/services/FileInfoService.js'; const serverLogger = new Logger('server', 'gray', false); @@ -43,6 +43,7 @@ export class FileServerService { @Inject('driveFilesRepository') private driveFilesRepository: typeof DriveFiles, + private fileInfoService: FileInfoService, private downloadService: DownloadService, private imageProcessingService: ImageProcessingService, private videoProcessingService: VideoProcessingService, @@ -104,7 +105,7 @@ export class FileServerService { try { await this.downloadService.downloadUrl(file.uri, path); - const { mime, ext } = await detectType(path); + const { mime, ext } = await this.fileInfoService.detectType(path); const convertFile = async () => { if (isThumbnail) { @@ -154,7 +155,7 @@ export class FileServerService { } if (isThumbnail || isWebpublic) { - const { mime, ext } = await detectType(this.internalStorageService.resolvePath(key)); + const { mime, ext } = await this.fileInfoService.detectType(this.internalStorageService.resolvePath(key)); const filename = rename(file.name, { suffix: isThumbnail ? '-thumb' : '-web', extname: ext ? `.${ext}` : undefined, diff --git a/packages/backend/src/server/MediaProxyServerService.ts b/packages/backend/src/server/MediaProxyServerService.ts index 282f7f6acef0..d9d294e5421e 100644 --- a/packages/backend/src/server/MediaProxyServerService.ts +++ b/packages/backend/src/server/MediaProxyServerService.ts @@ -7,7 +7,6 @@ import sharp from 'sharp'; import { DI } from '@/di-symbols.js'; import { Config } from '@/config.js'; import { isMimeImage } from '@/misc/is-mime-image.js'; -import { detectType } from '@/misc/get-file-info.js'; import { createTemp } from '@/misc/create-temp.js'; import { DownloadService } from '@/services/DownloadService.js'; import { ImageProcessingService } from '@/services/ImageProcessingService.js'; @@ -15,6 +14,7 @@ import type { IImage } from '@/services/ImageProcessingService.js'; import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; import { StatusError } from '@/misc/status-error.js'; import Logger from '@/logger.js'; +import { FileInfoService } from '@/services/FileInfoService.js'; const serverLogger = new Logger('server', 'gray', false); @@ -24,6 +24,7 @@ export class MediaProxyServerService { @Inject(DI.config) private config: Config, + private fileInfoService: FileInfoService, private downloadService: DownloadService, private imageProcessingService: ImageProcessingService, ) { @@ -62,7 +63,7 @@ export class MediaProxyServerService { try { await this.downloadService.downloadUrl(url, path); - const { mime, ext } = await detectType(path); + const { mime, ext } = await this.fileInfoService.detectType(path); const isConvertibleImage = isMimeImage(mime, 'sharp-convertible-image'); let image: IImage; diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts index d2d841ef1cf8..96eca5901acc 100644 --- a/packages/backend/src/server/api/SignupApiService.ts +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -3,7 +3,6 @@ import rndstr from 'rndstr'; import bcrypt from 'bcryptjs'; import { DI } from '@/di-symbols.js'; import type { RegistrationTickets , UserPendings, UserProfiles , Users } from '@/models/index.js'; - import { Config } from '@/config.js'; import { MetaService } from '@/services/MetaService.js'; import { CaptchaService } from '@/services/CaptchaService.js'; @@ -11,7 +10,7 @@ import { IdService } from '@/services/IdService.js'; import { SignupService } from '@/services/SignupService.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { EmailService } from '@/services/EmailService.js'; -import { SigninService } from '../SigninService.js'; +import { SigninService } from './SigninService.js'; import type Koa from 'koa'; @Injectable() diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts index 24d93b213af4..26053b856355 100644 --- a/packages/backend/src/server/api/StreamingApiServerService.ts +++ b/packages/backend/src/server/api/StreamingApiServerService.ts @@ -8,6 +8,7 @@ import type { Blockings, ChannelFollowings, Followings, Mutings, UserProfiles } import { Config } from '@/config.js'; import { NoteReadService } from '@/services/NoteReadService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { NotificationService } from '@/services/NotificationService.js'; import { AuthenticateService } from './AuthenticateService.js'; import MainStreamConnection from './stream/index.js'; import { ChannelsService } from './stream/ChannelsService.js'; @@ -42,6 +43,7 @@ export class StreamingApiServerService { private noteReadService: NoteReadService, private authenticateService: AuthenticateService, private channelsService: ChannelsService, + private notificationService: NotificationService, ) { } @@ -84,6 +86,7 @@ export class StreamingApiServerService { this.channelsService, this.globalEventService, this.noteReadService, + this.notificationService, connection, ev, user, miapp, ); diff --git a/packages/backend/src/server/api/endpoints/email-address/available.ts b/packages/backend/src/server/api/endpoints/email-address/available.ts index 460987164715..b986fc658f28 100644 --- a/packages/backend/src/server/api/endpoints/email-address/available.ts +++ b/packages/backend/src/server/api/endpoints/email-address/available.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { EmailService } from '@/services/EmailService'; +import { EmailService } from '@/services/EmailService.js'; export const meta = { tags: ['users'], diff --git a/packages/backend/src/server/api/stream/index.ts b/packages/backend/src/server/api/stream/index.ts index 9c43f0318537..f1118aa576e3 100644 --- a/packages/backend/src/server/api/stream/index.ts +++ b/packages/backend/src/server/api/stream/index.ts @@ -7,7 +7,7 @@ import type { UserGroup } from '@/models/entities/UserGroup.js'; import type { Packed } from '@/misc/schema.js'; import type { GlobalEventService } from '@/services/GlobalEventService.js'; import type { NoteReadService } from '@/services/NoteReadService.js'; -import { readNotification } from '../common/read-notification.js'; +import type { NotificationService } from '@/services/NotificationService.js'; import type { ChannelsService } from './ChannelsService.js'; import type * as websocket from 'websocket'; import type { EventEmitter } from 'events'; @@ -40,6 +40,7 @@ export default class Connection { private channelsService: ChannelsService, private globalEventService: GlobalEventService, private noteReadService: NoteReadService, + private notificationService: NotificationService, wsConnection: websocket.connection, subscriber: EventEmitter, @@ -191,7 +192,7 @@ export default class Connection { private onReadNotification(payload: any) { if (!payload.id) return; - readNotification(this.user!.id, [payload.id]); + this.notificationService.readNotification(this.user!.id, [payload.id]); } /** diff --git a/packages/backend/src/services/CoreModule.ts b/packages/backend/src/services/CoreModule.ts index a8dffe0a8676..004a769d126c 100644 --- a/packages/backend/src/services/CoreModule.ts +++ b/packages/backend/src/services/CoreModule.ts @@ -51,6 +51,7 @@ import { VideoProcessingService } from './VideoProcessingService.js'; import { WebhookService } from './WebhookService.js'; import { ProxyAccountService } from './ProxyAccountService.js'; import { UtilityService } from './UtilityService.js'; +import { FileInfoService } from './FileInfoService.js'; @Module({ imports: [ @@ -107,6 +108,7 @@ import { UtilityService } from './UtilityService.js'; VideoProcessingService, WebhookService, UtilityService, + FileInfoService, ], exports: [ ChartsModule, @@ -160,6 +162,7 @@ import { UtilityService } from './UtilityService.js'; VideoProcessingService, WebhookService, UtilityService, + FileInfoService, ], }) export class CoreModule {} diff --git a/packages/backend/src/services/DriveService.ts b/packages/backend/src/services/DriveService.ts index d601a1f1e1de..f1b5645353b9 100644 --- a/packages/backend/src/services/DriveService.ts +++ b/packages/backend/src/services/DriveService.ts @@ -5,7 +5,6 @@ import sharp from 'sharp'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { DriveFiles , Users , DriveFolders , UserProfiles } from '@/models/index.js'; - import { Config } from '@/config.js'; import Logger from '@/Logger.js'; import type { IRemoteUser, User } from '@/models/entities/User.js'; @@ -16,7 +15,6 @@ import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { contentDisposition } from '@/misc/content-disposition.js'; -import { getFileInfo } from '@/misc/get-file-info.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { VideoProcessingService } from '@/services/VideoProcessingService.js'; import { ImageProcessingService } from '@/services/ImageProcessingService.js'; @@ -32,6 +30,7 @@ import { S3Service } from '@/services/S3Service.js'; import { InternalStorageService } from '@/services/InternalStorageService.js'; import { DriveFileEntityService } from './entities/DriveFileEntityService.js'; import { UserEntityService } from './entities/UserEntityService.js'; +import { FileInfoService } from './FileInfoService.js'; import type S3 from 'aws-sdk/clients/s3.js'; type AddFileArgs = { @@ -94,6 +93,7 @@ export class DriveService { @Inject('driveFoldersRepository') private driveFoldersRepository: typeof DriveFolders, + private fileInfoService: FileInfoService, private userEntityService: UserEntityService, private driveFileEntityService: DriveFileEntityService, private idService: IdService, @@ -419,7 +419,7 @@ export class DriveService { if (user && instance.sensitiveMediaDetection === 'local' && this.userEntityService.isRemoteUser(user)) skipNsfwCheck = true; if (user && instance.sensitiveMediaDetection === 'remote' && this.userEntityService.isLocalUser(user)) skipNsfwCheck = true; - const info = await getFileInfo(path, { + const info = await this.fileInfoService.getFileInfo(path, { skipSensitiveDetection: skipNsfwCheck, sensitiveThreshold: // 感度が高いほどしきい値は低くすることになる instance.sensitiveMediaDetectionSensitivity === 'veryHigh' ? 0.1 : @@ -707,7 +707,7 @@ export class DriveService { requestHeaders = null, }: UploadFromUrlArgs): Promise { let name = new URL(url).pathname.split('/').pop() || null; - if (name == null || !this.driveFilesRepository.validateFileName(name)) { + if (name == null || !this.driveFileEntityService.validateFileName(name)) { name = null; } diff --git a/packages/backend/src/services/FileInfoService.ts b/packages/backend/src/services/FileInfoService.ts new file mode 100644 index 000000000000..2630e17414d9 --- /dev/null +++ b/packages/backend/src/services/FileInfoService.ts @@ -0,0 +1,382 @@ +import * as fs from 'node:fs'; +import * as crypto from 'node:crypto'; +import { join } from 'node:path'; +import * as stream from 'node:stream'; +import * as util from 'node:util'; +import { Inject, Injectable } from '@nestjs/common'; +import { FSWatcher } from 'chokidar'; +import { fileTypeFromFile } from 'file-type'; +import FFmpeg from 'fluent-ffmpeg'; +import isSvg from 'is-svg'; +import probeImageSize from 'probe-image-size'; +import { type predictionType } from 'nsfwjs'; +import sharp from 'sharp'; +import { encode } from 'blurhash'; +import { createTempDir } from '@/misc/create-temp.js'; +import { AiService } from './AiService.js'; + +const pipeline = util.promisify(stream.pipeline); + +export type FileInfo = { + size: number; + md5: string; + type: { + mime: string; + ext: string | null; + }; + width?: number; + height?: number; + orientation?: number; + blurhash?: string; + sensitive: boolean; + porn: boolean; + warnings: string[]; +}; + +const TYPE_OCTET_STREAM = { + mime: 'application/octet-stream', + ext: null, +}; + +const TYPE_SVG = { + mime: 'image/svg+xml', + ext: 'svg', +}; +@Injectable() +export class FileInfoService { + constructor( + private aiService: AiService, + ) { + } + + /** + * Get file information + */ + public async getFileInfo(path: string, opts: { + skipSensitiveDetection: boolean; + sensitiveThreshold?: number; + sensitiveThresholdForPorn?: number; + enableSensitiveMediaDetectionForVideos?: boolean; + }): Promise { + const warnings = [] as string[]; + + const size = await this.getFileSize(path); + const md5 = await this.#calcHash(path); + + let type = await this.detectType(path); + + // image dimensions + let width: number | undefined; + let height: number | undefined; + let orientation: number | undefined; + + if (['image/jpeg', 'image/gif', 'image/png', 'image/apng', 'image/webp', 'image/bmp', 'image/tiff', 'image/svg+xml', 'image/vnd.adobe.photoshop'].includes(type.mime)) { + const imageSize = await this.#detectImageSize(path).catch(e => { + warnings.push(`detectImageSize failed: ${e}`); + return undefined; + }); + + // うまく判定できない画像は octet-stream にする + if (!imageSize) { + warnings.push('cannot detect image dimensions'); + type = TYPE_OCTET_STREAM; + } else if (imageSize.wUnits === 'px') { + width = imageSize.width; + height = imageSize.height; + orientation = imageSize.orientation; + + // 制限を超えている画像は octet-stream にする + if (imageSize.width > 16383 || imageSize.height > 16383) { + warnings.push('image dimensions exceeds limits'); + type = TYPE_OCTET_STREAM; + } + } else { + warnings.push(`unsupported unit type: ${imageSize.wUnits}`); + } + } + + let blurhash: string | undefined; + + if (['image/jpeg', 'image/gif', 'image/png', 'image/apng', 'image/webp', 'image/svg+xml'].includes(type.mime)) { + blurhash = await this.#getBlurhash(path).catch(e => { + warnings.push(`getBlurhash failed: ${e}`); + return undefined; + }); + } + + let sensitive = false; + let porn = false; + + if (!opts.skipSensitiveDetection) { + await this.#detectSensitivity( + path, + type.mime, + opts.sensitiveThreshold ?? 0.5, + opts.sensitiveThresholdForPorn ?? 0.75, + opts.enableSensitiveMediaDetectionForVideos ?? false, + ).then(value => { + [sensitive, porn] = value; + }, error => { + warnings.push(`detectSensitivity failed: ${error}`); + }); + } + + return { + size, + md5, + type, + width, + height, + orientation, + blurhash, + sensitive, + porn, + warnings, + }; + } + + async #detectSensitivity(source: string, mime: string, sensitiveThreshold: number, sensitiveThresholdForPorn: number, analyzeVideo: boolean): Promise<[sensitive: boolean, porn: boolean]> { + let sensitive = false; + let porn = false; + + function judgePrediction(result: readonly predictionType[]): [sensitive: boolean, porn: boolean] { + let sensitive = false; + let porn = false; + + if ((result.find(x => x.className === 'Sexy')?.probability ?? 0) > sensitiveThreshold) sensitive = true; + if ((result.find(x => x.className === 'Hentai')?.probability ?? 0) > sensitiveThreshold) sensitive = true; + if ((result.find(x => x.className === 'Porn')?.probability ?? 0) > sensitiveThreshold) sensitive = true; + + if ((result.find(x => x.className === 'Porn')?.probability ?? 0) > sensitiveThresholdForPorn) porn = true; + + return [sensitive, porn]; + } + + if (['image/jpeg', 'image/png', 'image/webp'].includes(mime)) { + const result = await this.aiService.detectSensitive(source); + if (result) { + [sensitive, porn] = judgePrediction(result); + } + } else if (analyzeVideo && (mime === 'image/apng' || mime.startsWith('video/'))) { + const [outDir, disposeOutDir] = await createTempDir(); + try { + const command = FFmpeg() + .input(source) + .inputOptions([ + '-skip_frame', 'nokey', // 可能ならキーフレームのみを取得してほしいとする(そうなるとは限らない) + '-lowres', '3', // 元の画質でデコードする必要はないので 1/8 画質でデコードしてもよいとする(そうなるとは限らない) + ]) + .noAudio() + .videoFilters([ + { + filter: 'select', // フレームのフィルタリング + options: { + e: 'eq(pict_type,PICT_TYPE_I)', // I-Frame のみをフィルタする(VP9 とかはデコードしてみないとわからないっぽい) + }, + }, + { + filter: 'blackframe', // 暗いフレームの検出 + options: { + amount: '0', // 暗さに関わらず全てのフレームで測定値を取る + }, + }, + { + filter: 'metadata', + options: { + mode: 'select', // フレーム選択モード + key: 'lavfi.blackframe.pblack', // フレームにおける暗部の百分率(前のフィルタからのメタデータを参照する) + value: '50', + function: 'less', // 50% 未満のフレームを選択する(50% 以上暗部があるフレームだと誤検知を招くかもしれないので) + }, + }, + { + filter: 'scale', + options: { + w: 299, + h: 299, + }, + }, + ]) + .format('image2') + .output(join(outDir, '%d.png')) + .outputOptions(['-vsync', '0']); // 可変フレームレートにすることで穴埋めをさせない + const results: ReturnType[] = []; + let frameIndex = 0; + let targetIndex = 0; + let nextIndex = 1; + for await (const path of this.#asyncIterateFrames(outDir, command)) { + try { + const index = frameIndex++; + if (index !== targetIndex) { + continue; + } + targetIndex = nextIndex; + nextIndex += index; // fibonacci sequence によってフレーム数制限を掛ける + const result = await this.aiService.detectSensitive(path); + if (result) { + results.push(judgePrediction(result)); + } + } finally { + fs.promises.unlink(path); + } + } + sensitive = results.filter(x => x[0]).length >= Math.ceil(results.length * sensitiveThreshold); + porn = results.filter(x => x[1]).length >= Math.ceil(results.length * sensitiveThresholdForPorn); + } finally { + disposeOutDir(); + } + } + + return [sensitive, porn]; + } + + async *#asyncIterateFrames(cwd: string, command: FFmpeg.FfmpegCommand): AsyncGenerator { + const watcher = new FSWatcher({ + cwd, + disableGlobbing: true, + }); + let finished = false; + command.once('end', () => { + finished = true; + watcher.close(); + }); + command.run(); + for (let i = 1; true; i++) { // eslint-disable-line @typescript-eslint/no-unnecessary-condition + const current = `${i}.png`; + const next = `${i + 1}.png`; + const framePath = join(cwd, current); + if (await this.#exists(join(cwd, next))) { + yield framePath; + } else if (!finished) { // eslint-disable-line @typescript-eslint/no-unnecessary-condition + watcher.add(next); + await new Promise((resolve, reject) => { + watcher.on('add', function onAdd(path) { + if (path === next) { // 次フレームの書き出しが始まっているなら、現在フレームの書き出しは終わっている + watcher.unwatch(current); + watcher.off('add', onAdd); + resolve(); + } + }); + command.once('end', resolve); // 全てのフレームを処理し終わったなら、最終フレームである現在フレームの書き出しは終わっている + command.once('error', reject); + }); + yield framePath; + } else if (await this.#exists(framePath)) { + yield framePath; + } else { + return; + } + } + } + + #exists(path: string): Promise { + return fs.promises.access(path).then(() => true, () => false); + } + + /** + * Detect MIME Type and extension + */ + public async detectType(path: string): Promise<{ + mime: string; + ext: string | null; +}> { + // Check 0 byte + const fileSize = await this.getFileSize(path); + if (fileSize === 0) { + return TYPE_OCTET_STREAM; + } + + const type = await fileTypeFromFile(path); + + if (type) { + // XMLはSVGかもしれない + if (type.mime === 'application/xml' && await this.checkSvg(path)) { + return TYPE_SVG; + } + + return { + mime: type.mime, + ext: type.ext, + }; + } + + // 種類が不明でもSVGかもしれない + if (await this.checkSvg(path)) { + return TYPE_SVG; + } + + // それでも種類が不明なら application/octet-stream にする + return TYPE_OCTET_STREAM; + } + + /** + * Check the file is SVG or not + */ + public async checkSvg(path: string) { + try { + const size = await this.getFileSize(path); + if (size > 1 * 1024 * 1024) return false; + return isSvg(fs.readFileSync(path)); + } catch { + return false; + } + } + + /** + * Get file size + */ + public async getFileSize(path: string): Promise { + const getStat = util.promisify(fs.stat); + return (await getStat(path)).size; + } + + /** + * Calculate MD5 hash + */ + async #calcHash(path: string): Promise { + const hash = crypto.createHash('md5').setEncoding('hex'); + await pipeline(fs.createReadStream(path), hash); + return hash.read(); + } + + /** + * Detect dimensions of image + */ + async #detectImageSize(path: string): Promise<{ + width: number; + height: number; + wUnits: string; + hUnits: string; + orientation?: number; +}> { + const readable = fs.createReadStream(path); + const imageSize = await probeImageSize(readable); + readable.destroy(); + return imageSize; + } + + /** + * Calculate average color of image + */ + #getBlurhash(path: string): Promise { + return new Promise((resolve, reject) => { + sharp(path) + .raw() + .ensureAlpha() + .resize(64, 64, { fit: 'inside' }) + .toBuffer((err, buffer, { width, height }) => { + if (err) return reject(err); + + let hash; + + try { + hash = encode(new Uint8ClampedArray(buffer), width, height, 7, 7); + } catch (e) { + return reject(e); + } + + resolve(hash); + }); + }); + } +} diff --git a/packages/backend/src/services/UserCacheService.ts b/packages/backend/src/services/UserCacheService.ts index 6839a3352647..e605b74e0cdb 100644 --- a/packages/backend/src/services/UserCacheService.ts +++ b/packages/backend/src/services/UserCacheService.ts @@ -3,7 +3,7 @@ import Redis from 'ioredis'; import type { Users } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; import type { CacheableLocalUser, CacheableUser, ILocalUser } from '@/models/entities/User.js'; -import { UserEntityService } from './entities/UserEntityService'; +import { UserEntityService } from './entities/UserEntityService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() From 80bd586eafe407ba9e761b0f0901b2e478d3dfe4 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 10:28:56 +0900 Subject: [PATCH 145/180] Update DownloadService.ts --- packages/backend/src/services/DownloadService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/services/DownloadService.ts b/packages/backend/src/services/DownloadService.ts index 31d4206b50e8..89ef8a373953 100644 --- a/packages/backend/src/services/DownloadService.ts +++ b/packages/backend/src/services/DownloadService.ts @@ -10,7 +10,7 @@ import { DI } from '@/di-symbols.js'; import { Config } from '@/config.js'; import Logger from '@/logger.js'; import { HttpRequestService } from '@/services/HttpRequestService.js'; -import { createTemp } from '@/misc/create-temp'; +import { createTemp } from '@/misc/create-temp.js'; import { StatusError } from '@/misc/status-error.js'; const pipeline = util.promisify(stream.pipeline); From 99c0d359ca804d42ffb929654ea20383c384e6d1 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 10:29:43 +0900 Subject: [PATCH 146/180] Update VideoProcessingService.ts --- packages/backend/src/services/VideoProcessingService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/services/VideoProcessingService.ts b/packages/backend/src/services/VideoProcessingService.ts index 9391099c9dcf..2664996d04f1 100644 --- a/packages/backend/src/services/VideoProcessingService.ts +++ b/packages/backend/src/services/VideoProcessingService.ts @@ -4,7 +4,7 @@ import { DI } from '@/di-symbols.js'; import { Config } from '@/config.js'; import { ImageProcessingService } from '@/services/ImageProcessingService.js'; import type { IImage } from '@/services/ImageProcessingService.js'; -import { createTempDir } from '@/misc/create-temp'; +import { createTempDir } from '@/misc/create-temp.js'; @Injectable() export class VideoProcessingService { From 13041c4dd15b3bd6375ce935bfd369a35dc5463b Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 10:30:36 +0900 Subject: [PATCH 147/180] Update MessagingService.ts --- packages/backend/src/services/MessagingService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/services/MessagingService.ts b/packages/backend/src/services/MessagingService.ts index 311b5acf7d38..b9e15956fa9b 100644 --- a/packages/backend/src/services/MessagingService.ts +++ b/packages/backend/src/services/MessagingService.ts @@ -9,7 +9,7 @@ import type { Note } from '@/models/entities/Note.js'; import type { User, CacheableUser, IRemoteUser } from '@/models/entities/User.js'; import type { UserGroup } from '@/models/entities/UserGroup.js'; import { QueueService } from '@/queue/queue.service.js'; -import { toArray } from '@/prelude/array'; +import { toArray } from '@/prelude/array.js'; import { IdentifiableError } from '@/misc/identifiable-error'; import { IdService } from './IdService.js'; import { GlobalEventService } from './GlobalEventService.js'; From a26b7e0a2440f22d43784392c65766bcb137d6d8 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 10:31:33 +0900 Subject: [PATCH 148/180] Update MessagingService.ts --- packages/backend/src/services/MessagingService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/services/MessagingService.ts b/packages/backend/src/services/MessagingService.ts index b9e15956fa9b..e1f2eb9ec9f3 100644 --- a/packages/backend/src/services/MessagingService.ts +++ b/packages/backend/src/services/MessagingService.ts @@ -10,7 +10,7 @@ import type { User, CacheableUser, IRemoteUser } from '@/models/entities/User.js import type { UserGroup } from '@/models/entities/UserGroup.js'; import { QueueService } from '@/queue/queue.service.js'; import { toArray } from '@/prelude/array.js'; -import { IdentifiableError } from '@/misc/identifiable-error'; +import { IdentifiableError } from '@/misc/identifiable-error.js'; import { IdService } from './IdService.js'; import { GlobalEventService } from './GlobalEventService.js'; import { UserEntityService } from './entities/UserEntityService.js'; From a8950f03ce457e6a4a604e117c268bd8249bfbb3 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 10:36:09 +0900 Subject: [PATCH 149/180] wip --- .../backend/src/server/api/RateLimiterService.ts | 4 ++-- .../src/server/api/endpoints/blocking/create.ts | 16 ++++++---------- .../src/server/api/endpoints/blocking/delete.ts | 10 ++++++---- packages/backend/src/services/chart/core.ts | 2 +- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/packages/backend/src/server/api/RateLimiterService.ts b/packages/backend/src/server/api/RateLimiterService.ts index 38e4faa2a0fc..d390a47b8fa5 100644 --- a/packages/backend/src/server/api/RateLimiterService.ts +++ b/packages/backend/src/server/api/RateLimiterService.ts @@ -2,8 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import Limiter from 'ratelimiter'; import Redis from 'ioredis'; import { DI } from '@/di-symbols.js'; -import Logger from '@/logger'; -import type { IEndpointMeta } from './endpoints'; +import Logger from '@/logger.js'; +import type { IEndpointMeta } from './endpoints.js'; const logger = new Logger('limiter'); diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts index eefa1d21d029..031d9375af98 100644 --- a/packages/backend/src/server/api/endpoints/blocking/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking/create.ts @@ -1,12 +1,11 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import create from '@/services/blocking/create.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users , Blockings } from '@/models/index.js'; -import { NoteWatchings } from '@/models/index.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { UserBlockingService } from '@/services/UserBlockingService.js'; import { ApiError } from '../../error.js'; -import { getUser } from '../../common/getters.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['account'], @@ -66,6 +65,8 @@ export default class extends Endpoint { private blockingsRepository: typeof Blockings, private userEntityService: UserEntityService, + private getterService: GetterService, + private userBlockingService: UserBlockingService, ) { super(meta, paramDef, async (ps, me) => { const blocker = await this.usersRepository.findOneByOrFail({ id: me.id }); @@ -76,7 +77,7 @@ export default class extends Endpoint { } // Get blockee - const blockee = await getUser(ps.userId).catch(err => { + const blockee = await this.getterService.getUser(ps.userId).catch(err => { if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); throw err; }); @@ -91,12 +92,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.alreadyBlocking); } - await create(blocker, blockee); - - NoteWatchings.delete({ - userId: blocker.id, - noteUserId: blockee.id, - }); + await this.userBlockingService.block(blocker, blockee); return await this.userEntityService.pack(blockee.id, blocker, { detail: true, diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts index 989cb1998c90..d1f91d2096cf 100644 --- a/packages/backend/src/server/api/endpoints/blocking/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts @@ -1,11 +1,11 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import deleteBlocking from '@/services/blocking/delete.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users , Blockings } from '@/models/index.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { UserBlockingService } from '@/services/UserBlockingService.js'; import { ApiError } from '../../error.js'; -import { getUser } from '../../common/getters.js'; +import { GetterService } from '../../common/GetterService.js'; export const meta = { tags: ['account'], @@ -65,6 +65,8 @@ export default class extends Endpoint { private blockingsRepository: typeof Blockings, private userEntityService: UserEntityService, + private getterService: GetterService, + private userBlockingService: UserBlockingService, ) { super(meta, paramDef, async (ps, me) => { const blocker = await this.usersRepository.findOneByOrFail({ id: me.id }); @@ -75,7 +77,7 @@ export default class extends Endpoint { } // Get blockee - const blockee = await getUser(ps.userId).catch(err => { + const blockee = await this.getterService.getUser(ps.userId).catch(err => { if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); throw err; }); @@ -91,7 +93,7 @@ export default class extends Endpoint { } // Delete blocking - await deleteBlocking(blocker, blockee); + await this.userBlockingService.unblock(blocker, blockee); return await this.userEntityService.pack(blockee.id, blocker, { detail: true, diff --git a/packages/backend/src/services/chart/core.ts b/packages/backend/src/services/chart/core.ts index ff2c9aa4dca4..40301b6eba14 100644 --- a/packages/backend/src/services/chart/core.ts +++ b/packages/backend/src/services/chart/core.ts @@ -7,7 +7,7 @@ import * as nestedProperty from 'nested-property'; import { EntitySchema, LessThan, Between } from 'typeorm'; import { dateUTC, isTimeSame, isTimeBefore, subtractTime, addTime } from '@/prelude/time.js'; -import Logger from '../logger.js'; +import Logger from '@/logger.js'; import type { Repository, DataSource } from 'typeorm'; const logger = new Logger('chart', 'white', process.env.NODE_ENV !== 'test'); From 0629504f27e1ff4303c49d008379169b3d3469a2 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 10:37:28 +0900 Subject: [PATCH 150/180] Update timeline.ts --- .../backend/src/server/api/endpoints/channels/timeline.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index 31f2b01779ea..d0ad7f0686d1 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -2,9 +2,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Notes } from '@/models/index.js'; import { Channels } from '@/models/index.js'; -import { activeUsersChart } from '@/services/chart/index.js'; import { QueryService } from '@/services/QueryService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import ActiveUsersChart from '@/services/chart/charts/active-users.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -53,6 +53,7 @@ export default class extends Endpoint { private noteEntityService: NoteEntityService, private queryService: QueryService, + private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { const channel = await Channels.findOneBy({ @@ -82,7 +83,7 @@ export default class extends Endpoint { const timeline = await query.take(ps.limit).getMany(); - if (me) activeUsersChart.read(me); + if (me) this.activeUsersChart.read(me); return await this.noteEntityService.packMany(timeline, me); }); From 4e36e5f9d7b14d982ffa0554c13cf5b2e00532ff Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 10:40:29 +0900 Subject: [PATCH 151/180] Update NoteCreateService.ts --- .../backend/src/services/NoteCreateService.ts | 33 ++----------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/packages/backend/src/services/NoteCreateService.ts b/packages/backend/src/services/NoteCreateService.ts index 160eaaab3455..742da84fed6f 100644 --- a/packages/backend/src/services/NoteCreateService.ts +++ b/packages/backend/src/services/NoteCreateService.ts @@ -38,7 +38,6 @@ import { WebhookService } from '@/services/WebhookService.js'; import { HashtagService } from '@/services/HashtagService.js'; import { AntennaService } from '@/services/AntennaService.js'; import { QueueService } from '@/queue/queue.service.js'; -import es from '../db/elasticsearch.js'; import { NoteEntityService } from './entities/NoteEntityService.js'; import { UserEntityService } from './entities/UserEntityService.js'; import { NoteReadService } from './NoteReadService.js'; @@ -516,9 +515,6 @@ export class NoteCreateService { // If has in reply to note if (data.reply) { - // Fetch watchers - nmRelatedPromises.push(this.#notifyToWatchersOfReplyee(data.reply, user, nm)); - // 通知 if (data.reply.userHost === null) { const threadMuted = await NoteThreadMutings.findOneBy({ @@ -549,9 +545,6 @@ export class NoteCreateService { nm.push(data.renote.userId, type); } - // Fetch watchers - nmRelatedPromises.push(this.#notifyToWatchersOfRenotee(data.renote, user, nm, type)); - // Publish event if ((user.id !== data.renote.userId) && data.renote.userHost === null) { this.globalEventServie.publishMainStream(data.renote.userId, 'renote', noteObj); @@ -684,7 +677,7 @@ export class NoteCreateService { #index(note: Note) { if (note.text == null || this.config.elasticsearch == null) return; - + /* es!.index({ index: this.config.elasticsearch.index || 'misskey_note', id: note.id.toString(), @@ -693,29 +686,7 @@ export class NoteCreateService { userId: note.userId, userHost: note.userHost, }, - }); - } - - async #notifyToWatchersOfRenotee(renote: Note, user: { id: User['id']; }, nm: NotificationManager, type: NotificationType) { - const watchers = await NoteWatchings.findBy({ - noteId: renote.id, - userId: Not(user.id), - }); - - for (const watcher of watchers) { - nm.push(watcher.userId, type); - } - } - - async #notifyToWatchersOfReplyee(reply: Note, user: { id: User['id']; }, nm: NotificationManager) { - const watchers = await NoteWatchings.findBy({ - noteId: reply.id, - userId: Not(user.id), - }); - - for (const watcher of watchers) { - nm.push(watcher.userId, 'reply'); - } + });*/ } #incNotesCountOfUser(user: { id: User['id']; }) { From 74901975b2afb4215f581661e569751cdd54ab13 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 10:41:44 +0900 Subject: [PATCH 152/180] Update update.ts --- .../backend/src/server/api/endpoints/drive/files/update.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/drive/files/update.ts b/packages/backend/src/server/api/endpoints/drive/files/update.ts index 354e9d4dc348..a6108e9823a6 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts @@ -1,10 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; -import { publishDriveStream } from '@/services/stream.js'; import type { DriveFiles , DriveFolders } from '@/models/index.js'; import { Users } from '@/models/index.js'; import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -72,6 +72,7 @@ export default class extends Endpoint { private driveFoldersRepository: typeof DriveFolders, private driveFileEntityService: DriveFileEntityService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { const file = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); @@ -120,7 +121,7 @@ export default class extends Endpoint { const fileObj = await this.driveFileEntityService.pack(file, { self: true }); // Publish fileUpdated event - publishDriveStream(me.id, 'fileUpdated', fileObj); + this.globalEventService.publishDriveStream(me.id, 'fileUpdated', fileObj); return fileObj; }); From 3cbe5600d5bf971a2cdf1d0d49c27e1455ed8bf6 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 10:44:06 +0900 Subject: [PATCH 153/180] Update upload-from-url.ts --- .../endpoints/drive/files/upload-from-url.ts | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts b/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts index 652f730b336d..5c58017525a6 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts @@ -1,10 +1,11 @@ import ms from 'ms'; -import { uploadFromUrl } from '@/services/drive/upload-from-url.js'; -import { DriveFiles } from '@/models/index.js'; -import { publishMainStream } from '@/services/stream.js'; -import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js'; import { Inject, Injectable } from '@nestjs/common'; +import type { DriveFiles } from '@/models/index.js'; +import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; +import { DriveService } from '@/services/DriveService.js'; export const meta = { tags: ['drive'], @@ -35,13 +36,25 @@ export const paramDef = { } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, paramDef, async (ps, user, _1, _2, _3, ip, headers) => { - uploadFromUrl({ url: ps.url, user, folderId: ps.folderId, sensitive: ps.isSensitive, force: ps.force, comment: ps.comment, requestIp: ip, requestHeaders: headers }).then(file => { - DriveFiles.pack(file, { self: true }).then(packedFile => { - publishMainStream(user.id, 'urlUploadFinished', { - marker: ps.marker, - file: packedFile, +@Injectable() +export default class extends Endpoint { + constructor( + @Inject('driveFilesRepository') + private driveFilesRepository: typeof DriveFiles, + + private driveFileEntityService: DriveFileEntityService, + private driveService: DriveService, + private globalEventService: GlobalEventService, + ) { + super(meta, paramDef, async (ps, user, _1, _2, _3, ip, headers) => { + this.driveService.uploadFromUrl({ url: ps.url, user, folderId: ps.folderId, sensitive: ps.isSensitive, force: ps.force, comment: ps.comment, requestIp: ip, requestHeaders: headers }).then(file => { + this.driveFileEntityService.pack(file, { self: true }).then(packedFile => { + this.globalEventService.publishMainStream(user.id, 'urlUploadFinished', { + marker: ps.marker, + file: packedFile, + }); + }); }); }); - }); -}); + } +} From c0c4587e280f2e3a67e8dd0265c9d7df3113ad26 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 10:45:16 +0900 Subject: [PATCH 154/180] Update update.ts --- .../backend/src/server/api/endpoints/drive/folders/update.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/drive/folders/update.ts b/packages/backend/src/server/api/endpoints/drive/folders/update.ts index 4c19c27c98e2..07013b010ce8 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/update.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; -import { publishDriveStream } from '@/services/stream.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFolders } from '@/models/index.js'; import { DriveFolderEntityService } from '@/services/entities/DriveFolderEntityService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -57,6 +57,7 @@ export default class extends Endpoint { private driveFoldersRepository: typeof DriveFolders, private driveFolderEntityService: DriveFolderEntityService, + private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { // Fetch folder @@ -122,7 +123,7 @@ export default class extends Endpoint { const folderObj = await this.driveFolderEntityService.pack(folder); // Publish folderUpdated event - publishDriveStream(me.id, 'folderUpdated', folderObj); + this.globalEventService.publishDriveStream(me.id, 'folderUpdated', folderObj); return folderObj; }); From dc96a441513f7880638d4dba89ecb7d1766d97b3 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 10:47:03 +0900 Subject: [PATCH 155/180] wip --- packages/backend/src/server/api/endpoints/i/notifications.ts | 5 +++-- packages/backend/src/server/api/endpoints/i/unpin.ts | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index aa39434b4479..996ae21b43b0 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -7,7 +7,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { NoteReadService } from '@/services/NoteReadService.js'; import { NotificationEntityService } from '@/services/entities/NotificationEntityService.js'; -import { readNotification } from '../../common/read-notification.js'; +import { NotificationService } from '@/services/NotificationService.js'; export const meta = { tags: ['account', 'notifications'], @@ -68,6 +68,7 @@ export default class extends Endpoint { private userProfilesRepository: typeof UserProfiles, private notificationEntityService: NotificationEntityService, + private notificationService: NotificationService, private queryService: QueryService, private noteReadService: NoteReadService, ) { @@ -153,7 +154,7 @@ export default class extends Endpoint { // Mark all as read if (notifications.length > 0 && ps.markAsRead) { - readNotification(me.id, notifications.map(x => x.id)); + this.notificationService.readNotification(me.id, notifications.map(x => x.id)); } const notes = notifications.filter(notification => ['mention', 'reply', 'quote'].includes(notification.type)).map(notification => notification.note!); diff --git a/packages/backend/src/server/api/endpoints/i/unpin.ts b/packages/backend/src/server/api/endpoints/i/unpin.ts index 3b940af78708..2473d40530fb 100644 --- a/packages/backend/src/server/api/endpoints/i/unpin.ts +++ b/packages/backend/src/server/api/endpoints/i/unpin.ts @@ -1,5 +1,4 @@ import { Inject, Injectable } from '@nestjs/common'; -import { removePinned } from '@/services/i/pin.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; From d2d67cdbd7b4631fb40978c4a74ac3c192e57c59 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 10:48:37 +0900 Subject: [PATCH 156/180] Update messages.ts --- .../src/server/api/endpoints/messaging/messages.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/messaging/messages.ts b/packages/backend/src/server/api/endpoints/messaging/messages.ts index a1553f47ee75..5d8256ef93db 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages.ts @@ -6,8 +6,8 @@ import { UserGroups } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { MessagingMessageEntityService } from '@/services/entities/MessagingMessageEntityService.js'; +import { MessagingService } from '@/services/MessagingService.js'; import { ApiError } from '../../error.js'; -import { readUserMessagingMessage, readGroupMessagingMessage, deliverReadActivity } from '../../common/read-messaging-message.js'; import { GetterService } from '../../common/GetterService.js'; export const meta = { @@ -83,6 +83,7 @@ export default class extends Endpoint { private userGroupJoiningsRepository: typeof UserGroupJoinings, private messagingMessageEntityService: MessagingMessageEntityService, + private messagingService: MessagingService, private userEntityService: UserEntityService, private queryService: QueryService, private getterService: GetterService, @@ -113,11 +114,11 @@ export default class extends Endpoint { // Mark all as read if (ps.markAsRead) { - readUserMessagingMessage(me.id, recipient.id, messages.filter(m => m.recipientId === me.id).map(x => x.id)); + this.messagingService.readUserMessagingMessage(me.id, recipient.id, messages.filter(m => m.recipientId === me.id).map(x => x.id)); // リモートユーザーとのメッセージだったら既読配信 if (this.userEntityService.isLocalUser(me) && this.userEntityService.isRemoteUser(recipient)) { - deliverReadActivity(me, recipient, messages); + this.messagingService.deliverReadActivity(me, recipient, messages); } } @@ -149,7 +150,7 @@ export default class extends Endpoint { // Mark all as read if (ps.markAsRead) { - readGroupMessagingMessage(me.id, recipientGroup.id, messages.map(x => x.id)); + this.messagingService.readGroupMessagingMessage(me.id, recipientGroup.id, messages.map(x => x.id)); } return await Promise.all(messages.map(message => this.messagingMessageEntityService.pack(message, me, { From cab46f87833b13f2666600c3f66283d25d86e138 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 10:50:33 +0900 Subject: [PATCH 157/180] Update messaging.ts --- .../src/server/api/stream/channels/messaging.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/server/api/stream/channels/messaging.ts b/packages/backend/src/server/api/stream/channels/messaging.ts index bd1a5ce865fb..50e43acd57f8 100644 --- a/packages/backend/src/server/api/stream/channels/messaging.ts +++ b/packages/backend/src/server/api/stream/channels/messaging.ts @@ -2,8 +2,9 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserGroupJoinings , Users , MessagingMessages } from '@/models/index.js'; import type { User, ILocalUser, IRemoteUser } from '@/models/entities/User.js'; import type { UserGroup } from '@/models/entities/UserGroup.js'; +import { MessagingService } from '@/services/MessagingService.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; import Channel from '../channel.js'; -import { readUserMessagingMessage, readGroupMessagingMessage, deliverReadActivity } from '../../common/read-messaging-message.js'; import type { StreamMessages } from '../types.js'; class MessagingChannel extends Channel { @@ -22,6 +23,8 @@ class MessagingChannel extends Channel { private usersRepository: typeof Users, private userGroupJoiningsRepository: typeof UserGroupJoinings, private messagingMessagesRepository: typeof MessagingMessages, + private userEntityService: UserEntityService, + private messagingService: MessagingService, id: string, connection: Channel['connection'], @@ -76,16 +79,16 @@ class MessagingChannel extends Channel { switch (type) { case 'read': if (this.otherpartyId) { - readUserMessagingMessage(this.user!.id, this.otherpartyId, [body.id]); + this.messagingService.readUserMessagingMessage(this.user!.id, this.otherpartyId, [body.id]); // リモートユーザーからのメッセージだったら既読配信 if (this.userEntityService.isLocalUser(this.user!) && this.userEntityService.isRemoteUser(this.otherparty!)) { this.messagingMessagesRepository.findOneBy({ id: body.id }).then(message => { - if (message) deliverReadActivity(this.user as ILocalUser, this.otherparty as IRemoteUser, message); + if (message) this.messagingService.deliverReadActivity(this.user as ILocalUser, this.otherparty as IRemoteUser, message); }); } } else if (this.groupId) { - readGroupMessagingMessage(this.user!.id, this.groupId, [body.id]); + this.messagingService.readGroupMessagingMessage(this.user!.id, this.groupId, [body.id]); } break; } @@ -128,6 +131,9 @@ export class MessagingChannelService { @Inject('messagingMessagesRepository') private messagingMessagesRepository: typeof MessagingMessages, + + private userEntityService: UserEntityService, + private messagingService: MessagingService, ) { } @@ -136,6 +142,8 @@ export class MessagingChannelService { this.usersRepository, this.userGroupJoiningsRepository, this.messagingMessagesRepository, + this.userEntityService, + this.messagingService, id, connection, ); From 57a0dfcb8736cc4424ecd0ef32cc1752519f34e3 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 10:52:20 +0900 Subject: [PATCH 158/180] Update search.ts --- .../src/server/api/endpoints/notes/search.ts | 117 ++++-------------- 1 file changed, 27 insertions(+), 90 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index 997c31a19f7a..8809ebfdc75a 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -6,7 +6,6 @@ import { QueryService } from '@/services/QueryService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; -import es from '../../../../db/elasticsearch.js'; export const meta = { tags: ['notes'], @@ -62,97 +61,35 @@ export default class extends Endpoint { private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - if (es == null) { - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId); + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId); - if (ps.userId) { - query.andWhere('note.userId = :userId', { userId: ps.userId }); - } else if (ps.channelId) { - query.andWhere('note.channelId = :channelId', { channelId: ps.channelId }); - } - - query - .andWhere('note.text ILIKE :q', { q: `%${ps.query}%` }) - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('user.avatar', 'avatar') - .leftJoinAndSelect('user.banner', 'banner') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') - .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') - .leftJoinAndSelect('renote.user', 'renoteUser') - .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') - .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); - - this.queryService.generateVisibilityQuery(query, me); - if (me) this.queryService.generateMutedUserQuery(query, me); - if (me) this.queryService.generateBlockedUserQuery(query, me); - - const notes = await query.take(ps.limit).getMany(); - - return await this.noteEntityService.packMany(notes, me); - } else { - const userQuery = ps.userId != null ? [{ - term: { - userId: ps.userId, - }, - }] : []; - - const hostQuery = ps.userId == null ? - ps.host === null ? [{ - bool: { - must_not: { - exists: { - field: 'userHost', - }, - }, - }, - }] : ps.host !== undefined ? [{ - term: { - userHost: ps.host, - }, - }] : [] - : []; - - const result = await es.search({ - index: this.config.elasticsearch.index || 'misskey_note', - body: { - size: ps.limit, - from: ps.offset, - query: { - bool: { - must: [{ - simple_query_string: { - fields: ['text'], - query: ps.query.toLowerCase(), - default_operator: 'and', - }, - }, ...hostQuery, ...userQuery], - }, - }, - sort: [{ - _doc: 'desc', - }], - }, - }); - - const hits = result.body.hits.hits.map((hit: any) => hit._id); - - if (hits.length === 0) return []; - - // Fetch found notes - const notes = await this.notesRepository.find({ - where: { - id: In(hits), - }, - order: { - id: -1, - }, - }); - - return await this.noteEntityService.packMany(notes, me); + if (ps.userId) { + query.andWhere('note.userId = :userId', { userId: ps.userId }); + } else if (ps.channelId) { + query.andWhere('note.channelId = :channelId', { channelId: ps.channelId }); } + + query + .andWhere('note.text ILIKE :q', { q: `%${ps.query}%` }) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('user.avatar', 'avatar') + .leftJoinAndSelect('user.banner', 'banner') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar') + .leftJoinAndSelect('replyUser.banner', 'replyUserBanner') + .leftJoinAndSelect('renote.user', 'renoteUser') + .leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar') + .leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner'); + + this.queryService.generateVisibilityQuery(query, me); + if (me) this.queryService.generateMutedUserQuery(query, me); + if (me) this.queryService.generateBlockedUserQuery(query, me); + + const notes = await query.take(ps.limit).getMany(); + + return await this.noteEntityService.packMany(notes, me); }); } } From 16dafb2f070f156510fbbf901e33a83c939ccc98 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 10:53:32 +0900 Subject: [PATCH 159/180] Update timeline.ts --- packages/backend/src/server/api/endpoints/notes/timeline.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 417b58e3b2b0..28a884a4cc27 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -3,7 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notes , Followings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; -import ActiveUsersChart from '@/services/chart/charts/active-users'; +import ActiveUsersChart from '@/services/chart/charts/active-users.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { MetaService } from '@/services/MetaService.js'; From d03066e1e8269b2e66a94bb0553e461d15919d9e Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 10:54:37 +0900 Subject: [PATCH 160/180] Update state.ts --- .../backend/src/server/api/endpoints/notes/state.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/state.ts b/packages/backend/src/server/api/endpoints/notes/state.ts index ab9333142f3c..b9703e2898be 100644 --- a/packages/backend/src/server/api/endpoints/notes/state.ts +++ b/packages/backend/src/server/api/endpoints/notes/state.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notes , NoteThreadMutings } from '@/models/index.js'; -import { NoteFavorites, NoteWatchings } from '@/models/index.js'; +import { NoteFavorites } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; export const meta = { @@ -49,7 +49,7 @@ export default class extends Endpoint { super(meta, paramDef, async (ps, me) => { const note = await this.notesRepository.findOneByOrFail({ id: ps.noteId }); - const [favorite, watching, threadMuting] = await Promise.all([ + const [favorite, threadMuting] = await Promise.all([ NoteFavorites.count({ where: { userId: me.id, @@ -57,13 +57,6 @@ export default class extends Endpoint { }, take: 1, }), - NoteWatchings.count({ - where: { - userId: me.id, - noteId: note.id, - }, - take: 1, - }), this.noteThreadMutingsRepository.count({ where: { userId: me.id, @@ -75,7 +68,6 @@ export default class extends Endpoint { return { isFavorited: favorite !== 0, - isWatching: watching !== 0, isMutedThread: threadMuting !== 0, }; }); From fa2a0866083a9798da45fa7b01c62cd4c4a78880 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 11:02:53 +0900 Subject: [PATCH 161/180] Update config.ts --- packages/backend/src/config.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index c3b8ae084a37..052991429e2c 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -101,7 +101,7 @@ const _dirname = dirname(_filename); /** * Path of configuration directory */ -const dir = `${_dirname}/../../../../.config`; +const dir = `${_dirname}/../../../.config`; /** * Path of configuration file @@ -111,8 +111,8 @@ const path = process.env.NODE_ENV === 'test' : `${dir}/default.yml`; export function loadConfig() { - const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/meta.json`, 'utf-8')); - const clientManifest = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/_client_dist_/manifest.json`, 'utf-8')); + const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../built/meta.json`, 'utf-8')); + const clientManifest = JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_client_dist_/manifest.json`, 'utf-8')); const config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source; const mixin = {} as Mixin; From dd5e3a590fb6caa5c774e371afe6ddf71ae35dec Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 22:00:01 +0900 Subject: [PATCH 162/180] wip --- packages/backend/src/GlobalModule.ts | 35 + packages/backend/src/RepositoryModule.ts | 636 ++++++++++++------ packages/backend/src/app.module.ts | 16 +- packages/backend/src/db/redis.ts | 4 +- packages/backend/src/di-symbols.ts | 1 + packages/backend/src/misc/antenna-cache.ts | 36 - .../src/models/entities/Notification.ts | 2 +- packages/backend/src/queue/index.ts | 2 +- packages/backend/src/queue/initialize.ts | 36 - packages/backend/src/queue/queue.module.ts | 29 - .../src/server/ActivityPubServerService.ts | 2 +- .../backend/src/server/api/EndpointsModule.ts | 2 + .../api/endpoints/admin/accounts/delete.ts | 2 +- .../admin/drive/clean-remote-files.ts | 2 +- .../api/endpoints/admin/emoji/import-zip.ts | 2 +- .../server/api/endpoints/admin/queue/clear.ts | 2 +- .../endpoints/admin/queue/deliver-delayed.ts | 2 +- .../endpoints/admin/queue/inbox-delayed.ts | 2 +- .../server/api/endpoints/admin/queue/stats.ts | 2 +- .../admin/resolve-abuse-user-report.ts | 2 +- .../api/endpoints/export-custom-emojis.ts | 2 +- .../server/api/endpoints/i/export-blocking.ts | 2 +- .../api/endpoints/i/export-following.ts | 2 +- .../src/server/api/endpoints/i/export-mute.ts | 2 +- .../server/api/endpoints/i/export-notes.ts | 2 +- .../api/endpoints/i/export-user-lists.ts | 2 +- .../server/api/endpoints/i/import-blocking.ts | 2 +- .../api/endpoints/i/import-following.ts | 2 +- .../server/api/endpoints/i/import-muting.ts | 2 +- .../api/endpoints/i/import-user-lists.ts | 2 +- .../server/api/endpoints/notes/polls/vote.ts | 2 +- .../src/server/web/ClientServerService.ts | 2 +- .../backend/src/services/AntennaService.ts | 50 +- packages/backend/src/services/CoreModule.ts | 194 +++++- .../src/services/DeleteAccountService.ts | 2 +- packages/backend/src/services/DriveService.ts | 2 +- .../backend/src/services/MessagingService.ts | 2 +- .../backend/src/services/NoteCreateService.ts | 5 +- .../backend/src/services/NoteReadService.ts | 3 +- .../QueueService.ts} | 4 +- packages/backend/src/services/RelayService.ts | 2 +- .../src/services/UserBlockingService.ts | 2 +- .../backend/src/services/UserCacheService.ts | 3 +- .../src/services/UserFollowingService.ts | 2 +- .../backend/src/services/UserMutingService.ts | 2 +- .../src/services/UserSuspendService.ts | 2 +- .../backend/src/services/WebhookService.ts | 3 +- .../src/services/chart/ChartsModule.ts | 36 - .../entities/DriveFolderEntityService.ts | 2 - .../src/services/entities/EntityModule.ts | 69 -- .../services/entities/NoteEntityService.ts | 31 +- .../entities/NoteReactionEntityService.ts | 22 +- .../entities/NotificationEntityService.ts | 28 +- .../services/entities/UserEntityService.ts | 37 +- .../backend/src/services/queue/QueueModule.ts | 112 +++ .../activitypub/ApDeliverManagerService.ts | 2 +- .../remote/activitypub/ApInboxService.ts | 2 +- .../remote/activitypub/ApRendererService.ts | 10 +- .../activitypub/models/ApPersonService.ts | 105 ++- 59 files changed, 1032 insertions(+), 543 deletions(-) create mode 100644 packages/backend/src/GlobalModule.ts delete mode 100644 packages/backend/src/misc/antenna-cache.ts delete mode 100644 packages/backend/src/queue/initialize.ts delete mode 100644 packages/backend/src/queue/queue.module.ts rename packages/backend/src/{queue/queue.service.ts => services/QueueService.ts} (98%) delete mode 100644 packages/backend/src/services/chart/ChartsModule.ts delete mode 100644 packages/backend/src/services/entities/EntityModule.ts create mode 100644 packages/backend/src/services/queue/QueueModule.ts diff --git a/packages/backend/src/GlobalModule.ts b/packages/backend/src/GlobalModule.ts new file mode 100644 index 000000000000..0e10e972330a --- /dev/null +++ b/packages/backend/src/GlobalModule.ts @@ -0,0 +1,35 @@ +import { Global, Module } from '@nestjs/common'; +import { DI } from './di-symbols.js'; +import { loadConfig } from './config.js'; +import { db } from './db/postgre.js'; +import { redisClient, redisSubscriber } from './db/redis.js'; +import { RepositoryModule } from './RepositoryModule.js'; +import type { Provider } from '@nestjs/common'; + +const $config: Provider = { + provide: DI.config, + useValue: loadConfig(), +}; + +const $db: Provider = { + provide: DI.db, + useValue: db, +}; + +const $redis: Provider = { + provide: DI.redis, + useValue: redisClient, +}; + +const $redisSubscriber: Provider = { + provide: DI.redisSubscriber, + useValue: redisSubscriber, +}; + +@Global() +@Module({ + imports: [RepositoryModule], + providers: [$config, $db, $redis, $redisSubscriber], + exports: [$config, $db, $redis, $redisSubscriber, RepositoryModule], +}) +export class GlobalModule {} diff --git a/packages/backend/src/RepositoryModule.ts b/packages/backend/src/RepositoryModule.ts index 6a6c24b284fd..5664783b26ef 100644 --- a/packages/backend/src/RepositoryModule.ts +++ b/packages/backend/src/RepositoryModule.ts @@ -1,198 +1,454 @@ import { Module } from '@nestjs/common'; import { AbuseUserReports, AccessTokens, Ads, AnnouncementReads, Announcements, AntennaNotes, Antennas, Apps, AttestationChallenges, AuthSessions, Blockings, ChannelFollowings, ChannelNotePinings, Channels, ClipNotes, Clips, DriveFiles, DriveFolders, Emojis, Followings, FollowRequests, GalleryLikes, GalleryPosts, Hashtags, Instances, MessagingMessages, Metas, ModerationLogs, MutedNotes, Mutings, NoteFavorites, NoteReactions, Notes, NoteThreadMutings, NoteUnreads, Notifications, PageLikes, Pages, PasswordResetRequests, Polls, PollVotes, PromoNotes, PromoReads, RegistrationTickets, RegistryItems, Relays, Signins, SwSubscriptions, UsedUsernames, UserGroupInvitations, UserGroupJoinings, UserGroups, UserIps, UserKeypairs, UserListJoinings, UserLists, UserNotePinings, UserPendings, UserProfiles, UserPublickeys, Users, UserSecurityKeys, Webhooks } from '@/models/index.js'; +import type { Provider } from '@nestjs/common'; + +const $usersRepository: Provider = { + provide: 'usersRepository', + useValue: Users, +}; + +const $notesRepository: Provider = { + provide: 'notesRepository', + useValue: Notes, +}; + +const $announcementsRepository: Provider = { + provide: 'announcementsRepository', + useValue: Announcements, +}; + +const $announcementReadsRepository: Provider = { + provide: 'announcementReadsRepository', + useValue: AnnouncementReads, +}; + +const $appsRepository: Provider = { + provide: 'appsRepository', + useValue: Apps, +}; + +const $noteFavoritesRepository: Provider = { + provide: 'noteFavoritesRepository', + useValue: NoteFavorites, +}; + +const $noteThreadMutingsRepository: Provider = { + provide: 'noteThreadMutingsRepository', + useValue: NoteThreadMutings, +}; + +const $noteReactionsRepository: Provider = { + provide: 'noteReactionsRepository', + useValue: NoteReactions, +}; + +const $noteUnreadsRepository: Provider = { + provide: 'noteUnreadsRepository', + useValue: NoteUnreads, +}; + +const $pollsRepository: Provider = { + provide: 'pollsRepository', + useValue: Polls, +}; + +const $pollVotesRepository: Provider = { + provide: 'pollVotesRepository', + useValue: PollVotes, +}; + +const $userProfilesRepository: Provider = { + provide: 'userProfilesRepository', + useValue: UserProfiles, +}; + +const $userKeypairsRepository: Provider = { + provide: 'userKeypairsRepository', + useValue: UserKeypairs, +}; + +const $userPendingsRepository: Provider = { + provide: 'userPendingsRepository', + useValue: UserPendings, +}; + +const $attestationChallengesRepository: Provider = { + provide: 'attestationChallengesRepository', + useValue: AttestationChallenges, +}; + +const $userSecurityKeysRepository: Provider = { + provide: 'userSecurityKeysRepository', + useValue: UserSecurityKeys, +}; + +const $userPublickeysRepository: Provider = { + provide: 'userPublickeysRepository', + useValue: UserPublickeys, +}; + +const $userListsRepository: Provider = { + provide: 'userListsRepository', + useValue: UserLists, +}; + +const $userListJoiningsRepository: Provider = { + provide: 'userListJoiningsRepository', + useValue: UserListJoinings, +}; + +const $userGroupsRepository: Provider = { + provide: 'userGroupsRepository', + useValue: UserGroups, +}; + +const $userGroupJoiningsRepository: Provider = { + provide: 'userGroupJoiningsRepository', + useValue: UserGroupJoinings, +}; + +const $userGroupInvitationsRepository: Provider = { + provide: 'userGroupInvitationsRepository', + useValue: UserGroupInvitations, +}; + +const $userNotePiningsRepository: Provider = { + provide: 'userNotePiningsRepository', + useValue: UserNotePinings, +}; + +const $userIpsRepository: Provider = { + provide: 'userIpsRepository', + useValue: UserIps, +}; + +const $usedUsernamesRepository: Provider = { + provide: 'usedUsernamesRepository', + useValue: UsedUsernames, +}; + +const $followingsRepository: Provider = { + provide: 'followingsRepository', + useValue: Followings, +}; + +const $followRequestsRepository: Provider = { + provide: 'followRequestsRepository', + useValue: FollowRequests, +}; + +const $instancesRepository: Provider = { + provide: 'instancesRepository', + useValue: Instances, +}; + +const $emojisRepository: Provider = { + provide: 'emojisRepository', + useValue: Emojis, +}; + +const $driveFilesRepository: Provider = { + provide: 'driveFilesRepository', + useValue: DriveFiles, +}; + +const $driveFoldersRepository: Provider = { + provide: 'driveFoldersRepository', + useValue: DriveFolders, +}; + +const $notificationsRepository: Provider = { + provide: 'notificationsRepository', + useValue: Notifications, +}; + +const $metasRepository: Provider = { + provide: 'metasRepository', + useValue: Metas, +}; + +const $mutingsRepository: Provider = { + provide: 'mutingsRepository', + useValue: Mutings, +}; + +const $blockingsRepository: Provider = { + provide: 'blockingsRepository', + useValue: Blockings, +}; + +const $swSubscriptionsRepository: Provider = { + provide: 'swSubscriptionsRepository', + useValue: SwSubscriptions, +}; + +const $hashtagsRepository: Provider = { + provide: 'hashtagsRepository', + useValue: Hashtags, +}; + +const $abuseUserReportsRepository: Provider = { + provide: 'abuseUserReportsRepository', + useValue: AbuseUserReports, +}; + +const $registrationTicketsRepository: Provider = { + provide: 'registrationTicketsRepository', + useValue: RegistrationTickets, +}; + +const $authSessionsRepository: Provider = { + provide: 'authSessionsRepository', + useValue: AuthSessions, +}; + +const $accessTokensRepository: Provider = { + provide: 'accessTokensRepository', + useValue: AccessTokens, +}; + +const $signinsRepository: Provider = { + provide: 'signinsRepository', + useValue: Signins, +}; + +const $messagingMessagesRepository: Provider = { + provide: 'messagingMessagesRepository', + useValue: MessagingMessages, +}; + +const $pagesRepository: Provider = { + provide: 'pagesRepository', + useValue: Pages, +}; + +const $pageLikesRepository: Provider = { + provide: 'pageLikesRepository', + useValue: PageLikes, +}; + +const $galleryPostsRepository: Provider = { + provide: 'galleryPostsRepository', + useValue: GalleryPosts, +}; + +const $galleryLikesRepository: Provider = { + provide: 'galleryLikesRepository', + useValue: GalleryLikes, +}; + +const $moderationLogsRepository: Provider = { + provide: 'moderationLogsRepository', + useValue: ModerationLogs, +}; + +const $clipsRepository: Provider = { + provide: 'clipsRepository', + useValue: Clips, +}; + +const $clipNotesRepository: Provider = { + provide: 'clipNotesRepository', + useValue: ClipNotes, +}; + +const $antennasRepository: Provider = { + provide: 'antennasRepository', + useValue: Antennas, +}; + +const $antennaNotesRepository: Provider = { + provide: 'antennaNotesRepository', + useValue: AntennaNotes, +}; + +const $promoNotesRepository: Provider = { + provide: 'promoNotesRepository', + useValue: PromoNotes, +}; + +const $promoReadsRepository: Provider = { + provide: 'promoReadsRepository', + useValue: PromoReads, +}; + +const $relaysRepository: Provider = { + provide: 'relaysRepository', + useValue: Relays, +}; + +const $mutedNotesRepository: Provider = { + provide: 'mutedNotesRepository', + useValue: MutedNotes, +}; + +const $channelsRepository: Provider = { + provide: 'channelsRepository', + useValue: Channels, +}; + +const $channelFollowingsRepository: Provider = { + provide: 'channelFollowingsRepository', + useValue: ChannelFollowings, +}; + +const $channelNotePiningsRepository: Provider = { + provide: 'channelNotePiningsRepository', + useValue: ChannelNotePinings, +}; + +const $registryItemsRepository: Provider = { + provide: 'registryItemsRepository', + useValue: RegistryItems, +}; + +const $webhooksRepository: Provider = { + provide: 'webhooksRepository', + useValue: Webhooks, +}; + +const $adsRepository: Provider = { + provide: 'adsRepository', + useValue: Ads, +}; + +const $passwordResetRequestsRepository: Provider = { + provide: 'passwordResetRequestsRepository', + useValue: PasswordResetRequests, +}; @Module({ imports: [ ], - providers: [{ - provide: 'usersRepository', - useValue: Users, - }, { - provide: 'notesRepository', - useValue: Notes, - }, { - provide: 'announcementsRepository', - useValue: Announcements, - }, { - provide: 'announcementReadsRepository', - useValue: AnnouncementReads, - }, { - provide: 'appsRepository', - useValue: Apps, - }, { - provide: 'noteFavoritesRepository', - useValue: NoteFavorites, - }, { - provide: 'noteThreadMutingsRepository', - useValue: NoteThreadMutings, - }, { - provide: 'noteReactionsRepository', - useValue: NoteReactions, - }, { - provide: 'noteUnreadsRepository', - useValue: NoteUnreads, - }, { - provide: 'pollsRepository', - useValue: Polls, - }, { - provide: 'pollVotesRepository', - useValue: PollVotes, - }, { - provide: 'serProfilesRepository', - useValue: UserProfiles, - }, { - provide: 'userKeypairsRepository', - useValue: UserKeypairs, - }, { - provide: 'userPendingsRepository', - useValue: UserPendings, - }, { - provide: 'attestationChallengesRepository', - useValue: AttestationChallenges, - }, { - provide: 'userSecurityKeysRepository', - useValue: UserSecurityKeys, - }, { - provide: 'userPublickeysRepository', - useValue: UserPublickeys, - }, { - provide: 'userListsRepository', - useValue: UserLists, - }, { - provide: 'userListJoiningsRepository', - useValue: UserListJoinings, - }, { - provide: 'userGroupsRepository', - useValue: UserGroups, - }, { - provide: 'userGroupJoiningsRepository', - useValue: UserGroupJoinings, - }, { - provide: 'userGroupInvitationsRepository', - useValue: UserGroupInvitations, - }, { - provide: 'userNotePiningsRepository', - useValue: UserNotePinings, - }, { - provide: 'userIpsRepository', - useValue: UserIps, - }, { - provide: 'usedUsernamesRepository', - useValue: UsedUsernames, - }, { - provide: 'followingsRepository', - useValue: Followings, - }, { - provide: 'followRequestsRepository', - useValue: FollowRequests, - }, { - provide: 'instancesRepository', - useValue: Instances, - }, { - provide: 'emojisRepository', - useValue: Emojis, - }, { - provide: 'driveFilesRepository', - useValue: DriveFiles, - }, { - provide: 'driveFoldersRepository', - useValue: DriveFolders, - }, { - provide: 'notificationsRepository', - useValue: Notifications, - }, { - provide: 'metasRepository', - useValue: Metas, - }, { - provide: 'mutingsRepository', - useValue: Mutings, - }, { - provide: 'blockingsRepository', - useValue: Blockings, - }, { - provide: 'swSubscriptionsRepository', - useValue: SwSubscriptions, - }, { - provide: 'hashtagsRepository', - useValue: Hashtags, - }, { - provide: 'abuseUserReportsRepository', - useValue: AbuseUserReports, - }, { - provide: 'registrationTicketsRepository', - useValue: RegistrationTickets, - }, { - provide: 'authSessionsRepository', - useValue: AuthSessions, - }, { - provide: 'accessTokensRepository', - useValue: AccessTokens, - }, { - provide: 'signinsRepository', - useValue: Signins, - }, { - provide: 'messagingMessagesRepository', - useValue: MessagingMessages, - }, { - provide: 'pagesRepository', - useValue: Pages, - }, { - provide: 'pageLikesRepository', - useValue: PageLikes, - }, { - provide: 'galleryPostsRepository', - useValue: GalleryPosts, - }, { - provide: 'galleryLikesRepository', - useValue: GalleryLikes, - }, { - provide: 'moderationLogsRepository', - useValue: ModerationLogs, - }, { - provide: 'clipsRepository', - useValue: Clips, - }, { - provide: 'clipNotesRepository', - useValue: ClipNotes, - }, { - provide: 'antennasRepository', - useValue: Antennas, - }, { - provide: 'antennaNotesRepository', - useValue: AntennaNotes, - }, { - provide: 'promoNotesRepository', - useValue: PromoNotes, - }, { - provide: 'promoReadsRepository', - useValue: PromoReads, - }, { - provide: 'relaysRepository', - useValue: Relays, - }, { - provide: 'mutedNotesRepository', - useValue: MutedNotes, - }, { - provide: 'channelsRepository', - useValue: Channels, - }, { - provide: 'channelFollowingsRepository', - useValue: ChannelFollowings, - }, { - provide: 'channelNotePiningsRepository', - useValue: ChannelNotePinings, - }, { - provide: 'registryItemsRepository', - useValue: RegistryItems, - }, { - provide: 'webhooksRepository', - useValue: Webhooks, - }, { - provide: 'adsRepository', - useValue: Ads, - }, { - provide: 'passwordResetRequestsRepository', - useValue: PasswordResetRequests, - }], + providers: [ + $usersRepository, + $notesRepository, + $announcementsRepository, + $announcementReadsRepository, + $appsRepository, + $noteFavoritesRepository, + $noteThreadMutingsRepository, + $noteReactionsRepository, + $noteUnreadsRepository, + $pollsRepository, + $pollVotesRepository, + $userProfilesRepository, + $userKeypairsRepository, + $userPendingsRepository, + $attestationChallengesRepository, + $userSecurityKeysRepository, + $userPublickeysRepository, + $userListsRepository, + $userListJoiningsRepository, + $userGroupsRepository, + $userGroupJoiningsRepository, + $userGroupInvitationsRepository, + $userNotePiningsRepository, + $userIpsRepository, + $usedUsernamesRepository, + $followingsRepository, + $followRequestsRepository, + $instancesRepository, + $emojisRepository, + $driveFilesRepository, + $driveFoldersRepository, + $notificationsRepository, + $metasRepository, + $mutingsRepository, + $blockingsRepository, + $swSubscriptionsRepository, + $hashtagsRepository, + $abuseUserReportsRepository, + $registrationTicketsRepository, + $authSessionsRepository, + $accessTokensRepository, + $signinsRepository, + $messagingMessagesRepository, + $pagesRepository, + $pageLikesRepository, + $galleryPostsRepository, + $galleryLikesRepository, + $moderationLogsRepository, + $clipsRepository, + $clipNotesRepository, + $antennasRepository, + $antennaNotesRepository, + $promoNotesRepository, + $promoReadsRepository, + $relaysRepository, + $mutedNotesRepository, + $channelsRepository, + $channelFollowingsRepository, + $channelNotePiningsRepository, + $registryItemsRepository, + $webhooksRepository, + $adsRepository, + $passwordResetRequestsRepository, + ], + exports: [ + $usersRepository, + $notesRepository, + $announcementsRepository, + $announcementReadsRepository, + $appsRepository, + $noteFavoritesRepository, + $noteThreadMutingsRepository, + $noteReactionsRepository, + $noteUnreadsRepository, + $pollsRepository, + $pollVotesRepository, + $userProfilesRepository, + $userKeypairsRepository, + $userPendingsRepository, + $attestationChallengesRepository, + $userSecurityKeysRepository, + $userPublickeysRepository, + $userListsRepository, + $userListJoiningsRepository, + $userGroupsRepository, + $userGroupJoiningsRepository, + $userGroupInvitationsRepository, + $userNotePiningsRepository, + $userIpsRepository, + $usedUsernamesRepository, + $followingsRepository, + $followRequestsRepository, + $instancesRepository, + $emojisRepository, + $driveFilesRepository, + $driveFoldersRepository, + $notificationsRepository, + $metasRepository, + $mutingsRepository, + $blockingsRepository, + $swSubscriptionsRepository, + $hashtagsRepository, + $abuseUserReportsRepository, + $registrationTicketsRepository, + $authSessionsRepository, + $accessTokensRepository, + $signinsRepository, + $messagingMessagesRepository, + $pagesRepository, + $pageLikesRepository, + $galleryPostsRepository, + $galleryLikesRepository, + $moderationLogsRepository, + $clipsRepository, + $clipNotesRepository, + $antennasRepository, + $antennaNotesRepository, + $promoNotesRepository, + $promoReadsRepository, + $relaysRepository, + $mutedNotesRepository, + $channelsRepository, + $channelFollowingsRepository, + $channelNotePiningsRepository, + $registryItemsRepository, + $webhooksRepository, + $adsRepository, + $passwordResetRequestsRepository, + ], }) export class RepositoryModule {} diff --git a/packages/backend/src/app.module.ts b/packages/backend/src/app.module.ts index 4805957bdcc3..b6b212e0df98 100644 --- a/packages/backend/src/app.module.ts +++ b/packages/backend/src/app.module.ts @@ -1,25 +1,15 @@ import { Module } from '@nestjs/common'; -import { QueueModule } from '@/queue/queue.module.js'; +import { QueueModule } from '@/services/queue/QueueModule.js'; import { CoreModule } from './services/CoreModule.js'; -import { DI } from './di-symbols.js'; -import { loadConfig } from './config.js'; -import { db } from './db/postgre.js'; -import { RepositoryModule } from './RepositoryModule.js'; import { ServerModule } from './server/ServerModule.js'; +import { GlobalModule } from './GlobalModule.js'; @Module({ imports: [ - RepositoryModule, + GlobalModule, CoreModule, QueueModule, ServerModule, ], - providers: [{ - provide: DI.config, - useValue: loadConfig(), - }, { - provide: DI.db, - useValue: db, - }], }) export class AppModule {} diff --git a/packages/backend/src/db/redis.ts b/packages/backend/src/db/redis.ts index 8f333719e10c..68cb5ac7ee69 100644 --- a/packages/backend/src/db/redis.ts +++ b/packages/backend/src/db/redis.ts @@ -14,7 +14,7 @@ export function createConnection() { }); } -export const subsdcriber = createConnection(); -subsdcriber.subscribe(loadConfig().host); +export const redisSubscriber = createConnection(); +redisSubscriber.subscribe(loadConfig().host); export const redisClient = createConnection(); diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts index 21300ebd925e..f451cc4b778d 100644 --- a/packages/backend/src/di-symbols.ts +++ b/packages/backend/src/di-symbols.ts @@ -2,4 +2,5 @@ export const DI = { config: Symbol('config'), db: Symbol('db'), redis: Symbol('redis'), + redisSubscriber: Symbol('redisSubscriber'), }; diff --git a/packages/backend/src/misc/antenna-cache.ts b/packages/backend/src/misc/antenna-cache.ts deleted file mode 100644 index 449b034db994..000000000000 --- a/packages/backend/src/misc/antenna-cache.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Antennas } from '@/models/index.js'; -import type { Antenna } from '@/models/entities/Antenna.js'; -import { subsdcriber } from '../db/redis.js'; - -let antennasFetched = false; -let antennas: Antenna[] = []; - -export async function getAntennas() { - if (!antennasFetched) { - antennas = await Antennas.find(); - antennasFetched = true; - } - - return antennas; -} - -subsdcriber.on('message', async (_, data) => { - const obj = JSON.parse(data); - - if (obj.channel === 'internal') { - const { type, body } = obj.message; - switch (type) { - case 'antennaCreated': - antennas.push(body); - break; - case 'antennaUpdated': - antennas[antennas.findIndex(a => a.id === body.id)] = body; - break; - case 'antennaDeleted': - antennas = antennas.filter(a => a.id !== body.id); - break; - default: - break; - } - } -}); diff --git a/packages/backend/src/models/entities/Notification.ts b/packages/backend/src/models/entities/Notification.ts index 6be372e69ac6..53a7dda43ab9 100644 --- a/packages/backend/src/models/entities/Notification.ts +++ b/packages/backend/src/models/entities/Notification.ts @@ -2,7 +2,7 @@ import { Entity, Index, JoinColumn, ManyToOne, Column, PrimaryColumn } from 'typ import { notificationTypes } from '@/types.js'; import { id } from '../id.js'; import { User } from './User.js'; -import { Note } from './note.js'; +import { Note } from './Note.js'; import { FollowRequest } from './FollowRequest.js'; import { UserGroupInvitation } from './UserGroupInvitation.js'; import { AccessToken } from './AccessToken.js'; diff --git a/packages/backend/src/queue/index.ts b/packages/backend/src/queue/index.ts index 8e3edfb41903..9b10de917c1e 100644 --- a/packages/backend/src/queue/index.ts +++ b/packages/backend/src/queue/index.ts @@ -1,7 +1,7 @@ import { DI } from '@/di-symbols.js'; +import { QueueService } from '../services/QueueService.js'; import { getJobInfo } from './get-job-info.js'; -import { QueueService } from './queue.service.js'; import { SystemQueueProcessorsService } from './SystemQueueProcessorsService.js'; import { ObjectStorageQueueProcessorsService } from './ObjectStorageQueueProcessorsService.js'; import { DbQueueProcessorsService } from './DbQueueProcessorsService.js'; diff --git a/packages/backend/src/queue/initialize.ts b/packages/backend/src/queue/initialize.ts deleted file mode 100644 index cefe8e62c1e5..000000000000 --- a/packages/backend/src/queue/initialize.ts +++ /dev/null @@ -1,36 +0,0 @@ -import Bull from 'bull'; -import { loadConfig } from '@/config.js'; - -export function initialize(name: string, limitPerSec = -1) { - const config = loadConfig(); - - return new Bull(name, { - redis: { - port: config.redis.port, - host: config.redis.host, - family: config.redis.family == null ? 0 : config.redis.family, - password: config.redis.pass, - db: config.redis.db || 0, - }, - prefix: config.redis.prefix ? `${config.redis.prefix}:queue` : 'queue', - limiter: limitPerSec > 0 ? { - max: limitPerSec, - duration: 1000, - } : undefined, - settings: { - backoffStrategies: { - apBackoff, - }, - }, - }); -} - -// ref. https://github.com/misskey-dev/misskey/pull/7635#issue-971097019 -function apBackoff(attemptsMade: number, err: Error) { - const baseDelay = 60 * 1000; // 1min - const maxBackoff = 8 * 60 * 60 * 1000; // 8hours - let backoff = (Math.pow(2, attemptsMade) - 1) * baseDelay; - backoff = Math.min(backoff, maxBackoff); - backoff += Math.round(backoff * Math.random() * 0.2); - return backoff; -} diff --git a/packages/backend/src/queue/queue.module.ts b/packages/backend/src/queue/queue.module.ts deleted file mode 100644 index 27f8691722b5..000000000000 --- a/packages/backend/src/queue/queue.module.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Module } from '@nestjs/common'; -import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; -import { initialize as initializeQueue } from './initialize.js'; -import type Bull from 'bull'; -import type { DeliverJobData, InboxJobData, DbJobData, ObjectStorageJobData, EndedPollNotificationJobData, WebhookDeliverJobData } from './types.js'; - -export type SystemQueue = Bull.Queue>; -export type EndedPollNotificationQueue = Bull.Queue; -export type DeliverQueue = Bull.Queue; -export type InboxQueue = Bull.Queue; -export type DbQueue = Bull.Queue; -export type ObjectStorageQueue = Bull.Queue; -export type WebhookDeliverQueue = Bull.Queue; - -@Module({ - imports: [ - ], - providers: [ - { provide: 'queue:system', useValue: initializeQueue('system') }, - { provide: 'queue:endedPollNotification', useValue: initializeQueue('endedPollNotification') }, - { provide: 'queue:deliver', useFactory: (config: Config) => initializeQueue('deliver', config.deliverJobPerSec ?? 128), inject: [DI.config] }, - { provide: 'queue:inbox', useFactory: (config: Config) => initializeQueue('inbox', config.inboxJobPerSec ?? 16), inject: [DI.config] }, - { provide: 'queue:db', useValue: initializeQueue('db') }, - { provide: 'queue:objectStorage', useValue: initializeQueue('objectStorage') }, - { provide: 'queue:webhookDeliver', useValue: initializeQueue('webhookDeliver', 64) }, - ], -}) -export class QueueModule {} diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 0e1e7c395df8..5d0be7a2b1c1 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -9,7 +9,7 @@ import type { Emojis, NoteReactions , UserProfiles , UserNotePinings , Users } f import * as url from '@/prelude/url.js'; import { Config } from '@/config.js'; import { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; import type { ILocalUser, User } from '@/models/entities/User.js'; import { UserKeypairStoreService } from '@/services/UserKeypairStoreService.js'; import type { Following } from '@/models/entities/Following.js'; diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 35ecd41e0cd8..103bee4b7e1b 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -1,5 +1,6 @@ import { Module } from '@nestjs/common'; +import { CoreModule } from '@/services/CoreModule.js'; import * as ep___admin_meta from './endpoints/admin/meta.js'; import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js'; import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js'; @@ -315,6 +316,7 @@ import * as ep___admin_driveCapOverride from './endpoints/admin/drive-capacity-o @Module({ imports: [ + CoreModule, ], providers: [ { provide: 'ep:admin/meta', useClass: ep___admin_meta.default }, diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts index 806e764c293d..ec52123ee9c6 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { UserSuspendService } from '@/services/UserSuspendService.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts b/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts index 3b5a31e4df5c..e594ec7cb095 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; export const meta = { tags: ['admin'], diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts index 333af15d353a..40e92c13ae2b 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; export const meta = { secure: true, diff --git a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts index b8de046f0958..cee27ebbcf9a 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ModerationLogService } from '@/services/ModerationLogService.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; export const meta = { tags: ['admin'], diff --git a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts index e327e40d5b14..cee4a85cbee9 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts @@ -1,7 +1,7 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DeliverQueue } from '@/queue/queue.module.js'; +import { DeliverQueue } from '@/services/queue/QueueModule.js'; export const meta = { tags: ['admin'], diff --git a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts index a1010b5063a9..96bc9efe6c18 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts @@ -1,7 +1,7 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { InboxQueue } from '@/queue/queue.module.js'; +import { InboxQueue } from '@/services/queue/QueueModule.js'; export const meta = { tags: ['admin'], diff --git a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts index 1b2bc99f9dc4..59c3632c85b6 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from '@/queue/queue.module.js'; +import { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from '@/services/queue/QueueModule.js'; export const meta = { tags: ['admin'], diff --git a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts index 1b291d922b02..75b7ded748ba 100644 --- a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts +++ b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users , AbuseUserReports } from '@/models/index.js'; import { InstanceActorService } from '@/services/InstanceActorService.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; import { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/export-custom-emojis.ts b/packages/backend/src/server/api/endpoints/export-custom-emojis.ts index eb7c038c3341..420e78c04c5a 100644 --- a/packages/backend/src/server/api/endpoints/export-custom-emojis.ts +++ b/packages/backend/src/server/api/endpoints/export-custom-emojis.ts @@ -1,7 +1,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; export const meta = { secure: true, diff --git a/packages/backend/src/server/api/endpoints/i/export-blocking.ts b/packages/backend/src/server/api/endpoints/i/export-blocking.ts index 195e716bdc6b..1a588d9a7b30 100644 --- a/packages/backend/src/server/api/endpoints/i/export-blocking.ts +++ b/packages/backend/src/server/api/endpoints/i/export-blocking.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; export const meta = { secure: true, diff --git a/packages/backend/src/server/api/endpoints/i/export-following.ts b/packages/backend/src/server/api/endpoints/i/export-following.ts index 2c765a246d89..37146d190188 100644 --- a/packages/backend/src/server/api/endpoints/i/export-following.ts +++ b/packages/backend/src/server/api/endpoints/i/export-following.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; export const meta = { secure: true, diff --git a/packages/backend/src/server/api/endpoints/i/export-mute.ts b/packages/backend/src/server/api/endpoints/i/export-mute.ts index f6277f58a970..8c5fe1ccc825 100644 --- a/packages/backend/src/server/api/endpoints/i/export-mute.ts +++ b/packages/backend/src/server/api/endpoints/i/export-mute.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; export const meta = { secure: true, diff --git a/packages/backend/src/server/api/endpoints/i/export-notes.ts b/packages/backend/src/server/api/endpoints/i/export-notes.ts index 224086401301..9be12429e8e6 100644 --- a/packages/backend/src/server/api/endpoints/i/export-notes.ts +++ b/packages/backend/src/server/api/endpoints/i/export-notes.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; export const meta = { secure: true, diff --git a/packages/backend/src/server/api/endpoints/i/export-user-lists.ts b/packages/backend/src/server/api/endpoints/i/export-user-lists.ts index ffefb4857334..5f4983761cdc 100644 --- a/packages/backend/src/server/api/endpoints/i/export-user-lists.ts +++ b/packages/backend/src/server/api/endpoints/i/export-user-lists.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; export const meta = { secure: true, diff --git a/packages/backend/src/server/api/endpoints/i/import-blocking.ts b/packages/backend/src/server/api/endpoints/i/import-blocking.ts index a177467cd2c4..30e308311043 100644 --- a/packages/backend/src/server/api/endpoints/i/import-blocking.ts +++ b/packages/backend/src/server/api/endpoints/i/import-blocking.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; import { DriveFiles } from '@/models/index.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/i/import-following.ts b/packages/backend/src/server/api/endpoints/i/import-following.ts index f0c97d825e39..1d4b2f8270fd 100644 --- a/packages/backend/src/server/api/endpoints/i/import-following.ts +++ b/packages/backend/src/server/api/endpoints/i/import-following.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; import { DriveFiles } from '@/models/index.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/i/import-muting.ts b/packages/backend/src/server/api/endpoints/i/import-muting.ts index b46bf9d27e8c..384cf58ccb0d 100644 --- a/packages/backend/src/server/api/endpoints/i/import-muting.ts +++ b/packages/backend/src/server/api/endpoints/i/import-muting.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; import { DriveFiles } from '@/models/index.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/i/import-user-lists.ts b/packages/backend/src/server/api/endpoints/i/import-user-lists.ts index 09517d4575da..cd333e2db5dd 100644 --- a/packages/backend/src/server/api/endpoints/i/import-user-lists.ts +++ b/packages/backend/src/server/api/endpoints/i/import-user-lists.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; import { DriveFiles } from '@/models/index.js'; import { ApiError } from '../../error.js'; 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 56c426d0f48c..3159a4ea53c5 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -5,7 +5,7 @@ import type { IRemoteUser } from '@/models/entities/User.js'; import { IdService } from '@/services/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/common/GetterService.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; import { PollService } from '@/services/PollService.js'; import { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index bf9a9aab5309..eff07c4fd4ad 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -19,7 +19,7 @@ import { getNoteSummary } from '@/misc/get-note-summary.js'; import { DI } from '@/di-symbols.js'; import * as Acct from '@/misc/acct.js'; import { MetaService } from '@/services/MetaService.js'; -import { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from '@/queue/queue.module.js'; +import { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from '@/services/queue/QueueModule.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { PageEntityService } from '@/services/entities/PageEntityService.js'; diff --git a/packages/backend/src/services/AntennaService.ts b/packages/backend/src/services/AntennaService.ts index 523b76a50a06..62a4b169e5c3 100644 --- a/packages/backend/src/services/AntennaService.ts +++ b/packages/backend/src/services/AntennaService.ts @@ -1,5 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { UserGroupJoinings, UserListJoinings , AntennaNotes, Mutings, Notes, Users , Blockings } from '@/models/index.js'; +import Redis from 'ioredis'; +import type { UserGroupJoinings, UserListJoinings , AntennaNotes, Mutings, Notes, Users , Blockings, Antennas } from '@/models/index.js'; import type { Antenna } from '@/models/entities/Antenna.js'; import type { Note } from '@/models/entities/Note.js'; import type { User } from '@/models/entities/User.js'; @@ -9,13 +10,19 @@ import { GlobalEventService } from '@/services/GlobalEventService.js'; import * as Acct from '@/misc/acct.js'; import { Cache } from '@/misc/cache.js'; import type { Packed } from '@/misc/schema.js'; +import { DI } from '@/di-symbols.js'; import { UtilityService } from './UtilityService.js'; @Injectable() -export class AntennaService { +export class AntennaService implements OnApplicationShutdown { + #antennasFetched = false; + #antennas: Antenna[] = []; #blockingCache: Cache; constructor( + @Inject(DI.redisSubscriber) + private redisSubscriber: Redis.Redis, + @Inject('mutingsRepository') private mutingsRepository: typeof Mutings, @@ -28,6 +35,9 @@ export class AntennaService { @Inject('antennaNotesRepository') private antennaNotesRepository: typeof AntennaNotes, + @Inject('antennasRepository') + private antennasRepository: typeof Antennas, + @Inject('userGroupJoiningsRepository') private userGroupJoiningsRepository: typeof UserGroupJoinings, @@ -39,6 +49,33 @@ export class AntennaService { private globalEventServie: GlobalEventService, ) { this.#blockingCache = new Cache(1000 * 60 * 5); + + this.redisSubscriber.on('message', this.onRedisMessage); + } + + public onApplicationShutdown(signal?: string | undefined) { + this.redisSubscriber.off('message', this.onRedisMessage); + } + + private async onRedisMessage(_, data) { + const obj = JSON.parse(data); + + if (obj.channel === 'internal') { + const { type, body } = obj.message; + switch (type) { + case 'antennaCreated': + this.#antennas.push(body); + break; + case 'antennaUpdated': + this.#antennas[this.#antennas.findIndex(a => a.id === body.id)] = body; + break; + case 'antennaDeleted': + this.#antennas = this.#antennas.filter(a => a.id !== body.id); + break; + default: + break; + } + } } public async addNoteToAntenna(antenna: Antenna, note: Note, noteUser: { id: User['id']; }): Promise { @@ -176,4 +213,13 @@ export class AntennaService { return true; } + + public async getAntennas() { + if (!this.#antennasFetched) { + this.#antennas = await this.antennasRepository.find(); + this.#antennasFetched = true; + } + + return this.#antennas; + } } diff --git a/packages/backend/src/services/CoreModule.ts b/packages/backend/src/services/CoreModule.ts index 004a769d126c..cf549469d7bd 100644 --- a/packages/backend/src/services/CoreModule.ts +++ b/packages/backend/src/services/CoreModule.ts @@ -1,7 +1,5 @@ import { Module } from '@nestjs/common'; import { DI } from '../di-symbols.js'; -import { ChartsModule } from './chart/ChartsModule.js'; -import { EntityModule } from './entities/EntityModule.js'; import { AccountUpdateService } from './AccountUpdateService.js'; import { AiService } from './AiService.js'; import { AntennaService } from './AntennaService.js'; @@ -52,11 +50,73 @@ import { WebhookService } from './WebhookService.js'; import { ProxyAccountService } from './ProxyAccountService.js'; import { UtilityService } from './UtilityService.js'; import { FileInfoService } from './FileInfoService.js'; +import FederationChart from './chart/charts/federation.js'; +import NotesChart from './chart/charts/notes.js'; +import UsersChart from './chart/charts/users.js'; +import ActiveUsersChart from './chart/charts/active-users.js'; +import InstanceChart from './chart/charts/instance.js'; +import PerUserNotesChart from './chart/charts/per-user-notes.js'; +import DriveChart from './chart/charts/drive.js'; +import PerUserReactionsChart from './chart/charts/per-user-reactions.js'; +import HashtagChart from './chart/charts/hashtag.js'; +import PerUserFollowingChart from './chart/charts/per-user-following.js'; +import PerUserDriveChart from './chart/charts/per-user-drive.js'; +import ApRequestChart from './chart/charts/ap-request.js'; +import { ChartManagementService } from './chart/ChartManagementService.js'; +import { AbuseUserReportEntityService } from './entities/AbuseUserReportEntityService.js'; +import { AntennaEntityService } from './entities/AntennaEntityService.js'; +import { AppEntityService } from './entities/AppEntityService.js'; +import { AuthSessionEntityService } from './entities/AuthSessionEntityService.js'; +import { BlockingEntityService } from './entities/BlockingEntityService.js'; +import { ChannelEntityService } from './entities/ChannelEntityService.js'; +import { ClipEntityService } from './entities/ClipEntityService.js'; +import { DriveFileEntityService } from './entities/DriveFileEntityService.js'; +import { DriveFolderEntityService } from './entities/DriveFolderEntityService.js'; +import { EmojiEntityService } from './entities/EmojiEntityService.js'; +import { FollowingEntityService } from './entities/FollowingEntityService.js'; +import { FollowRequestEntityService } from './entities/FollowRequestEntityService.js'; +import { GalleryLikeEntityService } from './entities/GalleryLikeEntityService.js'; +import { GalleryPostEntityService } from './entities/GalleryPostEntityService.js'; +import { HashtagEntityService } from './entities/HashtagEntityService.js'; +import { InstanceEntityService } from './entities/InstanceEntityService.js'; +import { MessagingMessageEntityService } from './entities/MessagingMessageEntityService.js'; +import { ModerationLogEntityService } from './entities/ModerationLogEntityService.js'; +import { MutingEntityService } from './entities/MutingEntityService.js'; +import { NoteEntityService } from './entities/NoteEntityService.js'; +import { NoteFavoriteEntityService } from './entities/NoteFavoriteEntityService.js'; +import { NoteReactionEntityService } from './entities/NoteReactionEntityService.js'; +import { NotificationEntityService } from './entities/NotificationEntityService.js'; +import { PageEntityService } from './entities/PageEntityService.js'; +import { PageLikeEntityService } from './entities/PageLikeEntityService.js'; +import { SigninEntityService } from './entities/SigninEntityService.js'; +import { UserEntityService } from './entities/UserEntityService.js'; +import { UserGroupEntityService } from './entities/UserGroupEntityService.js'; +import { UserGroupInvitationEntityService } from './entities/UserGroupInvitationEntityService.js'; +import { UserListEntityService } from './entities/UserListEntityService.js'; +import { ApAudienceService } from './remote/activitypub/ApAudienceService.js'; +import { ApDbResolverService } from './remote/activitypub/ApDbResolverService.js'; +import { ApDeliverManagerService } from './remote/activitypub/ApDeliverManagerService.js'; +import { ApInboxService } from './remote/activitypub/ApInboxService.js'; +import { ApLoggerService } from './remote/activitypub/ApLoggerService.js'; +import { ApMfmService } from './remote/activitypub/ApMfmService.js'; +import { ApRendererService } from './remote/activitypub/ApRendererService.js'; +import { ApRequestService } from './remote/activitypub/ApRequestService.js'; +import { ApResolverService } from './remote/activitypub/ApResolverService.js'; +import { LdSignatureService } from './remote/activitypub/LdSignatureService.js'; +import { RemoteLoggerService } from './remote/RemoteLoggerService.js'; +import { ResolveUserService } from './remote/ResolveUserService.js'; +import { WebfingerService } from './remote/WebfingerService.js'; +import { ApImageService } from './remote/activitypub/models/ApImageService.js'; +import { ApMentionService } from './remote/activitypub/models/ApMentionService.js'; +import { ApNoteService } from './remote/activitypub/models/ApNoteService.js'; +import { ApPersonService } from './remote/activitypub/models/ApPersonService.js'; +import { ApQuestionService } from './remote/activitypub/models/ApQuestionService.js'; +import { QueueModule } from './queue/QueueModule.js'; +import { QueueService } from './QueueService.js'; @Module({ imports: [ - ChartsModule, - EntityModule, + QueueModule, ], providers: [ AccountUpdateService, @@ -109,10 +169,70 @@ import { FileInfoService } from './FileInfoService.js'; WebhookService, UtilityService, FileInfoService, + FederationChart, + NotesChart, + UsersChart, + ActiveUsersChart, + InstanceChart, + PerUserNotesChart, + DriveChart, + PerUserReactionsChart, + HashtagChart, + PerUserFollowingChart, + PerUserDriveChart, + ApRequestChart, + ChartManagementService, + AbuseUserReportEntityService, + AntennaEntityService, + AppEntityService, + AuthSessionEntityService, + BlockingEntityService, + ChannelEntityService, + ClipEntityService, + DriveFileEntityService, + DriveFolderEntityService, + EmojiEntityService, + FollowingEntityService, + FollowRequestEntityService, + GalleryLikeEntityService, + GalleryPostEntityService, + HashtagEntityService, + InstanceEntityService, + MessagingMessageEntityService, + ModerationLogEntityService, + MutingEntityService, + NoteEntityService, + NoteFavoriteEntityService, + NoteReactionEntityService, + NotificationEntityService, + PageEntityService, + PageLikeEntityService, + SigninEntityService, + UserEntityService, + UserGroupEntityService, + UserGroupInvitationEntityService, + UserListEntityService, + ApAudienceService, + ApDbResolverService, + ApDeliverManagerService, + ApInboxService, + ApLoggerService, + ApMfmService, + ApRendererService, + ApRequestService, + ApResolverService, + LdSignatureService, + RemoteLoggerService, + ResolveUserService, + WebfingerService, + ApImageService, + ApMentionService, + ApNoteService, + ApPersonService, + ApQuestionService, + QueueService, ], exports: [ - ChartsModule, - EntityModule, AccountUpdateService, AiService, AntennaService, @@ -163,6 +283,68 @@ import { FileInfoService } from './FileInfoService.js'; WebhookService, UtilityService, FileInfoService, + FederationChart, + NotesChart, + UsersChart, + ActiveUsersChart, + InstanceChart, + PerUserNotesChart, + DriveChart, + PerUserReactionsChart, + HashtagChart, + PerUserFollowingChart, + PerUserDriveChart, + ApRequestChart, + ChartManagementService, + AbuseUserReportEntityService, + AntennaEntityService, + AppEntityService, + AuthSessionEntityService, + BlockingEntityService, + ChannelEntityService, + ClipEntityService, + DriveFileEntityService, + DriveFolderEntityService, + EmojiEntityService, + FollowingEntityService, + FollowRequestEntityService, + GalleryLikeEntityService, + GalleryPostEntityService, + HashtagEntityService, + InstanceEntityService, + MessagingMessageEntityService, + ModerationLogEntityService, + MutingEntityService, + NoteEntityService, + NoteFavoriteEntityService, + NoteReactionEntityService, + NotificationEntityService, + PageEntityService, + PageLikeEntityService, + SigninEntityService, + UserEntityService, + UserGroupEntityService, + UserGroupInvitationEntityService, + UserListEntityService, + ApAudienceService, + ApDbResolverService, + ApDeliverManagerService, + ApInboxService, + ApLoggerService, + ApMfmService, + ApRendererService, + ApRequestService, + ApResolverService, + LdSignatureService, + RemoteLoggerService, + ResolveUserService, + WebfingerService, + ApImageService, + ApMentionService, + ApNoteService, + ApPersonService, + ApQuestionService, + QueueService, ], }) export class CoreModule {} diff --git a/packages/backend/src/services/DeleteAccountService.ts b/packages/backend/src/services/DeleteAccountService.ts index 35bd44eda288..f7c1c56e9a1e 100644 --- a/packages/backend/src/services/DeleteAccountService.ts +++ b/packages/backend/src/services/DeleteAccountService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users } from '@/models/index.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; import { UserSuspendService } from '@/services/UserSuspendService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; diff --git a/packages/backend/src/services/DriveService.ts b/packages/backend/src/services/DriveService.ts index f1b5645353b9..6b6a6a525c17 100644 --- a/packages/backend/src/services/DriveService.ts +++ b/packages/backend/src/services/DriveService.ts @@ -19,7 +19,7 @@ import { GlobalEventService } from '@/services/GlobalEventService.js'; import { VideoProcessingService } from '@/services/VideoProcessingService.js'; import { ImageProcessingService } from '@/services/ImageProcessingService.js'; import type { IImage } from '@/services/ImageProcessingService.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; import type { DriveFolder } from '@/models/entities/DriveFolder.js'; import { createTemp } from '@/misc/create-temp.js'; import DriveChart from '@/services/chart/charts/drive.js'; diff --git a/packages/backend/src/services/MessagingService.ts b/packages/backend/src/services/MessagingService.ts index e1f2eb9ec9f3..0a319ed6679f 100644 --- a/packages/backend/src/services/MessagingService.ts +++ b/packages/backend/src/services/MessagingService.ts @@ -8,7 +8,7 @@ import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; import type { Note } from '@/models/entities/Note.js'; import type { User, CacheableUser, IRemoteUser } from '@/models/entities/User.js'; import type { UserGroup } from '@/models/entities/UserGroup.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; import { toArray } from '@/prelude/array.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { IdService } from './IdService.js'; diff --git a/packages/backend/src/services/NoteCreateService.ts b/packages/backend/src/services/NoteCreateService.ts index 742da84fed6f..2c92064efa5c 100644 --- a/packages/backend/src/services/NoteCreateService.ts +++ b/packages/backend/src/services/NoteCreateService.ts @@ -20,7 +20,6 @@ import { checkWordMute } from '@/misc/check-word-mute.js'; import { countSameRenotes } from '@/misc/count-same-renotes.js'; import type { Channel } from '@/models/entities/Channel.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; -import { getAntennas } from '@/misc/antenna-cache.js'; import { Cache } from '@/misc/cache.js'; import type { UserProfile } from '@/models/entities/UserProfile.js'; import { db } from '@/db/postgre.js'; @@ -37,7 +36,7 @@ import { CreateNotificationService } from '@/services/CreateNotificationService. import { WebhookService } from '@/services/WebhookService.js'; import { HashtagService } from '@/services/HashtagService.js'; import { AntennaService } from '@/services/AntennaService.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; import { NoteEntityService } from './entities/NoteEntityService.js'; import { UserEntityService } from './entities/UserEntityService.js'; import { NoteReadService } from './NoteReadService.js'; @@ -427,7 +426,7 @@ export class NoteCreateService { }); // Antenna - for (const antenna of (await getAntennas())) { + for (const antenna of (await this.antennaService.getAntennas())) { this.antennaService.checkHitAntenna(antenna, note, user).then(hit => { if (hit) { this.antennaService.addNoteToAntenna(antenna, note, user); diff --git a/packages/backend/src/services/NoteReadService.ts b/packages/backend/src/services/NoteReadService.ts index 235cea68d0a1..92b76c130eb9 100644 --- a/packages/backend/src/services/NoteReadService.ts +++ b/packages/backend/src/services/NoteReadService.ts @@ -9,7 +9,6 @@ import type { Packed } from '@/misc/schema.js'; import type { Note } from '@/models/entities/Note.js'; import { IdService } from '@/services/IdService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; -import { getAntennas } from '@/misc/antenna-cache.js'; import { UserEntityService } from './entities/UserEntityService.js'; import { NotificationService } from './NotificationService.js'; import { AntennaService } from './AntennaService.js'; @@ -117,7 +116,7 @@ export class NoteReadService { select: ['followeeId'], })).map(x => x.followeeId)); - const myAntennas = (await getAntennas()).filter(a => a.userId === userId); + const myAntennas = (await this.antennaService.getAntennas()).filter(a => a.userId === userId); const readMentions: (Note | Packed<'Note'>)[] = []; const readSpecifiedNotes: (Note | Packed<'Note'>)[] = []; const readChannelNotes: (Note | Packed<'Note'>)[] = []; diff --git a/packages/backend/src/queue/queue.service.ts b/packages/backend/src/services/QueueService.ts similarity index 98% rename from packages/backend/src/queue/queue.service.ts rename to packages/backend/src/services/QueueService.ts index 2289cab04dbe..1260a1c131d9 100644 --- a/packages/backend/src/queue/queue.service.ts +++ b/packages/backend/src/services/QueueService.ts @@ -5,8 +5,8 @@ import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { Webhook, webhookEventTypes } from '@/models/entities/Webhook.js'; import { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; -import { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from './queue.module.js'; -import type { ThinUser } from './types.js'; +import { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from './queue/QueueModule.js'; +import type { ThinUser } from '../queue/types.js'; import type httpSignature from '@peertube/http-signature'; @Injectable() diff --git a/packages/backend/src/services/RelayService.ts b/packages/backend/src/services/RelayService.ts index 6c71e0011ac9..9f9ddd11c8c2 100644 --- a/packages/backend/src/services/RelayService.ts +++ b/packages/backend/src/services/RelayService.ts @@ -5,7 +5,7 @@ import type { Relays, Users } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; import { Cache } from '@/misc/cache.js'; import type { Relay } from '@/models/entities/Relay.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; import { CreateSystemUserService } from '@/services/CreateSystemUserService.js'; import { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; diff --git a/packages/backend/src/services/UserBlockingService.ts b/packages/backend/src/services/UserBlockingService.ts index 0b816d92d1c9..ae1a656688e1 100644 --- a/packages/backend/src/services/UserBlockingService.ts +++ b/packages/backend/src/services/UserBlockingService.ts @@ -4,7 +4,7 @@ import type { FollowRequests , Followings , UserLists , UserListJoinings , Users import { IdService } from '@/services/IdService.js'; import type { CacheableUser, User } from '@/models/entities/User.js'; import type { Blocking } from '@/models/entities/Blocking.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; import { UserEntityService } from './entities/UserEntityService.js'; diff --git a/packages/backend/src/services/UserCacheService.ts b/packages/backend/src/services/UserCacheService.ts index e605b74e0cdb..753d5cfbbcf7 100644 --- a/packages/backend/src/services/UserCacheService.ts +++ b/packages/backend/src/services/UserCacheService.ts @@ -3,6 +3,7 @@ import Redis from 'ioredis'; import type { Users } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; import type { CacheableLocalUser, CacheableUser, ILocalUser } from '@/models/entities/User.js'; +import { DI } from '@/di-symbols.js'; import { UserEntityService } from './entities/UserEntityService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; @@ -14,7 +15,7 @@ export class UserCacheService implements OnApplicationShutdown { public uriPersonCache: Cache; constructor( - @Inject('redisSubscriber') + @Inject(DI.redisSubscriber) private redisSubscriber: Redis.Redis, @Inject('usersRepository') diff --git a/packages/backend/src/services/UserFollowingService.ts b/packages/backend/src/services/UserFollowingService.ts index 69a3f568ba40..4a5acf49f7f5 100644 --- a/packages/backend/src/services/UserFollowingService.ts +++ b/packages/backend/src/services/UserFollowingService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users , Followings, FollowRequests , UserProfiles , Instances , Blockings } from '@/models/index.js'; import type { CacheableUser, ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; import PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { IdService } from '@/services/IdService.js'; diff --git a/packages/backend/src/services/UserMutingService.ts b/packages/backend/src/services/UserMutingService.ts index 3d3c1a33a9ab..d1941e4adce1 100644 --- a/packages/backend/src/services/UserMutingService.ts +++ b/packages/backend/src/services/UserMutingService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users , Mutings } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import type { User } from '@/models/entities/User.js'; diff --git a/packages/backend/src/services/UserSuspendService.ts b/packages/backend/src/services/UserSuspendService.ts index 0bde8b4ad225..53dbda12f569 100644 --- a/packages/backend/src/services/UserSuspendService.ts +++ b/packages/backend/src/services/UserSuspendService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Not, IsNull } from 'typeorm'; import type { Followings , Users } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { Config } from '@/config.js'; diff --git a/packages/backend/src/services/WebhookService.ts b/packages/backend/src/services/WebhookService.ts index 6bdbb38846fc..36b910bae0ad 100644 --- a/packages/backend/src/services/WebhookService.ts +++ b/packages/backend/src/services/WebhookService.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import Redis from 'ioredis'; import type { Webhooks } from '@/models/index.js'; import type { Webhook } from '@/models/entities/Webhook.js'; +import { DI } from '@/di-symbols.js'; import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() @@ -10,7 +11,7 @@ export class WebhookService implements OnApplicationShutdown { #webhooks: Webhook[] = []; constructor( - @Inject('redisSubscriber') + @Inject(DI.redisSubscriber) private redisSubscriber: Redis.Redis, @Inject('webhooksRepository') diff --git a/packages/backend/src/services/chart/ChartsModule.ts b/packages/backend/src/services/chart/ChartsModule.ts deleted file mode 100644 index 4ddbc4ec1d05..000000000000 --- a/packages/backend/src/services/chart/ChartsModule.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Module } from '@nestjs/common'; -import FederationChart from './charts/federation.js'; -import NotesChart from './charts/notes.js'; -import UsersChart from './charts/users.js'; -import ActiveUsersChart from './charts/active-users.js'; -import InstanceChart from './charts/instance.js'; -import PerUserNotesChart from './charts/per-user-notes.js'; -import DriveChart from './charts/drive.js'; -import PerUserReactionsChart from './charts/per-user-reactions.js'; -import HashtagChart from './charts/hashtag.js'; -import PerUserFollowingChart from './charts/per-user-following.js'; -import PerUserDriveChart from './charts/per-user-drive.js'; -import ApRequestChart from './charts/ap-request.js'; -import { ChartManagementService } from './ChartManagementService.js'; - -@Module({ - imports: [ - ], - providers: [ - FederationChart, - NotesChart, - UsersChart, - ActiveUsersChart, - InstanceChart, - PerUserNotesChart, - DriveChart, - PerUserReactionsChart, - HashtagChart, - PerUserFollowingChart, - PerUserDriveChart, - ApRequestChart, - - ChartManagementService, - ], -}) -export class ChartsModule {} diff --git a/packages/backend/src/services/entities/DriveFolderEntityService.ts b/packages/backend/src/services/entities/DriveFolderEntityService.ts index a480b1bb0d91..2446ee571025 100644 --- a/packages/backend/src/services/entities/DriveFolderEntityService.ts +++ b/packages/backend/src/services/entities/DriveFolderEntityService.ts @@ -16,8 +16,6 @@ export class DriveFolderEntityService { @Inject('driveFilesRepository') private driveFilesRepository: typeof DriveFiles, - - private userEntityService: UserEntityService, ) { } diff --git a/packages/backend/src/services/entities/EntityModule.ts b/packages/backend/src/services/entities/EntityModule.ts deleted file mode 100644 index 471ab9c808a9..000000000000 --- a/packages/backend/src/services/entities/EntityModule.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { Module } from '@nestjs/common'; -import { AbuseUserReportEntityService } from './AbuseUserReportEntityService.js'; -import { AntennaEntityService } from './AntennaEntityService.js'; -import { AppEntityService } from './AppEntityService.js'; -import { AuthSessionEntityService } from './AuthSessionEntityService.js'; -import { BlockingEntityService } from './BlockingEntityService.js'; -import { ChannelEntityService } from './ChannelEntityService.js'; -import { ClipEntityService } from './ClipEntityService.js'; -import { DriveFileEntityService } from './DriveFileEntityService.js'; -import { DriveFolderEntityService } from './DriveFolderEntityService.js'; -import { EmojiEntityService } from './EmojiEntityService.js'; -import { FollowingEntityService } from './FollowingEntityService.js'; -import { FollowRequestEntityService } from './FollowRequestEntityService.js'; -import { GalleryLikeEntityService } from './GalleryLikeEntityService.js'; -import { GalleryPostEntityService } from './GalleryPostEntityService.js'; -import { HashtagEntityService } from './HashtagEntityService.js'; -import { InstanceEntityService } from './InstanceEntityService.js'; -import { MessagingMessageEntityService } from './MessagingMessageEntityService.js'; -import { ModerationLogEntityService } from './ModerationLogEntityService.js'; -import { MutingEntityService } from './MutingEntityService.js'; -import { NoteEntityService } from './NoteEntityService.js'; -import { NoteFavoriteEntityService } from './NoteFavoriteEntityService.js'; -import { NoteReactionEntityService } from './NoteReactionEntityService.js'; -import { NotificationEntityService } from './NotificationEntityService.js'; -import { PageEntityService } from './PageEntityService.js'; -import { PageLikeEntityService } from './PageLikeEntityService.js'; -import { SigninEntityService } from './SigninEntityService.js'; -import { UserEntityService } from './UserEntityService.js'; -import { UserGroupEntityService } from './UserGroupEntityService.js'; -import { UserGroupInvitationEntityService } from './UserGroupInvitationEntityService.js'; -import { UserListEntityService } from './UserListEntityService.js'; - -@Module({ - imports: [ - ], - providers: [ - AbuseUserReportEntityService, - AntennaEntityService, - AppEntityService, - AuthSessionEntityService, - BlockingEntityService, - ChannelEntityService, - ClipEntityService, - DriveFileEntityService, - DriveFolderEntityService, - EmojiEntityService, - FollowingEntityService, - FollowRequestEntityService, - GalleryLikeEntityService, - GalleryPostEntityService, - HashtagEntityService, - InstanceEntityService, - MessagingMessageEntityService, - ModerationLogEntityService, - MutingEntityService, - NoteEntityService, - NoteFavoriteEntityService, - NoteReactionEntityService, - NotificationEntityService, - PageEntityService, - PageLikeEntityService, - SigninEntityService, - UserEntityService, - UserGroupEntityService, - UserGroupInvitationEntityService, - UserListEntityService, - ], -}) -export class EntityModule {} diff --git a/packages/backend/src/services/entities/NoteEntityService.ts b/packages/backend/src/services/entities/NoteEntityService.ts index 725fdd409878..c2c31ed8f6be 100644 --- a/packages/backend/src/services/entities/NoteEntityService.ts +++ b/packages/backend/src/services/entities/NoteEntityService.ts @@ -1,6 +1,7 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import { DataSource, In } from 'typeorm'; import * as mfm from 'mfm-js'; +import { ModuleRef } from '@nestjs/core'; import { DI } from '@/di-symbols.js'; import type { Notes , Polls, PollVotes , DriveFiles , Channels , Followings , Users , NoteReactions } from '@/models/index.js'; import { Config } from '@/config.js'; @@ -10,14 +11,21 @@ import { awaitAll } from '@/prelude/await-all.js'; import type { User } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; import type { NoteReaction } from '@/models/entities/NoteReaction.js'; -import { CustomEmojiService } from '../CustomEmojiService.js'; -import { ReactionService } from '../ReactionService.js'; -import { UserEntityService } from './UserEntityService.js'; -import { DriveFileEntityService } from './DriveFileEntityService.js'; +import type { CustomEmojiService } from '../CustomEmojiService.js'; +import type { ReactionService } from '../ReactionService.js'; +import type { UserEntityService } from './UserEntityService.js'; +import type { DriveFileEntityService } from './DriveFileEntityService.js'; @Injectable() export class NoteEntityService { + private userEntityService: UserEntityService; + private driveFileEntityService: DriveFileEntityService; + private customEmojiService: CustomEmojiService; + private reactionService: ReactionService; + constructor( + private moduleRef: ModuleRef, + @Inject(DI.db) private db: DataSource, @@ -45,14 +53,15 @@ export class NoteEntityService { @Inject('driveFilesRepository') private driveFilesRepository: typeof DriveFiles, - // 循環参照のため / for circular dependency - @Inject(forwardRef(() => UserEntityService)) - private userEntityService: UserEntityService, - - private driveFileEntityService: DriveFileEntityService, - private customEmojiService: CustomEmojiService, - private reactionService: ReactionService, + //private userEntityService: UserEntityService, + //private driveFileEntityService: DriveFileEntityService, + //private customEmojiService: CustomEmojiService, + //private reactionService: ReactionService, ) { + this.userEntityService = this.moduleRef.get('UserEntityService'); + this.driveFileEntityService = this.moduleRef.get('DriveFileEntityService'); + this.customEmojiService = this.moduleRef.get('CustomEmojiService'); + this.reactionService = this.moduleRef.get('ReactionService'); } async #hideNote(packedNote: Packed<'Note'>, meId: User['id'] | null) { diff --git a/packages/backend/src/services/entities/NoteReactionEntityService.ts b/packages/backend/src/services/entities/NoteReactionEntityService.ts index 23566f24ced0..a051cae6c992 100644 --- a/packages/backend/src/services/entities/NoteReactionEntityService.ts +++ b/packages/backend/src/services/entities/NoteReactionEntityService.ts @@ -6,20 +6,30 @@ import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { NoteReaction } from '@/models/entities/NoteReaction.js'; -import { ReactionService } from '../ReactionService.js'; -import { UserEntityService } from './UserEntityService.js'; -import { NoteEntityService } from './NoteEntityService.js'; +import type { ReactionService } from '../ReactionService.js'; +import type { UserEntityService } from './UserEntityService.js'; +import type { NoteEntityService } from './NoteEntityService.js'; +import { ModuleRef } from '@nestjs/core'; @Injectable() export class NoteReactionEntityService { + private userEntityService: UserEntityService; + private noteEntityService: NoteEntityService; + private reactionService: ReactionService; + constructor( + private moduleRef: ModuleRef, + @Inject('noteReactionsRepository') private noteReactionsRepository: typeof NoteReactions, - private userEntityService: UserEntityService, - private noteEntityService: NoteEntityService, - private reactionService: ReactionService, + //private userEntityService: UserEntityService, + //private noteEntityService: NoteEntityService, + //private reactionService: ReactionService, ) { + this.userEntityService = this.moduleRef.get('UserEntityService'); + this.noteEntityService = this.moduleRef.get('NoteEntityService'); + this.reactionService = this.moduleRef.get('ReactionService'); } public async pack( diff --git a/packages/backend/src/services/entities/NotificationEntityService.ts b/packages/backend/src/services/entities/NotificationEntityService.ts index 2dcb324cab6e..06a6c7a3ee1d 100644 --- a/packages/backend/src/services/entities/NotificationEntityService.ts +++ b/packages/backend/src/services/entities/NotificationEntityService.ts @@ -1,5 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; +import { ModuleRef } from '@nestjs/core'; import { DI } from '@/di-symbols.js'; import type { AccessTokens, NoteReactions , Notifications } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; @@ -7,14 +8,21 @@ import type { Notification } from '@/models/entities/Notification.js'; import type { NoteReaction } from '@/models/entities/NoteReaction.js'; import type { Note } from '@/models/entities/Note.js'; import type { Packed } from '@/misc/schema.js'; -import { CustomEmojiService } from '../CustomEmojiService.js'; -import { UserEntityService } from './UserEntityService.js'; -import { NoteEntityService } from './NoteEntityService.js'; -import { UserGroupInvitationEntityService } from './UserGroupInvitationEntityService.js'; +import type { CustomEmojiService } from '../CustomEmojiService.js'; +import type { UserEntityService } from './UserEntityService.js'; +import type { NoteEntityService } from './NoteEntityService.js'; +import type { UserGroupInvitationEntityService } from './UserGroupInvitationEntityService.js'; @Injectable() export class NotificationEntityService { + private userEntityService: UserEntityService; + private noteEntityService: NoteEntityService; + private userGroupInvitationEntityService: UserGroupInvitationEntityService; + private customEmojiService: CustomEmojiService; + constructor( + private moduleRef: ModuleRef, + @Inject('notificationsRepository') private notificationsRepository: typeof Notifications, @@ -24,11 +32,15 @@ export class NotificationEntityService { @Inject('accessTokensRepository') private accessTokensRepository: typeof AccessTokens, - private userEntityService: UserEntityService, - private noteEntityService: NoteEntityService, - private userGroupInvitationEntityService: UserGroupInvitationEntityService, - private customEmojiService: CustomEmojiService, + //private userEntityService: UserEntityService, + //private noteEntityService: NoteEntityService, + //private userGroupInvitationEntityService: UserGroupInvitationEntityService, + //private customEmojiService: CustomEmojiService, ) { + this.userEntityService = this.moduleRef.get('UserEntityService'); + this.noteEntityService = this.moduleRef.get('NoteEntityService'); + this.userGroupInvitationEntityService = this.moduleRef.get('UserGroupInvitationEntityService'); + this.customEmojiService = this.moduleRef.get('CustomEmojiService'); } public async pack( diff --git a/packages/backend/src/services/entities/UserEntityService.ts b/packages/backend/src/services/entities/UserEntityService.ts index fbf13de74ec1..1f3b67493b4f 100644 --- a/packages/backend/src/services/entities/UserEntityService.ts +++ b/packages/backend/src/services/entities/UserEntityService.ts @@ -1,22 +1,23 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import { EntityRepository, Repository, In, Not } from 'typeorm'; import Ajv from 'ajv'; +import { ModuleRef } from '@nestjs/core'; import { DI } from '@/di-symbols.js'; import type { Pages , AntennaNotes, Instances, MessagingMessages, UserSecurityKeys , Blockings, Mutings , Followings, FollowRequests, Users, DriveFiles, NoteUnreads, ChannelFollowings, Notifications, UserNotePinings, UserProfiles , AnnouncementReads, Announcements, UserGroupJoinings } from '@/models/index.js'; import { Config } from '@/config.js'; import type { Packed } from '@/misc/schema.js'; import type { Promiseable } from '@/prelude/await-all.js'; import { awaitAll } from '@/prelude/await-all.js'; -import { getAntennas } from '@/misc/antenna-cache.js'; import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js'; import { Cache } from '@/misc/cache.js'; import type { Instance } from '@/models/entities/Instance.js'; import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/entities/User.js'; -import { CustomEmojiService } from '../CustomEmojiService.js'; -import { NoteEntityService } from './NoteEntityService.js'; -import { DriveFileEntityService } from './DriveFileEntityService.js'; -import { PageEntityService } from './PageEntityService.js'; +import type { AntennaService } from '../AntennaService.js'; +import type { CustomEmojiService } from '../CustomEmojiService.js'; +import type { NoteEntityService } from './NoteEntityService.js'; +import type { DriveFileEntityService } from './DriveFileEntityService.js'; +import type { PageEntityService } from './PageEntityService.js'; type IsUserDetailed = Detailed extends true ? Packed<'UserDetailed'> : Packed<'UserLite'>; type IsMeAndIsUserDetailed = @@ -42,9 +43,16 @@ function isRemoteUser(user: User | { host: User['host'] }): boolean { @Injectable() export class UserEntityService { + private noteEntityService: NoteEntityService; + private driveFileEntityService: DriveFileEntityService; + private pageEntityService: PageEntityService; + private customEmojiService: CustomEmojiService; + private antennaService: AntennaService; #userInstanceCache: Cache; constructor( + private moduleRef: ModuleRef, + @Inject(DI.config) private config: Config, @@ -105,14 +113,17 @@ export class UserEntityService { @Inject('pagesRepository') private pagesRepository: typeof Pages, - // 循環参照のため / for circular dependency - @Inject(forwardRef(() => NoteEntityService)) - private noteEntityService: NoteEntityService, - - private driveFileEntityService: DriveFileEntityService, - private pageEntityService: PageEntityService, - private customEmojiService: CustomEmojiService, + //private noteEntityService: NoteEntityService, + //private driveFileEntityService: DriveFileEntityService, + //private pageEntityService: PageEntityService, + //private customEmojiService: CustomEmojiService, + //private antennaService: AntennaService, ) { + this.noteEntityService = this.moduleRef.get('NoteEntityService'); + this.driveFileEntityService = this.moduleRef.get('DriveFileEntityService'); + this.pageEntityService = this.moduleRef.get('PageEntityService'); + this.customEmojiService = this.moduleRef.get('CustomEmojiService'); + this.antennaService = this.moduleRef.get('AntennaService'); this.#userInstanceCache = new Cache(1000 * 60 * 60 * 3); } @@ -225,7 +236,7 @@ export class UserEntityService { } public async getHasUnreadAntenna(userId: User['id']): Promise { - const myAntennas = (await getAntennas()).filter(a => a.userId === userId); + const myAntennas = (await this.antennaService.getAntennas()).filter(a => a.userId === userId); const unread = myAntennas.length > 0 ? await this.antennaNotesRepository.findOneBy({ antennaId: In(myAntennas.map(x => x.id)), diff --git a/packages/backend/src/services/queue/QueueModule.ts b/packages/backend/src/services/queue/QueueModule.ts new file mode 100644 index 000000000000..7d4aac900087 --- /dev/null +++ b/packages/backend/src/services/queue/QueueModule.ts @@ -0,0 +1,112 @@ +import { Module } from '@nestjs/common'; +import Bull from 'bull'; +import { DI } from '@/di-symbols.js'; +import type { Config } from '@/config.js'; +import type { Provider } from '@nestjs/common'; +import type { DeliverJobData, InboxJobData, DbJobData, ObjectStorageJobData, EndedPollNotificationJobData, WebhookDeliverJobData } from '../../queue/types.js'; + +function q(config: Config, name: string, limitPerSec = -1) { + return new Bull(name, { + redis: { + port: config.redis.port, + host: config.redis.host, + family: config.redis.family == null ? 0 : config.redis.family, + password: config.redis.pass, + db: config.redis.db || 0, + }, + prefix: config.redis.prefix ? `${config.redis.prefix}:queue` : 'queue', + limiter: limitPerSec > 0 ? { + max: limitPerSec, + duration: 1000, + } : undefined, + settings: { + backoffStrategies: { + apBackoff, + }, + }, + }); +} + +// ref. https://github.com/misskey-dev/misskey/pull/7635#issue-971097019 +function apBackoff(attemptsMade: number, err: Error) { + const baseDelay = 60 * 1000; // 1min + const maxBackoff = 8 * 60 * 60 * 1000; // 8hours + let backoff = (Math.pow(2, attemptsMade) - 1) * baseDelay; + backoff = Math.min(backoff, maxBackoff); + backoff += Math.round(backoff * Math.random() * 0.2); + return backoff; +} + +export type SystemQueue = Bull.Queue>; +export type EndedPollNotificationQueue = Bull.Queue; +export type DeliverQueue = Bull.Queue; +export type InboxQueue = Bull.Queue; +export type DbQueue = Bull.Queue; +export type ObjectStorageQueue = Bull.Queue; +export type WebhookDeliverQueue = Bull.Queue; + +const $system: Provider = { + provide: 'queue:system', + useFactory: (config: Config) => q(config, 'system'), + inject: [DI.config], +}; + +const $endedPollNotification: Provider = { + provide: 'queue:endedPollNotification', + useFactory: (config: Config) => q(config, 'endedPollNotification'), + inject: [DI.config], +}; + +const $deliver: Provider = { + provide: 'queue:deliver', + useFactory: (config: Config) => q(config, 'deliver', config.deliverJobPerSec ?? 128), + inject: [DI.config], +}; + +const $inbox: Provider = { + provide: 'queue:inbox', + useFactory: (config: Config) => q(config, 'inbox', config.inboxJobPerSec ?? 16), + inject: [DI.config], +}; + +const $db: Provider = { + provide: 'queue:db', + useFactory: (config: Config) => q(config, 'db'), + inject: [DI.config], +}; + +const $objectStorage: Provider = { + provide: 'queue:objectStorage', + useFactory: (config: Config) => q(config, 'objectStorage'), + inject: [DI.config], +}; + +const $webhookDeliver: Provider = { + provide: 'queue:webhookDeliver', + useFactory: (config: Config) => q(config, 'webhookDeliver', 64), + inject: [DI.config], +}; + +@Module({ + imports: [ + ], + providers: [ + $system, + $endedPollNotification, + $deliver, + $inbox, + $db, + $objectStorage, + $webhookDeliver, + ], + exports: [ + $system, + $endedPollNotification, + $deliver, + $inbox, + $db, + $objectStorage, + $webhookDeliver, + ], +}) +export class QueueModule {} diff --git a/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts b/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts index 8f42039be6b2..6abac17f6b8f 100644 --- a/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts +++ b/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts @@ -4,7 +4,7 @@ import { DI } from '@/di-symbols.js'; import type { Followings , Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; interface IRecipe { diff --git a/packages/backend/src/services/remote/activitypub/ApInboxService.ts b/packages/backend/src/services/remote/activitypub/ApInboxService.ts index 3d98c4db0229..3e96b938b992 100644 --- a/packages/backend/src/services/remote/activitypub/ApInboxService.ts +++ b/packages/backend/src/services/remote/activitypub/ApInboxService.ts @@ -20,7 +20,7 @@ import { StatusError } from '@/misc/status-error.js'; import { UtilityService } from '@/services/UtilityService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; -import { QueueService } from '@/queue/queue.service.js'; +import { QueueService } from '@/services/QueueService.js'; import { MessagingService } from '@/services/MessagingService.js'; import { getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isPost, isRead, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js'; import { ApNoteService } from './models/ApNoteService.js'; diff --git a/packages/backend/src/services/remote/activitypub/ApRendererService.ts b/packages/backend/src/services/remote/activitypub/ApRendererService.ts index 191e7a71fb6e..87b6e6ce99c1 100644 --- a/packages/backend/src/services/remote/activitypub/ApRendererService.ts +++ b/packages/backend/src/services/remote/activitypub/ApRendererService.ts @@ -159,8 +159,8 @@ private driveFileEntityService: DriveFileEntityService, updated: emoji.updatedAt != null ? emoji.updatedAt.toISOString() : new Date().toISOString, icon: { type: 'Image', - mediaType: emoji.type || 'image/png', - url: emoji.publicUrl || emoji.originalUrl, // || emoji.originalUrl してるのは後方互換性のため + mediaType: emoji.type ?? 'image/png', + url: emoji.publicUrl ?? emoji.originalUrl, // || emoji.originalUrl してるのは後方互換性のため }, }; } @@ -339,7 +339,7 @@ private driveFileEntityService: DriveFileEntityService, id: In(note.mentions), }) : []; - const hashtagTags = (note.tags || []).map(tag => this.renderHashtag(tag)); + const hashtagTags = (note.tags ?? []).map(tag => this.renderHashtag(tag)); const mentionTags = mentionedUsers.map(u => this.renderMention(u)); const files = await getPromisedFiles(note.fileIds); @@ -449,7 +449,7 @@ private driveFileEntityService: DriveFileEntityService, const emojis = await this.#getEmojis(user.emojis); const apemojis = emojis.map(emoji => this.renderEmoji(emoji)); - const hashtagTags = (user.tags || []).map(tag => this.renderHashtag(tag)); + const hashtagTags = (user.tags ?? []).map(tag => this.renderHashtag(tag)); const tag = [ ...apemojis, @@ -498,7 +498,7 @@ private driveFileEntityService: DriveFileEntityService, type: 'Question', id: `${this.config.url}/questions/${note.id}`, actor: `${this.config.url}/users/${user.id}`, - content: note.text || '', + content: note.text ?? '', [poll.multiple ? 'anyOf' : 'oneOf']: poll.choices.map((text, i) => ({ name: text, _misskey_votes: poll.votes[i], diff --git a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts index a77ad6432e6a..b7b13d854667 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts @@ -1,42 +1,43 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { DataSource } from 'typeorm'; +import { ModuleRef } from '@nestjs/core'; import { DI } from '@/di-symbols.js'; import type { Followings , Instances, UserProfiles, UserPublickeys, Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type { CacheableUser, IRemoteUser } from '@/models/entities/User.js'; import { User } from '@/models/entities/User.js'; import { truncate } from '@/misc/truncate.js'; -import { UserCacheService } from '@/services/UserCacheService.js'; +import type { UserCacheService } from '@/services/UserCacheService.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import type Logger from '@/logger.js'; import type { Note } from '@/models/entities/Note.js'; -import { IdService } from '@/services/IdService.js'; -import { MfmService } from '@/services/MfmService.js'; +import type { IdService } from '@/services/IdService.js'; +import type { MfmService } from '@/services/MfmService.js'; import type { Emoji } from '@/models/entities/Emoji.js'; import { toArray } from '@/prelude/array.js'; -import { GlobalEventService } from '@/services/GlobalEventService.js'; -import { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; -import { FetchInstanceMetadataService } from '@/services/FetchInstanceMetadataService.js'; +import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import type { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; +import type { FetchInstanceMetadataService } from '@/services/FetchInstanceMetadataService.js'; import { UserProfile } from '@/models/entities/UserProfile.js'; import { UserPublickey } from '@/models/entities/UserPublickey.js'; -import UsersChart from '@/services/chart/charts/users.js'; -import InstanceChart from '@/services/chart/charts/instance.js'; -import { HashtagService } from '@/services/HashtagService.js'; +import type UsersChart from '@/services/chart/charts/users.js'; +import type InstanceChart from '@/services/chart/charts/instance.js'; +import type { HashtagService } from '@/services/HashtagService.js'; import { UserNotePining } from '@/models/entities/UserNotePining.js'; import { StatusError } from '@/misc/status-error.js'; -import { UtilityService } from '@/services/UtilityService.js'; -import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import type { UtilityService } from '@/services/UtilityService.js'; +import type { UserEntityService } from '@/services/entities/UserEntityService.js'; import { getApId, getApType, getOneApHrefNullable, isActor, isCollection, isCollectionOrOrderedCollection, isPropertyValue } from '../type.js'; -import { ApMfmService } from '../ApMfmService.js'; -import { ApResolverService } from '../ApResolverService.js'; -import { ApLoggerService } from '../ApLoggerService.js'; import { extractApHashtags } from './tag.js'; +import type { ApNoteService } from './ApNoteService.js'; +import type { ApMfmService } from '../ApMfmService.js'; +import type { ApResolverService , Resolver } from '../ApResolverService.js'; +import type { ApLoggerService } from '../ApLoggerService.js'; // eslint-disable-next-line @typescript-eslint/consistent-type-imports -import { ApNoteService } from './ApNoteService.js'; -import { ApImageService } from './ApImageService.js'; -import type { Resolver } from '../ApResolverService.js'; +import type { ApImageService } from './ApImageService.js'; + import type { IActor, IObject , IApPropertyValue } from '../type.js'; const nameLength = 128; @@ -74,9 +75,27 @@ function addService(target: { [x: string]: any }, source: IApPropertyValue) { @Injectable() export class ApPersonService { + private utilityService: UtilityService; + private userEntityService: UserEntityService; + private idService: IdService; + private globalEventService: GlobalEventService; + private federatedInstanceService: FederatedInstanceService; + private fetchInstanceMetadataService: FetchInstanceMetadataService; + private userCacheService: UserCacheService; + private apResolverService: ApResolverService; + private apNoteService: ApNoteService; + private apImageService: ApImageService; + private apMfmService: ApMfmService; + private mfmService: MfmService; + private hashtagService: HashtagService; + private usersChart: UsersChart; + private instanceChart: InstanceChart; + private apLoggerService: ApLoggerService; #logger: Logger; constructor( + private moduleRef: ModuleRef, + @Inject(DI.config) private config: Config, @@ -98,27 +117,39 @@ export class ApPersonService { @Inject('followingsRepository') private followingsRepository: typeof Followings, - private utilityService: UtilityService, - private userEntityService: UserEntityService, - private idService: IdService, - private globalEventService: GlobalEventService, - private federatedInstanceService: FederatedInstanceService, - private fetchInstanceMetadataService: FetchInstanceMetadataService, - private userCacheService: UserCacheService, - private apResolverService: ApResolverService, - - // 循環参照のため / for circular dependency - @Inject(forwardRef(() => ApNoteService)) - private apNoteService: ApNoteService, - - private apImageService: ApImageService, - private apMfmService: ApMfmService, - private mfmService: MfmService, - private hashtagService: HashtagService, - private usersChart: UsersChart, - private instanceChart: InstanceChart, - private apLoggerService: ApLoggerService, + //private utilityService: UtilityService, + //private userEntityService: UserEntityService, + //private idService: IdService, + //private globalEventService: GlobalEventService, + //private federatedInstanceService: FederatedInstanceService, + //private fetchInstanceMetadataService: FetchInstanceMetadataService, + //private userCacheService: UserCacheService, + //private apResolverService: ApResolverService, + //private apNoteService: ApNoteService, + //private apImageService: ApImageService, + //private apMfmService: ApMfmService, + //private mfmService: MfmService, + //private hashtagService: HashtagService, + //private usersChart: UsersChart, + //private instanceChart: InstanceChart, + //private apLoggerService: ApLoggerService, ) { + this.utilityService = this.moduleRef.get('UtilityService'); + this.userEntityService = this.moduleRef.get('UserEntityService'); + this.idService = this.moduleRef.get('IdService'); + this.globalEventService = this.moduleRef.get('GlobalEventService'); + this.federatedInstanceService = this.moduleRef.get('FederatedInstanceService'); + this.fetchInstanceMetadataService = this.moduleRef.get('FetchInstanceMetadataService'); + this.userCacheService = this.moduleRef.get('UserCacheService'); + this.apResolverService = this.moduleRef.get('ApResolverService'); + this.apNoteService = this.moduleRef.get('ApNoteService'); + this.apImageService = this.moduleRef.get('ApImageService'); + this.apMfmService = this.moduleRef.get('ApMfmService'); + this.mfmService = this.moduleRef.get('MfmService'); + this.hashtagService = this.moduleRef.get('HashtagService'); + this.usersChart = this.moduleRef.get('UsersChart'); + this.instanceChart = this.moduleRef.get('InstanceChart'); + this.apLoggerService = this.moduleRef.get('ApLoggerService'); this.#logger = this.apLoggerService.logger; } From 2647e5c63d079911dac31a49bae1fb6595b466df Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 23:26:05 +0900 Subject: [PATCH 163/180] wip --- .../services/chart/ChartManagementService.ts | 24 +++++++++---------- .../entities/AbuseUserReportEntityService.ts | 6 ++--- .../services/entities/AntennaEntityService.ts | 6 ++--- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/backend/src/services/chart/ChartManagementService.ts b/packages/backend/src/services/chart/ChartManagementService.ts index 683ef8f930ba..2020ca0ca6af 100644 --- a/packages/backend/src/services/chart/ChartManagementService.ts +++ b/packages/backend/src/services/chart/ChartManagementService.ts @@ -1,18 +1,18 @@ import { Injectable, Inject } from '@nestjs/common'; import { beforeShutdown } from '@/misc/before-shutdown.js'; -import type FederationChart from './charts/federation.js'; -import type NotesChart from './charts/notes.js'; -import type UsersChart from './charts/users.js'; -import type ActiveUsersChart from './charts/active-users.js'; -import type InstanceChart from './charts/instance.js'; -import type PerUserNotesChart from './charts/per-user-notes.js'; -import type DriveChart from './charts/drive.js'; -import type PerUserReactionsChart from './charts/per-user-reactions.js'; -import type HashtagChart from './charts/hashtag.js'; -import type PerUserFollowingChart from './charts/per-user-following.js'; -import type PerUserDriveChart from './charts/per-user-drive.js'; -import type ApRequestChart from './charts/ap-request.js'; +import FederationChart from './charts/federation.js'; +import NotesChart from './charts/notes.js'; +import UsersChart from './charts/users.js'; +import ActiveUsersChart from './charts/active-users.js'; +import InstanceChart from './charts/instance.js'; +import PerUserNotesChart from './charts/per-user-notes.js'; +import DriveChart from './charts/drive.js'; +import PerUserReactionsChart from './charts/per-user-reactions.js'; +import HashtagChart from './charts/hashtag.js'; +import PerUserFollowingChart from './charts/per-user-following.js'; +import PerUserDriveChart from './charts/per-user-drive.js'; +import ApRequestChart from './charts/ap-request.js'; @Injectable() export class ChartManagementService { diff --git a/packages/backend/src/services/entities/AbuseUserReportEntityService.ts b/packages/backend/src/services/entities/AbuseUserReportEntityService.ts index 135b22d6a3be..3fe6c3677a1a 100644 --- a/packages/backend/src/services/entities/AbuseUserReportEntityService.ts +++ b/packages/backend/src/services/entities/AbuseUserReportEntityService.ts @@ -8,8 +8,8 @@ import { UserEntityService } from './UserEntityService.js'; @Injectable() export class AbuseUserReportEntityService { constructor( - @Inject('abuseUserReportRepository') - private abuseUserReportRepository: typeof AbuseUserReports, + @Inject('abuseUserReportsRepository') + private abuseUserReportsRepository: typeof AbuseUserReports, private userEntityService: UserEntityService, ) { @@ -18,7 +18,7 @@ export class AbuseUserReportEntityService { public async pack( src: AbuseUserReport['id'] | AbuseUserReport, ) { - const report = typeof src === 'object' ? src : await this.abuseUserReportRepository.findOneByOrFail({ id: src }); + const report = typeof src === 'object' ? src : await this.abuseUserReportsRepository.findOneByOrFail({ id: src }); return await awaitAll({ id: report.id, diff --git a/packages/backend/src/services/entities/AntennaEntityService.ts b/packages/backend/src/services/entities/AntennaEntityService.ts index 72a1994b8a50..ecfe6e41f6ff 100644 --- a/packages/backend/src/services/entities/AntennaEntityService.ts +++ b/packages/backend/src/services/entities/AntennaEntityService.ts @@ -8,8 +8,8 @@ import type { Antenna } from '@/models/entities/Antenna.js'; @Injectable() export class AntennaEntityService { constructor( - @Inject('antennaRepository') - private antennaRepository: typeof Antennas, + @Inject('antennasRepository') + private antennasRepository: typeof Antennas, @Inject('antennaNotesRepository') private antennaNotesRepository: typeof AntennaNotes, @@ -22,7 +22,7 @@ export class AntennaEntityService { public async pack( src: Antenna['id'] | Antenna, ): Promise> { - const antenna = typeof src === 'object' ? src : await this.antennaRepository.findOneByOrFail({ id: src }); + const antenna = typeof src === 'object' ? src : await this.antennasRepository.findOneByOrFail({ id: src }); const hasUnreadNote = (await this.antennaNotesRepository.findOneBy({ antennaId: antenna.id, read: false })) != null; const userGroupJoining = antenna.userGroupJoiningId ? await this.userGroupJoiningsRepository.findOneBy({ id: antenna.userGroupJoiningId }) : null; From 99f2c76736b6bc9f94e36ecaf7be9e7704a80d12 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 23:34:58 +0900 Subject: [PATCH 164/180] wip --- packages/backend/src/server/api/EndpointsModule.ts | 2 ++ packages/backend/src/services/AntennaService.ts | 1 + .../src/services/entities/SigninEntityService.ts | 4 ++-- .../backend/src/services/entities/UserEntityService.ts | 10 +++++----- .../backend/src/services/remote/WebfingerService.ts | 4 ++-- .../src/services/remote/activitypub/ApLoggerService.ts | 2 +- .../src/services/remote/activitypub/ApMfmService.ts | 7 ++++--- .../services/remote/activitypub/ApRequestService.ts | 6 +++--- .../remote/activitypub/models/ApImageService.ts | 10 +++++----- .../remote/activitypub/models/ApNoteService.ts | 1 - .../remote/activitypub/models/ApQuestionService.ts | 7 ++++--- 11 files changed, 29 insertions(+), 25 deletions(-) diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 103bee4b7e1b..a24203f897bc 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -313,12 +313,14 @@ import * as ep___users_show from './endpoints/users/show.js'; import * as ep___users_stats from './endpoints/users/stats.js'; import * as ep___fetchRss from './endpoints/fetch-rss.js'; import * as ep___admin_driveCapOverride from './endpoints/admin/drive-capacity-override.js'; +import { GetterService } from './common/GetterService.js'; @Module({ imports: [ CoreModule, ], providers: [ + GetterService, { provide: 'ep:admin/meta', useClass: ep___admin_meta.default }, { provide: 'ep:admin/abuse-user-reports', useClass: ep___admin_abuseUserReports.default }, { provide: 'ep:admin/accounts/create', useClass: ep___admin_accounts_create.default }, diff --git a/packages/backend/src/services/AntennaService.ts b/packages/backend/src/services/AntennaService.ts index 62a4b169e5c3..90a31a2fa6bf 100644 --- a/packages/backend/src/services/AntennaService.ts +++ b/packages/backend/src/services/AntennaService.ts @@ -12,6 +12,7 @@ import { Cache } from '@/misc/cache.js'; import type { Packed } from '@/misc/schema.js'; import { DI } from '@/di-symbols.js'; import { UtilityService } from './UtilityService.js'; +import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() export class AntennaService implements OnApplicationShutdown { diff --git a/packages/backend/src/services/entities/SigninEntityService.ts b/packages/backend/src/services/entities/SigninEntityService.ts index 88bcb7301766..9cf762026b1d 100644 --- a/packages/backend/src/services/entities/SigninEntityService.ts +++ b/packages/backend/src/services/entities/SigninEntityService.ts @@ -11,8 +11,8 @@ import { UserEntityService } from './UserEntityService.js'; @Injectable() export class SigninEntityService { constructor( - @Inject('signinRepository') - private signinRepository: typeof Signins, + @Inject('signinsRepository') + private signinsRepository: typeof Signins, private userEntityService: UserEntityService, ) { diff --git a/packages/backend/src/services/entities/UserEntityService.ts b/packages/backend/src/services/entities/UserEntityService.ts index 1f3b67493b4f..5599b1d7b9d1 100644 --- a/packages/backend/src/services/entities/UserEntityService.ts +++ b/packages/backend/src/services/entities/UserEntityService.ts @@ -77,8 +77,8 @@ export class UserEntityService { @Inject('driveFilesRepository') private driveFilesRepository: typeof DriveFiles, - @Inject('noteUnreadRepository') - private noteUnreadRepository: typeof NoteUnreads, + @Inject('noteUnreadsRepository') + private noteUnreadsRepository: typeof NoteUnreads, @Inject('channelFollowingsRepository') private channelFollowingsRepository: typeof ChannelFollowings, @@ -249,7 +249,7 @@ export class UserEntityService { public async getHasUnreadChannel(userId: User['id']): Promise { const channels = await this.channelFollowingsRepository.findBy({ followerId: userId }); - const unread = channels.length > 0 ? await this.noteUnreadRepository.findOneBy({ + const unread = channels.length > 0 ? await this.noteUnreadsRepository.findOneBy({ userId: userId, noteChannelId: In(channels.map(x => x.followeeId)), }) : null; @@ -446,11 +446,11 @@ export class UserEntityService { isExplorable: user.isExplorable, isDeleted: user.isDeleted, hideOnlineStatus: user.hideOnlineStatus, - hasUnreadSpecifiedNotes: this.noteUnreadRepository.count({ + hasUnreadSpecifiedNotes: this.noteUnreadsRepository.count({ where: { userId: user.id, isSpecified: true }, take: 1, }).then(count => count > 0), - hasUnreadMentions: this.noteUnreadRepository.count({ + hasUnreadMentions: this.noteUnreadsRepository.count({ where: { userId: user.id, isMentioned: true }, take: 1, }).then(count => count > 0), diff --git a/packages/backend/src/services/remote/WebfingerService.ts b/packages/backend/src/services/remote/WebfingerService.ts index b04c70c7d6cc..e7ec1956a596 100644 --- a/packages/backend/src/services/remote/WebfingerService.ts +++ b/packages/backend/src/services/remote/WebfingerService.ts @@ -1,9 +1,9 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import { query as urlQuery } from '@/prelude/url.js'; -import type { HttpRequestService } from '@/services/HttpRequestService.js'; +import { HttpRequestService } from '@/services/HttpRequestService.js'; type ILink = { href: string; diff --git a/packages/backend/src/services/remote/activitypub/ApLoggerService.ts b/packages/backend/src/services/remote/activitypub/ApLoggerService.ts index eaa1ffb1609b..bf336d84acf6 100644 --- a/packages/backend/src/services/remote/activitypub/ApLoggerService.ts +++ b/packages/backend/src/services/remote/activitypub/ApLoggerService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; -import type { RemoteLoggerService } from '@/services/remote/RemoteLoggerService.js'; +import { RemoteLoggerService } from '@/services/remote/RemoteLoggerService.js'; @Injectable() export class ApLoggerService { diff --git a/packages/backend/src/services/remote/activitypub/ApMfmService.ts b/packages/backend/src/services/remote/activitypub/ApMfmService.ts index 222cf6168a17..eb4106b249b6 100644 --- a/packages/backend/src/services/remote/activitypub/ApMfmService.ts +++ b/packages/backend/src/services/remote/activitypub/ApMfmService.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; -import type { MfmService } from '@/services/MfmService.js'; -import type { IObject } from './type'; +import { Config } from '@/config.js'; +import { MfmService } from '@/services/MfmService.js'; +import { extractApHashtagObjects } from './models/tag.js'; +import type { IObject } from './type.js'; @Injectable() export class ApMfmService { diff --git a/packages/backend/src/services/remote/activitypub/ApRequestService.ts b/packages/backend/src/services/remote/activitypub/ApRequestService.ts index 65ba3334bea4..e01185511f26 100644 --- a/packages/backend/src/services/remote/activitypub/ApRequestService.ts +++ b/packages/backend/src/services/remote/activitypub/ApRequestService.ts @@ -2,10 +2,10 @@ import * as crypto from 'node:crypto'; import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type { User } from '@/models/entities/User.js'; -import type { UserKeypairStoreService } from '@/services/UserKeypairStoreService.js'; -import type { HttpRequestService } from '@/services/HttpRequestService.js'; +import { UserKeypairStoreService } from '@/services/UserKeypairStoreService.js'; +import { HttpRequestService } from '@/services/HttpRequestService.js'; type Request = { url: string; diff --git a/packages/backend/src/services/remote/activitypub/models/ApImageService.ts b/packages/backend/src/services/remote/activitypub/models/ApImageService.ts index b524ed25384a..5ea446346e52 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApImageService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApImageService.ts @@ -1,16 +1,16 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { DriveFiles } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type { CacheableRemoteUser } from '@/models/entities/User.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; -import type { MetaService } from '@/services/MetaService.js'; +import { MetaService } from '@/services/MetaService.js'; import { truncate } from '@/misc/truncate.js'; import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js'; -import type { DriveService } from '@/services/DriveService.js'; +import { DriveService } from '@/services/DriveService.js'; import type Logger from '@/logger.js'; -import type { ApResolverService } from '../ApResolverService.js'; -import type { ApLoggerService } from '../ApLoggerService.js'; +import { ApResolverService } from '../ApResolverService.js'; +import { ApLoggerService } from '../ApLoggerService.js'; @Injectable() export class ApImageService { diff --git a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts index aa06630855f6..a68306409c43 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts @@ -2,7 +2,6 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { DI } from '@/di-symbols.js'; import type { MessagingMessages , Polls , Emojis, Users } from '@/models/index.js'; - import { Config } from '@/config.js'; import type { CacheableRemoteUser } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; diff --git a/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts b/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts index 0f10efecbb55..53d6302257a7 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts @@ -1,12 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { Notes , Polls } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type { IPoll } from '@/models/entities/Poll.js'; import type Logger from '@/logger.js'; import { isQuestion } from '../type.js'; -import type { ApLoggerService } from '../ApLoggerService.js'; -import type { Resolver , ApResolverService } from '../ApResolverService.js'; +import { ApLoggerService } from '../ApLoggerService.js'; +import { ApResolverService } from '../ApResolverService.js'; +import type { Resolver } from '../ApResolverService.js'; import type { IObject , IQuestion } from '../type.js'; @Injectable() From c762a4d9cbfc34bff7fddf13ac50d4836e64a7e3 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 23:36:45 +0900 Subject: [PATCH 165/180] Update CoreModule.ts --- packages/backend/src/services/CoreModule.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/backend/src/services/CoreModule.ts b/packages/backend/src/services/CoreModule.ts index cf549469d7bd..ecf9a2c1408f 100644 --- a/packages/backend/src/services/CoreModule.ts +++ b/packages/backend/src/services/CoreModule.ts @@ -233,6 +233,7 @@ import { QueueService } from './QueueService.js'; QueueService, ], exports: [ + QueueModule, AccountUpdateService, AiService, AntennaService, From 4b474510b9b4c8452b2aba5f856f05673ae01b41 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 23:44:57 +0900 Subject: [PATCH 166/180] wip --- packages/backend/src/server/ServerModule.ts | 9 +++++++++ packages/backend/src/server/api/EndpointsModule.ts | 2 ++ .../api/endpoints/i/read-all-messaging-messages.ts | 6 +++--- .../src/server/api/endpoints/i/read-all-unread-notes.ts | 6 +++--- .../api/endpoints/users/groups/invitations/accept.ts | 6 +++--- .../src/server/api/endpoints/users/groups/invite.ts | 6 +++--- .../src/server/api/endpoints/users/groups/leave.ts | 6 +++--- .../src/server/api/endpoints/users/groups/pull.ts | 6 +++--- 8 files changed, 29 insertions(+), 18 deletions(-) diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts index 3e956e545630..70c1c0d5dbc3 100644 --- a/packages/backend/src/server/ServerModule.ts +++ b/packages/backend/src/server/ServerModule.ts @@ -21,6 +21,9 @@ import { SigninApiService } from './api/SigninApiService.js'; import { SigninService } from './api/SigninService.js'; import { SignupApiService } from './api/SignupApiService.js'; import { StreamingApiServerService } from './api/StreamingApiServerService.js'; +import { ClientServerService } from './web/ClientServerService.js'; +import { FeedService } from './web/FeedService.js'; +import { UrlPreviewService } from './web/UrlPreviewService.js'; @Module({ imports: [ @@ -28,6 +31,9 @@ import { StreamingApiServerService } from './api/StreamingApiServerService.js'; CoreModule, ], providers: [ + ClientServerService, + FeedService, + UrlPreviewService, ActivityPubServerService, FileServerService, MediaProxyServerService, @@ -49,5 +55,8 @@ import { StreamingApiServerService } from './api/StreamingApiServerService.js'; SignupApiService, StreamingApiServerService, ], + exports: [ + ServerService, + ], }) export class ServerModule {} diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index a24203f897bc..c199b5469e99 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -314,6 +314,7 @@ import * as ep___users_stats from './endpoints/users/stats.js'; import * as ep___fetchRss from './endpoints/fetch-rss.js'; import * as ep___admin_driveCapOverride from './endpoints/admin/drive-capacity-override.js'; import { GetterService } from './common/GetterService.js'; +import { ApiLoggerService } from './ApiLoggerService.js'; @Module({ imports: [ @@ -321,6 +322,7 @@ import { GetterService } from './common/GetterService.js'; ], providers: [ GetterService, + ApiLoggerService, { provide: 'ep:admin/meta', useClass: ep___admin_meta.default }, { provide: 'ep:admin/abuse-user-reports', useClass: ep___admin_abuseUserReports.default }, { provide: 'ep:admin/accounts/create', useClass: ep___admin_accounts_create.default }, diff --git a/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts b/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts index fa2b9c4f789e..cfb9580a2c7b 100644 --- a/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts +++ b/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts @@ -24,8 +24,8 @@ export default class extends Endpoint { @Inject('messagingMessagesRepository') private messagingMessagesRepository: typeof MessagingMessages, - @Inject('userGroupJoinings') - private userGroupJoinings: typeof UserGroupJoinings, + @Inject('userGroupJoiningsRepository') + private userGroupJoiningsRepository: typeof UserGroupJoinings, private globalEventService: GlobalEventService, ) { @@ -38,7 +38,7 @@ export default class extends Endpoint { isRead: true, }); - const joinings = await this.userGroupJoinings.findBy({ userId: me.id }); + const joinings = await this.userGroupJoiningsRepository.findBy({ userId: me.id }); await Promise.all(joinings.map(j => this.messagingMessagesRepository.createQueryBuilder().update() .set({ diff --git a/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts b/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts index b5b0c750af4f..872c01f53acb 100644 --- a/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts +++ b/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts @@ -21,14 +21,14 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('noteUnreadsJoinings') - private noteUnreadsJoinings: typeof NoteUnreads, + @Inject('noteUnreadsRepository') + private noteUnreadsRepository: typeof NoteUnreads, private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { // Remove documents - await this.noteUnreadsJoinings.delete({ + await this.noteUnreadsRepository.delete({ userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts b/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts index 8c068876d110..641322479d1a 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts @@ -38,8 +38,8 @@ export default class extends Endpoint { @Inject('userGroupInvitationsRepository') private userGroupInvitationsRepository: typeof UserGroupInvitations, - @Inject('userGroupJoiningRepository') - private userGroupJoiningRepository: typeof UserGroupJoinings, + @Inject('userGroupJoiningsRepository') + private userGroupJoiningsRepository: typeof UserGroupJoinings, private idService: IdService, ) { @@ -58,7 +58,7 @@ export default class extends Endpoint { } // Push the user - await this.userGroupJoiningRepository.insert({ + await this.userGroupJoiningsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/users/groups/invite.ts b/packages/backend/src/server/api/endpoints/users/groups/invite.ts index 245668ed361b..4523e273a545 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invite.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invite.ts @@ -62,8 +62,8 @@ export default class extends Endpoint { @Inject('userGroupInvitationsRepository') private userGroupInvitationsRepository: typeof UserGroupInvitations, - @Inject('userGroupJoiningRepository') - private userGroupJoiningRepository: typeof UserGroupJoinings, + @Inject('userGroupJoiningsRepository') + private userGroupJoiningsRepository: typeof UserGroupJoinings, private idService: IdService, private getterService: GetterService, @@ -86,7 +86,7 @@ export default class extends Endpoint { throw err; }); - const joining = await this.userGroupJoiningRepository.findOneBy({ + const joining = await this.userGroupJoiningsRepository.findOneBy({ userGroupId: userGroup.id, userId: user.id, }); diff --git a/packages/backend/src/server/api/endpoints/users/groups/leave.ts b/packages/backend/src/server/api/endpoints/users/groups/leave.ts index 9d2fa738a9b2..60e95eb3a105 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/leave.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/leave.ts @@ -42,8 +42,8 @@ export default class extends Endpoint { @Inject('userGroupsRepository') private userGroupsRepository: typeof UserGroups, - @Inject('userGroupJoiningRepository') - private userGroupJoiningRepository: typeof UserGroupJoinings, + @Inject('userGroupJoiningsRepository') + private userGroupJoiningsRepository: typeof UserGroupJoinings, ) { super(meta, paramDef, async (ps, me) => { // Fetch the group @@ -59,7 +59,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.youAreOwner); } - await this.userGroupJoiningRepository.delete({ userGroupId: userGroup.id, userId: me.id }); + await this.userGroupJoiningsRepository.delete({ userGroupId: userGroup.id, userId: me.id }); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/groups/pull.ts b/packages/backend/src/server/api/endpoints/users/groups/pull.ts index 9365fee7fe63..54331920839a 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/pull.ts @@ -51,8 +51,8 @@ export default class extends Endpoint { @Inject('userGroupsRepository') private userGroupsRepository: typeof UserGroups, - @Inject('userGroupJoiningRepository') - private userGroupJoiningRepository: typeof UserGroupJoinings, + @Inject('userGroupJoiningsRepository') + private userGroupJoiningsRepository: typeof UserGroupJoinings, private getterService: GetterService, ) { @@ -78,7 +78,7 @@ export default class extends Endpoint { } // Pull the user - await this.userGroupJoiningRepository.delete({ userGroupId: userGroup.id, userId: user.id }); + await this.userGroupJoiningsRepository.delete({ userGroupId: userGroup.id, userId: user.id }); }); } } From 567b4ac6153875ef732be5303940064b010d3163 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 16 Sep 2022 23:49:55 +0900 Subject: [PATCH 167/180] Update ServerModule.ts --- packages/backend/src/server/ServerModule.ts | 30 +++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts index 70c1c0d5dbc3..fdb0e2d49c3d 100644 --- a/packages/backend/src/server/ServerModule.ts +++ b/packages/backend/src/server/ServerModule.ts @@ -24,6 +24,21 @@ import { StreamingApiServerService } from './api/StreamingApiServerService.js'; import { ClientServerService } from './web/ClientServerService.js'; import { FeedService } from './web/FeedService.js'; import { UrlPreviewService } from './web/UrlPreviewService.js'; +import { MainChannelService } from './api/stream/channels/main.js'; +import { AdminChannelService } from './api/stream/channels/admin.js'; +import { AntennaChannelService } from './api/stream/channels/antenna.js'; +import { ChannelChannelService } from './api/stream/channels/channel.js'; +import { DriveChannelService } from './api/stream/channels/drive.js'; +import { GlobalTimelineChannelService } from './api/stream/channels/global-timeline.js'; +import { HashtagChannelService } from './api/stream/channels/hashtag.js'; +import { HomeTimelineChannelService } from './api/stream/channels/home-timeline.js'; +import { HybridTimelineChannelService } from './api/stream/channels/hybrid-timeline.js'; +import { LocalTimelineChannelService } from './api/stream/channels/local-timeline.js'; +import { MessagingIndexChannelService } from './api/stream/channels/messaging-index.js'; +import { MessagingChannelService } from './api/stream/channels/messaging.js'; +import { QueueStatsChannelService } from './api/stream/channels/queue-stats.js'; +import { ServerStatsChannelService } from './api/stream/channels/server-stats.js'; +import { UserListChannelService } from './api/stream/channels/user-list.js'; @Module({ imports: [ @@ -54,6 +69,21 @@ import { UrlPreviewService } from './web/UrlPreviewService.js'; SigninService, SignupApiService, StreamingApiServerService, + MainChannelService, + AdminChannelService, + AntennaChannelService, + ChannelChannelService, + DriveChannelService, + GlobalTimelineChannelService, + HashtagChannelService, + HomeTimelineChannelService, + HybridTimelineChannelService, + LocalTimelineChannelService, + MessagingIndexChannelService, + MessagingChannelService, + QueueStatsChannelService, + ServerStatsChannelService, + UserListChannelService, ], exports: [ ServerService, From 80935d26a6b897eb6808a7a9196fff3065e83d7d Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 17 Sep 2022 00:10:07 +0900 Subject: [PATCH 168/180] wip --- .../api/endpoints/admin/accounts/create.ts | 7 +- packages/backend/src/services/CoreModule.ts | 345 ++++++++++++++++++ 2 files changed, 349 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts index e9e640d071ed..106dcaf71bab 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts @@ -1,9 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Users } from '@/models/index.js'; +import type { Users } from '@/models/index.js'; import { SignupService } from '@/services/SignupService.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { localUsernameSchema, passwordSchema } from '@/models/entities/User.js'; export const meta = { tags: ['admin'], @@ -24,8 +25,8 @@ export const meta = { export const paramDef = { type: 'object', properties: { - username: Users.localUsernameSchema, - password: Users.passwordSchema, + username: localUsernameSchema, + password: passwordSchema, }, required: ['username', 'password'], } as const; diff --git a/packages/backend/src/services/CoreModule.ts b/packages/backend/src/services/CoreModule.ts index ecf9a2c1408f..f6dd0debb9d8 100644 --- a/packages/backend/src/services/CoreModule.ts +++ b/packages/backend/src/services/CoreModule.ts @@ -113,6 +113,123 @@ import { ApPersonService } from './remote/activitypub/models/ApPersonService.js' import { ApQuestionService } from './remote/activitypub/models/ApQuestionService.js'; import { QueueModule } from './queue/QueueModule.js'; import { QueueService } from './QueueService.js'; +import type { Provider } from '@nestjs/common'; + +//#region 文字列ベースでのinjection用(循環参照対応のため) +const $AccountUpdateService: Provider = { provide: 'AccountUpdateService', useClass: AccountUpdateService }; +const $AiService: Provider = { provide: 'AiService', useClass: AiService }; +const $AntennaService: Provider = { provide: 'AntennaService', useClass: AntennaService }; +const $AppLockService: Provider = { provide: 'AppLockService', useClass: AppLockService }; +const $CaptchaService: Provider = { provide: 'CaptchaService', useClass: CaptchaService }; +const $CreateNotificationService: Provider = { provide: 'CreateNotificationService', useClass: CreateNotificationService }; +const $CreateSystemUserService: Provider = { provide: 'CreateSystemUserService', useClass: CreateSystemUserService }; +const $CustomEmojiService: Provider = { provide: 'CustomEmojiService', useClass: CustomEmojiService }; +const $DeleteAccountService: Provider = { provide: 'DeleteAccountService', useClass: DeleteAccountService }; +const $DownloadService: Provider = { provide: 'DownloadService', useClass: DownloadService }; +const $DriveService: Provider = { provide: 'DriveService', useClass: DriveService }; +const $EmailService: Provider = { provide: 'EmailService', useClass: EmailService }; +const $FederatedInstanceService: Provider = { provide: 'FederatedInstanceService', useClass: FederatedInstanceService }; +const $FetchInstanceMetadataService: Provider = { provide: 'FetchInstanceMetadataService', useClass: FetchInstanceMetadataService }; +const $GlobalEventService: Provider = { provide: 'GlobalEventService', useClass: GlobalEventService }; +const $HashtagService: Provider = { provide: 'HashtagService', useClass: HashtagService }; +const $HttpRequestService: Provider = { provide: 'HttpRequestService', useClass: HttpRequestService }; +const $IdService: Provider = { provide: 'IdService', useClass: IdService }; +const $ImageProcessingService: Provider = { provide: 'ImageProcessingService', useClass: ImageProcessingService }; +const $InstanceActorService: Provider = { provide: 'InstanceActorService', useClass: InstanceActorService }; +const $InternalStorageService: Provider = { provide: 'InternalStorageService', useClass: InternalStorageService }; +const $MessagingService: Provider = { provide: 'MessagingService', useClass: MessagingService }; +const $MetaService: Provider = { provide: 'MetaService', useClass: MetaService }; +const $MfmService: Provider = { provide: 'MfmService', useClass: MfmService }; +const $ModerationLogService: Provider = { provide: 'ModerationLogService', useClass: ModerationLogService }; +const $NoteCreateService: Provider = { provide: 'NoteCreateService', useClass: NoteCreateService }; +const $NoteDeleteService: Provider = { provide: 'NoteDeleteService', useClass: NoteDeleteService }; +const $NotePiningService: Provider = { provide: 'NotePiningService', useClass: NotePiningService }; +const $NoteReadService: Provider = { provide: 'NoteReadService', useClass: NoteReadService }; +const $NotificationService: Provider = { provide: 'NotificationService', useClass: NotificationService }; +const $PollService: Provider = { provide: 'PollService', useClass: PollService }; +const $ProxyAccountService: Provider = { provide: 'ProxyAccountService', useClass: ProxyAccountService }; +const $PushNotificationService: Provider = { provide: 'PushNotificationService', useClass: PushNotificationService }; +const $QueryService: Provider = { provide: 'QueryService', useClass: QueryService }; +const $ReactionService: Provider = { provide: 'ReactionService', useClass: ReactionService }; +const $RelayService: Provider = { provide: 'RelayService', useClass: RelayService }; +const $S3Service: Provider = { provide: 'S3Service', useClass: S3Service }; +const $SignupService: Provider = { provide: 'SignupService', useClass: SignupService }; +const $TwoFactorAuthenticationService: Provider = { provide: 'TwoFactorAuthenticationService', useClass: TwoFactorAuthenticationService }; +const $UserBlockingService: Provider = { provide: 'UserBlockingService', useClass: UserBlockingService }; +const $UserCacheService: Provider = { provide: 'UserCacheService', useClass: UserCacheService }; +const $UserFollowingService: Provider = { provide: 'UserFollowingService', useClass: UserFollowingService }; +const $UserKeypairStoreService: Provider = { provide: 'UserKeypairStoreService', useClass: UserKeypairStoreService }; +const $UserListService: Provider = { provide: 'UserListService', useClass: UserListService }; +const $UserMutingService: Provider = { provide: 'UserMutingService', useClass: UserMutingService }; +const $UserSuspendService: Provider = { provide: 'UserSuspendService', useClass: UserSuspendService }; +const $VideoProcessingService: Provider = { provide: 'VideoProcessingService', useClass: VideoProcessingService }; +const $WebhookService: Provider = { provide: 'WebhookService', useClass: WebhookService }; +const $UtilityService: Provider = { provide: 'UtilityService', useClass: UtilityService }; +const $FileInfoService: Provider = { provide: 'FileInfoService', useClass: FileInfoService }; +const $FederationChart: Provider = { provide: 'FederationChart', useClass: FederationChart }; +const $NotesChart: Provider = { provide: 'NotesChart', useClass: NotesChart }; +const $UsersChart: Provider = { provide: 'UsersChart', useClass: UsersChart }; +const $ActiveUsersChart: Provider = { provide: 'ActiveUsersChart', useClass: ActiveUsersChart }; +const $InstanceChart: Provider = { provide: 'InstanceChart', useClass: InstanceChart }; +const $PerUserNotesChart: Provider = { provide: 'PerUserNotesChart', useClass: PerUserNotesChart }; +const $DriveChart: Provider = { provide: 'DriveChart', useClass: DriveChart }; +const $PerUserReactionsChart: Provider = { provide: 'PerUserReactionsChart', useClass: PerUserReactionsChart }; +const $HashtagChart: Provider = { provide: 'HashtagChart', useClass: HashtagChart }; +const $PerUserFollowingChart: Provider = { provide: 'PerUserFollowingChart', useClass: PerUserFollowingChart }; +const $PerUserDriveChart: Provider = { provide: 'PerUserDriveChart', useClass: PerUserDriveChart }; +const $ApRequestChart: Provider = { provide: 'ApRequestChart', useClass: ApRequestChart }; +const $ChartManagementService: Provider = { provide: 'ChartManagementService', useClass: ChartManagementService }; + +const $AbuseUserReportEntityService: Provider = { provide: 'AbuseUserReportEntityService', useClass: AbuseUserReportEntityService }; +const $AntennaEntityService: Provider = { provide: 'AntennaEntityService', useClass: AntennaEntityService }; +const $AppEntityService: Provider = { provide: 'AppEntityService', useClass: AppEntityService }; +const $AuthSessionEntityService: Provider = { provide: 'AuthSessionEntityService', useClass: AuthSessionEntityService }; +const $BlockingEntityService: Provider = { provide: 'BlockingEntityService', useClass: BlockingEntityService }; +const $ChannelEntityService: Provider = { provide: 'ChannelEntityService', useClass: ChannelEntityService }; +const $ClipEntityService: Provider = { provide: 'ClipEntityService', useClass: ClipEntityService }; +const $DriveFileEntityService: Provider = { provide: 'DriveFileEntityService', useClass: DriveFileEntityService }; +const $DriveFolderEntityService: Provider = { provide: 'DriveFolderEntityService', useClass: DriveFolderEntityService }; +const $EmojiEntityService: Provider = { provide: 'EmojiEntityService', useClass: EmojiEntityService }; +const $FollowingEntityService: Provider = { provide: 'FollowingEntityService', useClass: FollowingEntityService }; +const $FollowRequestEntityService: Provider = { provide: 'FollowRequestEntityService', useClass: FollowRequestEntityService }; +const $GalleryLikeEntityService: Provider = { provide: 'GalleryLikeEntityService', useClass: GalleryLikeEntityService }; +const $GalleryPostEntityService: Provider = { provide: 'GalleryPostEntityService', useClass: GalleryPostEntityService }; +const $HashtagEntityService: Provider = { provide: 'HashtagEntityService', useClass: HashtagEntityService }; +const $InstanceEntityService: Provider = { provide: 'InstanceEntityService', useClass: InstanceEntityService }; +const $MessagingMessageEntityService: Provider = { provide: 'MessagingMessageEntityService', useClass: MessagingMessageEntityService }; +const $ModerationLogEntityService: Provider = { provide: 'ModerationLogEntityService', useClass: ModerationLogEntityService }; +const $MutingEntityService: Provider = { provide: 'MutingEntityService', useClass: MutingEntityService }; +const $NoteEntityService: Provider = { provide: 'NoteEntityService', useClass: NoteEntityService }; +const $NoteFavoriteEntityService: Provider = { provide: 'NoteFavoriteEntityService', useClass: NoteFavoriteEntityService }; +const $NoteReactionEntityService: Provider = { provide: 'NoteReactionEntityService', useClass: NoteReactionEntityService }; +const $NotificationEntityService: Provider = { provide: 'NotificationEntityService', useClass: NotificationEntityService }; +const $PageEntityService: Provider = { provide: 'PageEntityService', useClass: PageEntityService }; +const $PageLikeEntityService: Provider = { provide: 'PageLikeEntityService', useClass: PageLikeEntityService }; +const $SigninEntityService: Provider = { provide: 'SigninEntityService', useClass: SigninEntityService }; +const $UserEntityService: Provider = { provide: 'UserEntityService', useClass: UserEntityService }; +const $UserGroupEntityService: Provider = { provide: 'UserGroupEntityService', useClass: UserGroupEntityService }; +const $UserGroupInvitationEntityService: Provider = { provide: 'UserGroupInvitationEntityService', useClass: UserGroupInvitationEntityService }; +const $UserListEntityService: Provider = { provide: 'UserListEntityService', useClass: UserListEntityService }; + +const $ApAudienceService: Provider = { provide: 'ApAudienceService', useClass: ApAudienceService }; +const $ApDbResolverService: Provider = { provide: 'ApDbResolverService', useClass: ApDbResolverService }; +const $ApDeliverManagerService: Provider = { provide: 'ApDeliverManagerService', useClass: ApDeliverManagerService }; +const $ApInboxService: Provider = { provide: 'ApInboxService', useClass: ApInboxService }; +const $ApLoggerService: Provider = { provide: 'ApLoggerService', useClass: ApLoggerService }; +const $ApMfmService: Provider = { provide: 'ApMfmService', useClass: ApMfmService }; +const $ApRendererService: Provider = { provide: 'ApRendererService', useClass: ApRendererService }; +const $ApRequestService: Provider = { provide: 'ApRequestService', useClass: ApRequestService }; +const $ApResolverService: Provider = { provide: 'ApResolverService', useClass: ApResolverService }; +const $LdSignatureService: Provider = { provide: 'LdSignatureService', useClass: LdSignatureService }; +const $RemoteLoggerService: Provider = { provide: 'RemoteLoggerService', useClass: RemoteLoggerService }; +const $ResolveUserService: Provider = { provide: 'ResolveUserService', useClass: ResolveUserService }; +const $WebfingerService: Provider = { provide: 'WebfingerService', useClass: WebfingerService }; +const $ApImageService: Provider = { provide: 'ApImageService', useClass: ApImageService }; +const $ApMentionService: Provider = { provide: 'ApMentionService', useClass: ApMentionService }; +const $ApNoteService: Provider = { provide: 'ApNoteService', useClass: ApNoteService }; +const $ApPersonService: Provider = { provide: 'ApPersonService', useClass: ApPersonService }; +const $ApQuestionService: Provider = { provide: 'ApQuestionService', useClass: ApQuestionService }; +//#endregion @Module({ imports: [ @@ -231,6 +348,120 @@ import { QueueService } from './QueueService.js'; ApPersonService, ApQuestionService, QueueService, + + //#region 文字列ベースでのinjection用(循環参照対応のため) + $AccountUpdateService, + $AiService, + $AntennaService, + $AppLockService, + $CaptchaService, + $CreateNotificationService, + $CreateSystemUserService, + $CustomEmojiService, + $DeleteAccountService, + $DownloadService, + $DriveService, + $EmailService, + $FederatedInstanceService, + $FetchInstanceMetadataService, + $GlobalEventService, + $HashtagService, + $HttpRequestService, + $IdService, + $ImageProcessingService, + $InstanceActorService, + $InternalStorageService, + $MessagingService, + $MetaService, + $MfmService, + $ModerationLogService, + $NoteCreateService, + $NoteDeleteService, + $NotePiningService, + $NoteReadService, + $NotificationService, + $PollService, + $ProxyAccountService, + $PushNotificationService, + $QueryService, + $ReactionService, + $RelayService, + $S3Service, + $SignupService, + $TwoFactorAuthenticationService, + $UserBlockingService, + $UserCacheService, + $UserFollowingService, + $UserKeypairStoreService, + $UserListService, + $UserMutingService, + $UserSuspendService, + $VideoProcessingService, + $WebhookService, + $UtilityService, + $FileInfoService, + $FederationChart, + $NotesChart, + $UsersChart, + $ActiveUsersChart, + $InstanceChart, + $PerUserNotesChart, + $DriveChart, + $PerUserReactionsChart, + $HashtagChart, + $PerUserFollowingChart, + $PerUserDriveChart, + $ApRequestChart, + $ChartManagementService, + $AbuseUserReportEntityService, + $AntennaEntityService, + $AppEntityService, + $AuthSessionEntityService, + $BlockingEntityService, + $ChannelEntityService, + $ClipEntityService, + $DriveFileEntityService, + $DriveFolderEntityService, + $EmojiEntityService, + $FollowingEntityService, + $FollowRequestEntityService, + $GalleryLikeEntityService, + $GalleryPostEntityService, + $HashtagEntityService, + $InstanceEntityService, + $MessagingMessageEntityService, + $ModerationLogEntityService, + $MutingEntityService, + $NoteEntityService, + $NoteFavoriteEntityService, + $NoteReactionEntityService, + $NotificationEntityService, + $PageEntityService, + $PageLikeEntityService, + $SigninEntityService, + $UserEntityService, + $UserGroupEntityService, + $UserGroupInvitationEntityService, + $UserListEntityService, + $ApAudienceService, + $ApDbResolverService, + $ApDeliverManagerService, + $ApInboxService, + $ApLoggerService, + $ApMfmService, + $ApRendererService, + $ApRequestService, + $ApResolverService, + $LdSignatureService, + $RemoteLoggerService, + $ResolveUserService, + $WebfingerService, + $ApImageService, + $ApMentionService, + $ApNoteService, + $ApPersonService, + $ApQuestionService, + //#endregion ], exports: [ QueueModule, @@ -346,6 +577,120 @@ import { QueueService } from './QueueService.js'; ApPersonService, ApQuestionService, QueueService, + + //#region 文字列ベースでのinjection用(循環参照対応のため) + $AccountUpdateService, + $AiService, + $AntennaService, + $AppLockService, + $CaptchaService, + $CreateNotificationService, + $CreateSystemUserService, + $CustomEmojiService, + $DeleteAccountService, + $DownloadService, + $DriveService, + $EmailService, + $FederatedInstanceService, + $FetchInstanceMetadataService, + $GlobalEventService, + $HashtagService, + $HttpRequestService, + $IdService, + $ImageProcessingService, + $InstanceActorService, + $InternalStorageService, + $MessagingService, + $MetaService, + $MfmService, + $ModerationLogService, + $NoteCreateService, + $NoteDeleteService, + $NotePiningService, + $NoteReadService, + $NotificationService, + $PollService, + $ProxyAccountService, + $PushNotificationService, + $QueryService, + $ReactionService, + $RelayService, + $S3Service, + $SignupService, + $TwoFactorAuthenticationService, + $UserBlockingService, + $UserCacheService, + $UserFollowingService, + $UserKeypairStoreService, + $UserListService, + $UserMutingService, + $UserSuspendService, + $VideoProcessingService, + $WebhookService, + $UtilityService, + $FileInfoService, + $FederationChart, + $NotesChart, + $UsersChart, + $ActiveUsersChart, + $InstanceChart, + $PerUserNotesChart, + $DriveChart, + $PerUserReactionsChart, + $HashtagChart, + $PerUserFollowingChart, + $PerUserDriveChart, + $ApRequestChart, + $ChartManagementService, + $AbuseUserReportEntityService, + $AntennaEntityService, + $AppEntityService, + $AuthSessionEntityService, + $BlockingEntityService, + $ChannelEntityService, + $ClipEntityService, + $DriveFileEntityService, + $DriveFolderEntityService, + $EmojiEntityService, + $FollowingEntityService, + $FollowRequestEntityService, + $GalleryLikeEntityService, + $GalleryPostEntityService, + $HashtagEntityService, + $InstanceEntityService, + $MessagingMessageEntityService, + $ModerationLogEntityService, + $MutingEntityService, + $NoteEntityService, + $NoteFavoriteEntityService, + $NoteReactionEntityService, + $NotificationEntityService, + $PageEntityService, + $PageLikeEntityService, + $SigninEntityService, + $UserEntityService, + $UserGroupEntityService, + $UserGroupInvitationEntityService, + $UserListEntityService, + $ApAudienceService, + $ApDbResolverService, + $ApDeliverManagerService, + $ApInboxService, + $ApLoggerService, + $ApMfmService, + $ApRendererService, + $ApRequestService, + $ApResolverService, + $LdSignatureService, + $RemoteLoggerService, + $ResolveUserService, + $WebfingerService, + $ApImageService, + $ApMentionService, + $ApNoteService, + $ApPersonService, + $ApQuestionService, + //#endregion ], }) export class CoreModule {} From 5ddf4eca9c6dc63ad91c8bd055ef3e90525e1b9c Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 17 Sep 2022 00:11:29 +0900 Subject: [PATCH 169/180] Update ServerService.ts --- packages/backend/src/server/ServerService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index b17bd4c2dcd6..c9eaff224570 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -82,7 +82,7 @@ export class ServerService { }); } - koa.use(mount('/api', this.apiServerService.createApiServer(app))); + koa.use(mount('/api', this.apiServerService.createApiServer(koa))); koa.use(mount('/files', this.fileServerService.createServer())); koa.use(mount('/proxy', this.mediaProxyServerService.createServer())); From ede750eda4aac1a7ac3274544343e6a9c87fe856 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 17 Sep 2022 00:30:38 +0900 Subject: [PATCH 170/180] Update EndpointsModule.ts --- .../backend/src/server/api/EndpointsModule.ts | 1252 +++++++++++++---- 1 file changed, 940 insertions(+), 312 deletions(-) diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index c199b5469e99..39fd9755834e 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -315,6 +315,320 @@ import * as ep___fetchRss from './endpoints/fetch-rss.js'; import * as ep___admin_driveCapOverride from './endpoints/admin/drive-capacity-override.js'; import { GetterService } from './common/GetterService.js'; import { ApiLoggerService } from './ApiLoggerService.js'; +import type { Provider } from '@nestjs/common'; + +const $admin_meta: Provider = { provide: 'ep:admin/meta', useClass: ep___admin_meta.default }; +const $admin_abuseUserReports: Provider = { provide: 'ep:admin/abuse-user-reports', useClass: ep___admin_abuseUserReports.default }; +const $admin_accounts_create: Provider = { provide: 'ep:admin/accounts/create', useClass: ep___admin_accounts_create.default }; +const $admin_accounts_delete: Provider = { provide: 'ep:admin/accounts/delete', useClass: ep___admin_accounts_delete.default }; +const $admin_ad_create: Provider = { provide: 'ep:admin/ad/create', useClass: ep___admin_ad_create.default }; +const $admin_ad_delete: Provider = { provide: 'ep:admin/ad/delete', useClass: ep___admin_ad_delete.default }; +const $admin_ad_list: Provider = { provide: 'ep:admin/ad/list', useClass: ep___admin_ad_list.default }; +const $admin_ad_update: Provider = { provide: 'ep:admin/ad/update', useClass: ep___admin_ad_update.default }; +const $admin_announcements_create: Provider = { provide: 'ep:admin/announcements/create', useClass: ep___admin_announcements_create.default }; +const $admin_announcements_delete: Provider = { provide: 'ep:admin/announcements/delete', useClass: ep___admin_announcements_delete.default }; +const $admin_announcements_list: Provider = { provide: 'ep:admin/announcements/list', useClass: ep___admin_announcements_list.default }; +const $admin_announcements_update: Provider = { provide: 'ep:admin/announcements/update', useClass: ep___admin_announcements_update.default }; +const $admin_deleteAllFilesOfAUser: Provider = { provide: 'ep:admin/delete-all-files-of-a-user', useClass: ep___admin_deleteAllFilesOfAUser.default }; +const $admin_drive_cleanRemoteFiles: Provider = { provide: 'ep:admin/drive/clean-remote-files', useClass: ep___admin_drive_cleanRemoteFiles.default }; +const $admin_drive_cleanup: Provider = { provide: 'ep:admin/drive/cleanup', useClass: ep___admin_drive_cleanup.default }; +const $admin_drive_files: Provider = { provide: 'ep:admin/drive/files', useClass: ep___admin_drive_files.default }; +const $admin_drive_showFile: Provider = { provide: 'ep:admin/drive/show-file', useClass: ep___admin_drive_showFile.default }; +const $admin_emoji_addAliasesBulk: Provider = { provide: 'ep:admin/emoji/add-aliases-bulk', useClass: ep___admin_emoji_addAliasesBulk.default }; +const $admin_emoji_add: Provider = { provide: 'ep:admin/emoji/add', useClass: ep___admin_emoji_add.default }; +const $admin_emoji_copy: Provider = { provide: 'ep:admin/emoji/copy', useClass: ep___admin_emoji_copy.default }; +const $admin_emoji_deleteBulk: Provider = { provide: 'ep:admin/emoji/delete-bulk', useClass: ep___admin_emoji_deleteBulk.default }; +const $admin_emoji_delete: Provider = { provide: 'ep:admin/emoji/delete', useClass: ep___admin_emoji_delete.default }; +const $admin_emoji_importZip: Provider = { provide: 'ep:admin/emoji/import-zip', useClass: ep___admin_emoji_importZip.default }; +const $admin_emoji_listRemote: Provider = { provide: 'ep:admin/emoji/list-remote', useClass: ep___admin_emoji_listRemote.default }; +const $admin_emoji_list: Provider = { provide: 'ep:admin/emoji/list', useClass: ep___admin_emoji_list.default }; +const $admin_emoji_removeAliasesBulk: Provider = { provide: 'ep:admin/emoji/remove-aliases-bulk', useClass: ep___admin_emoji_removeAliasesBulk.default }; +const $admin_emoji_setAliasesBulk: Provider = { provide: 'ep:admin/emoji/set-aliases-bulk', useClass: ep___admin_emoji_setAliasesBulk.default }; +const $admin_emoji_setCategoryBulk: Provider = { provide: 'ep:admin/emoji/set-category-bulk', useClass: ep___admin_emoji_setCategoryBulk.default }; +const $admin_emoji_update: Provider = { provide: 'ep:admin/emoji/update', useClass: ep___admin_emoji_update.default }; +const $admin_federation_deleteAllFiles: Provider = { provide: 'ep:admin/federation/delete-all-files', useClass: ep___admin_federation_deleteAllFiles.default }; +const $admin_federation_refreshRemoteInstanceMetadata: Provider = { provide: 'ep:admin/federation/refresh-remote-instance-metadata', useClass: ep___admin_federation_refreshRemoteInstanceMetadata.default }; +const $admin_federation_removeAllFollowing: Provider = { provide: 'ep:admin/federation/remove-all-following', useClass: ep___admin_federation_removeAllFollowing.default }; +const $admin_federation_updateInstance: Provider = { provide: 'ep:admin/federation/update-instance', useClass: ep___admin_federation_updateInstance.default }; +const $admin_getIndexStats: Provider = { provide: 'ep:admin/get-index-stats', useClass: ep___admin_getIndexStats.default }; +const $admin_getTableStats: Provider = { provide: 'ep:admin/get-table-stats', useClass: ep___admin_getTableStats.default }; +const $admin_getUserIps: Provider = { provide: 'ep:admin/get-user-ips', useClass: ep___admin_getUserIps.default }; +const $admin_invite: Provider = { provide: 'ep:admin/invite', useClass: ep___admin_invite.default }; +const $admin_moderators_add: Provider = { provide: 'ep:admin/moderators/add', useClass: ep___admin_moderators_add.default }; +const $admin_moderators_remove: Provider = { provide: 'ep:admin/moderators/remove', useClass: ep___admin_moderators_remove.default }; +const $admin_promo_create: Provider = { provide: 'ep:admin/promo/create', useClass: ep___admin_promo_create.default }; +const $admin_queue_clear: Provider = { provide: 'ep:admin/queue/clear', useClass: ep___admin_queue_clear.default }; +const $admin_queue_deliverDelayed: Provider = { provide: 'ep:admin/queue/deliver-delayed', useClass: ep___admin_queue_deliverDelayed.default }; +const $admin_queue_inboxDelayed: Provider = { provide: 'ep:admin/queue/inbox-delayed', useClass: ep___admin_queue_inboxDelayed.default }; +const $admin_queue_stats: Provider = { provide: 'ep:admin/queue/stats', useClass: ep___admin_queue_stats.default }; +const $admin_relays_add: Provider = { provide: 'ep:admin/relays/add', useClass: ep___admin_relays_add.default }; +const $admin_relays_list: Provider = { provide: 'ep:admin/relays/list', useClass: ep___admin_relays_list.default }; +const $admin_relays_remove: Provider = { provide: 'ep:admin/relays/remove', useClass: ep___admin_relays_remove.default }; +const $admin_resetPassword: Provider = { provide: 'ep:admin/reset-password', useClass: ep___admin_resetPassword.default }; +const $admin_resolveAbuseUserReport: Provider = { provide: 'ep:admin/resolve-abuse-user-report', useClass: ep___admin_resolveAbuseUserReport.default }; +const $admin_sendEmail: Provider = { provide: 'ep:admin/send-email', useClass: ep___admin_sendEmail.default }; +const $admin_serverInfo: Provider = { provide: 'ep:admin/server-info', useClass: ep___admin_serverInfo.default }; +const $admin_showModerationLogs: Provider = { provide: 'ep:admin/show-moderation-logs', useClass: ep___admin_showModerationLogs.default }; +const $admin_showUser: Provider = { provide: 'ep:admin/show-user', useClass: ep___admin_showUser.default }; +const $admin_showUsers: Provider = { provide: 'ep:admin/show-users', useClass: ep___admin_showUsers.default }; +const $admin_silenceUser: Provider = { provide: 'ep:admin/silence-user', useClass: ep___admin_silenceUser.default }; +const $admin_suspendUser: Provider = { provide: 'ep:admin/suspend-user', useClass: ep___admin_suspendUser.default }; +const $admin_unsilenceUser: Provider = { provide: 'ep:admin/unsilence-user', useClass: ep___admin_unsilenceUser.default }; +const $admin_unsuspendUser: Provider = { provide: 'ep:admin/unsuspend-user', useClass: ep___admin_unsuspendUser.default }; +const $admin_updateMeta: Provider = { provide: 'ep:admin/update-meta', useClass: ep___admin_updateMeta.default }; +const $admin_deleteAccount: Provider = { provide: 'ep:admin/delete-account', useClass: ep___admin_deleteAccount.default }; +const $admin_updateUserNote: Provider = { provide: 'ep:admin/update-user-note', useClass: ep___admin_updateUserNote.default }; +const $announcements: Provider = { provide: 'ep:announcements', useClass: ep___announcements.default }; +const $antennas_create: Provider = { provide: 'ep:antennas/create', useClass: ep___antennas_create.default }; +const $antennas_delete: Provider = { provide: 'ep:antennas/delete', useClass: ep___antennas_delete.default }; +const $antennas_list: Provider = { provide: 'ep:antennas/list', useClass: ep___antennas_list.default }; +const $antennas_notes: Provider = { provide: 'ep:antennas/notes', useClass: ep___antennas_notes.default }; +const $antennas_show: Provider = { provide: 'ep:antennas/show', useClass: ep___antennas_show.default }; +const $antennas_update: Provider = { provide: 'ep:antennas/update', useClass: ep___antennas_update.default }; +const $ap_get: Provider = { provide: 'ep:ap/get', useClass: ep___ap_get.default }; +const $ap_show: Provider = { provide: 'ep:ap/show', useClass: ep___ap_show.default }; +const $app_create: Provider = { provide: 'ep:app/create', useClass: ep___app_create.default }; +const $app_show: Provider = { provide: 'ep:app/show', useClass: ep___app_show.default }; +const $auth_accept: Provider = { provide: 'ep:auth/accept', useClass: ep___auth_accept.default }; +const $auth_session_generate: Provider = { provide: 'ep:auth/session/generate', useClass: ep___auth_session_generate.default }; +const $auth_session_show: Provider = { provide: 'ep:auth/session/show', useClass: ep___auth_session_show.default }; +const $auth_session_userkey: Provider = { provide: 'ep:auth/session/userkey', useClass: ep___auth_session_userkey.default }; +const $blocking_create: Provider = { provide: 'ep:blocking/create', useClass: ep___blocking_create.default }; +const $blocking_delete: Provider = { provide: 'ep:blocking/delete', useClass: ep___blocking_delete.default }; +const $blocking_list: Provider = { provide: 'ep:blocking/list', useClass: ep___blocking_list.default }; +const $channels_create: Provider = { provide: 'ep:channels/create', useClass: ep___channels_create.default }; +const $channels_featured: Provider = { provide: 'ep:channels/featured', useClass: ep___channels_featured.default }; +const $channels_follow: Provider = { provide: 'ep:channels/follow', useClass: ep___channels_follow.default }; +const $channels_followed: Provider = { provide: 'ep:channels/followed', useClass: ep___channels_followed.default }; +const $channels_owned: Provider = { provide: 'ep:channels/owned', useClass: ep___channels_owned.default }; +const $channels_show: Provider = { provide: 'ep:channels/show', useClass: ep___channels_show.default }; +const $channels_timeline: Provider = { provide: 'ep:channels/timeline', useClass: ep___channels_timeline.default }; +const $channels_unfollow: Provider = { provide: 'ep:channels/unfollow', useClass: ep___channels_unfollow.default }; +const $channels_update: Provider = { provide: 'ep:channels/update', useClass: ep___channels_update.default }; +const $charts_activeUsers: Provider = { provide: 'ep:charts/active-users', useClass: ep___charts_activeUsers.default }; +const $charts_apRequest: Provider = { provide: 'ep:charts/ap-request', useClass: ep___charts_apRequest.default }; +const $charts_drive: Provider = { provide: 'ep:charts/drive', useClass: ep___charts_drive.default }; +const $charts_federation: Provider = { provide: 'ep:charts/federation', useClass: ep___charts_federation.default }; +const $charts_hashtag: Provider = { provide: 'ep:charts/hashtag', useClass: ep___charts_hashtag.default }; +const $charts_instance: Provider = { provide: 'ep:charts/instance', useClass: ep___charts_instance.default }; +const $charts_notes: Provider = { provide: 'ep:charts/notes', useClass: ep___charts_notes.default }; +const $charts_user_drive: Provider = { provide: 'ep:charts/user/drive', useClass: ep___charts_user_drive.default }; +const $charts_user_following: Provider = { provide: 'ep:charts/user/following', useClass: ep___charts_user_following.default }; +const $charts_user_notes: Provider = { provide: 'ep:charts/user/notes', useClass: ep___charts_user_notes.default }; +const $charts_user_reactions: Provider = { provide: 'ep:charts/user/reactions', useClass: ep___charts_user_reactions.default }; +const $charts_users: Provider = { provide: 'ep:charts/users', useClass: ep___charts_users.default }; +const $clips_addNote: Provider = { provide: 'ep:clips/add-note', useClass: ep___clips_addNote.default }; +const $clips_removeNote: Provider = { provide: 'ep:clips/remove-note', useClass: ep___clips_removeNote.default }; +const $clips_create: Provider = { provide: 'ep:clips/create', useClass: ep___clips_create.default }; +const $clips_delete: Provider = { provide: 'ep:clips/delete', useClass: ep___clips_delete.default }; +const $clips_list: Provider = { provide: 'ep:clips/list', useClass: ep___clips_list.default }; +const $clips_notes: Provider = { provide: 'ep:clips/notes', useClass: ep___clips_notes.default }; +const $clips_show: Provider = { provide: 'ep:clips/show', useClass: ep___clips_show.default }; +const $clips_update: Provider = { provide: 'ep:clips/update', useClass: ep___clips_update.default }; +const $drive: Provider = { provide: 'ep:drive', useClass: ep___drive.default }; +const $drive_files: Provider = { provide: 'ep:drive/files', useClass: ep___drive_files.default }; +const $drive_files_attachedNotes: Provider = { provide: 'ep:drive/files/attached-notes', useClass: ep___drive_files_attachedNotes.default }; +const $drive_files_checkExistence: Provider = { provide: 'ep:drive/files/check-existence', useClass: ep___drive_files_checkExistence.default }; +const $drive_files_create: Provider = { provide: 'ep:drive/files/create', useClass: ep___drive_files_create.default }; +const $drive_files_delete: Provider = { provide: 'ep:drive/files/delete', useClass: ep___drive_files_delete.default }; +const $drive_files_findByHash: Provider = { provide: 'ep:drive/files/find-by-hash', useClass: ep___drive_files_findByHash.default }; +const $drive_files_find: Provider = { provide: 'ep:drive/files/find', useClass: ep___drive_files_find.default }; +const $drive_files_show: Provider = { provide: 'ep:drive/files/show', useClass: ep___drive_files_show.default }; +const $drive_files_update: Provider = { provide: 'ep:drive/files/update', useClass: ep___drive_files_update.default }; +const $drive_files_uploadFromUrl: Provider = { provide: 'ep:drive/files/upload-from-url', useClass: ep___drive_files_uploadFromUrl.default }; +const $drive_folders: Provider = { provide: 'ep:drive/folders', useClass: ep___drive_folders.default }; +const $drive_folders_create: Provider = { provide: 'ep:drive/folders/create', useClass: ep___drive_folders_create.default }; +const $drive_folders_delete: Provider = { provide: 'ep:drive/folders/delete', useClass: ep___drive_folders_delete.default }; +const $drive_folders_find: Provider = { provide: 'ep:drive/folders/find', useClass: ep___drive_folders_find.default }; +const $drive_folders_show: Provider = { provide: 'ep:drive/folders/show', useClass: ep___drive_folders_show.default }; +const $drive_folders_update: Provider = { provide: 'ep:drive/folders/update', useClass: ep___drive_folders_update.default }; +const $drive_stream: Provider = { provide: 'ep:drive/stream', useClass: ep___drive_stream.default }; +const $emailAddress_available: Provider = { provide: 'ep:email-address/available', useClass: ep___emailAddress_available.default }; +const $endpoint: Provider = { provide: 'ep:endpoint', useClass: ep___endpoint.default }; +const $endpoints: Provider = { provide: 'ep:endpoints', useClass: ep___endpoints.default }; +const $exportCustomEmojis: Provider = { provide: 'ep:export-custom-emojis', useClass: ep___exportCustomEmojis.default }; +const $federation_followers: Provider = { provide: 'ep:federation/followers', useClass: ep___federation_followers.default }; +const $federation_following: Provider = { provide: 'ep:federation/following', useClass: ep___federation_following.default }; +const $federation_instances: Provider = { provide: 'ep:federation/instances', useClass: ep___federation_instances.default }; +const $federation_showInstance: Provider = { provide: 'ep:federation/show-instance', useClass: ep___federation_showInstance.default }; +const $federation_updateRemoteUser: Provider = { provide: 'ep:federation/update-remote-user', useClass: ep___federation_updateRemoteUser.default }; +const $federation_users: Provider = { provide: 'ep:federation/users', useClass: ep___federation_users.default }; +const $federation_stats: Provider = { provide: 'ep:federation/stats', useClass: ep___federation_stats.default }; +const $following_create: Provider = { provide: 'ep:following/create', useClass: ep___following_create.default }; +const $following_delete: Provider = { provide: 'ep:following/delete', useClass: ep___following_delete.default }; +const $following_invalidate: Provider = { provide: 'ep:following/invalidate', useClass: ep___following_invalidate.default }; +const $following_requests_accept: Provider = { provide: 'ep:following/requests/accept', useClass: ep___following_requests_accept.default }; +const $following_requests_cancel: Provider = { provide: 'ep:following/requests/cancel', useClass: ep___following_requests_cancel.default }; +const $following_requests_list: Provider = { provide: 'ep:following/requests/list', useClass: ep___following_requests_list.default }; +const $following_requests_reject: Provider = { provide: 'ep:following/requests/reject', useClass: ep___following_requests_reject.default }; +const $gallery_featured: Provider = { provide: 'ep:gallery/featured', useClass: ep___gallery_featured.default }; +const $gallery_popular: Provider = { provide: 'ep:gallery/popular', useClass: ep___gallery_popular.default }; +const $gallery_posts: Provider = { provide: 'ep:gallery/posts', useClass: ep___gallery_posts.default }; +const $gallery_posts_create: Provider = { provide: 'ep:gallery/posts/create', useClass: ep___gallery_posts_create.default }; +const $gallery_posts_delete: Provider = { provide: 'ep:gallery/posts/delete', useClass: ep___gallery_posts_delete.default }; +const $gallery_posts_like: Provider = { provide: 'ep:gallery/posts/like', useClass: ep___gallery_posts_like.default }; +const $gallery_posts_show: Provider = { provide: 'ep:gallery/posts/show', useClass: ep___gallery_posts_show.default }; +const $gallery_posts_unlike: Provider = { provide: 'ep:gallery/posts/unlike', useClass: ep___gallery_posts_unlike.default }; +const $gallery_posts_update: Provider = { provide: 'ep:gallery/posts/update', useClass: ep___gallery_posts_update.default }; +const $getOnlineUsersCount: Provider = { provide: 'ep:get-online-users-count', useClass: ep___getOnlineUsersCount.default }; +const $hashtags_list: Provider = { provide: 'ep:hashtags/list', useClass: ep___hashtags_list.default }; +const $hashtags_search: Provider = { provide: 'ep:hashtags/search', useClass: ep___hashtags_search.default }; +const $hashtags_show: Provider = { provide: 'ep:hashtags/show', useClass: ep___hashtags_show.default }; +const $hashtags_trend: Provider = { provide: 'ep:hashtags/trend', useClass: ep___hashtags_trend.default }; +const $hashtags_users: Provider = { provide: 'ep:hashtags/users', useClass: ep___hashtags_users.default }; +const $i: Provider = { provide: 'ep:i', useClass: ep___i.default }; +const $i_2fa_done: Provider = { provide: 'ep:i/2fa/done', useClass: ep___i_2fa_done.default }; +const $i_2fa_keyDone: Provider = { provide: 'ep:i/2fa/key-done', useClass: ep___i_2fa_keyDone.default }; +const $i_2fa_passwordLess: Provider = { provide: 'ep:i/2fa/password-less', useClass: ep___i_2fa_passwordLess.default }; +const $i_2fa_registerKey: Provider = { provide: 'ep:i/2fa/register-key', useClass: ep___i_2fa_registerKey.default }; +const $i_2fa_register: Provider = { provide: 'ep:i/2fa/register', useClass: ep___i_2fa_register.default }; +const $i_2fa_removeKey: Provider = { provide: 'ep:i/2fa/remove-key', useClass: ep___i_2fa_removeKey.default }; +const $i_2fa_unregister: Provider = { provide: 'ep:i/2fa/unregister', useClass: ep___i_2fa_unregister.default }; +const $i_apps: Provider = { provide: 'ep:i/apps', useClass: ep___i_apps.default }; +const $i_authorizedApps: Provider = { provide: 'ep:i/authorized-apps', useClass: ep___i_authorizedApps.default }; +const $i_changePassword: Provider = { provide: 'ep:i/change-password', useClass: ep___i_changePassword.default }; +const $i_deleteAccount: Provider = { provide: 'ep:i/delete-account', useClass: ep___i_deleteAccount.default }; +const $i_exportBlocking: Provider = { provide: 'ep:i/export-blocking', useClass: ep___i_exportBlocking.default }; +const $i_exportFollowing: Provider = { provide: 'ep:i/export-following', useClass: ep___i_exportFollowing.default }; +const $i_exportMute: Provider = { provide: 'ep:i/export-mute', useClass: ep___i_exportMute.default }; +const $i_exportNotes: Provider = { provide: 'ep:i/export-notes', useClass: ep___i_exportNotes.default }; +const $i_exportUserLists: Provider = { provide: 'ep:i/export-user-lists', useClass: ep___i_exportUserLists.default }; +const $i_favorites: Provider = { provide: 'ep:i/favorites', useClass: ep___i_favorites.default }; +const $i_gallery_likes: Provider = { provide: 'ep:i/gallery/likes', useClass: ep___i_gallery_likes.default }; +const $i_gallery_posts: Provider = { provide: 'ep:i/gallery/posts', useClass: ep___i_gallery_posts.default }; +const $i_getWordMutedNotesCount: Provider = { provide: 'ep:i/get-word-muted-notes-count', useClass: ep___i_getWordMutedNotesCount.default }; +const $i_importBlocking: Provider = { provide: 'ep:i/import-blocking', useClass: ep___i_importBlocking.default }; +const $i_importFollowing: Provider = { provide: 'ep:i/import-following', useClass: ep___i_importFollowing.default }; +const $i_importMuting: Provider = { provide: 'ep:i/import-muting', useClass: ep___i_importMuting.default }; +const $i_importUserLists: Provider = { provide: 'ep:i/import-user-lists', useClass: ep___i_importUserLists.default }; +const $i_notifications: Provider = { provide: 'ep:i/notifications', useClass: ep___i_notifications.default }; +const $i_pageLikes: Provider = { provide: 'ep:i/page-likes', useClass: ep___i_pageLikes.default }; +const $i_pages: Provider = { provide: 'ep:i/pages', useClass: ep___i_pages.default }; +const $i_pin: Provider = { provide: 'ep:i/pin', useClass: ep___i_pin.default }; +const $i_readAllMessagingMessages: Provider = { provide: 'ep:i/read-all-messaging-messages', useClass: ep___i_readAllMessagingMessages.default }; +const $i_readAllUnreadNotes: Provider = { provide: 'ep:i/read-all-unread-notes', useClass: ep___i_readAllUnreadNotes.default }; +const $i_readAnnouncement: Provider = { provide: 'ep:i/read-announcement', useClass: ep___i_readAnnouncement.default }; +const $i_regenerateToken: Provider = { provide: 'ep:i/regenerate-token', useClass: ep___i_regenerateToken.default }; +const $i_registry_getAll: Provider = { provide: 'ep:i/registry/get-all', useClass: ep___i_registry_getAll.default }; +const $i_registry_getDetail: Provider = { provide: 'ep:i/registry/get-detail', useClass: ep___i_registry_getDetail.default }; +const $i_registry_get: Provider = { provide: 'ep:i/registry/get', useClass: ep___i_registry_get.default }; +const $i_registry_keysWithType: Provider = { provide: 'ep:i/registry/keys-with-type', useClass: ep___i_registry_keysWithType.default }; +const $i_registry_keys: Provider = { provide: 'ep:i/registry/keys', useClass: ep___i_registry_keys.default }; +const $i_registry_remove: Provider = { provide: 'ep:i/registry/remove', useClass: ep___i_registry_remove.default }; +const $i_registry_scopes: Provider = { provide: 'ep:i/registry/scopes', useClass: ep___i_registry_scopes.default }; +const $i_registry_set: Provider = { provide: 'ep:i/registry/set', useClass: ep___i_registry_set.default }; +const $i_revokeToken: Provider = { provide: 'ep:i/revoke-token', useClass: ep___i_revokeToken.default }; +const $i_signinHistory: Provider = { provide: 'ep:i/signin-history', useClass: ep___i_signinHistory.default }; +const $i_unpin: Provider = { provide: 'ep:i/unpin', useClass: ep___i_unpin.default }; +const $i_updateEmail: Provider = { provide: 'ep:i/update-email', useClass: ep___i_updateEmail.default }; +const $i_update: Provider = { provide: 'ep:i/update', useClass: ep___i_update.default }; +const $i_userGroupInvites: Provider = { provide: 'ep:i/user-group-invites', useClass: ep___i_userGroupInvites.default }; +const $i_webhooks_create: Provider = { provide: 'ep:i/webhooks/create', useClass: ep___i_webhooks_create.default }; +const $i_webhooks_list: Provider = { provide: 'ep:i/webhooks/list', useClass: ep___i_webhooks_list.default }; +const $i_webhooks_show: Provider = { provide: 'ep:i/webhooks/show', useClass: ep___i_webhooks_show.default }; +const $i_webhooks_update: Provider = { provide: 'ep:i/webhooks/update', useClass: ep___i_webhooks_update.default }; +const $i_webhooks_delete: Provider = { provide: 'ep:i/webhooks/delete', useClass: ep___i_webhooks_delete.default }; +const $messaging_history: Provider = { provide: 'ep:messaging/history', useClass: ep___messaging_history.default }; +const $messaging_messages: Provider = { provide: 'ep:messaging/messages', useClass: ep___messaging_messages.default }; +const $messaging_messages_create: Provider = { provide: 'ep:messaging/messages/create', useClass: ep___messaging_messages_create.default }; +const $messaging_messages_delete: Provider = { provide: 'ep:messaging/messages/delete', useClass: ep___messaging_messages_delete.default }; +const $messaging_messages_read: Provider = { provide: 'ep:messaging/messages/read', useClass: ep___messaging_messages_read.default }; +const $meta: Provider = { provide: 'ep:meta', useClass: ep___meta.default }; +const $miauth_genToken: Provider = { provide: 'ep:miauth/gen-token', useClass: ep___miauth_genToken.default }; +const $mute_create: Provider = { provide: 'ep:mute/create', useClass: ep___mute_create.default }; +const $mute_delete: Provider = { provide: 'ep:mute/delete', useClass: ep___mute_delete.default }; +const $mute_list: Provider = { provide: 'ep:mute/list', useClass: ep___mute_list.default }; +const $my_apps: Provider = { provide: 'ep:my/apps', useClass: ep___my_apps.default }; +const $notes: Provider = { provide: 'ep:notes', useClass: ep___notes.default }; +const $notes_children: Provider = { provide: 'ep:notes/children', useClass: ep___notes_children.default }; +const $notes_clips: Provider = { provide: 'ep:notes/clips', useClass: ep___notes_clips.default }; +const $notes_conversation: Provider = { provide: 'ep:notes/conversation', useClass: ep___notes_conversation.default }; +const $notes_create: Provider = { provide: 'ep:notes/create', useClass: ep___notes_create.default }; +const $notes_delete: Provider = { provide: 'ep:notes/delete', useClass: ep___notes_delete.default }; +const $notes_favorites_create: Provider = { provide: 'ep:notes/favorites/create', useClass: ep___notes_favorites_create.default }; +const $notes_favorites_delete: Provider = { provide: 'ep:notes/favorites/delete', useClass: ep___notes_favorites_delete.default }; +const $notes_featured: Provider = { provide: 'ep:notes/featured', useClass: ep___notes_featured.default }; +const $notes_globalTimeline: Provider = { provide: 'ep:notes/global-timeline', useClass: ep___notes_globalTimeline.default }; +const $notes_hybridTimeline: Provider = { provide: 'ep:notes/hybrid-timeline', useClass: ep___notes_hybridTimeline.default }; +const $notes_localTimeline: Provider = { provide: 'ep:notes/local-timeline', useClass: ep___notes_localTimeline.default }; +const $notes_mentions: Provider = { provide: 'ep:notes/mentions', useClass: ep___notes_mentions.default }; +const $notes_polls_recommendation: Provider = { provide: 'ep:notes/polls/recommendation', useClass: ep___notes_polls_recommendation.default }; +const $notes_polls_vote: Provider = { provide: 'ep:notes/polls/vote', useClass: ep___notes_polls_vote.default }; +const $notes_reactions: Provider = { provide: 'ep:notes/reactions', useClass: ep___notes_reactions.default }; +const $notes_reactions_create: Provider = { provide: 'ep:notes/reactions/create', useClass: ep___notes_reactions_create.default }; +const $notes_reactions_delete: Provider = { provide: 'ep:notes/reactions/delete', useClass: ep___notes_reactions_delete.default }; +const $notes_renotes: Provider = { provide: 'ep:notes/renotes', useClass: ep___notes_renotes.default }; +const $notes_replies: Provider = { provide: 'ep:notes/replies', useClass: ep___notes_replies.default }; +const $notes_searchByTag: Provider = { provide: 'ep:notes/search-by-tag', useClass: ep___notes_searchByTag.default }; +const $notes_search: Provider = { provide: 'ep:notes/search', useClass: ep___notes_search.default }; +const $notes_show: Provider = { provide: 'ep:notes/show', useClass: ep___notes_show.default }; +const $notes_state: Provider = { provide: 'ep:notes/state', useClass: ep___notes_state.default }; +const $notes_threadMuting_create: Provider = { provide: 'ep:notes/thread-muting/create', useClass: ep___notes_threadMuting_create.default }; +const $notes_threadMuting_delete: Provider = { provide: 'ep:notes/thread-muting/delete', useClass: ep___notes_threadMuting_delete.default }; +const $notes_timeline: Provider = { provide: 'ep:notes/timeline', useClass: ep___notes_timeline.default }; +const $notes_translate: Provider = { provide: 'ep:notes/translate', useClass: ep___notes_translate.default }; +const $notes_unrenote: Provider = { provide: 'ep:notes/unrenote', useClass: ep___notes_unrenote.default }; +const $notes_userListTimeline: Provider = { provide: 'ep:notes/user-list-timeline', useClass: ep___notes_userListTimeline.default }; +const $notifications_create: Provider = { provide: 'ep:notifications/create', useClass: ep___notifications_create.default }; +const $notifications_markAllAsRead: Provider = { provide: 'ep:notifications/mark-all-as-read', useClass: ep___notifications_markAllAsRead.default }; +const $notifications_read: Provider = { provide: 'ep:notifications/read', useClass: ep___notifications_read.default }; +const $pagePush: Provider = { provide: 'ep:page-push', useClass: ep___pagePush.default }; +const $pages_create: Provider = { provide: 'ep:pages/create', useClass: ep___pages_create.default }; +const $pages_delete: Provider = { provide: 'ep:pages/delete', useClass: ep___pages_delete.default }; +const $pages_featured: Provider = { provide: 'ep:pages/featured', useClass: ep___pages_featured.default }; +const $pages_like: Provider = { provide: 'ep:pages/like', useClass: ep___pages_like.default }; +const $pages_show: Provider = { provide: 'ep:pages/show', useClass: ep___pages_show.default }; +const $pages_unlike: Provider = { provide: 'ep:pages/unlike', useClass: ep___pages_unlike.default }; +const $pages_update: Provider = { provide: 'ep:pages/update', useClass: ep___pages_update.default }; +const $ping: Provider = { provide: 'ep:ping', useClass: ep___ping.default }; +const $pinnedUsers: Provider = { provide: 'ep:pinned-users', useClass: ep___pinnedUsers.default }; +const $promo_read: Provider = { provide: 'ep:promo/read', useClass: ep___promo_read.default }; +const $requestResetPassword: Provider = { provide: 'ep:request-reset-password', useClass: ep___requestResetPassword.default }; +const $resetDb: Provider = { provide: 'ep:reset-db', useClass: ep___resetDb.default }; +const $resetPassword: Provider = { provide: 'ep:reset-password', useClass: ep___resetPassword.default }; +const $serverInfo: Provider = { provide: 'ep:server-info', useClass: ep___serverInfo.default }; +const $stats: Provider = { provide: 'ep:stats', useClass: ep___stats.default }; +const $sw_register: Provider = { provide: 'ep:sw/register', useClass: ep___sw_register.default }; +const $sw_unregister: Provider = { provide: 'ep:sw/unregister', useClass: ep___sw_unregister.default }; +const $test: Provider = { provide: 'ep:test', useClass: ep___test.default }; +const $username_available: Provider = { provide: 'ep:username/available', useClass: ep___username_available.default }; +const $users: Provider = { provide: 'ep:users', useClass: ep___users.default }; +const $users_clips: Provider = { provide: 'ep:users/clips', useClass: ep___users_clips.default }; +const $users_followers: Provider = { provide: 'ep:users/followers', useClass: ep___users_followers.default }; +const $users_following: Provider = { provide: 'ep:users/following', useClass: ep___users_following.default }; +const $users_gallery_posts: Provider = { provide: 'ep:users/gallery/posts', useClass: ep___users_gallery_posts.default }; +const $users_getFrequentlyRepliedUsers: Provider = { provide: 'ep:users/get-frequently-replied-users', useClass: ep___users_getFrequentlyRepliedUsers.default }; +const $users_groups_create: Provider = { provide: 'ep:users/groups/create', useClass: ep___users_groups_create.default }; +const $users_groups_delete: Provider = { provide: 'ep:users/groups/delete', useClass: ep___users_groups_delete.default }; +const $users_groups_invitations_accept: Provider = { provide: 'ep:users/groups/invitations/accept', useClass: ep___users_groups_invitations_accept.default }; +const $users_groups_invitations_reject: Provider = { provide: 'ep:users/groups/invitations/reject', useClass: ep___users_groups_invitations_reject.default }; +const $users_groups_invite: Provider = { provide: 'ep:users/groups/invite', useClass: ep___users_groups_invite.default }; +const $users_groups_joined: Provider = { provide: 'ep:users/groups/joined', useClass: ep___users_groups_joined.default }; +const $users_groups_leave: Provider = { provide: 'ep:users/groups/leave', useClass: ep___users_groups_leave.default }; +const $users_groups_owned: Provider = { provide: 'ep:users/groups/owned', useClass: ep___users_groups_owned.default }; +const $users_groups_pull: Provider = { provide: 'ep:users/groups/pull', useClass: ep___users_groups_pull.default }; +const $users_groups_show: Provider = { provide: 'ep:users/groups/show', useClass: ep___users_groups_show.default }; +const $users_groups_transfer: Provider = { provide: 'ep:users/groups/transfer', useClass: ep___users_groups_transfer.default }; +const $users_groups_update: Provider = { provide: 'ep:users/groups/update', useClass: ep___users_groups_update.default }; +const $users_lists_create: Provider = { provide: 'ep:users/lists/create', useClass: ep___users_lists_create.default }; +const $users_lists_delete: Provider = { provide: 'ep:users/lists/delete', useClass: ep___users_lists_delete.default }; +const $users_lists_list: Provider = { provide: 'ep:users/lists/list', useClass: ep___users_lists_list.default }; +const $users_lists_pull: Provider = { provide: 'ep:users/lists/pull', useClass: ep___users_lists_pull.default }; +const $users_lists_push: Provider = { provide: 'ep:users/lists/push', useClass: ep___users_lists_push.default }; +const $users_lists_show: Provider = { provide: 'ep:users/lists/show', useClass: ep___users_lists_show.default }; +const $users_lists_update: Provider = { provide: 'ep:users/lists/update', useClass: ep___users_lists_update.default }; +const $users_notes: Provider = { provide: 'ep:users/notes', useClass: ep___users_notes.default }; +const $users_pages: Provider = { provide: 'ep:users/pages', useClass: ep___users_pages.default }; +const $users_reactions: Provider = { provide: 'ep:users/reactions', useClass: ep___users_reactions.default }; +const $users_recommendation: Provider = { provide: 'ep:users/recommendation', useClass: ep___users_recommendation.default }; +const $users_relation: Provider = { provide: 'ep:users/relation', useClass: ep___users_relation.default }; +const $users_reportAbuse: Provider = { provide: 'ep:users/report-abuse', useClass: ep___users_reportAbuse.default }; +const $users_searchByUsernameAndHost: Provider = { provide: 'ep:users/search-by-username-and-host', useClass: ep___users_searchByUsernameAndHost.default }; +const $users_search: Provider = { provide: 'ep:users/search', useClass: ep___users_search.default }; +const $users_show: Provider = { provide: 'ep:users/show', useClass: ep___users_show.default }; +const $users_stats: Provider = { provide: 'ep:users/stats', useClass: ep___users_stats.default }; +const $admin_driveCapOverride: Provider = { provide: 'ep:admin/drive-capacity-override', useClass: ep___admin_driveCapOverride.default }; +const $fetchRss: Provider = { provide: 'ep:fetch-rss', useClass: ep___fetchRss.default }; @Module({ imports: [ @@ -323,318 +637,632 @@ import { ApiLoggerService } from './ApiLoggerService.js'; providers: [ GetterService, ApiLoggerService, - { provide: 'ep:admin/meta', useClass: ep___admin_meta.default }, - { provide: 'ep:admin/abuse-user-reports', useClass: ep___admin_abuseUserReports.default }, - { provide: 'ep:admin/accounts/create', useClass: ep___admin_accounts_create.default }, - { provide: 'ep:admin/accounts/delete', useClass: ep___admin_accounts_delete.default }, - { provide: 'ep:admin/ad/create', useClass: ep___admin_ad_create.default }, - { provide: 'ep:admin/ad/delete', useClass: ep___admin_ad_delete.default }, - { provide: 'ep:admin/ad/list', useClass: ep___admin_ad_list.default }, - { provide: 'ep:admin/ad/update', useClass: ep___admin_ad_update.default }, - { provide: 'ep:admin/announcements/create', useClass: ep___admin_announcements_create.default }, - { provide: 'ep:admin/announcements/delete', useClass: ep___admin_announcements_delete.default }, - { provide: 'ep:admin/announcements/list', useClass: ep___admin_announcements_list.default }, - { provide: 'ep:admin/announcements/update', useClass: ep___admin_announcements_update.default }, - { provide: 'ep:admin/delete-all-files-of-a-user', useClass: ep___admin_deleteAllFilesOfAUser.default }, - { provide: 'ep:admin/drive/clean-remote-files', useClass: ep___admin_drive_cleanRemoteFiles.default }, - { provide: 'ep:admin/drive/cleanup', useClass: ep___admin_drive_cleanup.default }, - { provide: 'ep:admin/drive/files', useClass: ep___admin_drive_files.default }, - { provide: 'ep:admin/drive/show-file', useClass: ep___admin_drive_showFile.default }, - { provide: 'ep:admin/emoji/add-aliases-bulk', useClass: ep___admin_emoji_addAliasesBulk.default }, - { provide: 'ep:admin/emoji/add', useClass: ep___admin_emoji_add.default }, - { provide: 'ep:admin/emoji/copy', useClass: ep___admin_emoji_copy.default }, - { provide: 'ep:admin/emoji/delete-bulk', useClass: ep___admin_emoji_deleteBulk.default }, - { provide: 'ep:admin/emoji/delete', useClass: ep___admin_emoji_delete.default }, - { provide: 'ep:admin/emoji/import-zip', useClass: ep___admin_emoji_importZip.default }, - { provide: 'ep:admin/emoji/list-remote', useClass: ep___admin_emoji_listRemote.default }, - { provide: 'ep:admin/emoji/list', useClass: ep___admin_emoji_list.default }, - { provide: 'ep:admin/emoji/remove-aliases-bulk', useClass: ep___admin_emoji_removeAliasesBulk.default }, - { provide: 'ep:admin/emoji/set-aliases-bulk', useClass: ep___admin_emoji_setAliasesBulk.default }, - { provide: 'ep:admin/emoji/set-category-bulk', useClass: ep___admin_emoji_setCategoryBulk.default }, - { provide: 'ep:admin/emoji/update', useClass: ep___admin_emoji_update.default }, - { provide: 'ep:admin/federation/delete-all-files', useClass: ep___admin_federation_deleteAllFiles.default }, - { provide: 'ep:admin/federation/refresh-remote-instance-metadata', useClass: ep___admin_federation_refreshRemoteInstanceMetadata.default }, - { provide: 'ep:admin/federation/remove-all-following', useClass: ep___admin_federation_removeAllFollowing.default }, - { provide: 'ep:admin/federation/update-instance', useClass: ep___admin_federation_updateInstance.default }, - { provide: 'ep:admin/get-index-stats', useClass: ep___admin_getIndexStats.default }, - { provide: 'ep:admin/get-table-stats', useClass: ep___admin_getTableStats.default }, - { provide: 'ep:admin/get-user-ips', useClass: ep___admin_getUserIps.default }, - { provide: 'ep:admin/invite', useClass: ep___admin_invite.default }, - { provide: 'ep:admin/moderators/add', useClass: ep___admin_moderators_add.default }, - { provide: 'ep:admin/moderators/remove', useClass: ep___admin_moderators_remove.default }, - { provide: 'ep:admin/promo/create', useClass: ep___admin_promo_create.default }, - { provide: 'ep:admin/queue/clear', useClass: ep___admin_queue_clear.default }, - { provide: 'ep:admin/queue/deliver-delayed', useClass: ep___admin_queue_deliverDelayed.default }, - { provide: 'ep:admin/queue/inbox-delayed', useClass: ep___admin_queue_inboxDelayed.default }, - { provide: 'ep:admin/queue/stats', useClass: ep___admin_queue_stats.default }, - { provide: 'ep:admin/relays/add', useClass: ep___admin_relays_add.default }, - { provide: 'ep:admin/relays/list', useClass: ep___admin_relays_list.default }, - { provide: 'ep:admin/relays/remove', useClass: ep___admin_relays_remove.default }, - { provide: 'ep:admin/reset-password', useClass: ep___admin_resetPassword.default }, - { provide: 'ep:admin/resolve-abuse-user-report', useClass: ep___admin_resolveAbuseUserReport.default }, - { provide: 'ep:admin/send-email', useClass: ep___admin_sendEmail.default }, - { provide: 'ep:admin/server-info', useClass: ep___admin_serverInfo.default }, - { provide: 'ep:admin/show-moderation-logs', useClass: ep___admin_showModerationLogs.default }, - { provide: 'ep:admin/show-user', useClass: ep___admin_showUser.default }, - { provide: 'ep:admin/show-users', useClass: ep___admin_showUsers.default }, - { provide: 'ep:admin/silence-user', useClass: ep___admin_silenceUser.default }, - { provide: 'ep:admin/suspend-user', useClass: ep___admin_suspendUser.default }, - { provide: 'ep:admin/unsilence-user', useClass: ep___admin_unsilenceUser.default }, - { provide: 'ep:admin/unsuspend-user', useClass: ep___admin_unsuspendUser.default }, - { provide: 'ep:admin/update-meta', useClass: ep___admin_updateMeta.default }, - { provide: 'ep:admin/delete-account', useClass: ep___admin_deleteAccount.default }, - { provide: 'ep:admin/update-user-note', useClass: ep___admin_updateUserNote.default }, - { provide: 'ep:announcements', useClass: ep___announcements.default }, - { provide: 'ep:antennas/create', useClass: ep___antennas_create.default }, - { provide: 'ep:antennas/delete', useClass: ep___antennas_delete.default }, - { provide: 'ep:antennas/list', useClass: ep___antennas_list.default }, - { provide: 'ep:antennas/notes', useClass: ep___antennas_notes.default }, - { provide: 'ep:antennas/show', useClass: ep___antennas_show.default }, - { provide: 'ep:antennas/update', useClass: ep___antennas_update.default }, - { provide: 'ep:ap/get', useClass: ep___ap_get.default }, - { provide: 'ep:ap/show', useClass: ep___ap_show.default }, - { provide: 'ep:app/create', useClass: ep___app_create.default }, - { provide: 'ep:app/show', useClass: ep___app_show.default }, - { provide: 'ep:auth/accept', useClass: ep___auth_accept.default }, - { provide: 'ep:auth/session/generate', useClass: ep___auth_session_generate.default }, - { provide: 'ep:auth/session/show', useClass: ep___auth_session_show.default }, - { provide: 'ep:auth/session/userkey', useClass: ep___auth_session_userkey.default }, - { provide: 'ep:blocking/create', useClass: ep___blocking_create.default }, - { provide: 'ep:blocking/delete', useClass: ep___blocking_delete.default }, - { provide: 'ep:blocking/list', useClass: ep___blocking_list.default }, - { provide: 'ep:channels/create', useClass: ep___channels_create.default }, - { provide: 'ep:channels/featured', useClass: ep___channels_featured.default }, - { provide: 'ep:channels/follow', useClass: ep___channels_follow.default }, - { provide: 'ep:channels/followed', useClass: ep___channels_followed.default }, - { provide: 'ep:channels/owned', useClass: ep___channels_owned.default }, - { provide: 'ep:channels/show', useClass: ep___channels_show.default }, - { provide: 'ep:channels/timeline', useClass: ep___channels_timeline.default }, - { provide: 'ep:channels/unfollow', useClass: ep___channels_unfollow.default }, - { provide: 'ep:channels/update', useClass: ep___channels_update.default }, - { provide: 'ep:charts/active-users', useClass: ep___charts_activeUsers.default }, - { provide: 'ep:charts/ap-request', useClass: ep___charts_apRequest.default }, - { provide: 'ep:charts/drive', useClass: ep___charts_drive.default }, - { provide: 'ep:charts/federation', useClass: ep___charts_federation.default }, - { provide: 'ep:charts/hashtag', useClass: ep___charts_hashtag.default }, - { provide: 'ep:charts/instance', useClass: ep___charts_instance.default }, - { provide: 'ep:charts/notes', useClass: ep___charts_notes.default }, - { provide: 'ep:charts/user/drive', useClass: ep___charts_user_drive.default }, - { provide: 'ep:charts/user/following', useClass: ep___charts_user_following.default }, - { provide: 'ep:charts/user/notes', useClass: ep___charts_user_notes.default }, - { provide: 'ep:charts/user/reactions', useClass: ep___charts_user_reactions.default }, - { provide: 'ep:charts/users', useClass: ep___charts_users.default }, - { provide: 'ep:clips/add-note', useClass: ep___clips_addNote.default }, - { provide: 'ep:clips/remove-note', useClass: ep___clips_removeNote.default }, - { provide: 'ep:clips/create', useClass: ep___clips_create.default }, - { provide: 'ep:clips/delete', useClass: ep___clips_delete.default }, - { provide: 'ep:clips/list', useClass: ep___clips_list.default }, - { provide: 'ep:clips/notes', useClass: ep___clips_notes.default }, - { provide: 'ep:clips/show', useClass: ep___clips_show.default }, - { provide: 'ep:clips/update', useClass: ep___clips_update.default }, - { provide: 'ep:drive', useClass: ep___drive.default }, - { provide: 'ep:drive/files', useClass: ep___drive_files.default }, - { provide: 'ep:drive/files/attached-notes', useClass: ep___drive_files_attachedNotes.default }, - { provide: 'ep:drive/files/check-existence', useClass: ep___drive_files_checkExistence.default }, - { provide: 'ep:drive/files/create', useClass: ep___drive_files_create.default }, - { provide: 'ep:drive/files/delete', useClass: ep___drive_files_delete.default }, - { provide: 'ep:drive/files/find-by-hash', useClass: ep___drive_files_findByHash.default }, - { provide: 'ep:drive/files/find', useClass: ep___drive_files_find.default }, - { provide: 'ep:drive/files/show', useClass: ep___drive_files_show.default }, - { provide: 'ep:drive/files/update', useClass: ep___drive_files_update.default }, - { provide: 'ep:drive/files/upload-from-url', useClass: ep___drive_files_uploadFromUrl.default }, - { provide: 'ep:drive/folders', useClass: ep___drive_folders.default }, - { provide: 'ep:drive/folders/create', useClass: ep___drive_folders_create.default }, - { provide: 'ep:drive/folders/delete', useClass: ep___drive_folders_delete.default }, - { provide: 'ep:drive/folders/find', useClass: ep___drive_folders_find.default }, - { provide: 'ep:drive/folders/show', useClass: ep___drive_folders_show.default }, - { provide: 'ep:drive/folders/update', useClass: ep___drive_folders_update.default }, - { provide: 'ep:drive/stream', useClass: ep___drive_stream.default }, - { provide: 'ep:email-address/available', useClass: ep___emailAddress_available.default }, - { provide: 'ep:endpoint', useClass: ep___endpoint.default }, - { provide: 'ep:endpoints', useClass: ep___endpoints.default }, - { provide: 'ep:export-custom-emojis', useClass: ep___exportCustomEmojis.default }, - { provide: 'ep:federation/followers', useClass: ep___federation_followers.default }, - { provide: 'ep:federation/following', useClass: ep___federation_following.default }, - { provide: 'ep:federation/instances', useClass: ep___federation_instances.default }, - { provide: 'ep:federation/show-instance', useClass: ep___federation_showInstance.default }, - { provide: 'ep:federation/update-remote-user', useClass: ep___federation_updateRemoteUser.default }, - { provide: 'ep:federation/users', useClass: ep___federation_users.default }, - { provide: 'ep:federation/stats', useClass: ep___federation_stats.default }, - { provide: 'ep:following/create', useClass: ep___following_create.default }, - { provide: 'ep:following/delete', useClass: ep___following_delete.default }, - { provide: 'ep:following/invalidate', useClass: ep___following_invalidate.default }, - { provide: 'ep:following/requests/accept', useClass: ep___following_requests_accept.default }, - { provide: 'ep:following/requests/cancel', useClass: ep___following_requests_cancel.default }, - { provide: 'ep:following/requests/list', useClass: ep___following_requests_list.default }, - { provide: 'ep:following/requests/reject', useClass: ep___following_requests_reject.default }, - { provide: 'ep:gallery/featured', useClass: ep___gallery_featured.default }, - { provide: 'ep:gallery/popular', useClass: ep___gallery_popular.default }, - { provide: 'ep:gallery/posts', useClass: ep___gallery_posts.default }, - { provide: 'ep:gallery/posts/create', useClass: ep___gallery_posts_create.default }, - { provide: 'ep:gallery/posts/delete', useClass: ep___gallery_posts_delete.default }, - { provide: 'ep:gallery/posts/like', useClass: ep___gallery_posts_like.default }, - { provide: 'ep:gallery/posts/show', useClass: ep___gallery_posts_show.default }, - { provide: 'ep:gallery/posts/unlike', useClass: ep___gallery_posts_unlike.default }, - { provide: 'ep:gallery/posts/update', useClass: ep___gallery_posts_update.default }, - { provide: 'ep:get-online-users-count', useClass: ep___getOnlineUsersCount.default }, - { provide: 'ep:hashtags/list', useClass: ep___hashtags_list.default }, - { provide: 'ep:hashtags/search', useClass: ep___hashtags_search.default }, - { provide: 'ep:hashtags/show', useClass: ep___hashtags_show.default }, - { provide: 'ep:hashtags/trend', useClass: ep___hashtags_trend.default }, - { provide: 'ep:hashtags/users', useClass: ep___hashtags_users.default }, - { provide: 'ep:i', useClass: ep___i.default }, - { provide: 'ep:i/2fa/done', useClass: ep___i_2fa_done.default }, - { provide: 'ep:i/2fa/key-done', useClass: ep___i_2fa_keyDone.default }, - { provide: 'ep:i/2fa/password-less', useClass: ep___i_2fa_passwordLess.default }, - { provide: 'ep:i/2fa/register-key', useClass: ep___i_2fa_registerKey.default }, - { provide: 'ep:i/2fa/register', useClass: ep___i_2fa_register.default }, - { provide: 'ep:i/2fa/remove-key', useClass: ep___i_2fa_removeKey.default }, - { provide: 'ep:i/2fa/unregister', useClass: ep___i_2fa_unregister.default }, - { provide: 'ep:i/apps', useClass: ep___i_apps.default }, - { provide: 'ep:i/authorized-apps', useClass: ep___i_authorizedApps.default }, - { provide: 'ep:i/change-password', useClass: ep___i_changePassword.default }, - { provide: 'ep:i/delete-account', useClass: ep___i_deleteAccount.default }, - { provide: 'ep:i/export-blocking', useClass: ep___i_exportBlocking.default }, - { provide: 'ep:i/export-following', useClass: ep___i_exportFollowing.default }, - { provide: 'ep:i/export-mute', useClass: ep___i_exportMute.default }, - { provide: 'ep:i/export-notes', useClass: ep___i_exportNotes.default }, - { provide: 'ep:i/export-user-lists', useClass: ep___i_exportUserLists.default }, - { provide: 'ep:i/favorites', useClass: ep___i_favorites.default }, - { provide: 'ep:i/gallery/likes', useClass: ep___i_gallery_likes.default }, - { provide: 'ep:i/gallery/posts', useClass: ep___i_gallery_posts.default }, - { provide: 'ep:i/get-word-muted-notes-count', useClass: ep___i_getWordMutedNotesCount.default }, - { provide: 'ep:i/import-blocking', useClass: ep___i_importBlocking.default }, - { provide: 'ep:i/import-following', useClass: ep___i_importFollowing.default }, - { provide: 'ep:i/import-muting', useClass: ep___i_importMuting.default }, - { provide: 'ep:i/import-user-lists', useClass: ep___i_importUserLists.default }, - { provide: 'ep:i/notifications', useClass: ep___i_notifications.default }, - { provide: 'ep:i/page-likes', useClass: ep___i_pageLikes.default }, - { provide: 'ep:i/pages', useClass: ep___i_pages.default }, - { provide: 'ep:i/pin', useClass: ep___i_pin.default }, - { provide: 'ep:i/read-all-messaging-messages', useClass: ep___i_readAllMessagingMessages.default }, - { provide: 'ep:i/read-all-unread-notes', useClass: ep___i_readAllUnreadNotes.default }, - { provide: 'ep:i/read-announcement', useClass: ep___i_readAnnouncement.default }, - { provide: 'ep:i/regenerate-token', useClass: ep___i_regenerateToken.default }, - { provide: 'ep:i/registry/get-all', useClass: ep___i_registry_getAll.default }, - { provide: 'ep:i/registry/get-detail', useClass: ep___i_registry_getDetail.default }, - { provide: 'ep:i/registry/get', useClass: ep___i_registry_get.default }, - { provide: 'ep:i/registry/keys-with-type', useClass: ep___i_registry_keysWithType.default }, - { provide: 'ep:i/registry/keys', useClass: ep___i_registry_keys.default }, - { provide: 'ep:i/registry/remove', useClass: ep___i_registry_remove.default }, - { provide: 'ep:i/registry/scopes', useClass: ep___i_registry_scopes.default }, - { provide: 'ep:i/registry/set', useClass: ep___i_registry_set.default }, - { provide: 'ep:i/revoke-token', useClass: ep___i_revokeToken.default }, - { provide: 'ep:i/signin-history', useClass: ep___i_signinHistory.default }, - { provide: 'ep:i/unpin', useClass: ep___i_unpin.default }, - { provide: 'ep:i/update-email', useClass: ep___i_updateEmail.default }, - { provide: 'ep:i/update', useClass: ep___i_update.default }, - { provide: 'ep:i/user-group-invites', useClass: ep___i_userGroupInvites.default }, - { provide: 'ep:i/webhooks/create', useClass: ep___i_webhooks_create.default }, - { provide: 'ep:i/webhooks/list', useClass: ep___i_webhooks_list.default }, - { provide: 'ep:i/webhooks/show', useClass: ep___i_webhooks_show.default }, - { provide: 'ep:i/webhooks/update', useClass: ep___i_webhooks_update.default }, - { provide: 'ep:i/webhooks/delete', useClass: ep___i_webhooks_delete.default }, - { provide: 'ep:messaging/history', useClass: ep___messaging_history.default }, - { provide: 'ep:messaging/messages', useClass: ep___messaging_messages.default }, - { provide: 'ep:messaging/messages/create', useClass: ep___messaging_messages_create.default }, - { provide: 'ep:messaging/messages/delete', useClass: ep___messaging_messages_delete.default }, - { provide: 'ep:messaging/messages/read', useClass: ep___messaging_messages_read.default }, - { provide: 'ep:meta', useClass: ep___meta.default }, - { provide: 'ep:miauth/gen-token', useClass: ep___miauth_genToken.default }, - { provide: 'ep:mute/create', useClass: ep___mute_create.default }, - { provide: 'ep:mute/delete', useClass: ep___mute_delete.default }, - { provide: 'ep:mute/list', useClass: ep___mute_list.default }, - { provide: 'ep:my/apps', useClass: ep___my_apps.default }, - { provide: 'ep:notes', useClass: ep___notes.default }, - { provide: 'ep:notes/children', useClass: ep___notes_children.default }, - { provide: 'ep:notes/clips', useClass: ep___notes_clips.default }, - { provide: 'ep:notes/conversation', useClass: ep___notes_conversation.default }, - { provide: 'ep:notes/create', useClass: ep___notes_create.default }, - { provide: 'ep:notes/delete', useClass: ep___notes_delete.default }, - { provide: 'ep:notes/favorites/create', useClass: ep___notes_favorites_create.default }, - { provide: 'ep:notes/favorites/delete', useClass: ep___notes_favorites_delete.default }, - { provide: 'ep:notes/featured', useClass: ep___notes_featured.default }, - { provide: 'ep:notes/global-timeline', useClass: ep___notes_globalTimeline.default }, - { provide: 'ep:notes/hybrid-timeline', useClass: ep___notes_hybridTimeline.default }, - { provide: 'ep:notes/local-timeline', useClass: ep___notes_localTimeline.default }, - { provide: 'ep:notes/mentions', useClass: ep___notes_mentions.default }, - { provide: 'ep:notes/polls/recommendation', useClass: ep___notes_polls_recommendation.default }, - { provide: 'ep:notes/polls/vote', useClass: ep___notes_polls_vote.default }, - { provide: 'ep:notes/reactions', useClass: ep___notes_reactions.default }, - { provide: 'ep:notes/reactions/create', useClass: ep___notes_reactions_create.default }, - { provide: 'ep:notes/reactions/delete', useClass: ep___notes_reactions_delete.default }, - { provide: 'ep:notes/renotes', useClass: ep___notes_renotes.default }, - { provide: 'ep:notes/replies', useClass: ep___notes_replies.default }, - { provide: 'ep:notes/search-by-tag', useClass: ep___notes_searchByTag.default }, - { provide: 'ep:notes/search', useClass: ep___notes_search.default }, - { provide: 'ep:notes/show', useClass: ep___notes_show.default }, - { provide: 'ep:notes/state', useClass: ep___notes_state.default }, - { provide: 'ep:notes/thread-muting/create', useClass: ep___notes_threadMuting_create.default }, - { provide: 'ep:notes/thread-muting/delete', useClass: ep___notes_threadMuting_delete.default }, - { provide: 'ep:notes/timeline', useClass: ep___notes_timeline.default }, - { provide: 'ep:notes/translate', useClass: ep___notes_translate.default }, - { provide: 'ep:notes/unrenote', useClass: ep___notes_unrenote.default }, - { provide: 'ep:notes/user-list-timeline', useClass: ep___notes_userListTimeline.default }, - { provide: 'ep:notifications/create', useClass: ep___notifications_create.default }, - { provide: 'ep:notifications/mark-all-as-read', useClass: ep___notifications_markAllAsRead.default }, - { provide: 'ep:notifications/read', useClass: ep___notifications_read.default }, - { provide: 'ep:page-push', useClass: ep___pagePush.default }, - { provide: 'ep:pages/create', useClass: ep___pages_create.default }, - { provide: 'ep:pages/delete', useClass: ep___pages_delete.default }, - { provide: 'ep:pages/featured', useClass: ep___pages_featured.default }, - { provide: 'ep:pages/like', useClass: ep___pages_like.default }, - { provide: 'ep:pages/show', useClass: ep___pages_show.default }, - { provide: 'ep:pages/unlike', useClass: ep___pages_unlike.default }, - { provide: 'ep:pages/update', useClass: ep___pages_update.default }, - { provide: 'ep:ping', useClass: ep___ping.default }, - { provide: 'ep:pinned-users', useClass: ep___pinnedUsers.default }, - { provide: 'ep:promo/read', useClass: ep___promo_read.default }, - { provide: 'ep:request-reset-password', useClass: ep___requestResetPassword.default }, - { provide: 'ep:reset-db', useClass: ep___resetDb.default }, - { provide: 'ep:reset-password', useClass: ep___resetPassword.default }, - { provide: 'ep:server-info', useClass: ep___serverInfo.default }, - { provide: 'ep:stats', useClass: ep___stats.default }, - { provide: 'ep:sw/register', useClass: ep___sw_register.default }, - { provide: 'ep:sw/unregister', useClass: ep___sw_unregister.default }, - { provide: 'ep:test', useClass: ep___test.default }, - { provide: 'ep:username/available', useClass: ep___username_available.default }, - { provide: 'ep:users', useClass: ep___users.default }, - { provide: 'ep:users/clips', useClass: ep___users_clips.default }, - { provide: 'ep:users/followers', useClass: ep___users_followers.default }, - { provide: 'ep:users/following', useClass: ep___users_following.default }, - { provide: 'ep:users/gallery/posts', useClass: ep___users_gallery_posts.default }, - { provide: 'ep:users/get-frequently-replied-users', useClass: ep___users_getFrequentlyRepliedUsers.default }, - { provide: 'ep:users/groups/create', useClass: ep___users_groups_create.default }, - { provide: 'ep:users/groups/delete', useClass: ep___users_groups_delete.default }, - { provide: 'ep:users/groups/invitations/accept', useClass: ep___users_groups_invitations_accept.default }, - { provide: 'ep:users/groups/invitations/reject', useClass: ep___users_groups_invitations_reject.default }, - { provide: 'ep:users/groups/invite', useClass: ep___users_groups_invite.default }, - { provide: 'ep:users/groups/joined', useClass: ep___users_groups_joined.default }, - { provide: 'ep:users/groups/leave', useClass: ep___users_groups_leave.default }, - { provide: 'ep:users/groups/owned', useClass: ep___users_groups_owned.default }, - { provide: 'ep:users/groups/pull', useClass: ep___users_groups_pull.default }, - { provide: 'ep:users/groups/show', useClass: ep___users_groups_show.default }, - { provide: 'ep:users/groups/transfer', useClass: ep___users_groups_transfer.default }, - { provide: 'ep:users/groups/update', useClass: ep___users_groups_update.default }, - { provide: 'ep:users/lists/create', useClass: ep___users_lists_create.default }, - { provide: 'ep:users/lists/delete', useClass: ep___users_lists_delete.default }, - { provide: 'ep:users/lists/list', useClass: ep___users_lists_list.default }, - { provide: 'ep:users/lists/pull', useClass: ep___users_lists_pull.default }, - { provide: 'ep:users/lists/push', useClass: ep___users_lists_push.default }, - { provide: 'ep:users/lists/show', useClass: ep___users_lists_show.default }, - { provide: 'ep:users/lists/update', useClass: ep___users_lists_update.default }, - { provide: 'ep:users/notes', useClass: ep___users_notes.default }, - { provide: 'ep:users/pages', useClass: ep___users_pages.default }, - { provide: 'ep:users/reactions', useClass: ep___users_reactions.default }, - { provide: 'ep:users/recommendation', useClass: ep___users_recommendation.default }, - { provide: 'ep:users/relation', useClass: ep___users_relation.default }, - { provide: 'ep:users/report-abuse', useClass: ep___users_reportAbuse.default }, - { provide: 'ep:users/search-by-username-and-host', useClass: ep___users_searchByUsernameAndHost.default }, - { provide: 'ep:users/search', useClass: ep___users_search.default }, - { provide: 'ep:users/show', useClass: ep___users_show.default }, - { provide: 'ep:users/stats', useClass: ep___users_stats.default }, - { provide: 'ep:admin/drive-capacity-override', useClass: ep___admin_driveCapOverride.default }, - { provide: 'ep:fetch-rss', useClass: ep___fetchRss.default }, + $admin_meta, + $admin_abuseUserReports, + $admin_accounts_create, + $admin_accounts_delete, + $admin_ad_create, + $admin_ad_delete, + $admin_ad_list, + $admin_ad_update, + $admin_announcements_create, + $admin_announcements_delete, + $admin_announcements_list, + $admin_announcements_update, + $admin_deleteAllFilesOfAUser, + $admin_drive_cleanRemoteFiles, + $admin_drive_cleanup, + $admin_drive_files, + $admin_drive_showFile, + $admin_emoji_addAliasesBulk, + $admin_emoji_add, + $admin_emoji_copy, + $admin_emoji_deleteBulk, + $admin_emoji_delete, + $admin_emoji_importZip, + $admin_emoji_listRemote, + $admin_emoji_list, + $admin_emoji_removeAliasesBulk, + $admin_emoji_setAliasesBulk, + $admin_emoji_setCategoryBulk, + $admin_emoji_update, + $admin_federation_deleteAllFiles, + $admin_federation_refreshRemoteInstanceMetadata, + $admin_federation_removeAllFollowing, + $admin_federation_updateInstance, + $admin_getIndexStats, + $admin_getTableStats, + $admin_getUserIps, + $admin_invite, + $admin_moderators_add, + $admin_moderators_remove, + $admin_promo_create, + $admin_queue_clear, + $admin_queue_deliverDelayed, + $admin_queue_inboxDelayed, + $admin_queue_stats, + $admin_relays_add, + $admin_relays_list, + $admin_relays_remove, + $admin_resetPassword, + $admin_resolveAbuseUserReport, + $admin_sendEmail, + $admin_serverInfo, + $admin_showModerationLogs, + $admin_showUser, + $admin_showUsers, + $admin_silenceUser, + $admin_suspendUser, + $admin_unsilenceUser, + $admin_unsuspendUser, + $admin_updateMeta, + $admin_deleteAccount, + $admin_updateUserNote, + $announcements, + $antennas_create, + $antennas_delete, + $antennas_list, + $antennas_notes, + $antennas_show, + $antennas_update, + $ap_get, + $ap_show, + $app_create, + $app_show, + $auth_accept, + $auth_session_generate, + $auth_session_show, + $auth_session_userkey, + $blocking_create, + $blocking_delete, + $blocking_list, + $channels_create, + $channels_featured, + $channels_follow, + $channels_followed, + $channels_owned, + $channels_show, + $channels_timeline, + $channels_unfollow, + $channels_update, + $charts_activeUsers, + $charts_apRequest, + $charts_drive, + $charts_federation, + $charts_hashtag, + $charts_instance, + $charts_notes, + $charts_user_drive, + $charts_user_following, + $charts_user_notes, + $charts_user_reactions, + $charts_users, + $clips_addNote, + $clips_removeNote, + $clips_create, + $clips_delete, + $clips_list, + $clips_notes, + $clips_show, + $clips_update, + $drive, + $drive_files, + $drive_files_attachedNotes, + $drive_files_checkExistence, + $drive_files_create, + $drive_files_delete, + $drive_files_findByHash, + $drive_files_find, + $drive_files_show, + $drive_files_update, + $drive_files_uploadFromUrl, + $drive_folders, + $drive_folders_create, + $drive_folders_delete, + $drive_folders_find, + $drive_folders_show, + $drive_folders_update, + $drive_stream, + $emailAddress_available, + $endpoint, + $endpoints, + $exportCustomEmojis, + $federation_followers, + $federation_following, + $federation_instances, + $federation_showInstance, + $federation_updateRemoteUser, + $federation_users, + $federation_stats, + $following_create, + $following_delete, + $following_invalidate, + $following_requests_accept, + $following_requests_cancel, + $following_requests_list, + $following_requests_reject, + $gallery_featured, + $gallery_popular, + $gallery_posts, + $gallery_posts_create, + $gallery_posts_delete, + $gallery_posts_like, + $gallery_posts_show, + $gallery_posts_unlike, + $gallery_posts_update, + $getOnlineUsersCount, + $hashtags_list, + $hashtags_search, + $hashtags_show, + $hashtags_trend, + $hashtags_users, + $i, + $i_2fa_done, + $i_2fa_keyDone, + $i_2fa_passwordLess, + $i_2fa_registerKey, + $i_2fa_register, + $i_2fa_removeKey, + $i_2fa_unregister, + $i_apps, + $i_authorizedApps, + $i_changePassword, + $i_deleteAccount, + $i_exportBlocking, + $i_exportFollowing, + $i_exportMute, + $i_exportNotes, + $i_exportUserLists, + $i_favorites, + $i_gallery_likes, + $i_gallery_posts, + $i_getWordMutedNotesCount, + $i_importBlocking, + $i_importFollowing, + $i_importMuting, + $i_importUserLists, + $i_notifications, + $i_pageLikes, + $i_pages, + $i_pin, + $i_readAllMessagingMessages, + $i_readAllUnreadNotes, + $i_readAnnouncement, + $i_regenerateToken, + $i_registry_getAll, + $i_registry_getDetail, + $i_registry_get, + $i_registry_keysWithType, + $i_registry_keys, + $i_registry_remove, + $i_registry_scopes, + $i_registry_set, + $i_revokeToken, + $i_signinHistory, + $i_unpin, + $i_updateEmail, + $i_update, + $i_userGroupInvites, + $i_webhooks_create, + $i_webhooks_list, + $i_webhooks_show, + $i_webhooks_update, + $i_webhooks_delete, + $messaging_history, + $messaging_messages, + $messaging_messages_create, + $messaging_messages_delete, + $messaging_messages_read, + $meta, + $miauth_genToken, + $mute_create, + $mute_delete, + $mute_list, + $my_apps, + $notes, + $notes_children, + $notes_clips, + $notes_conversation, + $notes_create, + $notes_delete, + $notes_favorites_create, + $notes_favorites_delete, + $notes_featured, + $notes_globalTimeline, + $notes_hybridTimeline, + $notes_localTimeline, + $notes_mentions, + $notes_polls_recommendation, + $notes_polls_vote, + $notes_reactions, + $notes_reactions_create, + $notes_reactions_delete, + $notes_renotes, + $notes_replies, + $notes_searchByTag, + $notes_search, + $notes_show, + $notes_state, + $notes_threadMuting_create, + $notes_threadMuting_delete, + $notes_timeline, + $notes_translate, + $notes_unrenote, + $notes_userListTimeline, + $notifications_create, + $notifications_markAllAsRead, + $notifications_read, + $pagePush, + $pages_create, + $pages_delete, + $pages_featured, + $pages_like, + $pages_show, + $pages_unlike, + $pages_update, + $ping, + $pinnedUsers, + $promo_read, + $requestResetPassword, + $resetDb, + $resetPassword, + $serverInfo, + $stats, + $sw_register, + $sw_unregister, + $test, + $username_available, + $users, + $users_clips, + $users_followers, + $users_following, + $users_gallery_posts, + $users_getFrequentlyRepliedUsers, + $users_groups_create, + $users_groups_delete, + $users_groups_invitations_accept, + $users_groups_invitations_reject, + $users_groups_invite, + $users_groups_joined, + $users_groups_leave, + $users_groups_owned, + $users_groups_pull, + $users_groups_show, + $users_groups_transfer, + $users_groups_update, + $users_lists_create, + $users_lists_delete, + $users_lists_list, + $users_lists_pull, + $users_lists_push, + $users_lists_show, + $users_lists_update, + $users_notes, + $users_pages, + $users_reactions, + $users_recommendation, + $users_relation, + $users_reportAbuse, + $users_searchByUsernameAndHost, + $users_search, + $users_show, + $users_stats, + $admin_driveCapOverride, + $fetchRss, + ], + exports: [ + $admin_meta, + $admin_abuseUserReports, + $admin_accounts_create, + $admin_accounts_delete, + $admin_ad_create, + $admin_ad_delete, + $admin_ad_list, + $admin_ad_update, + $admin_announcements_create, + $admin_announcements_delete, + $admin_announcements_list, + $admin_announcements_update, + $admin_deleteAllFilesOfAUser, + $admin_drive_cleanRemoteFiles, + $admin_drive_cleanup, + $admin_drive_files, + $admin_drive_showFile, + $admin_emoji_addAliasesBulk, + $admin_emoji_add, + $admin_emoji_copy, + $admin_emoji_deleteBulk, + $admin_emoji_delete, + $admin_emoji_importZip, + $admin_emoji_listRemote, + $admin_emoji_list, + $admin_emoji_removeAliasesBulk, + $admin_emoji_setAliasesBulk, + $admin_emoji_setCategoryBulk, + $admin_emoji_update, + $admin_federation_deleteAllFiles, + $admin_federation_refreshRemoteInstanceMetadata, + $admin_federation_removeAllFollowing, + $admin_federation_updateInstance, + $admin_getIndexStats, + $admin_getTableStats, + $admin_getUserIps, + $admin_invite, + $admin_moderators_add, + $admin_moderators_remove, + $admin_promo_create, + $admin_queue_clear, + $admin_queue_deliverDelayed, + $admin_queue_inboxDelayed, + $admin_queue_stats, + $admin_relays_add, + $admin_relays_list, + $admin_relays_remove, + $admin_resetPassword, + $admin_resolveAbuseUserReport, + $admin_sendEmail, + $admin_serverInfo, + $admin_showModerationLogs, + $admin_showUser, + $admin_showUsers, + $admin_silenceUser, + $admin_suspendUser, + $admin_unsilenceUser, + $admin_unsuspendUser, + $admin_updateMeta, + $admin_deleteAccount, + $admin_updateUserNote, + $announcements, + $antennas_create, + $antennas_delete, + $antennas_list, + $antennas_notes, + $antennas_show, + $antennas_update, + $ap_get, + $ap_show, + $app_create, + $app_show, + $auth_accept, + $auth_session_generate, + $auth_session_show, + $auth_session_userkey, + $blocking_create, + $blocking_delete, + $blocking_list, + $channels_create, + $channels_featured, + $channels_follow, + $channels_followed, + $channels_owned, + $channels_show, + $channels_timeline, + $channels_unfollow, + $channels_update, + $charts_activeUsers, + $charts_apRequest, + $charts_drive, + $charts_federation, + $charts_hashtag, + $charts_instance, + $charts_notes, + $charts_user_drive, + $charts_user_following, + $charts_user_notes, + $charts_user_reactions, + $charts_users, + $clips_addNote, + $clips_removeNote, + $clips_create, + $clips_delete, + $clips_list, + $clips_notes, + $clips_show, + $clips_update, + $drive, + $drive_files, + $drive_files_attachedNotes, + $drive_files_checkExistence, + $drive_files_create, + $drive_files_delete, + $drive_files_findByHash, + $drive_files_find, + $drive_files_show, + $drive_files_update, + $drive_files_uploadFromUrl, + $drive_folders, + $drive_folders_create, + $drive_folders_delete, + $drive_folders_find, + $drive_folders_show, + $drive_folders_update, + $drive_stream, + $emailAddress_available, + $endpoint, + $endpoints, + $exportCustomEmojis, + $federation_followers, + $federation_following, + $federation_instances, + $federation_showInstance, + $federation_updateRemoteUser, + $federation_users, + $federation_stats, + $following_create, + $following_delete, + $following_invalidate, + $following_requests_accept, + $following_requests_cancel, + $following_requests_list, + $following_requests_reject, + $gallery_featured, + $gallery_popular, + $gallery_posts, + $gallery_posts_create, + $gallery_posts_delete, + $gallery_posts_like, + $gallery_posts_show, + $gallery_posts_unlike, + $gallery_posts_update, + $getOnlineUsersCount, + $hashtags_list, + $hashtags_search, + $hashtags_show, + $hashtags_trend, + $hashtags_users, + $i, + $i_2fa_done, + $i_2fa_keyDone, + $i_2fa_passwordLess, + $i_2fa_registerKey, + $i_2fa_register, + $i_2fa_removeKey, + $i_2fa_unregister, + $i_apps, + $i_authorizedApps, + $i_changePassword, + $i_deleteAccount, + $i_exportBlocking, + $i_exportFollowing, + $i_exportMute, + $i_exportNotes, + $i_exportUserLists, + $i_favorites, + $i_gallery_likes, + $i_gallery_posts, + $i_getWordMutedNotesCount, + $i_importBlocking, + $i_importFollowing, + $i_importMuting, + $i_importUserLists, + $i_notifications, + $i_pageLikes, + $i_pages, + $i_pin, + $i_readAllMessagingMessages, + $i_readAllUnreadNotes, + $i_readAnnouncement, + $i_regenerateToken, + $i_registry_getAll, + $i_registry_getDetail, + $i_registry_get, + $i_registry_keysWithType, + $i_registry_keys, + $i_registry_remove, + $i_registry_scopes, + $i_registry_set, + $i_revokeToken, + $i_signinHistory, + $i_unpin, + $i_updateEmail, + $i_update, + $i_userGroupInvites, + $i_webhooks_create, + $i_webhooks_list, + $i_webhooks_show, + $i_webhooks_update, + $i_webhooks_delete, + $messaging_history, + $messaging_messages, + $messaging_messages_create, + $messaging_messages_delete, + $messaging_messages_read, + $meta, + $miauth_genToken, + $mute_create, + $mute_delete, + $mute_list, + $my_apps, + $notes, + $notes_children, + $notes_clips, + $notes_conversation, + $notes_create, + $notes_delete, + $notes_favorites_create, + $notes_favorites_delete, + $notes_featured, + $notes_globalTimeline, + $notes_hybridTimeline, + $notes_localTimeline, + $notes_mentions, + $notes_polls_recommendation, + $notes_polls_vote, + $notes_reactions, + $notes_reactions_create, + $notes_reactions_delete, + $notes_renotes, + $notes_replies, + $notes_searchByTag, + $notes_search, + $notes_show, + $notes_state, + $notes_threadMuting_create, + $notes_threadMuting_delete, + $notes_timeline, + $notes_translate, + $notes_unrenote, + $notes_userListTimeline, + $notifications_create, + $notifications_markAllAsRead, + $notifications_read, + $pagePush, + $pages_create, + $pages_delete, + $pages_featured, + $pages_like, + $pages_show, + $pages_unlike, + $pages_update, + $ping, + $pinnedUsers, + $promo_read, + $requestResetPassword, + $resetDb, + $resetPassword, + $serverInfo, + $stats, + $sw_register, + $sw_unregister, + $test, + $username_available, + $users, + $users_clips, + $users_followers, + $users_following, + $users_gallery_posts, + $users_getFrequentlyRepliedUsers, + $users_groups_create, + $users_groups_delete, + $users_groups_invitations_accept, + $users_groups_invitations_reject, + $users_groups_invite, + $users_groups_joined, + $users_groups_leave, + $users_groups_owned, + $users_groups_pull, + $users_groups_show, + $users_groups_transfer, + $users_groups_update, + $users_lists_create, + $users_lists_delete, + $users_lists_list, + $users_lists_pull, + $users_lists_push, + $users_lists_show, + $users_lists_update, + $users_notes, + $users_pages, + $users_reactions, + $users_recommendation, + $users_relation, + $users_reportAbuse, + $users_searchByUsernameAndHost, + $users_search, + $users_show, + $users_stats, + $admin_driveCapOverride, + $fetchRss, ], }) export class EndpointsModule {} From 7079fee99eedbfe59ba05a9efbe14c5d67a4fc58 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 17 Sep 2022 00:50:52 +0900 Subject: [PATCH 171/180] wip --- packages/backend/src/app.module.ts | 2 + packages/backend/src/boot/worker.ts | 5 +- .../src/queue/DbQueueProcessorsService.ts | 28 ++-- .../ObjectStorageQueueProcessorsService.ts | 6 +- .../backend/src/queue/QueueProcessorModule.ts | 18 +++ .../src/queue/QueueProcessorService.ts | 141 ++++++++++++++++++ .../src/queue/SystemQueueProcessorsService.ts | 12 +- packages/backend/src/queue/index.ts | 136 ----------------- .../CheckExpiredMutingsProcessorService.ts | 6 +- .../processors/CleanChartsProcessorService.ts | 28 ++-- .../queue/processors/CleanProcessorService.ts | 4 +- .../CleanRemoteFilesProcessorService.ts | 6 +- .../DeleteAccountProcessorService.ts | 6 +- .../DeleteDriveFilesProcessorService.ts | 6 +- .../processors/DeleteFileProcessorService.ts | 6 +- .../EndedPollNotificationProcessorService.ts | 6 +- .../ExportCustomEmojisProcessorService.ts | 11 +- .../processors/ExportNotesProcessorService.ts | 6 +- .../ImportCustomEmojisProcessorService.ts | 13 +- .../ResyncChartsProcessorService.ts | 28 ++-- .../processors/TickChartsProcessorService.ts | 28 ++-- .../src/server/api/ApiServerService.ts | 2 +- 22 files changed, 265 insertions(+), 239 deletions(-) create mode 100644 packages/backend/src/queue/QueueProcessorModule.ts create mode 100644 packages/backend/src/queue/QueueProcessorService.ts delete mode 100644 packages/backend/src/queue/index.ts diff --git a/packages/backend/src/app.module.ts b/packages/backend/src/app.module.ts index b6b212e0df98..863bc147274c 100644 --- a/packages/backend/src/app.module.ts +++ b/packages/backend/src/app.module.ts @@ -3,6 +3,7 @@ import { QueueModule } from '@/services/queue/QueueModule.js'; import { CoreModule } from './services/CoreModule.js'; import { ServerModule } from './server/ServerModule.js'; import { GlobalModule } from './GlobalModule.js'; +import { QueueProcessorModule } from './queue/QueueProcessorModule.js'; @Module({ imports: [ @@ -10,6 +11,7 @@ import { GlobalModule } from './GlobalModule.js'; CoreModule, QueueModule, ServerModule, + QueueProcessorModule, ], }) export class AppModule {} diff --git a/packages/backend/src/boot/worker.ts b/packages/backend/src/boot/worker.ts index f72db535150d..88fab3e41bb8 100644 --- a/packages/backend/src/boot/worker.ts +++ b/packages/backend/src/boot/worker.ts @@ -3,8 +3,8 @@ import { NestFactory } from '@nestjs/core'; import { envOption } from '@/env.js'; import { ChartManagementService } from '@/services/chart/ChartManagementService.js'; import { ServerService } from '@/server/ServerService.js'; +import { QueueProcessorService } from '@/queue/QueueProcessorService.js'; import { initDb } from '../db/postgre.js'; -import initializeQueue from '../queue/index.js'; import { AppModule } from '../app.module.js'; /** @@ -21,7 +21,8 @@ export async function workerMain() { // start job queue if (!envOption.onlyServer) { - await initializeQueue(app); + const queueProcessorService = app.get(QueueProcessorService); + queueProcessorService.start(); } app.get(ChartManagementService).run(); diff --git a/packages/backend/src/queue/DbQueueProcessorsService.ts b/packages/backend/src/queue/DbQueueProcessorsService.ts index 02529c4e2450..b4e22dcad23c 100644 --- a/packages/backend/src/queue/DbQueueProcessorsService.ts +++ b/packages/backend/src/queue/DbQueueProcessorsService.ts @@ -1,20 +1,20 @@ import { Inject, Injectable } from '@nestjs/common'; import type { DbJobData } from '@/queue/types.js'; import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; -import type { DeleteDriveFilesProcessorService } from './processors/DeleteDriveFilesProcessorService.js'; -import type { ExportCustomEmojisProcessorService } from './processors/ExportCustomEmojisProcessorService.js'; -import type { ExportNotesProcessorService } from './processors/ExportNotesProcessorService.js'; -import type { ExportFollowingProcessorService } from './processors/ExportFollowingProcessorService.js'; -import type { ExportMutingProcessorService } from './processors/ExportMutingProcessorService.js'; -import type { ExportBlockingProcessorService } from './processors/ExportBlockingProcessorService.js'; -import type { ExportUserListsProcessorService } from './processors/ExportUserListsProcessorService.js'; -import type { ImportFollowingProcessorService } from './processors/ImportFollowingProcessorService.js'; -import type { ImportMutingProcessorService } from './processors/ImportMutingProcessorService.js'; -import type { ImportBlockingProcessorService } from './processors/ImportBlockingProcessorService.js'; -import type { ImportUserListsProcessorService } from './processors/ImportUserListsProcessorService.js'; -import type { ImportCustomEmojisProcessorService } from './processors/ImportCustomEmojisProcessorService.js'; -import type { DeleteAccountProcessorService } from './processors/DeleteAccountProcessorService.js'; +import { Config } from '@/config.js'; +import { DeleteDriveFilesProcessorService } from './processors/DeleteDriveFilesProcessorService.js'; +import { ExportCustomEmojisProcessorService } from './processors/ExportCustomEmojisProcessorService.js'; +import { ExportNotesProcessorService } from './processors/ExportNotesProcessorService.js'; +import { ExportFollowingProcessorService } from './processors/ExportFollowingProcessorService.js'; +import { ExportMutingProcessorService } from './processors/ExportMutingProcessorService.js'; +import { ExportBlockingProcessorService } from './processors/ExportBlockingProcessorService.js'; +import { ExportUserListsProcessorService } from './processors/ExportUserListsProcessorService.js'; +import { ImportFollowingProcessorService } from './processors/ImportFollowingProcessorService.js'; +import { ImportMutingProcessorService } from './processors/ImportMutingProcessorService.js'; +import { ImportBlockingProcessorService } from './processors/ImportBlockingProcessorService.js'; +import { ImportUserListsProcessorService } from './processors/ImportUserListsProcessorService.js'; +import { ImportCustomEmojisProcessorService } from './processors/ImportCustomEmojisProcessorService.js'; +import { DeleteAccountProcessorService } from './processors/DeleteAccountProcessorService.js'; import type Bull from 'bull'; @Injectable() diff --git a/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts b/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts index b7cf39905c96..9e0fd0cf9464 100644 --- a/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts +++ b/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import type { ObjectStorageJobData } from '@/queue/types.js'; import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; -import type { CleanRemoteFilesProcessorService } from './processors/CleanRemoteFilesProcessorService.js'; -import type { DeleteFileProcessorService } from './processors/DeleteFileProcessorService.js'; +import { Config } from '@/config.js'; +import { CleanRemoteFilesProcessorService } from './processors/CleanRemoteFilesProcessorService.js'; +import { DeleteFileProcessorService } from './processors/DeleteFileProcessorService.js'; import type Bull from 'bull'; @Injectable() diff --git a/packages/backend/src/queue/QueueProcessorModule.ts b/packages/backend/src/queue/QueueProcessorModule.ts new file mode 100644 index 000000000000..98348c8640b1 --- /dev/null +++ b/packages/backend/src/queue/QueueProcessorModule.ts @@ -0,0 +1,18 @@ +import { Module } from '@nestjs/common'; +import { CoreModule } from '@/services/CoreModule.js'; +import { QueueLoggerService } from './QueueLoggerService.js'; +import { QueueProcessorService } from './QueueProcessorService.js'; + +@Module({ + imports: [ + CoreModule, + ], + providers: [ + QueueLoggerService, + QueueProcessorService, + ], + exports: [ + QueueProcessorService, + ], +}) +export class QueueProcessorModule {} diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts new file mode 100644 index 000000000000..e46e8f65b5f0 --- /dev/null +++ b/packages/backend/src/queue/QueueProcessorService.ts @@ -0,0 +1,141 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { ModuleRef } from '@nestjs/core'; +import { Config } from '@/config.js'; +import { DI } from '@/di-symbols.js'; +import type Logger from '@/logger.js'; +import { QueueService } from '../services/QueueService.js'; +import { getJobInfo } from './get-job-info.js'; +import { SystemQueueProcessorsService } from './SystemQueueProcessorsService.js'; +import { ObjectStorageQueueProcessorsService } from './ObjectStorageQueueProcessorsService.js'; +import { DbQueueProcessorsService } from './DbQueueProcessorsService.js'; +import { WebhookDeliverProcessorService } from './processors/WebhookDeliverProcessorService.js'; +import { EndedPollNotificationProcessorService } from './processors/EndedPollNotificationProcessorService.js'; +import { DeliverProcessorService } from './processors/DeliverProcessorService.js'; +import { InboxProcessorService } from './processors/InboxProcessorService.js'; +import { QueueLoggerService } from './QueueLoggerService.js'; + +@Injectable() +export class QueueProcessorService { + #logger: Logger; + + constructor( + @Inject(DI.config) + private config: Config, + + private queueLoggerService: QueueLoggerService, + private queueService: QueueService, + private systemQueueProcessorsService: SystemQueueProcessorsService, + private objectStorageQueueProcessorsService: ObjectStorageQueueProcessorsService, + private dbQueueProcessorsService: DbQueueProcessorsService, + private webhookDeliverProcessorService: WebhookDeliverProcessorService, + private endedPollNotificationProcessorService: EndedPollNotificationProcessorService, + private deliverProcessorService: DeliverProcessorService, + private inboxProcessorService: InboxProcessorService, + ) { + this.#logger = this.queueLoggerService.logger; + } + + public start() { + function renderError(e: Error): any { + return { + stack: e.stack, + message: e.message, + name: e.name, + }; + } + + const systemLogger = this.#logger.createSubLogger('system'); + const deliverLogger = this.#logger.createSubLogger('deliver'); + const webhookLogger = this.#logger.createSubLogger('webhook'); + const inboxLogger = this.#logger.createSubLogger('inbox'); + const dbLogger = this.#logger.createSubLogger('db'); + const objectStorageLogger = this.#logger.createSubLogger('objectStorage'); + + this.queueService.systemQueue + .on('waiting', (jobId) => systemLogger.debug(`waiting id=${jobId}`)) + .on('active', (job) => systemLogger.debug(`active id=${job.id}`)) + .on('completed', (job, result) => systemLogger.debug(`completed(${result}) id=${job.id}`)) + .on('failed', (job, err) => systemLogger.warn(`failed(${err}) id=${job.id}`, { job, e: renderError(err) })) + .on('error', (job: any, err: Error) => systemLogger.error(`error ${err}`, { job, e: renderError(err) })) + .on('stalled', (job) => systemLogger.warn(`stalled id=${job.id}`)); + + this.queueService.deliverQueue + .on('waiting', (jobId) => deliverLogger.debug(`waiting id=${jobId}`)) + .on('active', (job) => deliverLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`)) + .on('completed', (job, result) => deliverLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`)) + .on('failed', (job, err) => deliverLogger.warn(`failed(${err}) ${getJobInfo(job)} to=${job.data.to}`)) + .on('error', (job: any, err: Error) => deliverLogger.error(`error ${err}`, { job, e: renderError(err) })) + .on('stalled', (job) => deliverLogger.warn(`stalled ${getJobInfo(job)} to=${job.data.to}`)); + + this.queueService.inboxQueue + .on('waiting', (jobId) => inboxLogger.debug(`waiting id=${jobId}`)) + .on('active', (job) => inboxLogger.debug(`active ${getJobInfo(job, true)}`)) + .on('completed', (job, result) => inboxLogger.debug(`completed(${result}) ${getJobInfo(job, true)}`)) + .on('failed', (job, err) => inboxLogger.warn(`failed(${err}) ${getJobInfo(job)} activity=${job.data.activity ? job.data.activity.id : 'none'}`, { job, e: renderError(err) })) + .on('error', (job: any, err: Error) => inboxLogger.error(`error ${err}`, { job, e: renderError(err) })) + .on('stalled', (job) => inboxLogger.warn(`stalled ${getJobInfo(job)} activity=${job.data.activity ? job.data.activity.id : 'none'}`)); + + this.queueService.dbQueue + .on('waiting', (jobId) => dbLogger.debug(`waiting id=${jobId}`)) + .on('active', (job) => dbLogger.debug(`active id=${job.id}`)) + .on('completed', (job, result) => dbLogger.debug(`completed(${result}) id=${job.id}`)) + .on('failed', (job, err) => dbLogger.warn(`failed(${err}) id=${job.id}`, { job, e: renderError(err) })) + .on('error', (job: any, err: Error) => dbLogger.error(`error ${err}`, { job, e: renderError(err) })) + .on('stalled', (job) => dbLogger.warn(`stalled id=${job.id}`)); + + this.queueService.objectStorageQueue + .on('waiting', (jobId) => objectStorageLogger.debug(`waiting id=${jobId}`)) + .on('active', (job) => objectStorageLogger.debug(`active id=${job.id}`)) + .on('completed', (job, result) => objectStorageLogger.debug(`completed(${result}) id=${job.id}`)) + .on('failed', (job, err) => objectStorageLogger.warn(`failed(${err}) id=${job.id}`, { job, e: renderError(err) })) + .on('error', (job: any, err: Error) => objectStorageLogger.error(`error ${err}`, { job, e: renderError(err) })) + .on('stalled', (job) => objectStorageLogger.warn(`stalled id=${job.id}`)); + + this.queueService.webhookDeliverQueue + .on('waiting', (jobId) => webhookLogger.debug(`waiting id=${jobId}`)) + .on('active', (job) => webhookLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`)) + .on('completed', (job, result) => webhookLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`)) + .on('failed', (job, err) => webhookLogger.warn(`failed(${err}) ${getJobInfo(job)} to=${job.data.to}`)) + .on('error', (job: any, err: Error) => webhookLogger.error(`error ${err}`, { job, e: renderError(err) })) + .on('stalled', (job) => webhookLogger.warn(`stalled ${getJobInfo(job)} to=${job.data.to}`)); + + this.queueService.deliverQueue.process(this.config.deliverJobConcurrency ?? 128, this.deliverProcessorService.process); + this.queueService.inboxQueue.process(this.config.inboxJobConcurrency ?? 16, this.inboxProcessorService.process); + this.queueService.endedPollNotificationQueue.process(this.endedPollNotificationProcessorService.process); + this.queueService.webhookDeliverQueue.process(64, this.webhookDeliverProcessorService.process); + this.dbQueueProcessorsService.start(this.queueService.dbQueue); + this.objectStorageQueueProcessorsService.start(this.queueService.objectStorageQueue); + + this.queueService.systemQueue.add('tickCharts', { + }, { + repeat: { cron: '55 * * * *' }, + removeOnComplete: true, + }); + + this.queueService.systemQueue.add('resyncCharts', { + }, { + repeat: { cron: '0 0 * * *' }, + removeOnComplete: true, + }); + + this.queueService.systemQueue.add('cleanCharts', { + }, { + repeat: { cron: '0 0 * * *' }, + removeOnComplete: true, + }); + + this.queueService.systemQueue.add('clean', { + }, { + repeat: { cron: '0 0 * * *' }, + removeOnComplete: true, + }); + + this.queueService.systemQueue.add('checkExpiredMutings', { + }, { + repeat: { cron: '*/5 * * * *' }, + removeOnComplete: true, + }); + + this.systemQueueProcessorsService.start(this.queueService.systemQueue); + } +} diff --git a/packages/backend/src/queue/SystemQueueProcessorsService.ts b/packages/backend/src/queue/SystemQueueProcessorsService.ts index cb94e4e87da1..df5b5fb8af3f 100644 --- a/packages/backend/src/queue/SystemQueueProcessorsService.ts +++ b/packages/backend/src/queue/SystemQueueProcessorsService.ts @@ -1,11 +1,11 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; -import type { TickChartsProcessorService } from './processors/TickChartsProcessorService.js'; -import type { ResyncChartsProcessorService } from './processors/ResyncChartsProcessorService.js'; -import type { CleanChartsProcessorService } from './processors/CleanChartsProcessorService.js'; -import type { CheckExpiredMutingsProcessorService } from './processors/CheckExpiredMutingsProcessorService.js'; -import type { CleanProcessorService } from './processors/CleanProcessorService.js'; +import { Config } from '@/config.js'; +import { TickChartsProcessorService } from './processors/TickChartsProcessorService.js'; +import { ResyncChartsProcessorService } from './processors/ResyncChartsProcessorService.js'; +import { CleanChartsProcessorService } from './processors/CleanChartsProcessorService.js'; +import { CheckExpiredMutingsProcessorService } from './processors/CheckExpiredMutingsProcessorService.js'; +import { CleanProcessorService } from './processors/CleanProcessorService.js'; import type Bull from 'bull'; @Injectable() diff --git a/packages/backend/src/queue/index.ts b/packages/backend/src/queue/index.ts deleted file mode 100644 index 9b10de917c1e..000000000000 --- a/packages/backend/src/queue/index.ts +++ /dev/null @@ -1,136 +0,0 @@ - -import { DI } from '@/di-symbols.js'; -import { QueueService } from '../services/QueueService.js'; -import { getJobInfo } from './get-job-info.js'; -import { SystemQueueProcessorsService } from './SystemQueueProcessorsService.js'; -import { ObjectStorageQueueProcessorsService } from './ObjectStorageQueueProcessorsService.js'; -import { DbQueueProcessorsService } from './DbQueueProcessorsService.js'; -import { WebhookDeliverProcessorService } from './processors/WebhookDeliverProcessorService.js'; -import { EndedPollNotificationProcessorService } from './processors/EndedPollNotificationProcessorService.js'; -import { DeliverProcessorService } from './processors/DeliverProcessorService.js'; -import { InboxProcessorService } from './processors/InboxProcessorService.js'; -import { QueueLoggerService } from './QueueLoggerService.js'; -import type { INestApplicationContext } from '@nestjs/common'; - -export default function(app: INestApplicationContext) { - const config = app.get(DI.config); - const queueLoggerService = app.get(QueueLoggerService); - const queueLogger = queueLoggerService.logger; - const queueService = app.get(QueueService); - const systemQueue = queueService.systemQueue; - const deliverQueue = queueService.deliverQueue; - const inboxQueue = queueService.inboxQueue; - const dbQueue = queueService.dbQueue; - const objectStorageQueue = queueService.objectStorageQueue; - const webhookDeliverQueue = queueService.webhookDeliverQueue; - const endedPollNotificationQueue = queueService.endedPollNotificationQueue; - const systemQueueProcessorsService = app.get(SystemQueueProcessorsService); - const objectStorageQueueProcessorsService = app.get(ObjectStorageQueueProcessorsService); - const dbQueueProcessorsService = app.get(DbQueueProcessorsService); - const webhookDeliverProcessorService = app.get(WebhookDeliverProcessorService); - const endedPollNotificationProcessorService = app.get(EndedPollNotificationProcessorService); - const deliverProcessorService = app.get(DeliverProcessorService); - const inboxProcessorService = app.get(InboxProcessorService); - - function renderError(e: Error): any { - return { - stack: e.stack, - message: e.message, - name: e.name, - }; - } - - const systemLogger = queueLogger.createSubLogger('system'); - const deliverLogger = queueLogger.createSubLogger('deliver'); - const webhookLogger = queueLogger.createSubLogger('webhook'); - const inboxLogger = queueLogger.createSubLogger('inbox'); - const dbLogger = queueLogger.createSubLogger('db'); - const objectStorageLogger = queueLogger.createSubLogger('objectStorage'); - - systemQueue - .on('waiting', (jobId) => systemLogger.debug(`waiting id=${jobId}`)) - .on('active', (job) => systemLogger.debug(`active id=${job.id}`)) - .on('completed', (job, result) => systemLogger.debug(`completed(${result}) id=${job.id}`)) - .on('failed', (job, err) => systemLogger.warn(`failed(${err}) id=${job.id}`, { job, e: renderError(err) })) - .on('error', (job: any, err: Error) => systemLogger.error(`error ${err}`, { job, e: renderError(err) })) - .on('stalled', (job) => systemLogger.warn(`stalled id=${job.id}`)); - - deliverQueue - .on('waiting', (jobId) => deliverLogger.debug(`waiting id=${jobId}`)) - .on('active', (job) => deliverLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`)) - .on('completed', (job, result) => deliverLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`)) - .on('failed', (job, err) => deliverLogger.warn(`failed(${err}) ${getJobInfo(job)} to=${job.data.to}`)) - .on('error', (job: any, err: Error) => deliverLogger.error(`error ${err}`, { job, e: renderError(err) })) - .on('stalled', (job) => deliverLogger.warn(`stalled ${getJobInfo(job)} to=${job.data.to}`)); - - inboxQueue - .on('waiting', (jobId) => inboxLogger.debug(`waiting id=${jobId}`)) - .on('active', (job) => inboxLogger.debug(`active ${getJobInfo(job, true)}`)) - .on('completed', (job, result) => inboxLogger.debug(`completed(${result}) ${getJobInfo(job, true)}`)) - .on('failed', (job, err) => inboxLogger.warn(`failed(${err}) ${getJobInfo(job)} activity=${job.data.activity ? job.data.activity.id : 'none'}`, { job, e: renderError(err) })) - .on('error', (job: any, err: Error) => inboxLogger.error(`error ${err}`, { job, e: renderError(err) })) - .on('stalled', (job) => inboxLogger.warn(`stalled ${getJobInfo(job)} activity=${job.data.activity ? job.data.activity.id : 'none'}`)); - - dbQueue - .on('waiting', (jobId) => dbLogger.debug(`waiting id=${jobId}`)) - .on('active', (job) => dbLogger.debug(`active id=${job.id}`)) - .on('completed', (job, result) => dbLogger.debug(`completed(${result}) id=${job.id}`)) - .on('failed', (job, err) => dbLogger.warn(`failed(${err}) id=${job.id}`, { job, e: renderError(err) })) - .on('error', (job: any, err: Error) => dbLogger.error(`error ${err}`, { job, e: renderError(err) })) - .on('stalled', (job) => dbLogger.warn(`stalled id=${job.id}`)); - - objectStorageQueue - .on('waiting', (jobId) => objectStorageLogger.debug(`waiting id=${jobId}`)) - .on('active', (job) => objectStorageLogger.debug(`active id=${job.id}`)) - .on('completed', (job, result) => objectStorageLogger.debug(`completed(${result}) id=${job.id}`)) - .on('failed', (job, err) => objectStorageLogger.warn(`failed(${err}) id=${job.id}`, { job, e: renderError(err) })) - .on('error', (job: any, err: Error) => objectStorageLogger.error(`error ${err}`, { job, e: renderError(err) })) - .on('stalled', (job) => objectStorageLogger.warn(`stalled id=${job.id}`)); - - webhookDeliverQueue - .on('waiting', (jobId) => webhookLogger.debug(`waiting id=${jobId}`)) - .on('active', (job) => webhookLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`)) - .on('completed', (job, result) => webhookLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`)) - .on('failed', (job, err) => webhookLogger.warn(`failed(${err}) ${getJobInfo(job)} to=${job.data.to}`)) - .on('error', (job: any, err: Error) => webhookLogger.error(`error ${err}`, { job, e: renderError(err) })) - .on('stalled', (job) => webhookLogger.warn(`stalled ${getJobInfo(job)} to=${job.data.to}`)); - - deliverQueue.process(config.deliverJobConcurrency || 128, deliverProcessorService.process); - inboxQueue.process(config.inboxJobConcurrency || 16, inboxProcessorService.process); - endedPollNotificationQueue.process(endedPollNotificationProcessorService.process); - webhookDeliverQueue.process(64, webhookDeliverProcessorService.process); - dbQueueProcessorsService.start(dbQueue); - objectStorageQueueProcessorsService.start(objectStorageQueue); - - systemQueue.add('tickCharts', { - }, { - repeat: { cron: '55 * * * *' }, - removeOnComplete: true, - }); - - systemQueue.add('resyncCharts', { - }, { - repeat: { cron: '0 0 * * *' }, - removeOnComplete: true, - }); - - systemQueue.add('cleanCharts', { - }, { - repeat: { cron: '0 0 * * *' }, - removeOnComplete: true, - }); - - systemQueue.add('clean', { - }, { - repeat: { cron: '0 0 * * *' }, - removeOnComplete: true, - }); - - systemQueue.add('checkExpiredMutings', { - }, { - repeat: { cron: '*/5 * * * *' }, - removeOnComplete: true, - }); - - systemQueueProcessorsService.start(systemQueue); -} diff --git a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts index 3d5673cc819b..4d6518b5a12b 100644 --- a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts +++ b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts @@ -2,11 +2,11 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { Mutings } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import type { GlobalEventService } from '@/services/GlobalEventService.js'; +import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; @Injectable() export class CheckExpiredMutingsProcessorService { diff --git a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts index 742047395b47..52b84b64b131 100644 --- a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts @@ -1,21 +1,21 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import type FederationChart from '@/services/chart/charts/federation.js'; -import type NotesChart from '@/services/chart/charts/notes.js'; -import type UsersChart from '@/services/chart/charts/users.js'; -import type ActiveUsersChart from '@/services/chart/charts/active-users.js'; -import type InstanceChart from '@/services/chart/charts/instance.js'; -import type PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; -import type DriveChart from '@/services/chart/charts/drive.js'; -import type PerUserReactionsChart from '@/services/chart/charts/per-user-reactions.js'; -import type HashtagChart from '@/services/chart/charts/hashtag.js'; -import type PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; -import type PerUserDriveChart from '@/services/chart/charts/per-user-drive.js'; -import type ApRequestChart from '@/services/chart/charts/ap-request.js'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; +import FederationChart from '@/services/chart/charts/federation.js'; +import NotesChart from '@/services/chart/charts/notes.js'; +import UsersChart from '@/services/chart/charts/users.js'; +import ActiveUsersChart from '@/services/chart/charts/active-users.js'; +import InstanceChart from '@/services/chart/charts/instance.js'; +import PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; +import DriveChart from '@/services/chart/charts/drive.js'; +import PerUserReactionsChart from '@/services/chart/charts/per-user-reactions.js'; +import HashtagChart from '@/services/chart/charts/hashtag.js'; +import PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; +import PerUserDriveChart from '@/services/chart/charts/per-user-drive.js'; +import ApRequestChart from '@/services/chart/charts/ap-request.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; @Injectable() diff --git a/packages/backend/src/queue/processors/CleanProcessorService.ts b/packages/backend/src/queue/processors/CleanProcessorService.ts index 216ea0fa998d..8af68f89008e 100644 --- a/packages/backend/src/queue/processors/CleanProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanProcessorService.ts @@ -2,10 +2,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, LessThan, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { UserIps } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; @Injectable() export class CleanProcessorService { diff --git a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts index 360be350e07f..ed2e53d7d45f 100644 --- a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts @@ -2,11 +2,11 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan, Not } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { DriveFiles } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import type { DriveService } from '@/services/DriveService.js'; +import { DriveService } from '@/services/DriveService.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; @Injectable() export class CleanRemoteFilesProcessorService { diff --git a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts index 9b0843d3cc2e..8af30058b0c0 100644 --- a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts @@ -3,14 +3,14 @@ import { MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { DriveFiles, UserProfiles } from '@/models/index.js'; import { Notes, Users } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import type { DriveService } from '@/services/DriveService.js'; +import { DriveService } from '@/services/DriveService.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { Note } from '@/models/entities/Note.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserDeleteJobData } from '../types.js'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; @Injectable() export class DeleteAccountProcessorService { diff --git a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts index ae805fa3975d..86f930f4e034 100644 --- a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts @@ -2,12 +2,12 @@ import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { Users, DriveFiles } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import type { DriveService } from '@/services/DriveService.js'; +import { DriveService } from '@/services/DriveService.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserJobData } from '../types.js'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; @Injectable() export class DeleteDriveFilesProcessorService { diff --git a/packages/backend/src/queue/processors/DeleteFileProcessorService.ts b/packages/backend/src/queue/processors/DeleteFileProcessorService.ts index 4b9084243084..e03e11fd1950 100644 --- a/packages/backend/src/queue/processors/DeleteFileProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteFileProcessorService.ts @@ -1,11 +1,11 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import type { DriveService } from '@/services/DriveService.js'; +import { DriveService } from '@/services/DriveService.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { ObjectStorageFileJobData } from '../types.js'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; @Injectable() export class DeleteFileProcessorService { diff --git a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts index f40a9601f7ad..7ecc2ccc20e5 100644 --- a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts +++ b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts @@ -2,12 +2,12 @@ import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { PollVotes , Notes } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import type { CreateNotificationService } from '@/services/CreateNotificationService.js'; +import { CreateNotificationService } from '@/services/CreateNotificationService.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { EndedPollNotificationJobData } from '../types.js'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; @Injectable() export class EndedPollNotificationProcessorService { diff --git a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts index ae11917b423e..2612f87a40f9 100644 --- a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts @@ -7,13 +7,13 @@ import mime from 'mime-types'; import archiver from 'archiver'; import { DI } from '@/di-symbols.js'; import type { Emojis, Users } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import type { DriveService } from '@/services/DriveService.js'; +import { DriveService } from '@/services/DriveService.js'; import { createTemp, createTempDir } from '@/misc/create-temp.js'; -import { downloadUrl } from '@/misc/download-url.js'; +import { DownloadService } from '@/services/DownloadService.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; @Injectable() export class ExportCustomEmojisProcessorService { @@ -30,6 +30,7 @@ export class ExportCustomEmojisProcessorService { private emojisRepository: typeof Emojis, private driveService: DriveService, + private downloadService: DownloadService, private queueLoggerService: QueueLoggerService, ) { this.queueLoggerService.logger.createSubLogger('export-custom-emojis'); @@ -86,7 +87,7 @@ export class ExportCustomEmojisProcessorService { let downloaded = false; try { - await downloadUrl(emoji.originalUrl, emojiPath); + await this.downloadService.downloadUrl(emoji.originalUrl, emojiPath); downloaded = true; } catch (e) { // TODO: 何度か再試行 this.#logger.error(e instanceof Error ? e : new Error(e as string)); diff --git a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts index 0d08e4946b36..6a58493569d7 100644 --- a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts @@ -4,15 +4,15 @@ import { IsNull, MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI } from '@/di-symbols.js'; import type { Notes, Polls , Users } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import type { DriveService } from '@/services/DriveService.js'; +import { DriveService } from '@/services/DriveService.js'; import { createTemp } from '@/misc/create-temp.js'; import type { Poll } from '@/models/entities/Poll.js'; import type { Note } from '@/models/entities/Note.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserJobData } from '../types.js'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; @Injectable() export class ExportNotesProcessorService { diff --git a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts index 0e3c1b982b97..01dfff9a2182 100644 --- a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts @@ -1,19 +1,18 @@ import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; -import { IsNull, MoreThan } from 'typeorm'; +import { IsNull, MoreThan , DataSource } from 'typeorm'; import unzipper from 'unzipper'; import { DI } from '@/di-symbols.js'; import type { Emojis , DriveFiles , Users } from '@/models/index.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import type { CustomEmojiService } from '@/services/CustomEmojiService.js'; +import { CustomEmojiService } from '@/services/CustomEmojiService.js'; import { createTempDir } from '@/misc/create-temp.js'; -import type { DriveService } from '@/services/DriveService.js'; -import type { DownloadService } from '@/services/DownloadService.js'; -import type { DataSource } from 'typeorm'; +import { DriveService } from '@/services/DriveService.js'; +import { DownloadService } from '@/services/DownloadService.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserImportJobData } from '../types.js'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; // TODO: 名前衝突時の動作を選べるようにする @Injectable() diff --git a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts index 064bc7ab1224..95c476d6849c 100644 --- a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts @@ -1,21 +1,21 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import type FederationChart from '@/services/chart/charts/federation.js'; -import type NotesChart from '@/services/chart/charts/notes.js'; -import type UsersChart from '@/services/chart/charts/users.js'; -import type ActiveUsersChart from '@/services/chart/charts/active-users.js'; -import type InstanceChart from '@/services/chart/charts/instance.js'; -import type PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; -import type DriveChart from '@/services/chart/charts/drive.js'; -import type PerUserReactionsChart from '@/services/chart/charts/per-user-reactions.js'; -import type HashtagChart from '@/services/chart/charts/hashtag.js'; -import type PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; -import type PerUserDriveChart from '@/services/chart/charts/per-user-drive.js'; -import type ApRequestChart from '@/services/chart/charts/ap-request.js'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; +import FederationChart from '@/services/chart/charts/federation.js'; +import NotesChart from '@/services/chart/charts/notes.js'; +import UsersChart from '@/services/chart/charts/users.js'; +import ActiveUsersChart from '@/services/chart/charts/active-users.js'; +import InstanceChart from '@/services/chart/charts/instance.js'; +import PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; +import DriveChart from '@/services/chart/charts/drive.js'; +import PerUserReactionsChart from '@/services/chart/charts/per-user-reactions.js'; +import HashtagChart from '@/services/chart/charts/hashtag.js'; +import PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; +import PerUserDriveChart from '@/services/chart/charts/per-user-drive.js'; +import ApRequestChart from '@/services/chart/charts/ap-request.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; @Injectable() diff --git a/packages/backend/src/queue/processors/TickChartsProcessorService.ts b/packages/backend/src/queue/processors/TickChartsProcessorService.ts index d27c284f7770..c5aacca5bd1d 100644 --- a/packages/backend/src/queue/processors/TickChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/TickChartsProcessorService.ts @@ -1,21 +1,21 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; +import { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import type FederationChart from '@/services/chart/charts/federation.js'; -import type NotesChart from '@/services/chart/charts/notes.js'; -import type UsersChart from '@/services/chart/charts/users.js'; -import type ActiveUsersChart from '@/services/chart/charts/active-users.js'; -import type InstanceChart from '@/services/chart/charts/instance.js'; -import type PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; -import type DriveChart from '@/services/chart/charts/drive.js'; -import type PerUserReactionsChart from '@/services/chart/charts/per-user-reactions.js'; -import type HashtagChart from '@/services/chart/charts/hashtag.js'; -import type PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; -import type PerUserDriveChart from '@/services/chart/charts/per-user-drive.js'; -import type ApRequestChart from '@/services/chart/charts/ap-request.js'; -import type { QueueLoggerService } from '../QueueLoggerService.js'; +import FederationChart from '@/services/chart/charts/federation.js'; +import NotesChart from '@/services/chart/charts/notes.js'; +import UsersChart from '@/services/chart/charts/users.js'; +import ActiveUsersChart from '@/services/chart/charts/active-users.js'; +import InstanceChart from '@/services/chart/charts/instance.js'; +import PerUserNotesChart from '@/services/chart/charts/per-user-notes.js'; +import DriveChart from '@/services/chart/charts/drive.js'; +import PerUserReactionsChart from '@/services/chart/charts/per-user-reactions.js'; +import HashtagChart from '@/services/chart/charts/hashtag.js'; +import PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; +import PerUserDriveChart from '@/services/chart/charts/per-user-drive.js'; +import ApRequestChart from '@/services/chart/charts/ap-request.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; @Injectable() diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts index 08c4dceb2e65..7e9ea5920dd0 100644 --- a/packages/backend/src/server/api/ApiServerService.ts +++ b/packages/backend/src/server/api/ApiServerService.ts @@ -43,7 +43,7 @@ export class ApiServerService { const handlers: Record = {}; for (const endpoint of endpoints) { - handlers[endpoint.name] = this.moduleRef.get('ep:' + endpoint.name).exec; + handlers[endpoint.name] = this.moduleRef.get('ep:' + endpoint.name, { strict: false }).exec; } // Init app From 25b5bcdb42d1493087c830200ee6ef349bc9154d Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 17 Sep 2022 00:52:49 +0900 Subject: [PATCH 172/180] Update QueueProcessorModule.ts --- packages/backend/src/queue/QueueProcessorModule.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/backend/src/queue/QueueProcessorModule.ts b/packages/backend/src/queue/QueueProcessorModule.ts index 98348c8640b1..a3a3698b19a2 100644 --- a/packages/backend/src/queue/QueueProcessorModule.ts +++ b/packages/backend/src/queue/QueueProcessorModule.ts @@ -2,6 +2,13 @@ import { Module } from '@nestjs/common'; import { CoreModule } from '@/services/CoreModule.js'; import { QueueLoggerService } from './QueueLoggerService.js'; import { QueueProcessorService } from './QueueProcessorService.js'; +import { DbQueueProcessorsService } from './DbQueueProcessorsService.js'; +import { ObjectStorageQueueProcessorsService } from './ObjectStorageQueueProcessorsService.js'; +import { DeliverProcessorService } from './processors/DeliverProcessorService.js'; +import { EndedPollNotificationProcessorService } from './processors/EndedPollNotificationProcessorService.js'; +import { InboxProcessorService } from './processors/InboxProcessorService.js'; +import { WebhookDeliverProcessorService } from './processors/WebhookDeliverProcessorService.js'; +import { SystemQueueProcessorsService } from './SystemQueueProcessorsService.js'; @Module({ imports: [ @@ -9,6 +16,13 @@ import { QueueProcessorService } from './QueueProcessorService.js'; ], providers: [ QueueLoggerService, + SystemQueueProcessorsService, + ObjectStorageQueueProcessorsService, + DbQueueProcessorsService, + WebhookDeliverProcessorService, + EndedPollNotificationProcessorService, + DeliverProcessorService, + InboxProcessorService, QueueProcessorService, ], exports: [ From 2591f4dc053ec4f5287d3bf49744edaf5e67eaed Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 17 Sep 2022 00:59:17 +0900 Subject: [PATCH 173/180] wip --- .../backend/src/queue/QueueProcessorModule.ts | 40 +++++++++++++++++++ .../CheckExpiredMutingsProcessorService.ts | 2 +- .../processors/CleanChartsProcessorService.ts | 2 +- .../queue/processors/CleanProcessorService.ts | 2 +- .../CleanRemoteFilesProcessorService.ts | 2 +- .../DeleteAccountProcessorService.ts | 2 +- .../DeleteDriveFilesProcessorService.ts | 2 +- .../processors/DeleteFileProcessorService.ts | 2 +- .../processors/DeliverProcessorService.ts | 2 +- .../EndedPollNotificationProcessorService.ts | 2 +- .../ExportBlockingProcessorService.ts | 2 +- .../ExportCustomEmojisProcessorService.ts | 2 +- .../ExportFollowingProcessorService.ts | 2 +- .../ExportMutingProcessorService.ts | 2 +- .../processors/ExportNotesProcessorService.ts | 2 +- .../ExportUserListsProcessorService.ts | 2 +- .../ImportBlockingProcessorService.ts | 2 +- .../ImportCustomEmojisProcessorService.ts | 2 +- .../ImportFollowingProcessorService.ts | 2 +- .../ImportMutingProcessorService.ts | 2 +- .../ImportUserListsProcessorService.ts | 2 +- .../queue/processors/InboxProcessorService.ts | 2 +- .../ResyncChartsProcessorService.ts | 2 +- .../processors/TickChartsProcessorService.ts | 2 +- .../WebhookDeliverProcessorService.ts | 2 +- 25 files changed, 64 insertions(+), 24 deletions(-) diff --git a/packages/backend/src/queue/QueueProcessorModule.ts b/packages/backend/src/queue/QueueProcessorModule.ts index a3a3698b19a2..6e789a105f0e 100644 --- a/packages/backend/src/queue/QueueProcessorModule.ts +++ b/packages/backend/src/queue/QueueProcessorModule.ts @@ -9,6 +9,26 @@ import { EndedPollNotificationProcessorService } from './processors/EndedPollNot import { InboxProcessorService } from './processors/InboxProcessorService.js'; import { WebhookDeliverProcessorService } from './processors/WebhookDeliverProcessorService.js'; import { SystemQueueProcessorsService } from './SystemQueueProcessorsService.js'; +import { CheckExpiredMutingsProcessorService } from './processors/CheckExpiredMutingsProcessorService.js'; +import { CleanChartsProcessorService } from './processors/CleanChartsProcessorService.js'; +import { CleanProcessorService } from './processors/CleanProcessorService.js'; +import { CleanRemoteFilesProcessorService } from './processors/CleanRemoteFilesProcessorService.js'; +import { DeleteAccountProcessorService } from './processors/DeleteAccountProcessorService.js'; +import { DeleteDriveFilesProcessorService } from './processors/DeleteDriveFilesProcessorService.js'; +import { DeleteFileProcessorService } from './processors/DeleteFileProcessorService.js'; +import { ExportBlockingProcessorService } from './processors/ExportBlockingProcessorService.js'; +import { ExportCustomEmojisProcessorService } from './processors/ExportCustomEmojisProcessorService.js'; +import { ExportFollowingProcessorService } from './processors/ExportFollowingProcessorService.js'; +import { ExportMutingProcessorService } from './processors/ExportMutingProcessorService.js'; +import { ExportNotesProcessorService } from './processors/ExportNotesProcessorService.js'; +import { ExportUserListsProcessorService } from './processors/ExportUserListsProcessorService.js'; +import { ImportBlockingProcessorService } from './processors/ImportBlockingProcessorService.js'; +import { ImportCustomEmojisProcessorService } from './processors/ImportCustomEmojisProcessorService.js'; +import { ImportFollowingProcessorService } from './processors/ImportFollowingProcessorService.js'; +import { ImportMutingProcessorService } from './processors/ImportMutingProcessorService.js'; +import { ImportUserListsProcessorService } from './processors/ImportUserListsProcessorService.js'; +import { ResyncChartsProcessorService } from './processors/ResyncChartsProcessorService.js'; +import { TickChartsProcessorService } from './processors/TickChartsProcessorService.js'; @Module({ imports: [ @@ -16,6 +36,26 @@ import { SystemQueueProcessorsService } from './SystemQueueProcessorsService.js' ], providers: [ QueueLoggerService, + TickChartsProcessorService, + ResyncChartsProcessorService, + CleanChartsProcessorService, + CheckExpiredMutingsProcessorService, + CleanProcessorService, + DeleteDriveFilesProcessorService, + ExportCustomEmojisProcessorService, + ExportNotesProcessorService, + ExportFollowingProcessorService, + ExportMutingProcessorService, + ExportBlockingProcessorService, + ExportUserListsProcessorService, + ImportFollowingProcessorService, + ImportMutingProcessorService, + ImportBlockingProcessorService, + ImportUserListsProcessorService, + ImportCustomEmojisProcessorService, + DeleteAccountProcessorService, + DeleteFileProcessorService, + CleanRemoteFilesProcessorService, SystemQueueProcessorsService, ObjectStorageQueueProcessorsService, DbQueueProcessorsService, diff --git a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts index 4d6518b5a12b..978e51449a43 100644 --- a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts +++ b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts @@ -22,7 +22,7 @@ export class CheckExpiredMutingsProcessorService { private globalEventService: GlobalEventService, private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('check-expired-mutings'); + this.#logger = this.queueLoggerService.logger.createSubLogger('check-expired-mutings'); } public async process(job: Bull.Job>, done: () => void): Promise { diff --git a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts index 52b84b64b131..63dce277545b 100644 --- a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts @@ -41,7 +41,7 @@ export class CleanChartsProcessorService { private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('clean-charts'); + this.#logger = this.queueLoggerService.logger.createSubLogger('clean-charts'); } public async process(job: Bull.Job>, done: () => void): Promise { diff --git a/packages/backend/src/queue/processors/CleanProcessorService.ts b/packages/backend/src/queue/processors/CleanProcessorService.ts index 8af68f89008e..acbff6c808b4 100644 --- a/packages/backend/src/queue/processors/CleanProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanProcessorService.ts @@ -20,7 +20,7 @@ export class CleanProcessorService { private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('clean'); + this.#logger = this.queueLoggerService.logger.createSubLogger('clean'); } public async process(job: Bull.Job>, done: () => void): Promise { diff --git a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts index ed2e53d7d45f..59557a20a978 100644 --- a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts @@ -22,7 +22,7 @@ export class CleanRemoteFilesProcessorService { private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('clean-remote-files'); + this.#logger = this.queueLoggerService.logger.createSubLogger('clean-remote-files'); } public async process(job: Bull.Job>, done: () => void): Promise { diff --git a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts index 8af30058b0c0..007c99db5b03 100644 --- a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts @@ -35,7 +35,7 @@ export class DeleteAccountProcessorService { private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('delete-account'); + this.#logger = this.queueLoggerService.logger.createSubLogger('delete-account'); } public async process(job: Bull.Job): Promise { diff --git a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts index 86f930f4e034..d61e13e1baa6 100644 --- a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts @@ -26,7 +26,7 @@ export class DeleteDriveFilesProcessorService { private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('delete-drive-files'); + this.#logger = this.queueLoggerService.logger.createSubLogger('delete-drive-files'); } public async process(job: Bull.Job, done: () => void): Promise { diff --git a/packages/backend/src/queue/processors/DeleteFileProcessorService.ts b/packages/backend/src/queue/processors/DeleteFileProcessorService.ts index e03e11fd1950..cf6d0d85f8b2 100644 --- a/packages/backend/src/queue/processors/DeleteFileProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteFileProcessorService.ts @@ -18,7 +18,7 @@ export class DeleteFileProcessorService { private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('delete-file'); + this.#logger = this.queueLoggerService.logger.createSubLogger('delete-file'); } public async process(job: Bull.Job): Promise { diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts index 08d0366b37d5..84eca8b05483 100644 --- a/packages/backend/src/queue/processors/DeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts @@ -45,7 +45,7 @@ export class DeliverProcessorService { private federationChart: FederationChart, private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('deliver'); + this.#logger = this.queueLoggerService.logger.createSubLogger('deliver'); this.#suspendedHostsCache = new Cache(1000 * 60 * 60); this.#latest = null; } diff --git a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts index 7ecc2ccc20e5..2cc772564a4b 100644 --- a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts +++ b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts @@ -26,7 +26,7 @@ export class EndedPollNotificationProcessorService { private createNotificationService: CreateNotificationService, private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('ended-poll-notification'); + this.#logger = this.queueLoggerService.logger.createSubLogger('ended-poll-notification'); } public async process(job: Bull.Job, done: () => void): Promise { diff --git a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts index e81839fd5712..73b1eb2c9da6 100644 --- a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts @@ -31,7 +31,7 @@ export class ExportBlockingProcessorService { private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('export-blocking'); + this.#logger = this.queueLoggerService.logger.createSubLogger('export-blocking'); } public async process(job: Bull.Job, done: () => void): Promise { diff --git a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts index 2612f87a40f9..af82fff74ea5 100644 --- a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts @@ -33,7 +33,7 @@ export class ExportCustomEmojisProcessorService { private downloadService: DownloadService, private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('export-custom-emojis'); + this.#logger = this.queueLoggerService.logger.createSubLogger('export-custom-emojis'); } public async process(job: Bull.Job, done: () => void): Promise { diff --git a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts index 10681933fd8e..6405215af50b 100644 --- a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts @@ -36,7 +36,7 @@ export class ExportFollowingProcessorService { private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('export-following'); + this.#logger = this.queueLoggerService.logger.createSubLogger('export-following'); } public async process(job: Bull.Job, done: () => void): Promise { diff --git a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts index d7add3ddc573..7e96c190b0b6 100644 --- a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts @@ -34,7 +34,7 @@ export class ExportMutingProcessorService { private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('export-muting'); + this.#logger = this.queueLoggerService.logger.createSubLogger('export-muting'); } public async process(job: Bull.Job, done: () => void): Promise { diff --git a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts index 6a58493569d7..847270524929 100644 --- a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts @@ -34,7 +34,7 @@ export class ExportNotesProcessorService { private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('export-notes'); + this.#logger = this.queueLoggerService.logger.createSubLogger('export-notes'); } public async process(job: Bull.Job, done: () => void): Promise { diff --git a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts index b724ec4cc8df..82c58b703ab0 100644 --- a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts @@ -34,7 +34,7 @@ export class ExportUserListsProcessorService { private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('export-user-lists'); + this.#logger = this.queueLoggerService.logger.createSubLogger('export-user-lists'); } public async process(job: Bull.Job, done: () => void): Promise { diff --git a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts index 4a2ef315a65d..3e51afc75abd 100644 --- a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts @@ -37,7 +37,7 @@ export class ImportBlockingProcessorService { private downloadService: DownloadService, private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('import-blocking'); + this.#logger = this.queueLoggerService.logger.createSubLogger('import-blocking'); } public async process(job: Bull.Job, done: () => void): Promise { diff --git a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts index 01dfff9a2182..f11154ae1171 100644 --- a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts @@ -40,7 +40,7 @@ export class ImportCustomEmojisProcessorService { private downloadService: DownloadService, private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('import-custom-emojis'); + this.#logger = this.queueLoggerService.logger.createSubLogger('import-custom-emojis'); } public async process(job: Bull.Job, done: () => void): Promise { diff --git a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts index e5c58235613a..fb0b44eb2e92 100644 --- a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts @@ -34,7 +34,7 @@ export class ImportFollowingProcessorService { private downloadService: DownloadService, private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('import-following'); + this.#logger = this.queueLoggerService.logger.createSubLogger('import-following'); } public async process(job: Bull.Job, done: () => void): Promise { diff --git a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts index 48c1bd5b2fe0..b2478cc0d887 100644 --- a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts @@ -35,7 +35,7 @@ export class ImportMutingProcessorService { private downloadService: DownloadService, private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('import-muting'); + this.#logger = this.queueLoggerService.logger.createSubLogger('import-muting'); } public async process(job: Bull.Job, done: () => void): Promise { diff --git a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts index 7b6f2e5dd5fa..c004cbae4884 100644 --- a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts @@ -42,7 +42,7 @@ export class ImportUserListsProcessorService { private downloadService: DownloadService, private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('import-user-lists'); + this.#logger = this.queueLoggerService.logger.createSubLogger('import-user-lists'); } public async process(job: Bull.Job, done: () => void): Promise { diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index c5fabd46d0bc..934249e528d5 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -58,7 +58,7 @@ export class InboxProcessorService { private federationChart: FederationChart, private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('inbox'); + this.#logger = this.queueLoggerService.logger.createSubLogger('inbox'); } public async process(job: Bull.Job): Promise { diff --git a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts index 95c476d6849c..718b96c2709d 100644 --- a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts @@ -41,7 +41,7 @@ export class ResyncChartsProcessorService { private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('resync-charts'); + this.#logger = this.queueLoggerService.logger.createSubLogger('resync-charts'); } public async process(job: Bull.Job>, done: () => void): Promise { diff --git a/packages/backend/src/queue/processors/TickChartsProcessorService.ts b/packages/backend/src/queue/processors/TickChartsProcessorService.ts index c5aacca5bd1d..a52fd7bf0153 100644 --- a/packages/backend/src/queue/processors/TickChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/TickChartsProcessorService.ts @@ -41,7 +41,7 @@ export class TickChartsProcessorService { private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('tick-charts'); + this.#logger = this.queueLoggerService.logger.createSubLogger('tick-charts'); } public async process(job: Bull.Job>, done: () => void): Promise { diff --git a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts index ced2c34d167a..ae0177cf8c92 100644 --- a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts @@ -24,7 +24,7 @@ export class WebhookDeliverProcessorService { private httpRequestService: HttpRequestService, private queueLoggerService: QueueLoggerService, ) { - this.queueLoggerService.logger.createSubLogger('webhook'); + this.#logger = this.queueLoggerService.logger.createSubLogger('webhook'); } public async process(job: Bull.Job): Promise { From 69506771865ef5ea4b5ffe3236b23c2796e78d4c Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 17 Sep 2022 01:27:12 +0900 Subject: [PATCH 174/180] wip --- .../src/queue/DbQueueProcessorsService.ts | 26 +++++++++---------- .../ObjectStorageQueueProcessorsService.ts | 4 +-- .../src/queue/SystemQueueProcessorsService.ts | 10 +++---- .../backend/src/server/api/ApiCallService.ts | 21 ++++++++------- .../backend/src/services/AntennaService.ts | 6 +++-- .../services/entities/UserEntityService.ts | 4 +-- 6 files changed, 37 insertions(+), 34 deletions(-) diff --git a/packages/backend/src/queue/DbQueueProcessorsService.ts b/packages/backend/src/queue/DbQueueProcessorsService.ts index b4e22dcad23c..fcc9873a6f40 100644 --- a/packages/backend/src/queue/DbQueueProcessorsService.ts +++ b/packages/backend/src/queue/DbQueueProcessorsService.ts @@ -41,19 +41,19 @@ export class DbQueueProcessorsService { public start(dbQueue: Bull.Queue) { const jobs = { - deleteDriveFiles: this.deleteDriveFilesProcessorService.process, - exportCustomEmojis: this.exportCustomEmojisProcessorService.process, - exportNotes: this.exportNotesProcessorService.process, - exportFollowing: this.exportFollowingProcessorService.process, - exportMuting: this.exportMutingProcessorService.process, - exportBlocking: this.exportBlockingProcessorService.process, - exportUserLists: this.exportUserListsProcessorService.process, - importFollowing: this.importFollowingProcessorService.process, - importMuting: this.importMutingProcessorService.process, - importBlocking: this.importBlockingProcessorService.process, - importUserLists: this.importUserListsProcessorService.process, - importCustomEmojis: this.importCustomEmojisProcessorService.process, - deleteAccount: this.deleteAccountProcessorService.process, + deleteDriveFiles: (job, done) => this.deleteDriveFilesProcessorService.process(job, done), + exportCustomEmojis: (job, done) => this.exportCustomEmojisProcessorService.process(job, done), + exportNotes: (job, done) => this.exportNotesProcessorService.process(job, done), + exportFollowing: (job, done) => this.exportFollowingProcessorService.process(job, done), + exportMuting: (job, done) => this.exportMutingProcessorService.process(job, done), + exportBlocking: (job, done) => this.exportBlockingProcessorService.process(job, done), + exportUserLists: (job, done) => this.exportUserListsProcessorService.process(job, done), + importFollowing: (job, done) => this.importFollowingProcessorService.process(job, done), + importMuting: (job, done) => this.importMutingProcessorService.process(job, done), + importBlocking: (job, done) => this.importBlockingProcessorService.process(job, done), + importUserLists: (job, done) => this.importUserListsProcessorService.process(job, done), + importCustomEmojis: (job, done) => this.importCustomEmojisProcessorService.process(job, done), + deleteAccount: (job, done) => this.deleteAccountProcessorService.process(job, done), } as Record>>; for (const [k, v] of Object.entries(jobs)) { diff --git a/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts b/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts index 9e0fd0cf9464..402c038be0ac 100644 --- a/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts +++ b/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts @@ -19,8 +19,8 @@ export class ObjectStorageQueueProcessorsService { public start(q: Bull.Queue) { const jobs = { - deleteFile: this.deleteFileProcessorService.process, - cleanRemoteFiles: this.cleanRemoteFilesProcessorService.process, + deleteFile: (job, done) => this.deleteFileProcessorService.process(job, done), + cleanRemoteFiles: (job, done) => this.cleanRemoteFilesProcessorService.process(job, done), } as Record>>; for (const [k, v] of Object.entries(jobs)) { diff --git a/packages/backend/src/queue/SystemQueueProcessorsService.ts b/packages/backend/src/queue/SystemQueueProcessorsService.ts index df5b5fb8af3f..7c227296e79e 100644 --- a/packages/backend/src/queue/SystemQueueProcessorsService.ts +++ b/packages/backend/src/queue/SystemQueueProcessorsService.ts @@ -24,11 +24,11 @@ export class SystemQueueProcessorsService { public start(dbQueue: Bull.Queue>) { const jobs = { - tickCharts: this.tickChartsProcessorService.process, - resyncCharts: this.resyncChartsProcessorService.process, - cleanCharts: this.cleanChartsProcessorService.process, - checkExpiredMutings: this.checkExpiredMutingsProcessorService.process, - clean: this.cleanProcessorService.process, + tickCharts: (job, done) => this.tickChartsProcessorService.process(job, done), + resyncCharts: (job, done) => this.resyncChartsProcessorService.process(job, done), + cleanCharts: (job, done) => this.cleanChartsProcessorService.process(job, done), + checkExpiredMutings: (job, done) => this.checkExpiredMutingsProcessorService.process(job, done), + clean: (job, done) => this.cleanProcessorService.process(job, done), } as Record> | Bull.ProcessPromiseFunction>>; for (const [k, v] of Object.entries(jobs)) { diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index b78fb91669f1..f25e89af3bed 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -221,24 +221,25 @@ export class ApiCallService implements OnApplicationShutdown { // API invoking const before = performance.now(); - return await exec(data, user, token, ctx?.file, ctx?.ip, ctx?.headers).catch((e: Error) => { - if (e instanceof ApiError) { - throw e; + return await exec(data, user, token, ctx?.file, ctx?.ip, ctx?.headers).catch((err: Error) => { + if (err instanceof ApiError) { + throw err; } else { - this.#logger.error(`Internal error occurred in ${ep.name}: ${e.message}`, { + this.#logger.error(`Internal error occurred in ${ep.name}: ${err.message}`, { ep: ep.name, ps: data, e: { - message: e.message, - code: e.name, - stack: e.stack, + message: err.message, + code: err.name, + stack: err.stack, }, }); + console.error(err); throw new ApiError(null, { e: { - message: e.message, - code: e.name, - stack: e.stack, + message: err.message, + code: err.name, + stack: err.stack, }, }); } diff --git a/packages/backend/src/services/AntennaService.ts b/packages/backend/src/services/AntennaService.ts index 90a31a2fa6bf..ca7ded26807f 100644 --- a/packages/backend/src/services/AntennaService.ts +++ b/packages/backend/src/services/AntennaService.ts @@ -16,8 +16,8 @@ import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() export class AntennaService implements OnApplicationShutdown { - #antennasFetched = false; - #antennas: Antenna[] = []; + #antennasFetched: boolean; + #antennas: Antenna[]; #blockingCache: Cache; constructor( @@ -49,6 +49,8 @@ export class AntennaService implements OnApplicationShutdown { private idService: IdService, private globalEventServie: GlobalEventService, ) { + this.#antennasFetched = false; + this.#antennas = []; this.#blockingCache = new Cache(1000 * 60 * 5); this.redisSubscriber.on('message', this.onRedisMessage); diff --git a/packages/backend/src/services/entities/UserEntityService.ts b/packages/backend/src/services/entities/UserEntityService.ts index 5599b1d7b9d1..dfaa895914d5 100644 --- a/packages/backend/src/services/entities/UserEntityService.ts +++ b/packages/backend/src/services/entities/UserEntityService.ts @@ -413,8 +413,8 @@ export class UserEntityService { birthday: profile!.birthday, lang: profile!.lang, fields: profile!.fields, - followersCount: followersCount || 0, - followingCount: followingCount || 0, + followersCount: followersCount ?? 0, + followingCount: followingCount ?? 0, notesCount: user.notesCount, pinnedNoteIds: pins.map(pin => pin.noteId), pinnedNotes: this.noteEntityService.packMany(pins.map(pin => pin.note!), me, { From 9e68e2ddd763511a5ec053815c70fc4396f0be70 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 17 Sep 2022 02:22:33 +0900 Subject: [PATCH 175/180] :rocket: --- .../backend/src/server/api/StreamingApiServerService.ts | 8 ++++---- packages/backend/src/services/InternalStorageService.ts | 2 +- packages/backend/src/services/QueryService.ts | 2 +- .../backend/src/services/entities/NoteEntityService.ts | 6 +++++- .../src/services/entities/NoteReactionEntityService.ts | 6 +++++- .../src/services/entities/NotificationEntityService.ts | 6 +++++- .../backend/src/services/entities/UserEntityService.ts | 8 ++++++-- .../src/services/remote/activitypub/ApMfmService.ts | 7 +++++++ .../src/services/remote/activitypub/ApRendererService.ts | 7 +++++-- .../services/remote/activitypub/models/ApPersonService.ts | 6 +++++- 10 files changed, 44 insertions(+), 14 deletions(-) diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts index 26053b856355..957f9c78b051 100644 --- a/packages/backend/src/server/api/StreamingApiServerService.ts +++ b/packages/backend/src/server/api/StreamingApiServerService.ts @@ -21,8 +21,8 @@ export class StreamingApiServerService { @Inject(DI.config) private config: Config, - @Inject(DI.redis) - private redisClient: Redis.Redis, + @Inject(DI.redisSubscriber) + private redisSubscriber: Redis.Redis, @Inject('followingsRepository') private followingsRepository: typeof Followings, @@ -75,7 +75,7 @@ export class StreamingApiServerService { ev.emit(parsed.channel, parsed.message); } - this.redisClient.on('message', onRedisMessage); + this.redisSubscriber.on('message', onRedisMessage); const main = new MainStreamConnection( this.followingsRepository, @@ -104,7 +104,7 @@ export class StreamingApiServerService { connection.once('close', () => { ev.removeAllListeners(); main.dispose(); - this.redisClient.off('message', onRedisMessage); + this.redisSubscriber.off('message', onRedisMessage); if (intervalId) clearInterval(intervalId); }); diff --git a/packages/backend/src/services/InternalStorageService.ts b/packages/backend/src/services/InternalStorageService.ts index af8a641b36cb..9bc3597baf87 100644 --- a/packages/backend/src/services/InternalStorageService.ts +++ b/packages/backend/src/services/InternalStorageService.ts @@ -9,7 +9,7 @@ import { Config } from '@/config.js'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); -const path = Path.resolve(_dirname, '../../../../../files'); +const path = Path.resolve(_dirname, '../../../../files'); @Injectable() export class InternalStorageService { diff --git a/packages/backend/src/services/QueryService.ts b/packages/backend/src/services/QueryService.ts index 7a5d879ad1a7..c085ad6fc022 100644 --- a/packages/backend/src/services/QueryService.ts +++ b/packages/backend/src/services/QueryService.ts @@ -8,7 +8,7 @@ import type { SelectQueryBuilder } from 'typeorm'; @Injectable() export class QueryService { constructor( - @Inject('usersRepository') + @Inject('userProfilesRepository') private userProfilesRepository: typeof UserProfiles, @Inject('followingsRepository') diff --git a/packages/backend/src/services/entities/NoteEntityService.ts b/packages/backend/src/services/entities/NoteEntityService.ts index c2c31ed8f6be..add95406d487 100644 --- a/packages/backend/src/services/entities/NoteEntityService.ts +++ b/packages/backend/src/services/entities/NoteEntityService.ts @@ -11,13 +11,14 @@ import { awaitAll } from '@/prelude/await-all.js'; import type { User } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; import type { NoteReaction } from '@/models/entities/NoteReaction.js'; +import type { OnModuleInit } from '@nestjs/common'; import type { CustomEmojiService } from '../CustomEmojiService.js'; import type { ReactionService } from '../ReactionService.js'; import type { UserEntityService } from './UserEntityService.js'; import type { DriveFileEntityService } from './DriveFileEntityService.js'; @Injectable() -export class NoteEntityService { +export class NoteEntityService implements OnModuleInit { private userEntityService: UserEntityService; private driveFileEntityService: DriveFileEntityService; private customEmojiService: CustomEmojiService; @@ -58,6 +59,9 @@ export class NoteEntityService { //private customEmojiService: CustomEmojiService, //private reactionService: ReactionService, ) { + } + + onModuleInit() { this.userEntityService = this.moduleRef.get('UserEntityService'); this.driveFileEntityService = this.moduleRef.get('DriveFileEntityService'); this.customEmojiService = this.moduleRef.get('CustomEmojiService'); diff --git a/packages/backend/src/services/entities/NoteReactionEntityService.ts b/packages/backend/src/services/entities/NoteReactionEntityService.ts index a051cae6c992..1f64ed27628d 100644 --- a/packages/backend/src/services/entities/NoteReactionEntityService.ts +++ b/packages/backend/src/services/entities/NoteReactionEntityService.ts @@ -3,6 +3,7 @@ import { DI } from '@/di-symbols.js'; import type { NoteReactions } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; +import type { OnModuleInit } from '@nestjs/common'; import type { } from '@/models/entities/Blocking.js'; import type { User } from '@/models/entities/User.js'; import type { NoteReaction } from '@/models/entities/NoteReaction.js'; @@ -12,7 +13,7 @@ import type { NoteEntityService } from './NoteEntityService.js'; import { ModuleRef } from '@nestjs/core'; @Injectable() -export class NoteReactionEntityService { +export class NoteReactionEntityService implements OnModuleInit { private userEntityService: UserEntityService; private noteEntityService: NoteEntityService; private reactionService: ReactionService; @@ -27,6 +28,9 @@ export class NoteReactionEntityService { //private noteEntityService: NoteEntityService, //private reactionService: ReactionService, ) { + } + + onModuleInit() { this.userEntityService = this.moduleRef.get('UserEntityService'); this.noteEntityService = this.moduleRef.get('NoteEntityService'); this.reactionService = this.moduleRef.get('ReactionService'); diff --git a/packages/backend/src/services/entities/NotificationEntityService.ts b/packages/backend/src/services/entities/NotificationEntityService.ts index 06a6c7a3ee1d..223d0c71136f 100644 --- a/packages/backend/src/services/entities/NotificationEntityService.ts +++ b/packages/backend/src/services/entities/NotificationEntityService.ts @@ -8,13 +8,14 @@ import type { Notification } from '@/models/entities/Notification.js'; import type { NoteReaction } from '@/models/entities/NoteReaction.js'; import type { Note } from '@/models/entities/Note.js'; import type { Packed } from '@/misc/schema.js'; +import type { OnModuleInit } from '@nestjs/common'; import type { CustomEmojiService } from '../CustomEmojiService.js'; import type { UserEntityService } from './UserEntityService.js'; import type { NoteEntityService } from './NoteEntityService.js'; import type { UserGroupInvitationEntityService } from './UserGroupInvitationEntityService.js'; @Injectable() -export class NotificationEntityService { +export class NotificationEntityService implements OnModuleInit { private userEntityService: UserEntityService; private noteEntityService: NoteEntityService; private userGroupInvitationEntityService: UserGroupInvitationEntityService; @@ -37,6 +38,9 @@ export class NotificationEntityService { //private userGroupInvitationEntityService: UserGroupInvitationEntityService, //private customEmojiService: CustomEmojiService, ) { + } + + onModuleInit() { this.userEntityService = this.moduleRef.get('UserEntityService'); this.noteEntityService = this.moduleRef.get('NoteEntityService'); this.userGroupInvitationEntityService = this.moduleRef.get('UserGroupInvitationEntityService'); diff --git a/packages/backend/src/services/entities/UserEntityService.ts b/packages/backend/src/services/entities/UserEntityService.ts index dfaa895914d5..d8e8135502c0 100644 --- a/packages/backend/src/services/entities/UserEntityService.ts +++ b/packages/backend/src/services/entities/UserEntityService.ts @@ -13,6 +13,7 @@ import { Cache } from '@/misc/cache.js'; import type { Instance } from '@/models/entities/Instance.js'; import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/entities/User.js'; +import type { OnModuleInit } from '@nestjs/common'; import type { AntennaService } from '../AntennaService.js'; import type { CustomEmojiService } from '../CustomEmojiService.js'; import type { NoteEntityService } from './NoteEntityService.js'; @@ -42,7 +43,7 @@ function isRemoteUser(user: User | { host: User['host'] }): boolean { } @Injectable() -export class UserEntityService { +export class UserEntityService implements OnModuleInit { private noteEntityService: NoteEntityService; private driveFileEntityService: DriveFileEntityService; private pageEntityService: PageEntityService; @@ -119,12 +120,15 @@ export class UserEntityService { //private customEmojiService: CustomEmojiService, //private antennaService: AntennaService, ) { + this.#userInstanceCache = new Cache(1000 * 60 * 60 * 3); + } + + onModuleInit() { this.noteEntityService = this.moduleRef.get('NoteEntityService'); this.driveFileEntityService = this.moduleRef.get('DriveFileEntityService'); this.pageEntityService = this.moduleRef.get('PageEntityService'); this.customEmojiService = this.moduleRef.get('CustomEmojiService'); this.antennaService = this.moduleRef.get('AntennaService'); - this.#userInstanceCache = new Cache(1000 * 60 * 60 * 3); } //#region Validators diff --git a/packages/backend/src/services/remote/activitypub/ApMfmService.ts b/packages/backend/src/services/remote/activitypub/ApMfmService.ts index eb4106b249b6..355dfaa1655a 100644 --- a/packages/backend/src/services/remote/activitypub/ApMfmService.ts +++ b/packages/backend/src/services/remote/activitypub/ApMfmService.ts @@ -1,7 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; +import * as mfm from 'mfm-js'; import { DI } from '@/di-symbols.js'; import { Config } from '@/config.js'; import { MfmService } from '@/services/MfmService.js'; +import type { Note } from '@/models/entities/Note.js'; import { extractApHashtagObjects } from './models/tag.js'; import type { IObject } from './type.js'; @@ -20,4 +22,9 @@ export class ApMfmService { return this.mfmService.fromHtml(html, hashtagNames); } + + public getNoteHtml(note: Note) { + if (!note.text) return ''; + return this.mfmService.toHtml(mfm.parse(note.text), JSON.parse(note.mentionedRemoteUsers)); + } } diff --git a/packages/backend/src/services/remote/activitypub/ApRendererService.ts b/packages/backend/src/services/remote/activitypub/ApRendererService.ts index 87b6e6ce99c1..e425260b91f1 100644 --- a/packages/backend/src/services/remote/activitypub/ApRendererService.ts +++ b/packages/backend/src/services/remote/activitypub/ApRendererService.ts @@ -20,7 +20,9 @@ import { UserKeypairStoreService } from '@/services/UserKeypairStoreService.js'; import { MfmService } from '@/services/MfmService.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; +import type { UserKeypair } from '@/models/entities/UserKeypair.js'; import { LdSignatureService } from './LdSignatureService.js'; +import { ApMfmService } from './ApMfmService.js'; import type { IActivity } from './type.js'; import type { IIdentifier } from './models/identifier.js'; @@ -52,6 +54,7 @@ export class ApRendererService { private driveFileEntityService: DriveFileEntityService, private ldSignatureService: LdSignatureService, private userKeypairStoreService: UserKeypairStoreService, + private apMfmService: ApMfmService, private mfmService: MfmService, ) { } @@ -359,7 +362,7 @@ private driveFileEntityService: DriveFileEntityService, const summary = note.cw === '' ? String.fromCharCode(0x200B) : note.cw; - const content = this.mfmService.toHtml(Object.assign({}, note, { + const content = this.apMfmService.getNoteHtml(Object.assign({}, note, { text: apText, })); @@ -374,7 +377,7 @@ private driveFileEntityService: DriveFileEntityService, const asPoll = poll ? { type: 'Question', - content: this.mfmService.toHtml(Object.assign({}, note, { + content: this.apMfmService.getNoteHtml(Object.assign({}, note, { text: text, })), [poll.expiresAt && poll.expiresAt < new Date() ? 'closed' : 'endTime']: poll.expiresAt, diff --git a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts index b7b13d854667..916251734429 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts @@ -31,6 +31,7 @@ import type { UtilityService } from '@/services/UtilityService.js'; import type { UserEntityService } from '@/services/entities/UserEntityService.js'; import { getApId, getApType, getOneApHrefNullable, isActor, isCollection, isCollectionOrOrderedCollection, isPropertyValue } from '../type.js'; import { extractApHashtags } from './tag.js'; +import type { OnModuleInit } from '@nestjs/common'; import type { ApNoteService } from './ApNoteService.js'; import type { ApMfmService } from '../ApMfmService.js'; import type { ApResolverService , Resolver } from '../ApResolverService.js'; @@ -74,7 +75,7 @@ function addService(target: { [x: string]: any }, source: IApPropertyValue) { } @Injectable() -export class ApPersonService { +export class ApPersonService implements OnModuleInit { private utilityService: UtilityService; private userEntityService: UserEntityService; private idService: IdService; @@ -134,6 +135,9 @@ export class ApPersonService { //private instanceChart: InstanceChart, //private apLoggerService: ApLoggerService, ) { + } + + onModuleInit() { this.utilityService = this.moduleRef.get('UtilityService'); this.userEntityService = this.moduleRef.get('UserEntityService'); this.idService = this.moduleRef.get('IdService'); From 889a7e3cda249364fc1bcb2adc65051cf35e72f3 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 17 Sep 2022 04:11:58 +0900 Subject: [PATCH 176/180] :accept: --- .../src/{app.module.ts => AppModule.ts} | 0 packages/backend/src/boot/master.ts | 14 ++- packages/backend/src/boot/worker.ts | 2 +- packages/backend/src/config.ts | 2 +- packages/backend/src/daemons/DaemonModule.ts | 24 +++++ .../backend/src/daemons/JanitorService.ts | 37 ++++++++ .../backend/src/daemons/QueueStatsService.ts | 77 +++++++++++++++ .../backend/src/daemons/ServerStatsService.ts | 95 +++++++++++++++++++ packages/backend/src/daemons/janitor.ts | 20 ---- packages/backend/src/daemons/queue-stats.ts | 60 ------------ packages/backend/src/daemons/server-stats.ts | 79 --------------- packages/backend/src/db/postgre.ts | 2 +- packages/backend/src/db/redis.ts | 2 +- packages/backend/src/logger.ts | 4 +- packages/backend/src/misc/acct.ts | 2 +- packages/backend/src/misc/before-shutdown.ts | 2 +- packages/backend/src/misc/get-note-summary.ts | 10 +- .../backend/src/misc/identifiable-error.ts | 2 +- .../src/queue/QueueProcessorService.ts | 8 +- .../queue/processors/InboxProcessorService.ts | 10 +- .../src/server/NodeinfoServerService.ts | 2 +- packages/backend/src/server/ServerService.ts | 2 +- .../src/server/api/SigninApiService.ts | 2 +- .../src/server/api/SignupApiService.ts | 2 +- .../server/api/endpoints/channels/create.ts | 2 +- .../backend/src/server/api/endpoints/drive.ts | 2 +- .../api/endpoints/drive/files/create.ts | 2 +- .../src/server/api/endpoints/notes/create.ts | 2 +- packages/backend/src/server/api/error.ts | 16 ++-- .../src/server/web/ClientServerService.ts | 20 ++-- .../backend/src/server/web/FeedService.ts | 16 +++- .../backend/src/services/CaptchaService.ts | 4 +- .../src/services/CreateNotificationService.ts | 4 +- .../src/services/CustomEmojiService.ts | 2 +- .../backend/src/services/DownloadService.ts | 4 +- packages/backend/src/services/DriveService.ts | 6 +- packages/backend/src/services/EmailService.ts | 2 +- .../services/FetchInstanceMetadataService.ts | 18 ++-- .../backend/src/services/HashtagService.ts | 2 +- .../src/services/HttpRequestService.ts | 10 +- .../src/services/ModerationLogService.ts | 2 +- .../backend/src/services/NoteCreateService.ts | 16 ++-- .../backend/src/services/NoteDeleteService.ts | 2 +- .../backend/src/services/NoteReadService.ts | 2 +- packages/backend/src/services/QueueService.ts | 4 +- .../backend/src/services/ReactionService.ts | 6 +- packages/backend/src/services/S3Service.ts | 4 +- .../src/services/UserFollowingService.ts | 2 +- .../src/services/UserSuspendService.ts | 4 +- .../entities/AbuseUserReportEntityService.ts | 6 +- .../services/entities/ClipEntityService.ts | 2 +- .../entities/DriveFileEntityService.ts | 10 +- .../services/entities/EmojiEntityService.ts | 4 +- .../entities/FollowingEntityService.ts | 4 +- .../entities/GalleryLikeEntityService.ts | 2 +- .../entities/GalleryPostEntityService.ts | 2 +- .../entities/MessagingMessageEntityService.ts | 10 +- .../entities/ModerationLogEntityService.ts | 2 +- .../services/entities/NoteEntityService.ts | 14 +-- .../entities/NoteFavoriteEntityService.ts | 2 +- .../entities/NotificationEntityService.ts | 8 +- .../services/entities/PageEntityService.ts | 11 ++- .../entities/PageLikeEntityService.ts | 2 +- .../services/entities/UserEntityService.ts | 24 ++--- .../UserGroupInvitationEntityService.ts | 2 +- .../backend/src/services/queue/QueueModule.ts | 2 +- .../src/services/remote/ResolveUserService.ts | 6 +- .../remote/activitypub/ApAudienceService.ts | 2 +- .../activitypub/ApDeliverManagerService.ts | 2 +- .../remote/activitypub/ApInboxService.ts | 28 +++--- .../remote/activitypub/ApRendererService.ts | 4 +- .../remote/activitypub/LdSignatureService.ts | 2 +- .../activitypub/models/ApNoteService.ts | 4 +- .../activitypub/models/ApPersonService.ts | 22 ++--- .../activitypub/models/ApQuestionService.ts | 2 +- packages/backend/test/utils.ts | 4 +- packages/client/src/scripts/aiscript/api.ts | 2 +- .../client/src/scripts/hpml/type-checker.ts | 12 ++- 78 files changed, 451 insertions(+), 360 deletions(-) rename packages/backend/src/{app.module.ts => AppModule.ts} (100%) create mode 100644 packages/backend/src/daemons/DaemonModule.ts create mode 100644 packages/backend/src/daemons/JanitorService.ts create mode 100644 packages/backend/src/daemons/QueueStatsService.ts create mode 100644 packages/backend/src/daemons/ServerStatsService.ts delete mode 100644 packages/backend/src/daemons/janitor.ts delete mode 100644 packages/backend/src/daemons/queue-stats.ts delete mode 100644 packages/backend/src/daemons/server-stats.ts diff --git a/packages/backend/src/app.module.ts b/packages/backend/src/AppModule.ts similarity index 100% rename from packages/backend/src/app.module.ts rename to packages/backend/src/AppModule.ts diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts index 3d741798c2d1..c070cc71a37a 100644 --- a/packages/backend/src/boot/master.ts +++ b/packages/backend/src/boot/master.ts @@ -6,13 +6,18 @@ import cluster from 'node:cluster'; import chalk from 'chalk'; import chalkTemplate from 'chalk-template'; import semver from 'semver'; +import { NestFactory } from '@nestjs/core'; import Logger from '@/logger.js'; import { loadConfig } from '@/config.js'; import type { Config } from '@/config.js'; import { lessThan } from '@/prelude/array.js'; import { showMachineInfo } from '@/misc/show-machine-info.js'; -import { envOption } from '../env.js'; +import { DaemonModule } from '@/daemons/DaemonModule.js'; +import { JanitorService } from '@/daemons/JanitorService.js'; +import { QueueStatsService } from '@/daemons/QueueStatsService.js'; +import { ServerStatsService } from '@/daemons/ServerStatsService.js'; import { db, initDb } from '../db/postgre.js'; +import { envOption } from '../env.js'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); @@ -74,9 +79,10 @@ export async function masterMain() { bootLogger.succ(`Now listening on port ${config.port} on ${config.url}`, null, true); if (!envOption.noDaemons) { - import('../daemons/server-stats.js').then(x => x.default()); - import('../daemons/queue-stats.js').then(x => x.default()); - import('../daemons/janitor.js').then(x => x.default()); + const daemons = await NestFactory.createApplicationContext(DaemonModule); + daemons.get(JanitorService).start(); + daemons.get(QueueStatsService).start(); + daemons.get(ServerStatsService).start(); } } diff --git a/packages/backend/src/boot/worker.ts b/packages/backend/src/boot/worker.ts index 88fab3e41bb8..b1c92f2f8b4f 100644 --- a/packages/backend/src/boot/worker.ts +++ b/packages/backend/src/boot/worker.ts @@ -5,7 +5,7 @@ import { ChartManagementService } from '@/services/chart/ChartManagementService. import { ServerService } from '@/server/ServerService.js'; import { QueueProcessorService } from '@/queue/QueueProcessorService.js'; import { initDb } from '../db/postgre.js'; -import { AppModule } from '../app.module.js'; +import { AppModule } from '../AppModule.js'; /** * Init worker process diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index 052991429e2c..11d8db5c04e0 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -121,7 +121,7 @@ export function loadConfig() { config.url = url.origin; - config.port = config.port || parseInt(process.env.PORT || '', 10); + config.port = config.port ?? parseInt(process.env.PORT ?? '', 10); mixin.version = meta.version; mixin.host = url.host; diff --git a/packages/backend/src/daemons/DaemonModule.ts b/packages/backend/src/daemons/DaemonModule.ts new file mode 100644 index 000000000000..69a95c3a3e93 --- /dev/null +++ b/packages/backend/src/daemons/DaemonModule.ts @@ -0,0 +1,24 @@ +import { Module } from '@nestjs/common'; +import { CoreModule } from '@/services/CoreModule.js'; +import { GlobalModule } from '@/GlobalModule.js'; +import { JanitorService } from './JanitorService.js'; +import { QueueStatsService } from './QueueStatsService.js'; +import { ServerStatsService } from './ServerStatsService.js'; + +@Module({ + imports: [ + GlobalModule, + CoreModule, + ], + providers: [ + JanitorService, + QueueStatsService, + ServerStatsService, + ], + exports: [ + JanitorService, + QueueStatsService, + ServerStatsService, + ], +}) +export class DaemonModule {} diff --git a/packages/backend/src/daemons/JanitorService.ts b/packages/backend/src/daemons/JanitorService.ts new file mode 100644 index 000000000000..33e69e15b311 --- /dev/null +++ b/packages/backend/src/daemons/JanitorService.ts @@ -0,0 +1,37 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { LessThan } from 'typeorm'; +import { DI } from '@/di-symbols.js'; +import type { AttestationChallenges } from '@/models/index.js'; +import type { OnApplicationShutdown } from '@nestjs/common'; + +const interval = 30 * 60 * 1000; + +@Injectable() +export class JanitorService implements OnApplicationShutdown { + #intervalId: NodeJS.Timer; + + constructor( + @Inject('attestationChallengesRepository') + private attestationChallengesRepository: typeof AttestationChallenges, + ) { + } + + /** + * Clean up database occasionally + */ + public start(): void { + const tick = async () => { + await this.attestationChallengesRepository.delete({ + createdAt: LessThan(new Date(new Date().getTime() - 5 * 60 * 1000)), + }); + }; + + tick(); + + this.#intervalId = setInterval(tick, interval); + } + + public onApplicationShutdown(signal?: string | undefined) { + clearInterval(this.#intervalId); + } +} diff --git a/packages/backend/src/daemons/QueueStatsService.ts b/packages/backend/src/daemons/QueueStatsService.ts new file mode 100644 index 000000000000..adbcb5b8ca84 --- /dev/null +++ b/packages/backend/src/daemons/QueueStatsService.ts @@ -0,0 +1,77 @@ +import { Inject, Injectable } from '@nestjs/common'; +import Xev from 'xev'; +import { DI } from '@/di-symbols.js'; +import { QueueService } from '@/services/QueueService.js'; +import type { OnApplicationShutdown } from '@nestjs/common'; + +const ev = new Xev(); + +const interval = 10000; + +@Injectable() +export class QueueStatsService implements OnApplicationShutdown { + #intervalId: NodeJS.Timer; + + constructor( + private queueService: QueueService, + ) { + } + + /** + * Report queue stats regularly + */ + public start(): void { + const log = [] as any[]; + + ev.on('requestQueueStatsLog', x => { + ev.emit(`queueStatsLog:${x.id}`, log.slice(0, x.length ?? 50)); + }); + + let activeDeliverJobs = 0; + let activeInboxJobs = 0; + + this.queueService.deliverQueue.on('global:active', () => { + activeDeliverJobs++; + }); + + this.queueService.inboxQueue.on('global:active', () => { + activeInboxJobs++; + }); + + const tick = async () => { + const deliverJobCounts = await this.queueService.deliverQueue.getJobCounts(); + const inboxJobCounts = await this.queueService.inboxQueue.getJobCounts(); + + const stats = { + deliver: { + activeSincePrevTick: activeDeliverJobs, + active: deliverJobCounts.active, + waiting: deliverJobCounts.waiting, + delayed: deliverJobCounts.delayed, + }, + inbox: { + activeSincePrevTick: activeInboxJobs, + active: inboxJobCounts.active, + waiting: inboxJobCounts.waiting, + delayed: inboxJobCounts.delayed, + }, + }; + + ev.emit('queueStats', stats); + + log.unshift(stats); + if (log.length > 200) log.pop(); + + activeDeliverJobs = 0; + activeInboxJobs = 0; + }; + + tick(); + + this.#intervalId = setInterval(tick, interval); + } + + public onApplicationShutdown(signal?: string | undefined) { + clearInterval(this.#intervalId); + } +} diff --git a/packages/backend/src/daemons/ServerStatsService.ts b/packages/backend/src/daemons/ServerStatsService.ts new file mode 100644 index 000000000000..3a6d40843194 --- /dev/null +++ b/packages/backend/src/daemons/ServerStatsService.ts @@ -0,0 +1,95 @@ +import { Inject, Injectable } from '@nestjs/common'; +import si from 'systeminformation'; +import Xev from 'xev'; +import * as osUtils from 'os-utils'; +import { DI } from '@/di-symbols.js'; +import type { OnApplicationShutdown } from '@nestjs/common'; + +const ev = new Xev(); + +const interval = 2000; + +const roundCpu = (num: number) => Math.round(num * 1000) / 1000; +const round = (num: number) => Math.round(num * 10) / 10; + +@Injectable() +export class ServerStatsService implements OnApplicationShutdown { + #intervalId: NodeJS.Timer; + + constructor( + ) { + } + + /** + * Report server stats regularly + */ + public start(): void { + const log = [] as any[]; + + ev.on('requestServerStatsLog', x => { + ev.emit(`serverStatsLog:${x.id}`, log.slice(0, x.length ?? 50)); + }); + + const tick = async () => { + const cpu = await cpuUsage(); + const memStats = await mem(); + const netStats = await net(); + const fsStats = await fs(); + + const stats = { + cpu: roundCpu(cpu), + mem: { + used: round(memStats.used - memStats.buffers - memStats.cached), + active: round(memStats.active), + }, + net: { + rx: round(Math.max(0, netStats.rx_sec)), + tx: round(Math.max(0, netStats.tx_sec)), + }, + fs: { + r: round(Math.max(0, fsStats.rIO_sec ?? 0)), + w: round(Math.max(0, fsStats.wIO_sec ?? 0)), + }, + }; + ev.emit('serverStats', stats); + log.unshift(stats); + if (log.length > 200) log.pop(); + }; + + tick(); + + this.#intervalId = setInterval(tick, interval); + } + + public onApplicationShutdown(signal?: string | undefined) { + clearInterval(this.#intervalId); + } +} + +// CPU STAT +function cpuUsage(): Promise { + return new Promise((res, rej) => { + osUtils.cpuUsage((cpuUsage) => { + res(cpuUsage); + }); + }); +} + +// MEMORY STAT +async function mem() { + const data = await si.mem(); + return data; +} + +// NETWORK STAT +async function net() { + const iface = await si.networkInterfaceDefault(); + const data = await si.networkStats(iface); + return data[0]; +} + +// FS STAT +async function fs() { + const data = await si.disksIO().catch(() => ({ rIO_sec: 0, wIO_sec: 0 })); + return data ?? { rIO_sec: 0, wIO_sec: 0 }; +} diff --git a/packages/backend/src/daemons/janitor.ts b/packages/backend/src/daemons/janitor.ts deleted file mode 100644 index f2a1bfcc2f88..000000000000 --- a/packages/backend/src/daemons/janitor.ts +++ /dev/null @@ -1,20 +0,0 @@ -// TODO: 消したい - -const interval = 30 * 60 * 1000; -import { AttestationChallenges } from '@/models/index.js'; -import { LessThan } from 'typeorm'; - -/** - * Clean up database occasionally - */ -export default function() { - async function tick() { - await AttestationChallenges.delete({ - createdAt: LessThan(new Date(new Date().getTime() - 5 * 60 * 1000)), - }); - } - - tick(); - - setInterval(tick, interval); -} diff --git a/packages/backend/src/daemons/queue-stats.ts b/packages/backend/src/daemons/queue-stats.ts deleted file mode 100644 index 1535abc6af93..000000000000 --- a/packages/backend/src/daemons/queue-stats.ts +++ /dev/null @@ -1,60 +0,0 @@ -import Xev from 'xev'; -import { deliverQueue, inboxQueue } from '../queue/queues.js'; - -const ev = new Xev(); - -const interval = 10000; - -/** - * Report queue stats regularly - */ -export default function() { - const log = [] as any[]; - - ev.on('requestQueueStatsLog', x => { - ev.emit(`queueStatsLog:${x.id}`, log.slice(0, x.length || 50)); - }); - - let activeDeliverJobs = 0; - let activeInboxJobs = 0; - - deliverQueue.on('global:active', () => { - activeDeliverJobs++; - }); - - inboxQueue.on('global:active', () => { - activeInboxJobs++; - }); - - async function tick() { - const deliverJobCounts = await deliverQueue.getJobCounts(); - const inboxJobCounts = await inboxQueue.getJobCounts(); - - const stats = { - deliver: { - activeSincePrevTick: activeDeliverJobs, - active: deliverJobCounts.active, - waiting: deliverJobCounts.waiting, - delayed: deliverJobCounts.delayed, - }, - inbox: { - activeSincePrevTick: activeInboxJobs, - active: inboxJobCounts.active, - waiting: inboxJobCounts.waiting, - delayed: inboxJobCounts.delayed, - }, - }; - - ev.emit('queueStats', stats); - - log.unshift(stats); - if (log.length > 200) log.pop(); - - activeDeliverJobs = 0; - activeInboxJobs = 0; - } - - tick(); - - setInterval(tick, interval); -} diff --git a/packages/backend/src/daemons/server-stats.ts b/packages/backend/src/daemons/server-stats.ts deleted file mode 100644 index faf4e6e4a4c2..000000000000 --- a/packages/backend/src/daemons/server-stats.ts +++ /dev/null @@ -1,79 +0,0 @@ -import si from 'systeminformation'; -import Xev from 'xev'; -import * as osUtils from 'os-utils'; - -const ev = new Xev(); - -const interval = 2000; - -const roundCpu = (num: number) => Math.round(num * 1000) / 1000; -const round = (num: number) => Math.round(num * 10) / 10; - -/** - * Report server stats regularly - */ -export default function() { - const log = [] as any[]; - - ev.on('requestServerStatsLog', x => { - ev.emit(`serverStatsLog:${x.id}`, log.slice(0, x.length || 50)); - }); - - async function tick() { - const cpu = await cpuUsage(); - const memStats = await mem(); - const netStats = await net(); - const fsStats = await fs(); - - const stats = { - cpu: roundCpu(cpu), - mem: { - used: round(memStats.used - memStats.buffers - memStats.cached), - active: round(memStats.active), - }, - net: { - rx: round(Math.max(0, netStats.rx_sec)), - tx: round(Math.max(0, netStats.tx_sec)), - }, - fs: { - r: round(Math.max(0, fsStats.rIO_sec ?? 0)), - w: round(Math.max(0, fsStats.wIO_sec ?? 0)), - }, - }; - ev.emit('serverStats', stats); - log.unshift(stats); - if (log.length > 200) log.pop(); - } - - tick(); - - setInterval(tick, interval); -} - -// CPU STAT -function cpuUsage(): Promise { - return new Promise((res, rej) => { - osUtils.cpuUsage((cpuUsage) => { - res(cpuUsage); - }); - }); -} - -// MEMORY STAT -async function mem() { - const data = await si.mem(); - return data; -} - -// NETWORK STAT -async function net() { - const iface = await si.networkInterfaceDefault(); - const data = await si.networkStats(iface); - return data[0]; -} - -// FS STAT -async function fs() { - const data = await si.disksIO().catch(() => ({ rIO_sec: 0, wIO_sec: 0 })); - return data || { rIO_sec: 0, wIO_sec: 0 }; -} diff --git a/packages/backend/src/db/postgre.ts b/packages/backend/src/db/postgre.ts index 1ed685e3a213..78b894b955a9 100644 --- a/packages/backend/src/db/postgre.ts +++ b/packages/backend/src/db/postgre.ts @@ -203,7 +203,7 @@ export const db = new DataSource({ family: config.redis.family == null ? 0 : config.redis.family, password: config.redis.pass, keyPrefix: `${config.redis.prefix}:query:`, - db: config.redis.db || 0, + db: config.redis.db ?? 0, }, } : false, logging: log, diff --git a/packages/backend/src/db/redis.ts b/packages/backend/src/db/redis.ts index 68cb5ac7ee69..2ca7d8de86e1 100644 --- a/packages/backend/src/db/redis.ts +++ b/packages/backend/src/db/redis.ts @@ -10,7 +10,7 @@ export function createConnection() { family: config.redis.family == null ? 0 : config.redis.family, password: config.redis.pass, keyPrefix: `${config.redis.prefix}:`, - db: config.redis.db || 0, + db: config.redis.db ?? 0, }); } diff --git a/packages/backend/src/logger.ts b/packages/backend/src/logger.ts index 35b7dbf3c940..672222068195 100644 --- a/packages/backend/src/logger.ts +++ b/packages/backend/src/logger.ts @@ -82,11 +82,11 @@ export default class Logger { public error(x: string | Error, data?: Record | null, important = false): void { // 実行を継続できない状況で使う if (x instanceof Error) { - data = data || {}; + data = data ?? {}; data.e = x; this.log('error', x.toString(), data, important); } else if (typeof x === 'object') { - this.log('error', `${(x as any).message || (x as any).name || x}`, data, important); + this.log('error', `${(x as any).message ?? (x as any).name ?? x}`, data, important); } else { this.log('error', `${x}`, data, important); } diff --git a/packages/backend/src/misc/acct.ts b/packages/backend/src/misc/acct.ts index c32cee86c971..d1a6852a956e 100644 --- a/packages/backend/src/misc/acct.ts +++ b/packages/backend/src/misc/acct.ts @@ -6,7 +6,7 @@ export type Acct = { export function parse(acct: string): Acct { if (acct.startsWith('@')) acct = acct.substr(1); const split = acct.split('@', 2); - return { username: split[0], host: split[1] || null }; + return { username: split[0], host: split[1] ?? null }; } export function toString(acct: Acct): string { diff --git a/packages/backend/src/misc/before-shutdown.ts b/packages/backend/src/misc/before-shutdown.ts index 93ac7a1f399e..74489293d7a2 100644 --- a/packages/backend/src/misc/before-shutdown.ts +++ b/packages/backend/src/misc/before-shutdown.ts @@ -65,7 +65,7 @@ async function shutdownHandler(signalOrEvent: string) { await listener(signalOrEvent); } catch (err) { if (err instanceof Error) { - console.warn(`A shutdown handler failed before completing with: ${err.message || err}`); + console.warn(`A shutdown handler failed before completing with: ${err.message ?? err}`); } } } diff --git a/packages/backend/src/misc/get-note-summary.ts b/packages/backend/src/misc/get-note-summary.ts index 3f35ccee8258..85bc2ec94de1 100644 --- a/packages/backend/src/misc/get-note-summary.ts +++ b/packages/backend/src/misc/get-note-summary.ts @@ -1,4 +1,4 @@ -import { Packed } from './schema.js'; +import type { Packed } from './schema.js'; /** * 投稿を表す文字列を取得します。 @@ -6,11 +6,11 @@ import { Packed } from './schema.js'; */ export const getNoteSummary = (note: Packed<'Note'>): string => { if (note.deletedAt) { - return `(❌⛔)`; + return '(❌⛔)'; } if (note.isHidden) { - return `(⛔)`; + return '(⛔)'; } let summary = ''; @@ -23,13 +23,13 @@ export const getNoteSummary = (note: Packed<'Note'>): string => { } // ファイルが添付されているとき - if ((note.files || []).length !== 0) { + if ((note.files ?? []).length !== 0) { summary += ` (📎${note.files!.length})`; } // 投票が添付されているとき if (note.poll) { - summary += ` (📊)`; + summary += ' (📊)'; } // 返信のとき diff --git a/packages/backend/src/misc/identifiable-error.ts b/packages/backend/src/misc/identifiable-error.ts index 2d7c6bd0c63d..e394123f1bf3 100644 --- a/packages/backend/src/misc/identifiable-error.ts +++ b/packages/backend/src/misc/identifiable-error.ts @@ -7,7 +7,7 @@ export class IdentifiableError extends Error { constructor(id: string, message?: string) { super(message); - this.message = message || ''; + this.message = message ?? ''; this.id = id; } } diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts index e46e8f65b5f0..7a1c12dbf910 100644 --- a/packages/backend/src/queue/QueueProcessorService.ts +++ b/packages/backend/src/queue/QueueProcessorService.ts @@ -99,10 +99,10 @@ export class QueueProcessorService { .on('error', (job: any, err: Error) => webhookLogger.error(`error ${err}`, { job, e: renderError(err) })) .on('stalled', (job) => webhookLogger.warn(`stalled ${getJobInfo(job)} to=${job.data.to}`)); - this.queueService.deliverQueue.process(this.config.deliverJobConcurrency ?? 128, this.deliverProcessorService.process); - this.queueService.inboxQueue.process(this.config.inboxJobConcurrency ?? 16, this.inboxProcessorService.process); - this.queueService.endedPollNotificationQueue.process(this.endedPollNotificationProcessorService.process); - this.queueService.webhookDeliverQueue.process(64, this.webhookDeliverProcessorService.process); + this.queueService.deliverQueue.process(this.config.deliverJobConcurrency ?? 128, (job, done) => this.deliverProcessorService.process(job)); + this.queueService.inboxQueue.process(this.config.inboxJobConcurrency ?? 16, (job, done) => this.inboxProcessorService.process(job)); + this.queueService.endedPollNotificationQueue.process((job, done) => this.endedPollNotificationProcessorService.process(job, done)); + this.queueService.webhookDeliverQueue.process(64, (job, done) => this.webhookDeliverProcessorService.process(job)); this.dbQueueProcessorsService.start(this.queueService.dbQueue); this.objectStorageQueueProcessorsService.start(this.queueService.objectStorageQueue); diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index 934249e528d5..a303d045ae21 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -94,13 +94,13 @@ export class InboxProcessorService { if (authUser == null) { try { authUser = await this.apDbResolverService.getAuthUserFromApId(getApId(activity.actor)); - } catch (e) { + } catch (err) { // 対象が4xxならスキップ - if (e instanceof StatusError) { - if (e.isClientError) { - return `skip: Ignored deleted actors on both ends ${activity.actor} - ${e.statusCode}`; + if (err instanceof StatusError) { + if (err.isClientError) { + return `skip: Ignored deleted actors on both ends ${activity.actor} - ${err.statusCode}`; } - throw `Error in actor ${activity.actor} - ${e.statusCode || e}`; + throw `Error in actor ${activity.actor} - ${err.statusCode ?? err}`; } } } diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index 7682a94adc70..6681873e25da 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -101,7 +101,7 @@ export class NodeinfoServerService { enableEmail: meta.enableEmail, enableServiceWorker: meta.enableServiceWorker, proxyAccountName: proxyAccount ? proxyAccount.username : null, - themeColor: meta.themeColor || '#86b300', + themeColor: meta.themeColor ?? '#86b300', }, }; }; diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index c9eaff224570..63fac8d0408e 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -59,7 +59,7 @@ export class ServerService { const koa = new Koa(); koa.proxy = true; - if (!['production', 'test'].includes(process.env.NODE_ENV || '')) { + if (!['production', 'test'].includes(process.env.NODE_ENV ?? '')) { // Logger koa.use(koaLogger(str => { serverLogger.info(str); diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index 938174cd11c1..dcc9be1b6f24 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -123,7 +123,7 @@ export class SigninApiService { success: false, }); - error(status || 500, failure || { id: '4e30e80c-e338-45a0-8c8f-44455efa3b76' }); + error(status ?? 500, failure ?? { id: '4e30e80c-e338-45a0-8c8f-44455efa3b76' }); }; if (!profile.twoFactorEnabled) { diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts index 96eca5901acc..02130dfd179c 100644 --- a/packages/backend/src/server/api/SignupApiService.ts +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -64,7 +64,7 @@ export class SignupApiService { const username = body['username']; const password = body['password']; - const host: string | null = process.env.NODE_ENV === 'test' ? (body['host'] || null) : null; + const host: string | null = process.env.NODE_ENV === 'test' ? (body['host'] ?? null) : null; const invitationCode = body['invitationCode']; const emailAddress = body['emailAddress']; diff --git a/packages/backend/src/server/api/endpoints/channels/create.ts b/packages/backend/src/server/api/endpoints/channels/create.ts index c45e9b3a7a48..70af5a97e6fe 100644 --- a/packages/backend/src/server/api/endpoints/channels/create.ts +++ b/packages/backend/src/server/api/endpoints/channels/create.ts @@ -69,7 +69,7 @@ export default class extends Endpoint { createdAt: new Date(), userId: me.id, name: ps.name, - description: ps.description || null, + description: ps.description ?? null, bannerId: banner ? banner.id : null, } as Channel).then(x => this.channelsRepository.findOneByOrFail(x.identifiers[0])); diff --git a/packages/backend/src/server/api/endpoints/drive.ts b/packages/backend/src/server/api/endpoints/drive.ts index 0e43bf462e18..99dbc289802f 100644 --- a/packages/backend/src/server/api/endpoints/drive.ts +++ b/packages/backend/src/server/api/endpoints/drive.ts @@ -47,7 +47,7 @@ export default class extends Endpoint { const usage = await this.driveFileEntityService.calcDriveUsageOf(me.id); return { - capacity: 1024 * 1024 * (me.driveCapacityOverrideMb || instance.localDriveCapacityMb), + capacity: 1024 * 1024 * (me.driveCapacityOverrideMb ?? instance.localDriveCapacityMb), usage: usage, }; }); diff --git a/packages/backend/src/server/api/endpoints/drive/files/create.ts b/packages/backend/src/server/api/endpoints/drive/files/create.ts index 56a37c13d2c3..d082dfacda1f 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/create.ts @@ -77,7 +77,7 @@ export default class extends Endpoint { ) { super(meta, paramDef, async (ps, me, _, file, cleanup, ip, headers) => { // Get 'name' parameter - let name = ps.name || file.originalname; + let name = ps.name ?? file.originalname; if (name !== undefined && name !== null) { name = name.trim(); if (name.length === 0) { diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 79ba1aa28f8f..8b51384f8274 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -274,7 +274,7 @@ export default class extends Endpoint { multiple: ps.poll.multiple || false, expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null, } : undefined, - text: ps.text || undefined, + text: ps.text ?? undefined, reply, renote, cw: ps.cw, diff --git a/packages/backend/src/server/api/error.ts b/packages/backend/src/server/api/error.ts index 3f0861fdb148..347d5650ad08 100644 --- a/packages/backend/src/server/api/error.ts +++ b/packages/backend/src/server/api/error.ts @@ -8,8 +8,8 @@ export class ApiError extends Error { public httpStatusCode?: number; public info?: any; - constructor(e?: E | null | undefined, info?: any | null | undefined) { - if (e == null) e = { + constructor(err?: E | null | undefined, info?: any | null | undefined) { + if (err == null) err = { message: 'Internal error occurred. Please contact us if the error persists.', code: 'INTERNAL_ERROR', id: '5d37dbcb-891e-41ca-a3d6-e690c97775ac', @@ -17,12 +17,12 @@ export class ApiError extends Error { httpStatusCode: 500, }; - super(e.message); - this.message = e.message; - this.code = e.code; - this.id = e.id; - this.kind = e.kind || 'client'; - this.httpStatusCode = e.httpStatusCode; + super(err.message); + this.message = err.message; + this.code = err.code; + this.id = err.id; + this.kind = err.kind ?? 'client'; + this.httpStatusCode = err.httpStatusCode; this.info = info; } } diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index eff07c4fd4ad..71ea3c453060 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -92,8 +92,8 @@ export class ClientServerService { const instance = await this.metaService.fetch(true); - res.short_name = instance.name || 'Misskey'; - res.name = instance.name || 'Misskey'; + res.short_name = instance.name ?? 'Misskey'; + res.name = instance.name ?? 'Misskey'; if (instance.themeColor) res.theme_color = instance.themeColor; ctx.set('Cache-Control', 'max-age=300'); @@ -359,7 +359,7 @@ export class ClientServerService { user, profile, me, avatarUrl: await this.userEntityService.getAvatarUrl(user), sub: ctx.params.sub, - instanceName: meta.name || 'Misskey', + instanceName: meta.name ?? 'Misskey', icon: meta.iconUrl, themeColor: meta.themeColor, }); @@ -403,7 +403,7 @@ export class ClientServerService { avatarUrl: await this.userEntityService.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: note.userId })), // TODO: Let locale changeable by instance setting summary: getNoteSummary(_note), - instanceName: meta.name || 'Misskey', + instanceName: meta.name ?? 'Misskey', icon: meta.iconUrl, themeColor: meta.themeColor, }); @@ -439,7 +439,7 @@ export class ClientServerService { page: _page, profile, avatarUrl: await this.userEntityService.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: page.userId })), - instanceName: meta.name || 'Misskey', + instanceName: meta.name ?? 'Misskey', icon: meta.iconUrl, themeColor: meta.themeColor, }); @@ -471,7 +471,7 @@ export class ClientServerService { clip: _clip, profile, avatarUrl: await this.userEntityService.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: clip.userId })), - instanceName: meta.name || 'Misskey', + instanceName: meta.name ?? 'Misskey', icon: meta.iconUrl, themeColor: meta.themeColor, }); @@ -496,7 +496,7 @@ export class ClientServerService { post: _post, profile, avatarUrl: await this.userEntityService.getAvatarUrl(await this.usersRepository.findOneByOrFail({ id: post.userId })), - instanceName: meta.name || 'Misskey', + instanceName: meta.name ?? 'Misskey', icon: meta.iconUrl, themeColor: meta.themeColor, }); @@ -520,7 +520,7 @@ export class ClientServerService { const meta = await this.metaService.fetch(); await ctx.render('channel', { channel: _channel, - instanceName: meta.name || 'Misskey', + instanceName: meta.name ?? 'Misskey', icon: meta.iconUrl, themeColor: meta.themeColor, }); @@ -578,8 +578,8 @@ export class ClientServerService { const meta = await this.metaService.fetch(); await ctx.render('base', { img: meta.bannerUrl, - title: meta.name || 'Misskey', - instanceName: meta.name || 'Misskey', + title: meta.name ?? 'Misskey', + instanceName: meta.name ?? 'Misskey', desc: meta.description, icon: meta.iconUrl, themeColor: meta.themeColor, diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts index ce56de15f52f..201582ece127 100644 --- a/packages/backend/src/server/web/FeedService.ts +++ b/packages/backend/src/server/web/FeedService.ts @@ -1,9 +1,12 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, IsNull } from 'typeorm'; +import { Feed } from 'feed'; import { DI } from '@/di-symbols.js'; import type { DriveFiles, Notes, UserProfiles , Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type { User } from '@/models/entities/User.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; @Injectable() export class FeedService { @@ -22,6 +25,9 @@ export class FeedService { @Inject('driveFilesRepository') private driveFilesRepository: typeof DriveFiles, + + private userEntityService: UserEntityService, + private driveFileEntityService: DriveFileEntityService, ) { } @@ -50,13 +56,13 @@ export class FeedService { generator: 'Misskey', description: `${user.notesCount} Notes, ${profile.ffVisibility === 'public' ? user.followingCount : '?'} Following, ${profile.ffVisibility === 'public' ? user.followersCount : '?'} Followers${profile.description ? ` · ${profile.description}` : ''}`, link: author.link, - image: await this.usersRepository.getAvatarUrl(user), + image: await this.userEntityService.getAvatarUrl(user), feedLinks: { json: `${author.link}.json`, atom: `${author.link}.atom`, }, author, - copyright: user.name || user.username, + copyright: user.name ?? user.username, }); for (const note of notes) { @@ -69,9 +75,9 @@ export class FeedService { title: `New note by ${author.name}`, link: `${this.config.url}/notes/${note.id}`, date: note.createdAt, - description: note.cw || undefined, - content: note.text || undefined, - image: file ? this.driveFilesRepository.getPublicUrl(file) || undefined : undefined, + description: note.cw ?? undefined, + content: note.text ?? undefined, + image: file ? this.driveFileEntityService.getPublicUrl(file) ?? undefined : undefined, }); } diff --git a/packages/backend/src/services/CaptchaService.ts b/packages/backend/src/services/CaptchaService.ts index 5c4c8222d429..b447651078e8 100644 --- a/packages/backend/src/services/CaptchaService.ts +++ b/packages/backend/src/services/CaptchaService.ts @@ -34,8 +34,8 @@ export class CaptchaService { // TODO //timeout: 10 * 1000, agent: (url, bypassProxy) => this.httpRequestService.getAgentByUrl(url, bypassProxy), - }).catch(e => { - throw `${e.message || e}`; + }).catch(err => { + throw `${err.message ?? err}`; }); if (!res.ok) { diff --git a/packages/backend/src/services/CreateNotificationService.ts b/packages/backend/src/services/CreateNotificationService.ts index 6f7e981f0264..c78bc784d9fc 100644 --- a/packages/backend/src/services/CreateNotificationService.ts +++ b/packages/backend/src/services/CreateNotificationService.ts @@ -93,7 +93,7 @@ export class CreateNotificationService { /* const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); if (!userProfile.email || !userProfile.emailNotificationTypes.includes('follow')) return; - const locale = locales[userProfile.lang || 'ja-JP']; + const locale = locales[userProfile.lang ?? 'ja-JP']; const i18n = new I18n(locale); // TODO: render user information html sendEmail(userProfile.email, i18n.t('_email._follow.title'), `${follower.name} (@${Acct.toString(follower)})`, `${follower.name} (@${Acct.toString(follower)})`); @@ -104,7 +104,7 @@ export class CreateNotificationService { /* const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); if (!userProfile.email || !userProfile.emailNotificationTypes.includes('receiveFollowRequest')) return; - const locale = locales[userProfile.lang || 'ja-JP']; + const locale = locales[userProfile.lang ?? 'ja-JP']; const i18n = new I18n(locale); // TODO: render user information html sendEmail(userProfile.email, i18n.t('_email._receiveFollowRequest.title'), `${follower.name} (@${Acct.toString(follower)})`, `${follower.name} (@${Acct.toString(follower)})`); diff --git a/packages/backend/src/services/CustomEmojiService.ts b/packages/backend/src/services/CustomEmojiService.ts index 5acd572fd13a..502a4847c071 100644 --- a/packages/backend/src/services/CustomEmojiService.ts +++ b/packages/backend/src/services/CustomEmojiService.ts @@ -104,7 +104,7 @@ export class CustomEmojiService { const queryOrNull = async () => (await Emojis.findOneBy({ name, host: host ?? IsNull(), - })) || null; + })) ?? null; const emoji = await this.#cache.fetch(`${name} ${host}`, queryOrNull); diff --git a/packages/backend/src/services/DownloadService.ts b/packages/backend/src/services/DownloadService.ts index 89ef8a373953..d816dbc8b54d 100644 --- a/packages/backend/src/services/DownloadService.ts +++ b/packages/backend/src/services/DownloadService.ts @@ -33,7 +33,7 @@ export class DownloadService { const timeout = 30 * 1000; const operationTimeout = 60 * 1000; - const maxSize = this.config.maxFileSize || 262144000; + const maxSize = this.config.maxFileSize ?? 262144000; const req = got.stream(url, { headers: { @@ -111,7 +111,7 @@ export class DownloadService { } #isPrivateIp(ip: string): boolean { - for (const net of this.config.allowedPrivateNetworks || []) { + for (const net of this.config.allowedPrivateNetworks ?? []) { const cidr = new IPCIDR(net); if (cidr.contains(ip)) { return false; diff --git a/packages/backend/src/services/DriveService.ts b/packages/backend/src/services/DriveService.ts index 6b6a6a525c17..a25d381cdf81 100644 --- a/packages/backend/src/services/DriveService.ts +++ b/packages/backend/src/services/DriveService.ts @@ -130,7 +130,7 @@ export class DriveService { if (meta.useObjectStorage) { //#region ObjectStorage params - let [ext] = (name.match(/\.([a-zA-Z0-9_-]+)$/) || ['']); + let [ext] = (name.match(/\.([a-zA-Z0-9_-]+)$/) ?? ['']); if (ext === '') { if (type === 'image/jpeg') ext = '.jpg'; @@ -522,7 +522,7 @@ export class DriveService { file.folderId = folder !== null ? folder.id : null; file.comment = comment; file.properties = properties; - file.blurhash = info.blurhash || null; + file.blurhash = info.blurhash ?? null; file.isLink = isLink; file.requestIp = requestIp; file.requestHeaders = requestHeaders; @@ -706,7 +706,7 @@ export class DriveService { requestIp = null, requestHeaders = null, }: UploadFromUrlArgs): Promise { - let name = new URL(url).pathname.split('/').pop() || null; + let name = new URL(url).pathname.split('/').pop() ?? null; if (name == null || !this.driveFileEntityService.validateFileName(name)) { name = null; } diff --git a/packages/backend/src/services/EmailService.ts b/packages/backend/src/services/EmailService.ts index fdfab91131c7..67bd926a6d22 100644 --- a/packages/backend/src/services/EmailService.ts +++ b/packages/backend/src/services/EmailService.ts @@ -115,7 +115,7 @@ export class EmailService {
- +

${ subject }

diff --git a/packages/backend/src/services/FetchInstanceMetadataService.ts b/packages/backend/src/services/FetchInstanceMetadataService.ts index 2233c75d9fa8..0917b73c2eb8 100644 --- a/packages/backend/src/services/FetchInstanceMetadataService.ts +++ b/packages/backend/src/services/FetchInstanceMetadataService.ts @@ -80,13 +80,13 @@ export class FetchInstanceMetadataService { updates.softwareName = info.software?.name.toLowerCase(); updates.softwareVersion = info.software?.version; updates.openRegistrations = info.openRegistrations; - updates.maintainerName = info.metadata ? info.metadata.maintainer ? (info.metadata.maintainer.name || null) : null : null; - updates.maintainerEmail = info.metadata ? info.metadata.maintainer ? (info.metadata.maintainer.email || null) : null : null; + updates.maintainerName = info.metadata ? info.metadata.maintainer ? (info.metadata.maintainer.name ?? null) : null : null; + updates.maintainerEmail = info.metadata ? info.metadata.maintainer ? (info.metadata.maintainer.email ?? null) : null : null; } if (name) updates.name = name; if (description) updates.description = description; - if (icon || favicon) updates.iconUrl = icon || favicon; + if (icon || favicon) updates.iconUrl = icon ?? favicon; if (favicon) updates.faviconUrl = favicon; if (themeColor) updates.themeColor = themeColor; @@ -122,7 +122,7 @@ export class FetchInstanceMetadataService { const lnik1_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/1.0'); const lnik2_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.0'); const lnik2_1 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.1'); - const link = lnik2_1 || lnik2_0 || lnik1_0; + const link = lnik2_1 ?? lnik2_0 ?? lnik1_0; if (link == null) { throw 'No nodeinfo link provided'; @@ -222,7 +222,7 @@ export class FetchInstanceMetadataService { } async #getThemeColor(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record | null): Promise { - const themeColor = info?.metadata?.themeColor || doc?.querySelector('meta[name="theme-color"]')?.getAttribute('content') || manifest?.theme_color; + const themeColor = info?.metadata?.themeColor ?? doc?.querySelector('meta[name="theme-color"]')?.getAttribute('content') ?? manifest?.theme_color; if (themeColor) { const color = new tinycolor(themeColor); @@ -235,7 +235,7 @@ export class FetchInstanceMetadataService { async #getSiteName(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record | null): Promise { if (info && info.metadata) { if (info.metadata.nodeName || info.metadata.name) { - return info.metadata.nodeName || info.metadata.name; + return info.metadata.nodeName ?? info.metadata.name; } } @@ -248,7 +248,7 @@ export class FetchInstanceMetadataService { } if (manifest) { - return manifest.name || manifest.short_name; + return manifest.name ?? manifest.short_name; } return null; @@ -257,7 +257,7 @@ export class FetchInstanceMetadataService { async #getDescription(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record | null): Promise { if (info && info.metadata) { if (info.metadata.nodeDescription || info.metadata.description) { - return info.metadata.nodeDescription || info.metadata.description; + return info.metadata.nodeDescription ?? info.metadata.description; } } @@ -274,7 +274,7 @@ export class FetchInstanceMetadataService { } if (manifest) { - return manifest.name || manifest.short_name; + return manifest.name ?? manifest.short_name; } return null; diff --git a/packages/backend/src/services/HashtagService.ts b/packages/backend/src/services/HashtagService.ts index 5eca011f1f62..1816e9820b8b 100644 --- a/packages/backend/src/services/HashtagService.ts +++ b/packages/backend/src/services/HashtagService.ts @@ -32,7 +32,7 @@ export class HashtagService { await this.updateHashtag(user, tag, true, true); } - for (const tag of (user.tags || []).filter(x => !tags.includes(x))) { + for (const tag of (user.tags ?? []).filter(x => !tags.includes(x))) { await this.updateHashtag(user, tag, true, false); } } diff --git a/packages/backend/src/services/HttpRequestService.ts b/packages/backend/src/services/HttpRequestService.ts index a6f0a000a133..a75eec97f217 100644 --- a/packages/backend/src/services/HttpRequestService.ts +++ b/packages/backend/src/services/HttpRequestService.ts @@ -54,7 +54,7 @@ export class HttpRequestService { lookup: cache.lookup, } as https.AgentOptions); - const maxSockets = Math.max(256, config.deliverJobConcurrency || 128); + const maxSockets = Math.max(256, config.deliverJobConcurrency ?? 128); this.httpAgent = config.proxy ? new HttpProxyAgent({ @@ -99,7 +99,7 @@ export class HttpRequestService { headers: Object.assign({ 'User-Agent': this.config.userAgent, Accept: accept, - }, headers || {}), + }, headers ?? {}), timeout, }); @@ -113,7 +113,7 @@ export class HttpRequestService { headers: Object.assign({ 'User-Agent': this.config.userAgent, Accept: accept, - }, headers || {}), + }, headers ?? {}), timeout, }); @@ -128,7 +128,7 @@ export class HttpRequestService { timeout?: number, size?: number, }): Promise { - const timeout = args.timeout || 10 * 1000; + const timeout = args.timeout ?? 10 * 1000; const controller = new AbortController(); setTimeout(() => { @@ -140,7 +140,7 @@ export class HttpRequestService { headers: args.headers, body: args.body, timeout, - size: args.size || 10 * 1024 * 1024, + size: args.size ?? 10 * 1024 * 1024, agent: this.getAgentByUrl, signal: controller.signal, }); diff --git a/packages/backend/src/services/ModerationLogService.ts b/packages/backend/src/services/ModerationLogService.ts index 32e24aba7dfb..f5792fb66a7a 100644 --- a/packages/backend/src/services/ModerationLogService.ts +++ b/packages/backend/src/services/ModerationLogService.ts @@ -20,7 +20,7 @@ export class ModerationLogService { createdAt: new Date(), userId: moderator.id, type: type, - info: info || {}, + info: info ?? {}, }); } } diff --git a/packages/backend/src/services/NoteCreateService.ts b/packages/backend/src/services/NoteCreateService.ts index 2c92064efa5c..5a82d48e1fbe 100644 --- a/packages/backend/src/services/NoteCreateService.ts +++ b/packages/backend/src/services/NoteCreateService.ts @@ -247,14 +247,14 @@ export class NoteCreateService { const combinedTokens = tokens.concat(cwTokens).concat(choiceTokens); - tags = data.apHashtags || extractHashtags(combinedTokens); + tags = data.apHashtags ?? extractHashtags(combinedTokens); - emojis = data.apEmojis || extractCustomEmojisFromMfm(combinedTokens); + emojis = data.apEmojis ?? extractCustomEmojisFromMfm(combinedTokens); - mentionedUsers = data.apMentions || await this.#extractMentionedUsers(user, combinedTokens); + mentionedUsers = data.apMentions ?? await this.#extractMentionedUsers(user, combinedTokens); } - 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 })); @@ -518,7 +518,7 @@ export class NoteCreateService { if (data.reply.userHost === null) { const threadMuted = await NoteThreadMutings.findOneBy({ userId: data.reply.userId, - threadId: data.reply.threadId || data.reply.id, + threadId: data.reply.threadId ?? data.reply.id, }); if (!threadMuted) { @@ -635,7 +635,7 @@ export class NoteCreateService { for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) { const threadMuted = await NoteThreadMutings.findOneBy({ userId: u.id, - threadId: note.threadId || note.id, + threadId: note.threadId ?? note.id, }); if (threadMuted) { @@ -678,7 +678,7 @@ export class NoteCreateService { if (note.text == null || this.config.elasticsearch == null) return; /* es!.index({ - index: this.config.elasticsearch.index || 'misskey_note', + index: this.config.elasticsearch.index ?? 'misskey_note', id: note.id.toString(), body: { text: normalizeForSearch(note.text), @@ -703,7 +703,7 @@ export class NoteCreateService { const mentions = extractMentions(tokens); let mentionedUsers = (await Promise.all(mentions.map(m => - this.resolveUserService.resolveUser(m.username, m.host || user.host).catch(() => null), + this.resolveUserService.resolveUser(m.username, m.host ?? user.host).catch(() => null), ))).filter(x => x != null) as User[]; // Drop duplicate users diff --git a/packages/backend/src/services/NoteDeleteService.ts b/packages/backend/src/services/NoteDeleteService.ts index 9d3897dd4c01..f612afd2dad7 100644 --- a/packages/backend/src/services/NoteDeleteService.ts +++ b/packages/backend/src/services/NoteDeleteService.ts @@ -72,7 +72,7 @@ export class NoteDeleteService { } const content = this.apRendererService.renderActivity(renote - ? this.apRendererService.renderUndo(this.apRendererService.renderAnnounce(renote.uri || `${this.config.url}/notes/${renote.id}`, note), user) + ? this.apRendererService.renderUndo(this.apRendererService.renderAnnounce(renote.uri ?? `${this.config.url}/notes/${renote.id}`, note), user) : this.apRendererService.renderDelete(this.apRendererService.renderTombstone(`${this.config.url}/notes/${note.id}`), user)); this.#deliverToConcerned(user, note, content); diff --git a/packages/backend/src/services/NoteReadService.ts b/packages/backend/src/services/NoteReadService.ts index 92b76c130eb9..30830088f86b 100644 --- a/packages/backend/src/services/NoteReadService.ts +++ b/packages/backend/src/services/NoteReadService.ts @@ -61,7 +61,7 @@ export class NoteReadService { // スレッドミュート const threadMute = await this.noteThreadMutingsRepository.findOneBy({ userId: userId, - threadId: note.threadId || note.id, + threadId: note.threadId ?? note.id, }); if (threadMute) return; diff --git a/packages/backend/src/services/QueueService.ts b/packages/backend/src/services/QueueService.ts index 1260a1c131d9..767940b6a51f 100644 --- a/packages/backend/src/services/QueueService.ts +++ b/packages/backend/src/services/QueueService.ts @@ -37,7 +37,7 @@ export class QueueService { }; return this.deliverQueue.add(data, { - attempts: this.config.deliverJobMaxAttempts || 12, + attempts: this.config.deliverJobMaxAttempts ?? 12, timeout: 1 * 60 * 1000, // 1min backoff: { type: 'apBackoff', @@ -54,7 +54,7 @@ export class QueueService { }; return this.inboxQueue.add(data, { - attempts: this.config.inboxJobMaxAttempts || 8, + attempts: this.config.inboxJobMaxAttempts ?? 8, timeout: 5 * 60 * 1000, // 5min backoff: { type: 'apBackoff', diff --git a/packages/backend/src/services/ReactionService.ts b/packages/backend/src/services/ReactionService.ts index 26df110790bc..266e56a4d5c9 100644 --- a/packages/backend/src/services/ReactionService.ts +++ b/packages/backend/src/services/ReactionService.ts @@ -160,7 +160,7 @@ export class ReactionService { reaction: decodedReaction.reaction, emoji: emoji != null ? { name: emoji.host ? `${emoji.name}@${emoji.host}` : `${emoji.name}@.`, - url: emoji.publicUrl || emoji.originalUrl, // || emoji.originalUrl してるのは後方互換性のため + url: emoji.publicUrl ?? emoji.originalUrl, // || emoji.originalUrl してるのは後方互換性のため } : null, userId: user.id, }); @@ -317,10 +317,10 @@ export class ReactionService { if (custom) { const name = custom[1]; - const host = custom[2] || null; + const host = custom[2] ?? null; return { - reaction: `:${name}@${host || '.'}:`, // ローカル分は@以降を省略するのではなく.にする + reaction: `:${name}@${host ?? '.'}:`, // ローカル分は@以降を省略するのではなく.にする name, host, }; diff --git a/packages/backend/src/services/S3Service.ts b/packages/backend/src/services/S3Service.ts index 0cbb70217b22..9549e1999057 100644 --- a/packages/backend/src/services/S3Service.ts +++ b/packages/backend/src/services/S3Service.ts @@ -22,10 +22,10 @@ export class S3Service { : `${meta.objectStorageUseSSL ? 'https://' : 'http://'}example.net`; return new S3({ - endpoint: meta.objectStorageEndpoint || undefined, + endpoint: meta.objectStorageEndpoint ?? undefined, accessKeyId: meta.objectStorageAccessKey!, secretAccessKey: meta.objectStorageSecretKey!, - region: meta.objectStorageRegion || undefined, + region: meta.objectStorageRegion ?? undefined, sslEnabled: meta.objectStorageUseSSL, s3ForcePathStyle: !meta.objectStorageEndpoint // AWS with endPoint omitted ? false diff --git a/packages/backend/src/services/UserFollowingService.ts b/packages/backend/src/services/UserFollowingService.ts index 4a5acf49f7f5..be9613061111 100644 --- a/packages/backend/src/services/UserFollowingService.ts +++ b/packages/backend/src/services/UserFollowingService.ts @@ -549,7 +549,7 @@ export class UserFollowingService { followerId: follower.id, }); - const content = this.apRendererService.renderActivity(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee, request?.requestId || undefined), followee)); + const content = this.apRendererService.renderActivity(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee, request?.requestId ?? undefined), followee)); this.queueService.deliver(followee, content, follower.inbox); } diff --git a/packages/backend/src/services/UserSuspendService.ts b/packages/backend/src/services/UserSuspendService.ts index 53dbda12f569..d5ec83da1cb6 100644 --- a/packages/backend/src/services/UserSuspendService.ts +++ b/packages/backend/src/services/UserSuspendService.ts @@ -45,7 +45,7 @@ export class UserSuspendService { select: ['followerSharedInbox', 'followeeSharedInbox'], }); - const inboxes = followings.map(x => x.followerSharedInbox || x.followeeSharedInbox); + const inboxes = followings.map(x => x.followerSharedInbox ?? x.followeeSharedInbox); for (const inbox of inboxes) { if (inbox != null && !queue.includes(inbox)) queue.push(inbox); @@ -74,7 +74,7 @@ export class UserSuspendService { select: ['followerSharedInbox', 'followeeSharedInbox'], }); - const inboxes = followings.map(x => x.followerSharedInbox || x.followeeSharedInbox); + const inboxes = followings.map(x => x.followerSharedInbox ?? x.followeeSharedInbox); for (const inbox of inboxes) { if (inbox != null && !queue.includes(inbox)) queue.push(inbox); diff --git a/packages/backend/src/services/entities/AbuseUserReportEntityService.ts b/packages/backend/src/services/entities/AbuseUserReportEntityService.ts index 3fe6c3677a1a..f8fc34216c55 100644 --- a/packages/backend/src/services/entities/AbuseUserReportEntityService.ts +++ b/packages/backend/src/services/entities/AbuseUserReportEntityService.ts @@ -28,13 +28,13 @@ export class AbuseUserReportEntityService { reporterId: report.reporterId, targetUserId: report.targetUserId, assigneeId: report.assigneeId, - reporter: this.userEntityService.pack(report.reporter || report.reporterId, null, { + reporter: this.userEntityService.pack(report.reporter ?? report.reporterId, null, { detail: true, }), - targetUser: this.userEntityService.pack(report.targetUser || report.targetUserId, null, { + targetUser: this.userEntityService.pack(report.targetUser ?? report.targetUserId, null, { detail: true, }), - assignee: report.assigneeId ? this.userEntityService.pack(report.assignee || report.assigneeId, null, { + assignee: report.assigneeId ? this.userEntityService.pack(report.assignee ?? report.assigneeId, null, { detail: true, }) : null, forwarded: report.forwarded, diff --git a/packages/backend/src/services/entities/ClipEntityService.ts b/packages/backend/src/services/entities/ClipEntityService.ts index e45dfa8da13a..05a5534c7f84 100644 --- a/packages/backend/src/services/entities/ClipEntityService.ts +++ b/packages/backend/src/services/entities/ClipEntityService.ts @@ -27,7 +27,7 @@ export class ClipEntityService { id: clip.id, createdAt: clip.createdAt.toISOString(), userId: clip.userId, - user: this.userEntityService.pack(clip.user || clip.userId), + user: this.userEntityService.pack(clip.user ?? clip.userId), name: clip.name, description: clip.description, isPublic: clip.isPublic, diff --git a/packages/backend/src/services/entities/DriveFileEntityService.ts b/packages/backend/src/services/entities/DriveFileEntityService.ts index 467269728300..bb445b685abb 100644 --- a/packages/backend/src/services/entities/DriveFileEntityService.ts +++ b/packages/backend/src/services/entities/DriveFileEntityService.ts @@ -88,7 +88,7 @@ export class DriveFileEntityService { const isImage = file.type && ['image/png', 'image/apng', 'image/gif', 'image/jpeg', 'image/webp', 'image/svg+xml'].includes(file.type); - return thumbnail ? (file.thumbnailUrl || (isImage ? (file.webpublicUrl || file.url) : null)) : (file.webpublicUrl || file.url); + return thumbnail ? (file.thumbnailUrl ?? (isImage ? (file.webpublicUrl ?? file.url) : null)) : (file.webpublicUrl ?? file.url); } public async calcDriveUsageOf(user: User['id'] | { id: User['id'] }): Promise { @@ -101,7 +101,7 @@ export class DriveFileEntityService { .select('SUM(file.size)', 'sum') .getRawOne(); - return parseInt(sum, 10) || 0; + return parseInt(sum, 10) ?? 0; } public async calcDriveUsageOfHost(host: string): Promise { @@ -112,7 +112,7 @@ export class DriveFileEntityService { .select('SUM(file.size)', 'sum') .getRawOne(); - return parseInt(sum, 10) || 0; + return parseInt(sum, 10) ?? 0; } public async calcDriveUsageOfLocal(): Promise { @@ -123,7 +123,7 @@ export class DriveFileEntityService { .select('SUM(file.size)', 'sum') .getRawOne(); - return parseInt(sum, 10) || 0; + return parseInt(sum, 10) ?? 0; } public async calcDriveUsageOfRemote(): Promise { @@ -134,7 +134,7 @@ export class DriveFileEntityService { .select('SUM(file.size)', 'sum') .getRawOne(); - return parseInt(sum, 10) || 0; + return parseInt(sum, 10) ?? 0; } public async pack( diff --git a/packages/backend/src/services/entities/EmojiEntityService.ts b/packages/backend/src/services/entities/EmojiEntityService.ts index ec1018b29860..b216c39a6500 100644 --- a/packages/backend/src/services/entities/EmojiEntityService.ts +++ b/packages/backend/src/services/entities/EmojiEntityService.ts @@ -29,8 +29,8 @@ export class EmojiEntityService { name: emoji.name, category: emoji.category, host: emoji.host, - // || emoji.originalUrl してるのは後方互換性のため - url: emoji.publicUrl || emoji.originalUrl, + // ?? emoji.originalUrl してるのは後方互換性のため + url: emoji.publicUrl ?? emoji.originalUrl, }; } diff --git a/packages/backend/src/services/entities/FollowingEntityService.ts b/packages/backend/src/services/entities/FollowingEntityService.ts index 16c82c99528c..e028b204145a 100644 --- a/packages/backend/src/services/entities/FollowingEntityService.ts +++ b/packages/backend/src/services/entities/FollowingEntityService.ts @@ -75,10 +75,10 @@ export class FollowingEntityService { createdAt: following.createdAt.toISOString(), followeeId: following.followeeId, followerId: following.followerId, - followee: opts.populateFollowee ? this.userEntityService.pack(following.followee || following.followeeId, me, { + followee: opts.populateFollowee ? this.userEntityService.pack(following.followee ?? following.followeeId, me, { detail: true, }) : undefined, - follower: opts.populateFollower ? this.userEntityService.pack(following.follower || following.followerId, me, { + follower: opts.populateFollower ? this.userEntityService.pack(following.follower ?? following.followerId, me, { detail: true, }) : undefined, }); diff --git a/packages/backend/src/services/entities/GalleryLikeEntityService.ts b/packages/backend/src/services/entities/GalleryLikeEntityService.ts index 98eb1785c51c..cc0dd818909a 100644 --- a/packages/backend/src/services/entities/GalleryLikeEntityService.ts +++ b/packages/backend/src/services/entities/GalleryLikeEntityService.ts @@ -28,7 +28,7 @@ export class GalleryLikeEntityService { return { id: like.id, - post: await this.galleryPostEntityService.pack(like.post || like.postId, me), + post: await this.galleryPostEntityService.pack(like.post ?? like.postId, me), }; } diff --git a/packages/backend/src/services/entities/GalleryPostEntityService.ts b/packages/backend/src/services/entities/GalleryPostEntityService.ts index 8e29c7bc49f3..9f99a6ce0b6c 100644 --- a/packages/backend/src/services/entities/GalleryPostEntityService.ts +++ b/packages/backend/src/services/entities/GalleryPostEntityService.ts @@ -35,7 +35,7 @@ export class GalleryPostEntityService { createdAt: post.createdAt.toISOString(), updatedAt: post.updatedAt.toISOString(), userId: post.userId, - user: this.userEntityService.pack(post.user || post.userId, me), + user: this.userEntityService.pack(post.user ?? post.userId, me), title: post.title, description: post.description, fileIds: post.fileIds, diff --git a/packages/backend/src/services/entities/MessagingMessageEntityService.ts b/packages/backend/src/services/entities/MessagingMessageEntityService.ts index 152e2339fdd8..dd9cb1036332 100644 --- a/packages/backend/src/services/entities/MessagingMessageEntityService.ts +++ b/packages/backend/src/services/entities/MessagingMessageEntityService.ts @@ -8,6 +8,7 @@ import type { User } from '@/models/entities/User.js'; import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; import { UserEntityService } from './UserEntityService.js'; import { DriveFileEntityService } from './DriveFileEntityService.js'; +import { UserGroupEntityService } from './UserGroupEntityService.js'; @Injectable() export class MessagingMessageEntityService { @@ -16,6 +17,7 @@ export class MessagingMessageEntityService { private messagingMessagesRepository: typeof MessagingMessages, private userEntityService: UserEntityService, + private userGroupEntityService: UserGroupEntityService, private driveFileEntityService: DriveFileEntityService, ) { } @@ -28,7 +30,7 @@ export class MessagingMessageEntityService { populateGroup?: boolean, }, ): Promise> { - const opts = options || { + const opts = options ?? { populateRecipient: true, populateGroup: true, }; @@ -40,11 +42,11 @@ export class MessagingMessageEntityService { createdAt: message.createdAt.toISOString(), text: message.text, userId: message.userId, - user: await this.userEntityService.pack(message.user || message.userId, me), + user: await this.userEntityService.pack(message.user ?? message.userId, me), recipientId: message.recipientId, - recipient: message.recipientId && opts.populateRecipient ? await this.userEntityService.pack(message.recipient || message.recipientId, me) : undefined, + recipient: message.recipientId && opts.populateRecipient ? await this.userEntityService.pack(message.recipient ?? message.recipientId, me) : undefined, groupId: message.groupId, - group: message.groupId && opts.populateGroup ? await this.userGroupEntityService.pack(message.group || message.groupId) : undefined, + group: message.groupId && opts.populateGroup ? await this.userGroupEntityService.pack(message.group ?? message.groupId) : undefined, fileId: message.fileId, file: message.fileId ? await this.driveFileEntityService.pack(message.fileId) : null, isRead: message.isRead, diff --git a/packages/backend/src/services/entities/ModerationLogEntityService.ts b/packages/backend/src/services/entities/ModerationLogEntityService.ts index b94dfd4e252c..3129e3bcef30 100644 --- a/packages/backend/src/services/entities/ModerationLogEntityService.ts +++ b/packages/backend/src/services/entities/ModerationLogEntityService.ts @@ -29,7 +29,7 @@ export class ModerationLogEntityService { type: log.type, info: log.info, userId: log.userId, - user: this.userEntityService.pack(log.user || log.userId, null, { + user: this.userEntityService.pack(log.user ?? log.userId, null, { detail: true, }), }); diff --git a/packages/backend/src/services/entities/NoteEntityService.ts b/packages/backend/src/services/entities/NoteEntityService.ts index add95406d487..b0f6a4f62ed9 100644 --- a/packages/backend/src/services/entities/NoteEntityService.ts +++ b/packages/backend/src/services/entities/NoteEntityService.ts @@ -288,7 +288,7 @@ export class NoteEntityService implements OnModuleInit { text: text, cw: note.cw, visibility: note.visibility, - localOnly: note.localOnly || undefined, + localOnly: note.localOnly ?? undefined, visibleUserIds: note.visibility === 'specified' ? note.visibleUserIds : undefined, renoteCount: note.renoteCount, repliesCount: note.repliesCount, @@ -299,22 +299,22 @@ export class NoteEntityService implements OnModuleInit { files: this.driveFileEntityService.packMany(note.fileIds), replyId: note.replyId, renoteId: note.renoteId, - channelId: note.channelId || undefined, + channelId: note.channelId ?? undefined, channel: channel ? { id: channel.id, name: channel.name, } : undefined, mentions: note.mentions.length > 0 ? note.mentions : undefined, - uri: note.uri || undefined, - url: note.url || undefined, + uri: note.uri ?? undefined, + url: note.url ?? undefined, ...(opts.detail ? { - reply: note.replyId ? this.pack(note.reply || note.replyId, me, { + reply: note.replyId ? this.pack(note.reply ?? note.replyId, me, { detail: false, _hint_: options?._hint_, }) : undefined, - renote: note.renoteId ? this.pack(note.renote || note.renoteId, me, { + renote: note.renoteId ? this.pack(note.renote ?? note.renoteId, me, { detail: true, _hint_: options?._hint_, }) : undefined, @@ -366,7 +366,7 @@ export class NoteEntityService implements OnModuleInit { }); for (const target of targets) { - myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) || null); + myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) ?? null); } } diff --git a/packages/backend/src/services/entities/NoteFavoriteEntityService.ts b/packages/backend/src/services/entities/NoteFavoriteEntityService.ts index 096f3880ecc7..f076a119333e 100644 --- a/packages/backend/src/services/entities/NoteFavoriteEntityService.ts +++ b/packages/backend/src/services/entities/NoteFavoriteEntityService.ts @@ -29,7 +29,7 @@ export class NoteFavoriteEntityService { id: favorite.id, createdAt: favorite.createdAt.toISOString(), noteId: favorite.noteId, - note: await this.noteEntityService.pack(favorite.note || favorite.noteId, me), + note: await this.noteEntityService.pack(favorite.note ?? favorite.noteId, me), }; } diff --git a/packages/backend/src/services/entities/NotificationEntityService.ts b/packages/backend/src/services/entities/NotificationEntityService.ts index 223d0c71136f..ab1698d96873 100644 --- a/packages/backend/src/services/entities/NotificationEntityService.ts +++ b/packages/backend/src/services/entities/NotificationEntityService.ts @@ -64,7 +64,7 @@ export class NotificationEntityService implements OnModuleInit { type: notification.type, isRead: notification.isRead, userId: notification.notifierId, - user: notification.notifierId ? this.userEntityService.pack(notification.notifier || notification.notifierId) : null, + user: notification.notifierId ? this.userEntityService.pack(notification.notifier ?? notification.notifierId) : null, ...(notification.type === 'mention' ? { note: this.noteEntityService.pack(notification.note ?? notification.noteId!, { id: notification.notifieeId }, { detail: true, @@ -114,8 +114,8 @@ export class NotificationEntityService implements OnModuleInit { } : {}), ...(notification.type === 'app' ? { body: notification.customBody, - header: notification.customHeader || token?.name, - icon: notification.customIcon || token?.iconUrl, + header: notification.customHeader ?? token?.name, + icon: notification.customIcon ?? token?.iconUrl, } : {}), }); } @@ -137,7 +137,7 @@ export class NotificationEntityService implements OnModuleInit { }); for (const target of targets) { - myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) || null); + myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) ?? null); } await this.customEmojiService.prefetchEmojis(this.customEmojiService.aggregateNoteEmojis(notes)); diff --git a/packages/backend/src/services/entities/PageEntityService.ts b/packages/backend/src/services/entities/PageEntityService.ts index de37a0794c65..a65625a5942f 100644 --- a/packages/backend/src/services/entities/PageEntityService.ts +++ b/packages/backend/src/services/entities/PageEntityService.ts @@ -1,7 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { DriveFiles } from '@/models/index.js'; -import type { Pages , PageLikes } from '@/models/index.js'; +import type { DriveFiles , Pages , PageLikes } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; @@ -9,6 +8,7 @@ import type { User } from '@/models/entities/User.js'; import type { Page } from '@/models/entities/Page.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import { UserEntityService } from './UserEntityService.js'; +import { DriveFileEntityService } from './DriveFileEntityService.js'; @Injectable() export class PageEntityService { @@ -23,6 +23,7 @@ export class PageEntityService { private driveFilesRepository: typeof DriveFiles, private userEntityService: UserEntityService, + private driveFileEntityService: DriveFileEntityService, ) { } @@ -80,7 +81,7 @@ export class PageEntityService { createdAt: page.createdAt.toISOString(), updatedAt: page.updatedAt.toISOString(), userId: page.userId, - user: this.userEntityService.pack(page.user || page.userId, me), // { detail: true } すると無限ループするので注意 + user: this.userEntityService.pack(page.user ?? page.userId, me), // { detail: true } すると無限ループするので注意 content: page.content, variables: page.variables, title: page.title, @@ -91,8 +92,8 @@ export class PageEntityService { font: page.font, script: page.script, eyeCatchingImageId: page.eyeCatchingImageId, - eyeCatchingImage: page.eyeCatchingImageId ? await DriveFiles.pack(page.eyeCatchingImageId) : null, - attachedFiles: DriveFiles.packMany((await Promise.all(attachedFiles)).filter((x): x is DriveFile => x != null)), + eyeCatchingImage: page.eyeCatchingImageId ? await this.driveFileEntityService.pack(page.eyeCatchingImageId) : null, + attachedFiles: this.driveFileEntityService.packMany((await Promise.all(attachedFiles)).filter((x): x is DriveFile => x != null)), likedCount: page.likedCount, isLiked: meId ? await this.pageLikesRepository.findOneBy({ pageId: page.id, userId: meId }).then(x => x != null) : undefined, }); diff --git a/packages/backend/src/services/entities/PageLikeEntityService.ts b/packages/backend/src/services/entities/PageLikeEntityService.ts index 3bd3cbf84369..be43d7bc3e28 100644 --- a/packages/backend/src/services/entities/PageLikeEntityService.ts +++ b/packages/backend/src/services/entities/PageLikeEntityService.ts @@ -27,7 +27,7 @@ export class PageLikeEntityService { return { id: like.id, - page: await this.pageEntityService.pack(like.page || like.pageId, me), + page: await this.pageEntityService.pack(like.page ?? like.pageId, me), }; } diff --git a/packages/backend/src/services/entities/UserEntityService.ts b/packages/backend/src/services/entities/UserEntityService.ts index d8e8135502c0..49705966516d 100644 --- a/packages/backend/src/services/entities/UserEntityService.ts +++ b/packages/backend/src/services/entities/UserEntityService.ts @@ -300,10 +300,10 @@ export class UserEntityService implements OnModuleInit { public async getAvatarUrl(user: User): Promise { if (user.avatar) { - return this.driveFileEntityService.getPublicUrl(user.avatar, true) || this.getIdenticonUrl(user.id); + return this.driveFileEntityService.getPublicUrl(user.avatar, true) ?? this.getIdenticonUrl(user.id); } else if (user.avatarId) { const avatar = await this.driveFilesRepository.findOneByOrFail({ id: user.avatarId }); - return this.driveFileEntityService.getPublicUrl(avatar, true) || this.getIdenticonUrl(user.id); + return this.driveFileEntityService.getPublicUrl(avatar, true) ?? this.getIdenticonUrl(user.id); } else { return this.getIdenticonUrl(user.id); } @@ -311,7 +311,7 @@ export class UserEntityService implements OnModuleInit { public getAvatarUrlSync(user: User): string { if (user.avatar) { - return this.driveFileEntityService.getPublicUrl(user.avatar, true) || this.getIdenticonUrl(user.id); + return this.driveFileEntityService.getPublicUrl(user.avatar, true) ?? this.getIdenticonUrl(user.id); } else { return this.getIdenticonUrl(user.id); } @@ -379,12 +379,12 @@ export class UserEntityService implements OnModuleInit { username: user.username, host: user.host, avatarUrl: this.getAvatarUrlSync(user), - avatarBlurhash: user.avatar?.blurhash || null, + avatarBlurhash: user.avatar?.blurhash ?? null, avatarColor: null, // 後方互換性のため - isAdmin: user.isAdmin || falsy, - isModerator: user.isModerator || falsy, - isBot: user.isBot || falsy, - isCat: user.isCat || falsy, + isAdmin: user.isAdmin ?? falsy, + isModerator: user.isModerator ?? falsy, + isBot: user.isBot ?? falsy, + isCat: user.isCat ?? falsy, instance: user.host ? this.#userInstanceCache.fetch(user.host, () => this.instancesRepository.findOneBy({ host: user.host! }), v => v != null, @@ -407,11 +407,11 @@ export class UserEntityService implements OnModuleInit { updatedAt: user.updatedAt ? user.updatedAt.toISOString() : null, lastFetchedAt: user.lastFetchedAt ? user.lastFetchedAt.toISOString() : null, bannerUrl: user.banner ? this.driveFileEntityService.getPublicUrl(user.banner, false) : null, - bannerBlurhash: user.banner?.blurhash || null, + bannerBlurhash: user.banner?.blurhash ?? null, bannerColor: null, // 後方互換性のため isLocked: user.isLocked, - isSilenced: user.isSilenced || falsy, - isSuspended: user.isSuspended || falsy, + isSilenced: user.isSilenced ?? falsy, + isSuspended: user.isSuspended ?? falsy, description: profile!.description, location: profile!.location, birthday: profile!.birthday, @@ -469,7 +469,7 @@ export class UserEntityService implements OnModuleInit { mutedInstances: profile!.mutedInstances, mutingNotificationTypes: profile!.mutingNotificationTypes, emailNotificationTypes: profile!.emailNotificationTypes, - showTimelineReplies: user.showTimelineReplies || falsy, + showTimelineReplies: user.showTimelineReplies ?? falsy, } : {}), ...(opts.includeSecrets ? { diff --git a/packages/backend/src/services/entities/UserGroupInvitationEntityService.ts b/packages/backend/src/services/entities/UserGroupInvitationEntityService.ts index f9331be8efdb..896ed84819d5 100644 --- a/packages/backend/src/services/entities/UserGroupInvitationEntityService.ts +++ b/packages/backend/src/services/entities/UserGroupInvitationEntityService.ts @@ -26,7 +26,7 @@ export class UserGroupInvitationEntityService { return { id: invitation.id, - group: await this.userGroupEntityService.pack(invitation.userGroup || invitation.userGroupId), + group: await this.userGroupEntityService.pack(invitation.userGroup ?? invitation.userGroupId), }; } diff --git a/packages/backend/src/services/queue/QueueModule.ts b/packages/backend/src/services/queue/QueueModule.ts index 7d4aac900087..3a271ea37f8b 100644 --- a/packages/backend/src/services/queue/QueueModule.ts +++ b/packages/backend/src/services/queue/QueueModule.ts @@ -12,7 +12,7 @@ function q(config: Config, name: string, limitPerSec = -1) { host: config.redis.host, family: config.redis.family == null ? 0 : config.redis.family, password: config.redis.pass, - db: config.redis.db || 0, + db: config.redis.db ?? 0, }, prefix: config.redis.prefix ? `${config.redis.prefix}:queue` : 'queue', limiter: limitPerSec > 0 ? { diff --git a/packages/backend/src/services/remote/ResolveUserService.ts b/packages/backend/src/services/remote/ResolveUserService.ts index d266419a331e..83c35f425ca8 100644 --- a/packages/backend/src/services/remote/ResolveUserService.ts +++ b/packages/backend/src/services/remote/ResolveUserService.ts @@ -118,9 +118,9 @@ export class ResolveUserService { async #resolveSelf(acctLower: string) { this.#logger.info(`WebFinger for ${chalk.yellow(acctLower)}`); - const finger = await this.webfingerService.webfinger(acctLower).catch(e => { - this.#logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: ${ e.statusCode || e.message }`); - throw new Error(`Failed to WebFinger for ${acctLower}: ${ e.statusCode || e.message }`); + const finger = await this.webfingerService.webfinger(acctLower).catch(err => { + this.#logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: ${ err.statusCode ?? err.message }`); + throw new Error(`Failed to WebFinger for ${acctLower}: ${ err.statusCode ?? err.message }`); }); const self = finger.links.find(link => link.rel != null && link.rel.toLowerCase() === 'self'); if (!self) { diff --git a/packages/backend/src/services/remote/activitypub/ApAudienceService.ts b/packages/backend/src/services/remote/activitypub/ApAudienceService.ts index 3e3eff88c086..2ab7bd03030c 100644 --- a/packages/backend/src/services/remote/activitypub/ApAudienceService.ts +++ b/packages/backend/src/services/remote/activitypub/ApAudienceService.ts @@ -98,7 +98,7 @@ export class ApAudienceService { #isFollowers(id: string, actor: CacheableRemoteUser) { return ( - id === (actor.followersUri || `${actor.uri}/followers`) + id === (actor.followersUri ?? `${actor.uri}/followers`) ); } } diff --git a/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts b/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts index 6abac17f6b8f..edcb120b2327 100644 --- a/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts +++ b/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts @@ -176,7 +176,7 @@ class DeliverManager { }[]; for (const following of followers) { - const inbox = following.followerSharedInbox || following.followerInbox; + const inbox = following.followerSharedInbox ?? following.followerInbox; inboxes.add(inbox); } } diff --git a/packages/backend/src/services/remote/activitypub/ApInboxService.ts b/packages/backend/src/services/remote/activitypub/ApInboxService.ts index 3e96b938b992..c8d260aed01c 100644 --- a/packages/backend/src/services/remote/activitypub/ApInboxService.ts +++ b/packages/backend/src/services/remote/activitypub/ApInboxService.ts @@ -169,9 +169,9 @@ export class ApInboxService { const note = await this.apNoteService.fetchNote(targetUri); if (!note) return `skip: target note not found ${targetUri}`; - await this.apNoteService.extractEmojis(activity.tag || [], actor.host).catch(() => null); + await this.apNoteService.extractEmojis(activity.tag ?? [], actor.host).catch(() => null); - return await this.reactionService.create(actor, note, activity._misskey_reaction || activity.content || activity.name).catch(e => { + return await this.reactionService.create(actor, note, activity._misskey_reaction ?? activity.content ?? activity.name).catch(e => { if (e.id === '51c42bb4-931a-456b-bff7-e5a8a70dd298') { return 'skip: already reacted'; } else { @@ -203,15 +203,15 @@ export class ApInboxService { } async #accept(actor: CacheableRemoteUser, activity: IAccept): Promise { - const uri = activity.id || activity; + const uri = activity.id ?? activity; this.#logger.info(`Accept: ${uri}`); const resolver = this.apResolverService.createResolver(); - const object = await resolver.resolve(activity.object).catch(e => { - this.#logger.error(`Resolution failed: ${e}`); - throw e; + const object = await resolver.resolve(activity.object).catch(err => { + this.#logger.error(`Resolution failed: ${err}`); + throw err; }); if (isFollow(object)) return await this.#acceptFollow(actor, object); @@ -295,17 +295,17 @@ export class ApInboxService { let renote; try { renote = await this.apNoteService.resolveNote(targetUri); - } catch (e) { + } catch (err) { // 対象が4xxならスキップ - if (e instanceof StatusError) { - if (e.isClientError) { - this.#logger.warn(`Ignored announce target ${targetUri} - ${e.statusCode}`); + if (err instanceof StatusError) { + if (err.isClientError) { + this.#logger.warn(`Ignored announce target ${targetUri} - ${err.statusCode}`); return; } - this.#logger.warn(`Error in announce target ${targetUri} - ${e.statusCode || e}`); + this.#logger.warn(`Error in announce target ${targetUri} - ${err.statusCode ?? err}`); } - throw e; + throw err; } if (!await this.noteEntityService.isVisibleForMe(renote, actor.id)) return 'skip: invalid actor for this activity'; @@ -531,7 +531,7 @@ export class ApInboxService { } async #reject(actor: CacheableRemoteUser, activity: IReject): Promise { - const uri = activity.id || activity; + const uri = activity.id ?? activity; this.#logger.info(`Reject: ${uri}`); @@ -594,7 +594,7 @@ export class ApInboxService { throw new Error('invalid actor'); } - const uri = activity.id || activity; + const uri = activity.id ?? activity; this.#logger.info(`Undo: ${uri}`); diff --git a/packages/backend/src/services/remote/activitypub/ApRendererService.ts b/packages/backend/src/services/remote/activitypub/ApRendererService.ts index e425260b91f1..52a6cfd0d53a 100644 --- a/packages/backend/src/services/remote/activitypub/ApRendererService.ts +++ b/packages/backend/src/services/remote/activitypub/ApRendererService.ts @@ -163,7 +163,7 @@ private driveFileEntityService: DriveFileEntityService, icon: { type: 'Image', mediaType: emoji.type ?? 'image/png', - url: emoji.publicUrl ?? emoji.originalUrl, // || emoji.originalUrl してるのは後方互換性のため + url: emoji.publicUrl ?? emoji.originalUrl, // ?? emoji.originalUrl してるのは後方互換性のため }, }; } @@ -233,7 +233,7 @@ private driveFileEntityService: DriveFileEntityService, public renderKey(user: ILocalUser, key: UserKeypair, postfix?: string) { return { - id: `${this.config.url}/users/${user.id}${postfix || '/publickey'}`, + id: `${this.config.url}/users/${user.id}${postfix ?? '/publickey'}`, type: 'Key', owner: `${this.config.url}/users/${user.id}`, publicKeyPem: createPublicKey(key.publicKey).export({ diff --git a/packages/backend/src/services/remote/activitypub/LdSignatureService.ts b/packages/backend/src/services/remote/activitypub/LdSignatureService.ts index 8c10536fb7f5..8e52790ff6db 100644 --- a/packages/backend/src/services/remote/activitypub/LdSignatureService.ts +++ b/packages/backend/src/services/remote/activitypub/LdSignatureService.ts @@ -35,7 +35,7 @@ class LdSignature { creator, domain, nonce: crypto.randomBytes(16).toString('hex'), - created: (created || new Date()).toISOString(), + created: (created ?? new Date()).toISOString(), } as { type: string; creator: string; diff --git a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts index a68306409c43..b730ffd34346 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts @@ -270,7 +270,7 @@ export class ApNoteService { } } - const emojis = await this.extractEmojis(note.tag || [], actor.host).catch(e => { + const emojis = await this.extractEmojis(note.tag ?? [], actor.host).catch(e => { this.#logger.info(`extractEmojis: ${e}`); return [] as Emoji[]; }); @@ -281,7 +281,7 @@ export class ApNoteService { if (isMessaging) { for (const recipient of visibleUsers) { - await this.messagingService.createMessage(actor, recipient, undefined, text || undefined, (files && files.length > 0) ? files[0] : null, object.id); + await this.messagingService.createMessage(actor, recipient, undefined, text ?? undefined, (files && files.length > 0) ? files[0] : null, object.id); return null; } } diff --git a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts index 916251734429..33572fcee23c 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts @@ -271,7 +271,7 @@ export class ApPersonService implements OnModuleInit { const host = this.utilityService.toPuny(new URL(object.id).hostname); - const { fields } = this.analyzeAttachments(person.attachment || []); + const { fields } = this.analyzeAttachments(person.attachment ?? []); const tags = extractApHashtags(person.tag).map(tag => normalizeForSearch(tag)).splice(0, 32); @@ -297,7 +297,7 @@ export class ApPersonService implements OnModuleInit { usernameLower: person.preferredUsername!.toLowerCase(), host, inbox: person.inbox, - sharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined), + sharedInbox: person.sharedInbox ?? (person.endpoints ? person.endpoints.sharedInbox : undefined), followersUri: person.followers ? getApId(person.followers) : undefined, featured: person.featured ? getApId(person.featured) : undefined, uri: person.id, @@ -313,7 +313,7 @@ export class ApPersonService implements OnModuleInit { url: getOneApHrefNullable(person.url), fields, birthday: bday ? bday[0] : null, - location: person['vcard:Address'] || null, + location: person['vcard:Address'] ?? null, userHost: host, })); @@ -379,8 +379,8 @@ export class ApPersonService implements OnModuleInit { //#endregion //#region カスタム絵文字取得 - const emojis = await this.apNoteService.extractEmojis(person.tag || [], host).catch(e => { - this.#logger.info(`extractEmojis: ${e}`); + const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host).catch(err => { + this.#logger.info(`extractEmojis: ${err}`); return [] as Emoji[]; }); @@ -421,7 +421,7 @@ export class ApPersonService implements OnModuleInit { if (resolver == null) resolver = this.apResolverService.createResolver(); - const object = hint || await resolver.resolve(uri); + const object = hint ?? await resolver.resolve(uri); const person = this.#validateActor(object, uri); @@ -438,14 +438,14 @@ export class ApPersonService implements OnModuleInit { )); // カスタム絵文字取得 - const emojis = await this.apNoteService.extractEmojis(person.tag || [], exist.host).catch(e => { + const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], exist.host).catch(e => { this.#logger.info(`extractEmojis: ${e}`); return [] as Emoji[]; }); const emojiNames = emojis.map(emoji => emoji.name); - const { fields } = this.analyzeAttachments(person.attachment || []); + const { fields } = this.analyzeAttachments(person.attachment ?? []); const tags = extractApHashtags(person.tag).map(tag => normalizeForSearch(tag)).splice(0, 32); @@ -454,7 +454,7 @@ export class ApPersonService implements OnModuleInit { const updates = { lastFetchedAt: new Date(), inbox: person.inbox, - sharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined), + sharedInbox: person.sharedInbox ?? (person.endpoints ? person.endpoints.sharedInbox : undefined), followersUri: person.followers ? getApId(person.followers) : undefined, featured: person.featured, emojis: emojiNames, @@ -489,7 +489,7 @@ export class ApPersonService implements OnModuleInit { fields, description: person.summary ? this.apMfmService.htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null, birthday: bday ? bday[0] : null, - location: person['vcard:Address'] || null, + location: person['vcard:Address'] ?? null, }); this.globalEventService.publishInternalEvent('remoteUserUpdated', { id: exist.id }); @@ -501,7 +501,7 @@ export class ApPersonService implements OnModuleInit { await this.followingsRepository.update({ followerId: exist.id, }, { - followerSharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined), + followerSharedInbox: person.sharedInbox ?? (person.endpoints ? person.endpoints.sharedInbox : undefined), }); await this.updateFeatured(exist.id).catch(err => this.#logger.error(err)); diff --git a/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts b/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts index 53d6302257a7..afb9860ecd9a 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts @@ -86,7 +86,7 @@ export class ApQuestionService { if (question.type !== 'Question') throw new Error('object is not a Question'); - const apChoices = question.oneOf || question.anyOf; + const apChoices = question.oneOf ?? question.anyOf; let changed = false; diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts index 50e1b7b67525..5733cbe146ec 100644 --- a/packages/backend/test/utils.ts +++ b/packages/backend/test/utils.ts @@ -6,13 +6,13 @@ import * as childProcess from 'child_process'; import * as http from 'node:http'; import { SIGKILL } from 'constants'; import WebSocket from 'ws'; -import * as misskey from 'misskey-js'; import fetch from 'node-fetch'; import FormData from 'form-data'; import { DataSource } from 'typeorm'; import got, { RequestError } from 'got'; import loadConfig from '../src/config/load.js'; import { entities } from '../src/db/postgre.js'; +import type * as misskey from 'misskey-js'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); @@ -267,7 +267,7 @@ export async function initTestDb(justBorrow = false, initEntities?: any[]) { database: config.db.db, synchronize: true && !justBorrow, dropSchema: true && !justBorrow, - entities: initEntities || entities, + entities: initEntities ?? entities, }); await db.initialize(); diff --git a/packages/client/src/scripts/aiscript/api.ts b/packages/client/src/scripts/aiscript/api.ts index 01b8fd05fe02..6debcb8a1393 100644 --- a/packages/client/src/scripts/aiscript/api.ts +++ b/packages/client/src/scripts/aiscript/api.ts @@ -27,7 +27,7 @@ export function createAiScriptEnv(opts) { if (token) utils.assertString(token); apiRequests++; if (apiRequests > 16) return values.NULL; - const res = await os.api(ep.value, utils.valToJs(param), token ? token.value : (opts.token || null)); + const res = await os.api(ep.value, utils.valToJs(param), token ? token.value : (opts.token ?? null)); return utils.jsToVal(res); }), 'Mk:save': values.FN_NATIVE(([key, value]) => { diff --git a/packages/client/src/scripts/hpml/type-checker.ts b/packages/client/src/scripts/hpml/type-checker.ts index 9633b3cd01c6..24c9ed8bcb21 100644 --- a/packages/client/src/scripts/hpml/type-checker.ts +++ b/packages/client/src/scripts/hpml/type-checker.ts @@ -1,7 +1,9 @@ import autobind from 'autobind-decorator'; -import { Type, envVarsDef, PageVar } from '.'; -import { Expr, isLiteralValue, Variable } from './expr'; +import { isLiteralValue } from './expr'; import { funcDefs } from './lib'; +import { envVarsDef } from '.'; +import type { Type, PageVar } from '.'; +import type { Expr, Variable } from './expr'; type TypeError = { arg: number; @@ -44,14 +46,14 @@ export class HpmlTypeChecker { return { arg: i, expect: generic[arg], - actual: type + actual: type, }; } } else if (type !== arg) { return { arg: i, expect: arg, - actual: type + actual: type, }; } } @@ -81,7 +83,7 @@ export class HpmlTypeChecker { } if (typeof def.in[slot] === 'number') { - return generic[def.in[slot]] || null; + return generic[def.in[slot]] ?? null; } else { return def.in[slot]; } From de47bc91fc800a5c8ffc6220289c9e06d1619e60 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 17 Sep 2022 04:15:44 +0900 Subject: [PATCH 177/180] Update HttpRequestService.ts --- packages/backend/src/services/HttpRequestService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/services/HttpRequestService.ts b/packages/backend/src/services/HttpRequestService.ts index a75eec97f217..21cde12536da 100644 --- a/packages/backend/src/services/HttpRequestService.ts +++ b/packages/backend/src/services/HttpRequestService.ts @@ -141,7 +141,7 @@ export class HttpRequestService { body: args.body, timeout, size: args.size ?? 10 * 1024 * 1024, - agent: this.getAgentByUrl, + agent: (url) => this.getAgentByUrl(url), signal: controller.signal, }); From 69c3503be3933af073cf6c8deb5654f3f9389b5b Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 17 Sep 2022 04:21:07 +0900 Subject: [PATCH 178/180] :v: --- packages/client/.eslintrc.js | 3 +++ packages/shared/.eslintrc.js | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/packages/client/.eslintrc.js b/packages/client/.eslintrc.js index a5a4fd0f4362..01dedd1c69cc 100644 --- a/packages/client/.eslintrc.js +++ b/packages/client/.eslintrc.js @@ -21,6 +21,9 @@ module.exports = { 'allowSingleExtends': true, }, ], + '@typescript-eslint/prefer-nullish-coalescing': [ + 'error', + ], // window の禁止理由: グローバルスコープと衝突し、予期せぬ結果を招くため // e の禁止理由: error や event など、複数のキーワードの頭文字であり分かりにくいため 'id-denylist': ['error', 'window', 'e'], diff --git a/packages/shared/.eslintrc.js b/packages/shared/.eslintrc.js index 5a6a9c5af90b..2952b8ba16a9 100644 --- a/packages/shared/.eslintrc.js +++ b/packages/shared/.eslintrc.js @@ -24,6 +24,8 @@ module.exports = { 'semi-spacing': ['error', { 'before': false, 'after': true }], 'quotes': ['warn', 'single'], 'comma-dangle': ['warn', 'always-multiline'], + 'comma-spacing': ['error', { 'before': false, 'after': true }], + 'array-bracket-spacing': ['error', 'never'], 'keyword-spacing': ['error', { 'before': true, 'after': true, @@ -72,6 +74,9 @@ module.exports = { 'checksVoidReturn': false, }], '@typescript-eslint/consistent-type-imports': 'error', + '@typescript-eslint/prefer-nullish-coalescing': [ + 'error', + ], 'import/no-unresolved': ['off'], 'import/no-default-export': ['warn'], 'import/order': ['warn', { From a43d541a71bf5236d9d8f8f54298a764c3358abb Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 17 Sep 2022 05:01:44 +0900 Subject: [PATCH 179/180] :sparkles: --- packages/backend/src/RepositoryModule.ts | 127 +++++++++--------- .../backend/src/daemons/JanitorService.ts | 2 +- packages/backend/src/di-symbols.ts | 66 +++++++++ .../CheckExpiredMutingsProcessorService.ts | 2 +- .../queue/processors/CleanProcessorService.ts | 2 +- .../CleanRemoteFilesProcessorService.ts | 2 +- .../DeleteAccountProcessorService.ts | 8 +- .../DeleteDriveFilesProcessorService.ts | 4 +- .../processors/DeliverProcessorService.ts | 6 +- .../EndedPollNotificationProcessorService.ts | 6 +- .../ExportBlockingProcessorService.ts | 6 +- .../ExportCustomEmojisProcessorService.ts | 4 +- .../ExportFollowingProcessorService.ts | 6 +- .../ExportMutingProcessorService.ts | 8 +- .../processors/ExportNotesProcessorService.ts | 8 +- .../ExportUserListsProcessorService.ts | 6 +- .../ImportBlockingProcessorService.ts | 8 +- .../ImportCustomEmojisProcessorService.ts | 10 +- .../ImportFollowingProcessorService.ts | 4 +- .../ImportMutingProcessorService.ts | 4 +- .../ImportUserListsProcessorService.ts | 10 +- .../queue/processors/InboxProcessorService.ts | 4 +- .../WebhookDeliverProcessorService.ts | 2 +- .../src/server/ActivityPubServerService.ts | 18 +-- .../backend/src/server/FileServerService.ts | 2 +- .../src/server/NodeinfoServerService.ts | 6 +- packages/backend/src/server/ServerService.ts | 6 +- .../src/server/WellKnownServerService.ts | 2 +- .../backend/src/server/api/ApiCallService.ts | 2 +- .../src/server/api/ApiServerService.ts | 2 +- .../src/server/api/AuthenticateService.ts | 8 +- .../src/server/api/SigninApiService.ts | 10 +- .../backend/src/server/api/SigninService.ts | 4 +- .../src/server/api/SignupApiService.ts | 10 +- .../server/api/StreamingApiServerService.ts | 10 +- .../src/server/api/common/GetterService.ts | 6 +- .../api/endpoints/admin/abuse-user-reports.ts | 3 +- .../api/endpoints/admin/accounts/create.ts | 3 +- .../api/endpoints/admin/accounts/delete.ts | 3 +- .../server/api/endpoints/admin/ad/create.ts | 3 +- .../server/api/endpoints/admin/ad/delete.ts | 3 +- .../server/api/endpoints/admin/ad/update.ts | 3 +- .../endpoints/admin/announcements/create.ts | 3 +- .../endpoints/admin/announcements/delete.ts | 3 +- .../api/endpoints/admin/announcements/list.ts | 7 +- .../endpoints/admin/announcements/update.ts | 3 +- .../api/endpoints/admin/delete-account.ts | 3 +- .../admin/delete-all-files-of-a-user.ts | 3 +- .../admin/drive-capacity-override.ts | 3 +- .../api/endpoints/admin/drive/cleanup.ts | 3 +- .../server/api/endpoints/admin/drive/files.ts | 3 +- .../api/endpoints/admin/drive/show-file.ts | 3 +- .../endpoints/admin/emoji/add-aliases-bulk.ts | 2 +- .../server/api/endpoints/admin/emoji/add.ts | 4 +- .../server/api/endpoints/admin/emoji/copy.ts | 2 +- .../api/endpoints/admin/emoji/delete-bulk.ts | 2 +- .../api/endpoints/admin/emoji/delete.ts | 2 +- .../api/endpoints/admin/emoji/list-remote.ts | 3 +- .../server/api/endpoints/admin/emoji/list.ts | 3 +- .../admin/emoji/remove-aliases-bulk.ts | 2 +- .../endpoints/admin/emoji/set-aliases-bulk.ts | 2 +- .../admin/emoji/set-category-bulk.ts | 2 +- .../api/endpoints/admin/emoji/update.ts | 2 +- .../admin/federation/delete-all-files.ts | 3 +- .../refresh-remote-instance-metadata.ts | 3 +- .../admin/federation/remove-all-following.ts | 5 +- .../admin/federation/update-instance.ts | 3 +- .../api/endpoints/admin/get-user-ips.ts | 3 +- .../src/server/api/endpoints/admin/invite.ts | 3 +- .../api/endpoints/admin/moderators/add.ts | 3 +- .../api/endpoints/admin/moderators/remove.ts | 3 +- .../api/endpoints/admin/promo/create.ts | 3 +- .../api/endpoints/admin/reset-password.ts | 7 +- .../admin/resolve-abuse-user-report.ts | 7 +- .../endpoints/admin/show-moderation-logs.ts | 3 +- .../server/api/endpoints/admin/show-user.ts | 9 +- .../server/api/endpoints/admin/show-users.ts | 3 +- .../api/endpoints/admin/silence-user.ts | 3 +- .../api/endpoints/admin/suspend-user.ts | 9 +- .../api/endpoints/admin/unsilence-user.ts | 3 +- .../api/endpoints/admin/unsuspend-user.ts | 3 +- .../api/endpoints/admin/update-user-note.ts | 5 +- .../server/api/endpoints/antennas/create.ts | 9 +- .../server/api/endpoints/antennas/delete.ts | 3 +- .../src/server/api/endpoints/antennas/list.ts | 3 +- .../server/api/endpoints/antennas/notes.ts | 7 +- .../src/server/api/endpoints/antennas/show.ts | 3 +- .../server/api/endpoints/antennas/update.ts | 9 +- .../src/server/api/endpoints/ap/show.ts | 7 +- .../src/server/api/endpoints/app/create.ts | 3 +- .../src/server/api/endpoints/app/show.ts | 3 +- .../src/server/api/endpoints/auth/accept.ts | 9 +- .../api/endpoints/auth/session/generate.ts | 4 +- .../server/api/endpoints/auth/session/show.ts | 3 +- .../api/endpoints/auth/session/userkey.ts | 11 +- .../server/api/endpoints/blocking/create.ts | 7 +- .../server/api/endpoints/blocking/delete.ts | 7 +- .../src/server/api/endpoints/blocking/list.ts | 3 +- .../server/api/endpoints/channels/create.ts | 5 +- .../server/api/endpoints/channels/featured.ts | 3 +- .../server/api/endpoints/channels/follow.ts | 3 +- .../server/api/endpoints/channels/followed.ts | 3 +- .../server/api/endpoints/channels/owned.ts | 3 +- .../src/server/api/endpoints/channels/show.ts | 3 +- .../server/api/endpoints/channels/timeline.ts | 3 +- .../server/api/endpoints/channels/unfollow.ts | 3 +- .../server/api/endpoints/channels/update.ts | 7 +- .../src/server/api/endpoints/clips/create.ts | 3 +- .../src/server/api/endpoints/clips/delete.ts | 3 +- .../src/server/api/endpoints/clips/list.ts | 3 +- .../src/server/api/endpoints/clips/notes.ts | 7 +- .../server/api/endpoints/clips/remove-note.ts | 5 +- .../src/server/api/endpoints/clips/show.ts | 3 +- .../src/server/api/endpoints/clips/update.ts | 3 +- .../src/server/api/endpoints/drive/files.ts | 3 +- .../endpoints/drive/files/attached-notes.ts | 7 +- .../endpoints/drive/files/check-existence.ts | 3 +- .../api/endpoints/drive/files/create.ts | 3 +- .../api/endpoints/drive/files/delete.ts | 3 +- .../api/endpoints/drive/files/find-by-hash.ts | 3 +- .../server/api/endpoints/drive/files/find.ts | 3 +- .../server/api/endpoints/drive/files/show.ts | 3 +- .../api/endpoints/drive/files/update.ts | 7 +- .../endpoints/drive/files/upload-from-url.ts | 3 +- .../src/server/api/endpoints/drive/folders.ts | 3 +- .../api/endpoints/drive/folders/create.ts | 3 +- .../api/endpoints/drive/folders/delete.ts | 6 +- .../api/endpoints/drive/folders/find.ts | 3 +- .../api/endpoints/drive/folders/show.ts | 3 +- .../api/endpoints/drive/folders/update.ts | 3 +- .../src/server/api/endpoints/drive/stream.ts | 3 +- .../api/endpoints/federation/followers.ts | 3 +- .../api/endpoints/federation/following.ts | 3 +- .../api/endpoints/federation/instances.ts | 3 +- .../api/endpoints/federation/show-instance.ts | 3 +- .../server/api/endpoints/federation/stats.ts | 5 +- .../server/api/endpoints/federation/users.ts | 3 +- .../server/api/endpoints/following/create.ts | 7 +- .../server/api/endpoints/following/delete.ts | 7 +- .../api/endpoints/following/invalidate.ts | 7 +- .../endpoints/following/requests/cancel.ts | 3 +- .../api/endpoints/following/requests/list.ts | 3 +- .../server/api/endpoints/gallery/featured.ts | 3 +- .../server/api/endpoints/gallery/popular.ts | 3 +- .../src/server/api/endpoints/gallery/posts.ts | 3 +- .../api/endpoints/gallery/posts/create.ts | 3 +- .../api/endpoints/gallery/posts/delete.ts | 3 +- .../api/endpoints/gallery/posts/like.ts | 7 +- .../api/endpoints/gallery/posts/show.ts | 3 +- .../api/endpoints/gallery/posts/unlike.ts | 5 +- .../api/endpoints/gallery/posts/update.ts | 5 +- .../api/endpoints/get-online-users-count.ts | 3 +- .../src/server/api/endpoints/hashtags/list.ts | 3 +- .../server/api/endpoints/hashtags/search.ts | 3 +- .../src/server/api/endpoints/hashtags/show.ts | 3 +- .../server/api/endpoints/hashtags/trend.ts | 3 +- .../server/api/endpoints/hashtags/users.ts | 3 +- .../backend/src/server/api/endpoints/i.ts | 3 +- .../src/server/api/endpoints/i/2fa/done.ts | 3 +- .../server/api/endpoints/i/2fa/key-done.ts | 4 +- .../api/endpoints/i/2fa/password-less.ts | 3 +- .../api/endpoints/i/2fa/register-key.ts | 7 +- .../server/api/endpoints/i/2fa/register.ts | 2 +- .../server/api/endpoints/i/2fa/remove-key.ts | 8 +- .../server/api/endpoints/i/2fa/unregister.ts | 3 +- .../src/server/api/endpoints/i/apps.ts | 3 +- .../server/api/endpoints/i/authorized-apps.ts | 3 +- .../server/api/endpoints/i/change-password.ts | 3 +- .../server/api/endpoints/i/delete-account.ts | 7 +- .../src/server/api/endpoints/i/favorites.ts | 3 +- .../server/api/endpoints/i/gallery/likes.ts | 3 +- .../server/api/endpoints/i/gallery/posts.ts | 3 +- .../endpoints/i/get-word-muted-notes-count.ts | 3 +- .../server/api/endpoints/i/notifications.ts | 11 +- .../src/server/api/endpoints/i/page-likes.ts | 3 +- .../src/server/api/endpoints/i/pages.ts | 3 +- .../i/read-all-messaging-messages.ts | 5 +- .../api/endpoints/i/read-all-unread-notes.ts | 3 +- .../api/endpoints/i/read-announcement.ts | 7 +- .../api/endpoints/i/regenerate-token.ts | 7 +- .../api/endpoints/i/registry/get-all.ts | 3 +- .../api/endpoints/i/registry/get-detail.ts | 3 +- .../server/api/endpoints/i/registry/get.ts | 3 +- .../endpoints/i/registry/keys-with-type.ts | 3 +- .../server/api/endpoints/i/registry/keys.ts | 3 +- .../server/api/endpoints/i/registry/remove.ts | 3 +- .../server/api/endpoints/i/registry/scopes.ts | 3 +- .../server/api/endpoints/i/registry/set.ts | 4 +- .../server/api/endpoints/i/revoke-token.ts | 3 +- .../server/api/endpoints/i/signin-history.ts | 3 +- .../server/api/endpoints/i/update-email.ts | 4 +- .../src/server/api/endpoints/i/update.ts | 9 +- .../api/endpoints/i/user-group-invites.ts | 3 +- .../server/api/endpoints/i/webhooks/create.ts | 3 +- .../server/api/endpoints/i/webhooks/delete.ts | 3 +- .../server/api/endpoints/i/webhooks/list.ts | 3 +- .../server/api/endpoints/i/webhooks/show.ts | 3 +- .../server/api/endpoints/i/webhooks/update.ts | 4 +- .../server/api/endpoints/messaging/history.ts | 9 +- .../api/endpoints/messaging/messages.ts | 7 +- .../endpoints/messaging/messages/create.ts | 13 +- .../endpoints/messaging/messages/delete.ts | 4 +- .../api/endpoints/messaging/messages/read.ts | 3 +- .../backend/src/server/api/endpoints/meta.ts | 2 +- .../server/api/endpoints/miauth/gen-token.ts | 3 +- .../src/server/api/endpoints/mute/create.ts | 3 +- .../src/server/api/endpoints/mute/delete.ts | 3 +- .../src/server/api/endpoints/mute/list.ts | 3 +- .../src/server/api/endpoints/my/apps.ts | 3 +- .../backend/src/server/api/endpoints/notes.ts | 3 +- .../server/api/endpoints/notes/children.ts | 3 +- .../src/server/api/endpoints/notes/clips.ts | 5 +- .../api/endpoints/notes/conversation.ts | 3 +- .../src/server/api/endpoints/notes/create.ts | 9 +- .../src/server/api/endpoints/notes/delete.ts | 3 +- .../api/endpoints/notes/favorites/create.ts | 3 +- .../api/endpoints/notes/favorites/delete.ts | 3 +- .../server/api/endpoints/notes/featured.ts | 3 +- .../api/endpoints/notes/global-timeline.ts | 3 +- .../api/endpoints/notes/hybrid-timeline.ts | 7 +- .../api/endpoints/notes/local-timeline.ts | 5 +- .../server/api/endpoints/notes/mentions.ts | 7 +- .../endpoints/notes/polls/recommendation.ts | 11 +- .../server/api/endpoints/notes/polls/vote.ts | 11 +- .../server/api/endpoints/notes/reactions.ts | 3 +- .../src/server/api/endpoints/notes/renotes.ts | 3 +- .../src/server/api/endpoints/notes/replies.ts | 3 +- .../api/endpoints/notes/search-by-tag.ts | 3 +- .../src/server/api/endpoints/notes/search.ts | 2 +- .../src/server/api/endpoints/notes/show.ts | 3 +- .../src/server/api/endpoints/notes/state.ts | 7 +- .../endpoints/notes/thread-muting/create.ts | 7 +- .../endpoints/notes/thread-muting/delete.ts | 3 +- .../server/api/endpoints/notes/timeline.ts | 7 +- .../server/api/endpoints/notes/translate.ts | 4 +- .../server/api/endpoints/notes/unrenote.ts | 7 +- .../api/endpoints/notes/user-list-timeline.ts | 9 +- .../notifications/mark-all-as-read.ts | 3 +- .../src/server/api/endpoints/page-push.ts | 5 +- .../src/server/api/endpoints/pages/create.ts | 3 +- .../src/server/api/endpoints/pages/delete.ts | 3 +- .../server/api/endpoints/pages/featured.ts | 3 +- .../src/server/api/endpoints/pages/like.ts | 7 +- .../src/server/api/endpoints/pages/show.ts | 7 +- .../src/server/api/endpoints/pages/unlike.ts | 5 +- .../src/server/api/endpoints/pages/update.ts | 7 +- .../src/server/api/endpoints/pinned-users.ts | 3 +- .../src/server/api/endpoints/promo/read.ts | 3 +- .../api/endpoints/request-reset-password.ts | 4 +- .../server/api/endpoints/reset-password.ts | 8 +- .../backend/src/server/api/endpoints/stats.ts | 5 +- .../src/server/api/endpoints/sw/register.ts | 3 +- .../src/server/api/endpoints/sw/unregister.ts | 3 +- .../api/endpoints/username/available.ts | 7 +- .../backend/src/server/api/endpoints/users.ts | 3 +- .../src/server/api/endpoints/users/clips.ts | 3 +- .../server/api/endpoints/users/followers.ts | 9 +- .../server/api/endpoints/users/following.ts | 9 +- .../api/endpoints/users/gallery/posts.ts | 3 +- .../users/get-frequently-replied-users.ts | 5 +- .../api/endpoints/users/groups/create.ts | 5 +- .../api/endpoints/users/groups/delete.ts | 3 +- .../users/groups/invitations/accept.ts | 7 +- .../users/groups/invitations/reject.ts | 3 +- .../api/endpoints/users/groups/invite.ts | 7 +- .../api/endpoints/users/groups/joined.ts | 5 +- .../api/endpoints/users/groups/leave.ts | 5 +- .../api/endpoints/users/groups/owned.ts | 3 +- .../server/api/endpoints/users/groups/pull.ts | 6 +- .../server/api/endpoints/users/groups/show.ts | 5 +- .../api/endpoints/users/groups/transfer.ts | 5 +- .../api/endpoints/users/groups/update.ts | 3 +- .../api/endpoints/users/lists/create.ts | 3 +- .../api/endpoints/users/lists/delete.ts | 3 +- .../server/api/endpoints/users/lists/list.ts | 3 +- .../server/api/endpoints/users/lists/pull.ts | 5 +- .../server/api/endpoints/users/lists/push.ts | 9 +- .../server/api/endpoints/users/lists/show.ts | 3 +- .../api/endpoints/users/lists/update.ts | 3 +- .../src/server/api/endpoints/users/notes.ts | 3 +- .../server/api/endpoints/users/reactions.ts | 7 +- .../api/endpoints/users/recommendation.ts | 7 +- .../server/api/endpoints/users/relation.ts | 3 +- .../api/endpoints/users/report-abuse.ts | 7 +- .../users/search-by-username-and-host.ts | 7 +- .../src/server/api/endpoints/users/search.ts | 3 +- .../src/server/api/endpoints/users/show.ts | 3 +- .../src/server/api/endpoints/users/stats.ts | 19 +-- .../api/integration/DiscordServerService.ts | 6 +- .../api/integration/GithubServerService.ts | 4 +- .../api/integration/TwitterServerService.ts | 4 +- .../server/api/stream/channels/messaging.ts | 9 +- .../server/api/stream/channels/user-list.ts | 5 +- .../src/server/web/ClientServerService.ts | 16 +-- .../backend/src/server/web/FeedService.ts | 10 +- .../src/server/web/UrlPreviewService.ts | 2 +- .../src/services/AccountUpdateService.ts | 2 +- .../backend/src/services/AntennaService.ts | 16 +-- .../src/services/CreateNotificationService.ts | 11 +- .../src/services/CreateSystemUserService.ts | 2 +- .../src/services/CustomEmojiService.ts | 2 +- .../src/services/DeleteAccountService.ts | 3 +- packages/backend/src/services/DriveService.ts | 10 +- packages/backend/src/services/EmailService.ts | 2 +- .../src/services/FederatedInstanceService.ts | 3 +- .../services/FetchInstanceMetadataService.ts | 3 +- .../backend/src/services/HashtagService.ts | 4 +- .../src/services/InstanceActorService.ts | 4 +- .../backend/src/services/MessagingService.ts | 10 +- .../src/services/ModerationLogService.ts | 2 +- .../backend/src/services/NoteCreateService.ts | 6 +- .../backend/src/services/NoteDeleteService.ts | 2 +- .../backend/src/services/NotePiningService.ts | 6 +- .../backend/src/services/NoteReadService.ts | 17 ++- .../src/services/NotificationService.ts | 5 +- packages/backend/src/services/PollService.ts | 14 +- .../src/services/ProxyAccountService.ts | 3 +- .../src/services/PushNotificationService.ts | 2 +- packages/backend/src/services/QueryService.ts | 16 +-- .../backend/src/services/ReactionService.ts | 12 +- packages/backend/src/services/RelayService.ts | 5 +- .../backend/src/services/SignupService.ts | 4 +- .../TwoFactorAuthenticationService.ts | 2 +- .../src/services/UserBlockingService.ts | 15 ++- .../backend/src/services/UserCacheService.ts | 2 +- .../src/services/UserFollowingService.ts | 17 +-- .../src/services/UserKeypairStoreService.ts | 3 +- .../backend/src/services/UserListService.ts | 7 +- .../backend/src/services/UserMutingService.ts | 7 +- .../src/services/UserSuspendService.ts | 6 +- .../backend/src/services/WebhookService.ts | 2 +- .../src/services/chart/charts/drive.ts | 2 +- .../src/services/chart/charts/notes.ts | 2 +- .../chart/charts/per-user-following.ts | 2 +- .../src/services/chart/charts/test-grouped.ts | 2 +- .../chart/charts/test-intersection.ts | 2 +- .../src/services/chart/charts/test-unique.ts | 2 +- .../backend/src/services/chart/charts/test.ts | 2 +- .../src/services/chart/charts/users.ts | 2 +- .../entities/AbuseUserReportEntityService.ts | 2 +- .../services/entities/AntennaEntityService.ts | 6 +- .../src/services/entities/AppEntityService.ts | 4 +- .../entities/AuthSessionEntityService.ts | 2 +- .../entities/BlockingEntityService.ts | 2 +- .../services/entities/ChannelEntityService.ts | 8 +- .../services/entities/ClipEntityService.ts | 2 +- .../entities/DriveFileEntityService.ts | 6 +- .../entities/DriveFolderEntityService.ts | 4 +- .../services/entities/EmojiEntityService.ts | 2 +- .../entities/FollowRequestEntityService.ts | 2 +- .../entities/FollowingEntityService.ts | 2 +- .../entities/GalleryLikeEntityService.ts | 2 +- .../entities/GalleryPostEntityService.ts | 4 +- .../services/entities/HashtagEntityService.ts | 2 +- .../entities/InstanceEntityService.ts | 2 +- .../entities/MessagingMessageEntityService.ts | 2 +- .../entities/ModerationLogEntityService.ts | 2 +- .../services/entities/MutingEntityService.ts | 2 +- .../services/entities/NoteEntityService.ts | 18 +-- .../entities/NoteFavoriteEntityService.ts | 2 +- .../entities/NoteReactionEntityService.ts | 2 +- .../entities/NotificationEntityService.ts | 8 +- .../services/entities/PageEntityService.ts | 8 +- .../entities/PageLikeEntityService.ts | 2 +- .../services/entities/SigninEntityService.ts | 2 +- .../services/entities/UserEntityService.ts | 40 +++--- .../entities/UserGroupEntityService.ts | 4 +- .../UserGroupInvitationEntityService.ts | 2 +- .../entities/UserListEntityService.ts | 4 +- .../src/services/remote/ResolveUserService.ts | 2 +- .../remote/activitypub/ApDbResolverService.ts | 6 +- .../activitypub/ApDeliverManagerService.ts | 6 +- .../remote/activitypub/ApInboxService.ts | 14 +- .../remote/activitypub/ApRendererService.ts | 18 +-- .../remote/activitypub/ApResolverService.ts | 10 +- .../activitypub/models/ApImageService.ts | 2 +- .../activitypub/models/ApMentionService.ts | 2 +- .../activitypub/models/ApNoteService.ts | 8 +- .../activitypub/models/ApPersonService.ts | 17 ++- .../activitypub/models/ApQuestionService.ts | 8 +- 380 files changed, 1161 insertions(+), 856 deletions(-) diff --git a/packages/backend/src/RepositoryModule.ts b/packages/backend/src/RepositoryModule.ts index 5664783b26ef..8981c2a54af8 100644 --- a/packages/backend/src/RepositoryModule.ts +++ b/packages/backend/src/RepositoryModule.ts @@ -1,319 +1,320 @@ import { Module } from '@nestjs/common'; import { AbuseUserReports, AccessTokens, Ads, AnnouncementReads, Announcements, AntennaNotes, Antennas, Apps, AttestationChallenges, AuthSessions, Blockings, ChannelFollowings, ChannelNotePinings, Channels, ClipNotes, Clips, DriveFiles, DriveFolders, Emojis, Followings, FollowRequests, GalleryLikes, GalleryPosts, Hashtags, Instances, MessagingMessages, Metas, ModerationLogs, MutedNotes, Mutings, NoteFavorites, NoteReactions, Notes, NoteThreadMutings, NoteUnreads, Notifications, PageLikes, Pages, PasswordResetRequests, Polls, PollVotes, PromoNotes, PromoReads, RegistrationTickets, RegistryItems, Relays, Signins, SwSubscriptions, UsedUsernames, UserGroupInvitations, UserGroupJoinings, UserGroups, UserIps, UserKeypairs, UserListJoinings, UserLists, UserNotePinings, UserPendings, UserProfiles, UserPublickeys, Users, UserSecurityKeys, Webhooks } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; import type { Provider } from '@nestjs/common'; const $usersRepository: Provider = { - provide: 'usersRepository', + provide: DI.usersRepository, useValue: Users, }; const $notesRepository: Provider = { - provide: 'notesRepository', + provide: DI.notesRepository, useValue: Notes, }; const $announcementsRepository: Provider = { - provide: 'announcementsRepository', + provide: DI.announcementsRepository, useValue: Announcements, }; const $announcementReadsRepository: Provider = { - provide: 'announcementReadsRepository', + provide: DI.announcementReadsRepository, useValue: AnnouncementReads, }; const $appsRepository: Provider = { - provide: 'appsRepository', + provide: DI.appsRepository, useValue: Apps, }; const $noteFavoritesRepository: Provider = { - provide: 'noteFavoritesRepository', + provide: DI.noteFavoritesRepository, useValue: NoteFavorites, }; const $noteThreadMutingsRepository: Provider = { - provide: 'noteThreadMutingsRepository', + provide: DI.noteThreadMutingsRepository, useValue: NoteThreadMutings, }; const $noteReactionsRepository: Provider = { - provide: 'noteReactionsRepository', + provide: DI.noteReactionsRepository, useValue: NoteReactions, }; const $noteUnreadsRepository: Provider = { - provide: 'noteUnreadsRepository', + provide: DI.noteUnreadsRepository, useValue: NoteUnreads, }; const $pollsRepository: Provider = { - provide: 'pollsRepository', + provide: DI.pollsRepository, useValue: Polls, }; const $pollVotesRepository: Provider = { - provide: 'pollVotesRepository', + provide: DI.pollVotesRepository, useValue: PollVotes, }; const $userProfilesRepository: Provider = { - provide: 'userProfilesRepository', + provide: DI.userProfilesRepository, useValue: UserProfiles, }; const $userKeypairsRepository: Provider = { - provide: 'userKeypairsRepository', + provide: DI.userKeypairsRepository, useValue: UserKeypairs, }; const $userPendingsRepository: Provider = { - provide: 'userPendingsRepository', + provide: DI.userPendingsRepository, useValue: UserPendings, }; const $attestationChallengesRepository: Provider = { - provide: 'attestationChallengesRepository', + provide: DI.attestationChallengesRepository, useValue: AttestationChallenges, }; const $userSecurityKeysRepository: Provider = { - provide: 'userSecurityKeysRepository', + provide: DI.userSecurityKeysRepository, useValue: UserSecurityKeys, }; const $userPublickeysRepository: Provider = { - provide: 'userPublickeysRepository', + provide: DI.userPublickeysRepository, useValue: UserPublickeys, }; const $userListsRepository: Provider = { - provide: 'userListsRepository', + provide: DI.userListsRepository, useValue: UserLists, }; const $userListJoiningsRepository: Provider = { - provide: 'userListJoiningsRepository', + provide: DI.userListJoiningsRepository, useValue: UserListJoinings, }; const $userGroupsRepository: Provider = { - provide: 'userGroupsRepository', + provide: DI.userGroupsRepository, useValue: UserGroups, }; const $userGroupJoiningsRepository: Provider = { - provide: 'userGroupJoiningsRepository', + provide: DI.userGroupJoiningsRepository, useValue: UserGroupJoinings, }; const $userGroupInvitationsRepository: Provider = { - provide: 'userGroupInvitationsRepository', + provide: DI.userGroupInvitationsRepository, useValue: UserGroupInvitations, }; const $userNotePiningsRepository: Provider = { - provide: 'userNotePiningsRepository', + provide: DI.userNotePiningsRepository, useValue: UserNotePinings, }; const $userIpsRepository: Provider = { - provide: 'userIpsRepository', + provide: DI.userIpsRepository, useValue: UserIps, }; const $usedUsernamesRepository: Provider = { - provide: 'usedUsernamesRepository', + provide: DI.usedUsernamesRepository, useValue: UsedUsernames, }; const $followingsRepository: Provider = { - provide: 'followingsRepository', + provide: DI.followingsRepository, useValue: Followings, }; const $followRequestsRepository: Provider = { - provide: 'followRequestsRepository', + provide: DI.followRequestsRepository, useValue: FollowRequests, }; const $instancesRepository: Provider = { - provide: 'instancesRepository', + provide: DI.instancesRepository, useValue: Instances, }; const $emojisRepository: Provider = { - provide: 'emojisRepository', + provide: DI.emojisRepository, useValue: Emojis, }; const $driveFilesRepository: Provider = { - provide: 'driveFilesRepository', + provide: DI.driveFilesRepository, useValue: DriveFiles, }; const $driveFoldersRepository: Provider = { - provide: 'driveFoldersRepository', + provide: DI.driveFoldersRepository, useValue: DriveFolders, }; const $notificationsRepository: Provider = { - provide: 'notificationsRepository', + provide: DI.notificationsRepository, useValue: Notifications, }; const $metasRepository: Provider = { - provide: 'metasRepository', + provide: DI.metasRepository, useValue: Metas, }; const $mutingsRepository: Provider = { - provide: 'mutingsRepository', + provide: DI.mutingsRepository, useValue: Mutings, }; const $blockingsRepository: Provider = { - provide: 'blockingsRepository', + provide: DI.blockingsRepository, useValue: Blockings, }; const $swSubscriptionsRepository: Provider = { - provide: 'swSubscriptionsRepository', + provide: DI.swSubscriptionsRepository, useValue: SwSubscriptions, }; const $hashtagsRepository: Provider = { - provide: 'hashtagsRepository', + provide: DI.hashtagsRepository, useValue: Hashtags, }; const $abuseUserReportsRepository: Provider = { - provide: 'abuseUserReportsRepository', + provide: DI.abuseUserReportsRepository, useValue: AbuseUserReports, }; const $registrationTicketsRepository: Provider = { - provide: 'registrationTicketsRepository', + provide: DI.registrationTicketsRepository, useValue: RegistrationTickets, }; const $authSessionsRepository: Provider = { - provide: 'authSessionsRepository', + provide: DI.authSessionsRepository, useValue: AuthSessions, }; const $accessTokensRepository: Provider = { - provide: 'accessTokensRepository', + provide: DI.accessTokensRepository, useValue: AccessTokens, }; const $signinsRepository: Provider = { - provide: 'signinsRepository', + provide: DI.signinsRepository, useValue: Signins, }; const $messagingMessagesRepository: Provider = { - provide: 'messagingMessagesRepository', + provide: DI.messagingMessagesRepository, useValue: MessagingMessages, }; const $pagesRepository: Provider = { - provide: 'pagesRepository', + provide: DI.pagesRepository, useValue: Pages, }; const $pageLikesRepository: Provider = { - provide: 'pageLikesRepository', + provide: DI.pageLikesRepository, useValue: PageLikes, }; const $galleryPostsRepository: Provider = { - provide: 'galleryPostsRepository', + provide: DI.galleryPostsRepository, useValue: GalleryPosts, }; const $galleryLikesRepository: Provider = { - provide: 'galleryLikesRepository', + provide: DI.galleryLikesRepository, useValue: GalleryLikes, }; const $moderationLogsRepository: Provider = { - provide: 'moderationLogsRepository', + provide: DI.moderationLogsRepository, useValue: ModerationLogs, }; const $clipsRepository: Provider = { - provide: 'clipsRepository', + provide: DI.clipsRepository, useValue: Clips, }; const $clipNotesRepository: Provider = { - provide: 'clipNotesRepository', + provide: DI.clipNotesRepository, useValue: ClipNotes, }; const $antennasRepository: Provider = { - provide: 'antennasRepository', + provide: DI.antennasRepository, useValue: Antennas, }; const $antennaNotesRepository: Provider = { - provide: 'antennaNotesRepository', + provide: DI.antennaNotesRepository, useValue: AntennaNotes, }; const $promoNotesRepository: Provider = { - provide: 'promoNotesRepository', + provide: DI.promoNotesRepository, useValue: PromoNotes, }; const $promoReadsRepository: Provider = { - provide: 'promoReadsRepository', + provide: DI.promoReadsRepository, useValue: PromoReads, }; const $relaysRepository: Provider = { - provide: 'relaysRepository', + provide: DI.relaysRepository, useValue: Relays, }; const $mutedNotesRepository: Provider = { - provide: 'mutedNotesRepository', + provide: DI.mutedNotesRepository, useValue: MutedNotes, }; const $channelsRepository: Provider = { - provide: 'channelsRepository', + provide: DI.channelsRepository, useValue: Channels, }; const $channelFollowingsRepository: Provider = { - provide: 'channelFollowingsRepository', + provide: DI.channelFollowingsRepository, useValue: ChannelFollowings, }; const $channelNotePiningsRepository: Provider = { - provide: 'channelNotePiningsRepository', + provide: DI.channelNotePiningsRepository, useValue: ChannelNotePinings, }; const $registryItemsRepository: Provider = { - provide: 'registryItemsRepository', + provide: DI.registryItemsRepository, useValue: RegistryItems, }; const $webhooksRepository: Provider = { - provide: 'webhooksRepository', + provide: DI.webhooksRepository, useValue: Webhooks, }; const $adsRepository: Provider = { - provide: 'adsRepository', + provide: DI.adsRepository, useValue: Ads, }; const $passwordResetRequestsRepository: Provider = { - provide: 'passwordResetRequestsRepository', + provide: DI.passwordResetRequestsRepository, useValue: PasswordResetRequests, }; diff --git a/packages/backend/src/daemons/JanitorService.ts b/packages/backend/src/daemons/JanitorService.ts index 33e69e15b311..72045269cd39 100644 --- a/packages/backend/src/daemons/JanitorService.ts +++ b/packages/backend/src/daemons/JanitorService.ts @@ -11,7 +11,7 @@ export class JanitorService implements OnApplicationShutdown { #intervalId: NodeJS.Timer; constructor( - @Inject('attestationChallengesRepository') + @Inject(DI.attestationChallengesRepository) private attestationChallengesRepository: typeof AttestationChallenges, ) { } diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts index f451cc4b778d..cc775a9c8ae1 100644 --- a/packages/backend/src/di-symbols.ts +++ b/packages/backend/src/di-symbols.ts @@ -3,4 +3,70 @@ export const DI = { db: Symbol('db'), redis: Symbol('redis'), redisSubscriber: Symbol('redisSubscriber'), + + //#region Repositories + usersRepository: Symbol('usersRepository'), + notesRepository: Symbol('notesRepository'), + announcementsRepository: Symbol('announcementsRepository'), + announcementReadsRepository: Symbol('announcementReadsRepository'), + appsRepository: Symbol('appsRepository'), + noteFavoritesRepository: Symbol('noteFavoritesRepository'), + noteThreadMutingsRepository: Symbol('noteThreadMutingsRepository'), + noteReactionsRepository: Symbol('noteReactionsRepository'), + noteUnreadsRepository: Symbol('noteUnreadsRepository'), + pollsRepository: Symbol('pollsRepository'), + pollVotesRepository: Symbol('pollVotesRepository'), + userProfilesRepository: Symbol('userProfilesRepository'), + userKeypairsRepository: Symbol('userKeypairsRepository'), + userPendingsRepository: Symbol('userPendingsRepository'), + attestationChallengesRepository: Symbol('attestationChallengesRepository'), + userSecurityKeysRepository: Symbol('userSecurityKeysRepository'), + userPublickeysRepository: Symbol('userPublickeysRepository'), + userListsRepository: Symbol('userListsRepository'), + userListJoiningsRepository: Symbol('userListJoiningsRepository'), + userGroupsRepository: Symbol('userGroupsRepository'), + userGroupJoiningsRepository: Symbol('userGroupJoiningsRepository'), + userGroupInvitationsRepository: Symbol('userGroupInvitationsRepository'), + userNotePiningsRepository: Symbol('userNotePiningsRepository'), + userIpsRepository: Symbol('userIpsRepository'), + usedUsernamesRepository: Symbol('usedUsernamesRepository'), + followingsRepository: Symbol('followingsRepository'), + followRequestsRepository: Symbol('followRequestsRepository'), + instancesRepository: Symbol('instancesRepository'), + emojisRepository: Symbol('emojisRepository'), + driveFilesRepository: Symbol('driveFilesRepository'), + driveFoldersRepository: Symbol('driveFoldersRepository'), + notificationsRepository: Symbol('notificationsRepository'), + metasRepository: Symbol('metasRepository'), + mutingsRepository: Symbol('mutingsRepository'), + blockingsRepository: Symbol('blockingsRepository'), + swSubscriptionsRepository: Symbol('swSubscriptionsRepository'), + hashtagsRepository: Symbol('hashtagsRepository'), + abuseUserReportsRepository: Symbol('abuseUserReportsRepository'), + registrationTicketsRepository: Symbol('registrationTicketsRepository'), + authSessionsRepository: Symbol('authSessionsRepository'), + accessTokensRepository: Symbol('accessTokensRepository'), + signinsRepository: Symbol('signinsRepository'), + messagingMessagesRepository: Symbol('messagingMessagesRepository'), + pagesRepository: Symbol('pagesRepository'), + pageLikesRepository: Symbol('pageLikesRepository'), + galleryPostsRepository: Symbol('galleryPostsRepository'), + galleryLikesRepository: Symbol('galleryLikesRepository'), + moderationLogsRepository: Symbol('moderationLogsRepository'), + clipsRepository: Symbol('clipsRepository'), + clipNotesRepository: Symbol('clipNotesRepository'), + antennasRepository: Symbol('antennasRepository'), + antennaNotesRepository: Symbol('antennaNotesRepository'), + promoNotesRepository: Symbol('promoNotesRepository'), + promoReadsRepository: Symbol('promoReadsRepository'), + relaysRepository: Symbol('relaysRepository'), + mutedNotesRepository: Symbol('mutedNotesRepository'), + channelsRepository: Symbol('channelsRepository'), + channelFollowingsRepository: Symbol('channelFollowingsRepository'), + channelNotePiningsRepository: Symbol('channelNotePiningsRepository'), + registryItemsRepository: Symbol('registryItemsRepository'), + webhooksRepository: Symbol('webhooksRepository'), + adsRepository: Symbol('adsRepository'), + passwordResetRequestsRepository: Symbol('passwordResetRequestsRepository'), + //#endregion }; diff --git a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts index 978e51449a43..8ac8be4b3fc6 100644 --- a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts +++ b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts @@ -16,7 +16,7 @@ export class CheckExpiredMutingsProcessorService { @Inject(DI.config) private config: Config, - @Inject('mutingsRepository') + @Inject(DI.mutingsRepository) private mutingsRepository: typeof Mutings, private globalEventService: GlobalEventService, diff --git a/packages/backend/src/queue/processors/CleanProcessorService.ts b/packages/backend/src/queue/processors/CleanProcessorService.ts index acbff6c808b4..59fdbd7a5888 100644 --- a/packages/backend/src/queue/processors/CleanProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanProcessorService.ts @@ -15,7 +15,7 @@ export class CleanProcessorService { @Inject(DI.config) private config: Config, - @Inject('userIpsRepository') + @Inject(DI.userIpsRepository) private userIpsRepository: typeof UserIps, private queueLoggerService: QueueLoggerService, diff --git a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts index 59557a20a978..9bb924b12b1c 100644 --- a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts @@ -16,7 +16,7 @@ export class CleanRemoteFilesProcessorService { @Inject(DI.config) private config: Config, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private driveService: DriveService, diff --git a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts index 007c99db5b03..96aefe26ab4a 100644 --- a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts @@ -20,16 +20,16 @@ export class DeleteAccountProcessorService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private driveService: DriveService, diff --git a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts index d61e13e1baa6..c9b827129eae 100644 --- a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts @@ -17,10 +17,10 @@ export class DeleteDriveFilesProcessorService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private driveService: DriveService, diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts index 84eca8b05483..f6a274d3725f 100644 --- a/packages/backend/src/queue/processors/DeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { DriveFiles , Instances } from '@/models/index.js'; +import type { DriveFiles, Instances } from '@/models/index.js'; import { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { MetaService } from '@/services/MetaService.js'; @@ -29,10 +29,10 @@ export class DeliverProcessorService { @Inject(DI.config) private config: Config, - @Inject('instancesRepository') + @Inject(DI.instancesRepository) private instancesRepository: typeof Instances, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private metaService: MetaService, diff --git a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts index 2cc772564a4b..13158bdd933c 100644 --- a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts +++ b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { PollVotes , Notes } from '@/models/index.js'; +import type { PollVotes, Notes } from '@/models/index.js'; import { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { CreateNotificationService } from '@/services/CreateNotificationService.js'; @@ -17,10 +17,10 @@ export class EndedPollNotificationProcessorService { @Inject(DI.config) private config: Config, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('pollVotesRepository') + @Inject(DI.pollVotesRepository) private pollVotesRepository: typeof PollVotes, private createNotificationService: CreateNotificationService, diff --git a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts index 73b1eb2c9da6..2b67411d81a9 100644 --- a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts @@ -3,7 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI } from '@/di-symbols.js'; -import type { DriveFiles, UserProfiles , Notes , Users , Blockings } from '@/models/index.js'; +import type { DriveFiles, UserProfiles, Notes, Users, Blockings } from '@/models/index.js'; import { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/services/DriveService.js'; @@ -21,10 +21,10 @@ export class ExportBlockingProcessorService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('blockingsRepository') + @Inject(DI.blockingsRepository) private blockingsRepository: typeof Blockings, private utilityService: UtilityService, diff --git a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts index af82fff74ea5..ace2b9037a66 100644 --- a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts @@ -23,10 +23,10 @@ export class ExportCustomEmojisProcessorService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('emojisRepository') + @Inject(DI.emojisRepository) private emojisRepository: typeof Emojis, private driveService: DriveService, diff --git a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts index 6405215af50b..3de782ea810b 100644 --- a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts @@ -23,13 +23,13 @@ export class ExportFollowingProcessorService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, - @Inject('mutingsRepository') + @Inject(DI.mutingsRepository) private mutingsRepository: typeof Mutings, private utilityService: UtilityService, diff --git a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts index 7e96c190b0b6..14f61edcd420 100644 --- a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts @@ -3,7 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI } from '@/di-symbols.js'; -import type { Mutings, Users , Blockings } from '@/models/index.js'; +import type { Mutings, Users, Blockings } from '@/models/index.js'; import { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/services/DriveService.js'; @@ -21,13 +21,13 @@ export class ExportMutingProcessorService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('blockingsRepository') + @Inject(DI.blockingsRepository) private blockingsRepository: typeof Blockings, - @Inject('mutingsRepository') + @Inject(DI.mutingsRepository) private mutingsRepository: typeof Mutings, private utilityService: UtilityService, diff --git a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts index 847270524929..87cc802e6383 100644 --- a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts @@ -3,7 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI } from '@/di-symbols.js'; -import type { Notes, Polls , Users } from '@/models/index.js'; +import type { Notes, Polls, Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/services/DriveService.js'; @@ -22,13 +22,13 @@ export class ExportNotesProcessorService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('pollsRepository') + @Inject(DI.pollsRepository) private pollsRepository: typeof Polls, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private driveService: DriveService, diff --git a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts index 82c58b703ab0..41ee806ae1a9 100644 --- a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts @@ -21,13 +21,13 @@ export class ExportUserListsProcessorService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userListsRepository') + @Inject(DI.userListsRepository) private userListsRepository: typeof UserLists, - @Inject('userListJoiningsRepository') + @Inject(DI.userListJoiningsRepository) private userListJoiningsRepository: typeof UserListJoinings, private utilityService: UtilityService, diff --git a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts index 3e51afc75abd..a936c4909f50 100644 --- a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import { Users } from '@/models/index.js'; -import type { Blockings , DriveFiles } from '@/models/index.js'; +import type { Blockings, DriveFiles } from '@/models/index.js'; import { Config } from '@/config.js'; import type Logger from '@/logger.js'; import * as Acct from '@/misc/acct.js'; @@ -22,13 +22,13 @@ export class ImportBlockingProcessorService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('blockingsRepository') + @Inject(DI.blockingsRepository) private blockingsRepository: typeof Blockings, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private utilityService: UtilityService, diff --git a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts index f11154ae1171..0ebeedfac944 100644 --- a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts @@ -1,9 +1,9 @@ import * as fs from 'node:fs'; import { Inject, Injectable } from '@nestjs/common'; -import { IsNull, MoreThan , DataSource } from 'typeorm'; +import { IsNull, MoreThan, DataSource } from 'typeorm'; import unzipper from 'unzipper'; import { DI } from '@/di-symbols.js'; -import type { Emojis , DriveFiles , Users } from '@/models/index.js'; +import type { Emojis, DriveFiles, Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { CustomEmojiService } from '@/services/CustomEmojiService.js'; @@ -26,13 +26,13 @@ export class ImportCustomEmojisProcessorService { @Inject(DI.db) private db: DataSource, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, - @Inject('emojisRepository') + @Inject(DI.emojisRepository) private emojisRepository: typeof Emojis, private customEmojiService: CustomEmojiService, diff --git a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts index fb0b44eb2e92..e302d401f8af 100644 --- a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts @@ -22,10 +22,10 @@ export class ImportFollowingProcessorService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private utilityService: UtilityService, diff --git a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts index b2478cc0d887..04e48e0b7399 100644 --- a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts @@ -23,10 +23,10 @@ export class ImportMutingProcessorService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private utilityService: UtilityService, diff --git a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts index c004cbae4884..2ca4887eb020 100644 --- a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { DriveFiles , UserListJoinings , UserLists } from '@/models/index.js'; +import type { DriveFiles, UserListJoinings, UserLists } from '@/models/index.js'; import { Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type Logger from '@/logger.js'; @@ -23,16 +23,16 @@ export class ImportUserListsProcessorService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, - @Inject('userListsRepository') + @Inject(DI.userListsRepository) private userListsRepository: typeof UserLists, - @Inject('userListJoiningsRepository') + @Inject(DI.userListJoiningsRepository) private userListJoiningsRepository: typeof UserListJoinings, private utilityService: UtilityService, diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index a303d045ae21..68dcf6d77c61 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -38,10 +38,10 @@ export class InboxProcessorService { @Inject(DI.config) private config: Config, - @Inject('instancesRepository') + @Inject(DI.instancesRepository) private instancesRepository: typeof Instances, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private utilityService: UtilityService, diff --git a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts index ae0177cf8c92..0a09fc8f298e 100644 --- a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts @@ -18,7 +18,7 @@ export class WebhookDeliverProcessorService { @Inject(DI.config) private config: Config, - @Inject('webhooksRepository') + @Inject(DI.webhooksRepository) private webhooksRepository: typeof Webhooks, private httpRequestService: HttpRequestService, diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 5d0be7a2b1c1..5780ea902786 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -4,8 +4,8 @@ import json from 'koa-json-body'; import httpSignature from '@peertube/http-signature'; import { Brackets, In, IsNull, LessThan, Not } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { Followings , Notes } from '@/models/index.js'; -import type { Emojis, NoteReactions , UserProfiles , UserNotePinings , Users } from '@/models/index.js'; +import { Followings, Notes } from '@/models/index.js'; +import type { Emojis, NoteReactions, UserProfiles, UserNotePinings, Users } from '@/models/index.js'; import * as url from '@/prelude/url.js'; import { Config } from '@/config.js'; import { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; @@ -29,25 +29,25 @@ export class ActivityPubServerService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('noteReactionsRepository') + @Inject(DI.noteReactionsRepository) private noteReactionsRepository: typeof NoteReactions, - @Inject('emojisRepository') + @Inject(DI.emojisRepository) private emojisRepository: typeof Emojis, - @Inject('userNotePiningsRepository') + @Inject(DI.userNotePiningsRepository) private userNotePiningsRepository: typeof UserNotePinings, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, private utilityService: UtilityService, diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index 74b2ee8d615c..70010ae18b0b 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -40,7 +40,7 @@ export class FileServerService { @Inject(DI.config) private config: Config, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private fileInfoService: FileInfoService, diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index 6681873e25da..6150f30c6691 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import Router from '@koa/router'; import { IsNull, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { Notes , Users } from '@/models/index.js'; +import type { Notes, Users } from '@/models/index.js'; import { Config } from '@/config.js'; import { MetaService } from '@/services/MetaService.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; @@ -18,10 +18,10 @@ export class NodeinfoServerService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 63fac8d0408e..938d6fc46c5c 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -10,7 +10,7 @@ import * as slow from 'koa-slow'; import { IsNull } from 'typeorm'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { Config } from '@/config.js'; -import type { UserProfiles , Users } from '@/models/index.js'; +import type { UserProfiles, Users } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import Logger from '@/logger.js'; import { envOption } from '@/env.js'; @@ -35,10 +35,10 @@ export class ServerService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/WellKnownServerService.ts b/packages/backend/src/server/WellKnownServerService.ts index f75e22c1af8f..3392fd213dc4 100644 --- a/packages/backend/src/server/WellKnownServerService.ts +++ b/packages/backend/src/server/WellKnownServerService.ts @@ -16,7 +16,7 @@ export class WellKnownServerService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private nodeinfoServerService: NodeinfoServerService, diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index f25e89af3bed..02107da56ccc 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -28,7 +28,7 @@ export class ApiCallService implements OnApplicationShutdown { #userIpHistoriesClearIntervalId: NodeJS.Timer; constructor( - @Inject('userIpsRepository') + @Inject(DI.userIpsRepository) private userIpsRepository: typeof UserIps, private metaService: MetaService, diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts index 7e9ea5920dd0..63d9f3440bed 100644 --- a/packages/backend/src/server/api/ApiServerService.ts +++ b/packages/backend/src/server/api/ApiServerService.ts @@ -26,7 +26,7 @@ export class ApiServerService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/AuthenticateService.ts b/packages/backend/src/server/api/AuthenticateService.ts index dd891f467e61..c44e80e68c3d 100644 --- a/packages/backend/src/server/api/AuthenticateService.ts +++ b/packages/backend/src/server/api/AuthenticateService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { AccessTokens, Apps , Users } from '@/models/index.js'; +import type { AccessTokens, Apps, Users } from '@/models/index.js'; import type { CacheableLocalUser, ILocalUser } from '@/models/entities/User.js'; import type { AccessToken } from '@/models/entities/AccessToken.js'; import { Cache } from '@/misc/cache.js'; @@ -20,13 +20,13 @@ export class AuthenticateService { #appCache: Cache; constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('accessTokensRepository') + @Inject(DI.accessTokensRepository) private accessTokensRepository: typeof AccessTokens, - @Inject('appsRepository') + @Inject(DI.appsRepository) private appsRepository: typeof Apps, private userCacheService: UserCacheService, diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index dcc9be1b6f24..f99a4726b6c0 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -21,19 +21,19 @@ export class SigninApiService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userSecurityKeysRepository') + @Inject(DI.userSecurityKeysRepository) private userSecurityKeysRepository: typeof UserSecurityKeys, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, - @Inject('attestationChallengesRepository') + @Inject(DI.attestationChallengesRepository) private attestationChallengesRepository: typeof AttestationChallenges, - @Inject('signinsRepository') + @Inject(DI.signinsRepository) private signinsRepository: typeof Signins, private idService: IdService, diff --git a/packages/backend/src/server/api/SigninService.ts b/packages/backend/src/server/api/SigninService.ts index f646364fb1d4..bedd8310976d 100644 --- a/packages/backend/src/server/api/SigninService.ts +++ b/packages/backend/src/server/api/SigninService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { Signins , Users } from '@/models/index.js'; +import type { Signins, Users } from '@/models/index.js'; import { Config } from '@/config.js'; import { IdService } from '@/services/IdService.js'; import type { ILocalUser } from '@/models/entities/User.js'; @@ -14,7 +14,7 @@ export class SigninService { @Inject(DI.config) private config: Config, - @Inject('signinsRepository') + @Inject(DI.signinsRepository) private signinsRepository: typeof Signins, private signinEntityService: SigninEntityService, diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts index 02130dfd179c..7164fe1cf069 100644 --- a/packages/backend/src/server/api/SignupApiService.ts +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import rndstr from 'rndstr'; import bcrypt from 'bcryptjs'; import { DI } from '@/di-symbols.js'; -import type { RegistrationTickets , UserPendings, UserProfiles , Users } from '@/models/index.js'; +import type { RegistrationTickets, UserPendings, UserProfiles, Users } from '@/models/index.js'; import { Config } from '@/config.js'; import { MetaService } from '@/services/MetaService.js'; import { CaptchaService } from '@/services/CaptchaService.js'; @@ -19,16 +19,16 @@ export class SignupApiService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, - @Inject('userPendingsRepository') + @Inject(DI.userPendingsRepository) private userPendingsRepository: typeof UserPendings, - @Inject('registrationTicketsRepository') + @Inject(DI.registrationTicketsRepository) private registrationTicketsRepository: typeof RegistrationTickets, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts index 957f9c78b051..672e344a11e5 100644 --- a/packages/backend/src/server/api/StreamingApiServerService.ts +++ b/packages/backend/src/server/api/StreamingApiServerService.ts @@ -24,19 +24,19 @@ export class StreamingApiServerService { @Inject(DI.redisSubscriber) private redisSubscriber: Redis.Redis, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, - @Inject('mutingsRepository') + @Inject(DI.mutingsRepository) private mutingsRepository: typeof Mutings, - @Inject('blockingsRepository') + @Inject(DI.blockingsRepository) private blockingsRepository: typeof Blockings, - @Inject('channelFollowingsRepository') + @Inject(DI.channelFollowingsRepository) private channelFollowingsRepository: typeof ChannelFollowings, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, private globalEventService: GlobalEventService, diff --git a/packages/backend/src/server/api/common/GetterService.ts b/packages/backend/src/server/api/common/GetterService.ts index 76931aa299bd..e5b837d2faef 100644 --- a/packages/backend/src/server/api/common/GetterService.ts +++ b/packages/backend/src/server/api/common/GetterService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { Notes , Users } from '@/models/index.js'; +import type { Notes, Users } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import type { User } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; @@ -8,10 +8,10 @@ import type { Note } from '@/models/entities/Note.js'; @Injectable() export class GetterService { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, ) { } diff --git a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts index 6dccc073fff2..7963b1ba734c 100644 --- a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts +++ b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { AbuseUserReports } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -89,7 +90,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('abuseUserReportsRepository') + @Inject(DI.abuseUserReportsRepository) private abuseUserReportsRepository: typeof AbuseUserReports, private queryService: QueryService, diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts index 106dcaf71bab..4067ce853b01 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts @@ -5,6 +5,7 @@ import type { Users } from '@/models/index.js'; import { SignupService } from '@/services/SignupService.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { localUsernameSchema, passwordSchema } from '@/models/entities/User.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -35,7 +36,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts index ec52123ee9c6..2d2ffcab52c9 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts @@ -4,6 +4,7 @@ import type { Users } from '@/models/index.js'; import { QueueService } from '@/services/QueueService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { UserSuspendService } from '@/services/UserSuspendService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -24,7 +25,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private queueService: QueueService, diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts index 1c8d6b2b8454..32026a72a5d6 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Ads } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -28,7 +29,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('adsRepository') + @Inject(DI.adsRepository) private adsRepository: typeof Ads, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts index 76e1b45cb612..5d93e97416f6 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Ads } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -30,7 +31,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('adsRepository') + @Inject(DI.adsRepository) private adsRepository: typeof Ads, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/admin/ad/update.ts b/packages/backend/src/server/api/endpoints/admin/ad/update.ts index 7d15a1e3a0f6..54b96e566ae2 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/update.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Ads } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -37,7 +38,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private adsRepository: typeof Ads, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts index ad17c6c19d14..85795d7b6e50 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Announcements } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -59,7 +60,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('announcementsRepository') + @Inject(DI.announcementsRepository) private announcementsRepository: typeof Announcements, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts b/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts index 2e045a693192..5536b3a5db16 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Announcements } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -30,7 +31,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('announcementsRepository') + @Inject(DI.announcementsRepository) private announcementsRepository: typeof Announcements, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts index 88123c159ed8..47539a17bfa8 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { Announcements , AnnouncementReads } from '@/models/index.js'; +import type { Announcements, AnnouncementReads } from '@/models/index.js'; import type { Announcement } from '@/models/entities/Announcement.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -68,10 +69,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('announcementsRepository') + @Inject(DI.announcementsRepository) private announcementsRepository: typeof Announcements, - @Inject('announcementReadsRepository') + @Inject(DI.announcementReadsRepository) private announcementReadsRepository: typeof AnnouncementReads, private queryService: QueryService, diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts index e66d4db37caf..381b0427fe79 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Announcements } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -33,7 +34,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('announcementsRepository') + @Inject(DI.announcementsRepository) private announcementsRepository: typeof Announcements, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/admin/delete-account.ts b/packages/backend/src/server/api/endpoints/admin/delete-account.ts index fadf0025f079..ad796ea6fbd5 100644 --- a/packages/backend/src/server/api/endpoints/admin/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/admin/delete-account.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DeleteAccountService } from '@/services/DeleteAccountService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -25,7 +26,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private deleteAccountService: DeleteAccountService, diff --git a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts index 1d72af85c673..6cc5fa5a1bc2 100644 --- a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFiles } from '@/models/index.js'; import { DriveService } from '@/services/DriveService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -22,7 +23,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private driveService: DriveService, diff --git a/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts b/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts index 119328056d09..ddc92bc818f5 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; import { ModerationLogService } from '@/services/ModerationLogService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -23,7 +24,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private moderationLogService: ModerationLogService, diff --git a/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts b/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts index f273f1dfb09b..2514bea08636 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts @@ -3,6 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFiles } from '@/models/index.js'; import { DriveService } from '@/services/DriveService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -21,7 +22,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private driveService: DriveService, diff --git a/packages/backend/src/server/api/endpoints/admin/drive/files.ts b/packages/backend/src/server/api/endpoints/admin/drive/files.ts index e3dbb772eaa8..c0f734e40989 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/files.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { DriveFiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -43,7 +44,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private queryService: QueryService, diff --git a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts index 608d35424dcb..2d8894466f59 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { DriveFiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -173,7 +174,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts index 4ce428fb209c..93aab336b1e3 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts @@ -33,7 +33,7 @@ export default class extends Endpoint { @Inject(DI.db) private db: DataSource, - @Inject('emojisRepository') + @Inject(DI.emojisRepository) private emojisRepository: typeof Emojis, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 7ffe692cda70..9f7044f17537 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -42,10 +42,10 @@ export default class extends Endpoint { @Inject(DI.db) private db: DataSource, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, - @Inject('emojisRepository') + @Inject(DI.emojisRepository) private emojisRepository: typeof Emojis, private emojiEntityService: EmojiEntityService, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index 06d7b8aaedca..d128a701b56c 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -54,7 +54,7 @@ export default class extends Endpoint { @Inject(DI.db) private db: DataSource, - @Inject('emojisRepository') + @Inject(DI.emojisRepository) private emojisRepository: typeof Emojis, private emojiEntityService: EmojiEntityService, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts index b29befdc1273..66752016adb9 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts @@ -31,7 +31,7 @@ export default class extends Endpoint { @Inject(DI.db) private db: DataSource, - @Inject('emojisRepository') + @Inject(DI.emojisRepository) private emojisRepository: typeof Emojis, private moderationLogService: ModerationLogService, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts index 535967c92eae..c140a83113d6 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts @@ -38,7 +38,7 @@ export default class extends Endpoint { @Inject(DI.db) private db: DataSource, - @Inject('emojisRepository') + @Inject(DI.emojisRepository) private emojisRepository: typeof Emojis, private moderationLogService: ModerationLogService, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts index 249f2335c0d8..48a85c4bc676 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts @@ -4,6 +4,7 @@ import type { Emojis } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { UtilityService } from '@/services/UtilityService.js'; import { EmojiEntityService } from '@/services/entities/EmojiEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -74,7 +75,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('emojisRepository') + @Inject(DI.emojisRepository) private emojisRepository: typeof Emojis, private utilityService: UtilityService, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts index 9ab27b82499e..d725d2cb3f64 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Emojis } from '@/models/index.js'; import type { Emoji } from '@/models/entities/Emoji.js'; import { QueryService } from '@/services/QueryService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -67,7 +68,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('emojisRepository') + @Inject(DI.emojisRepository) private emojisRepository: typeof Emojis, private queryService: QueryService, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts index 37eff1657876..573f7762296b 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts @@ -33,7 +33,7 @@ export default class extends Endpoint { @Inject(DI.db) private db: DataSource, - @Inject('emojisRepository') + @Inject(DI.emojisRepository) private emojisRepository: typeof Emojis, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts index 2e285cf924df..292acce6df40 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts @@ -33,7 +33,7 @@ export default class extends Endpoint { @Inject(DI.db) private db: DataSource, - @Inject('emojisRepository') + @Inject(DI.emojisRepository) private emojisRepository: typeof Emojis, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts index ef69b9b02a04..505119895fd1 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts @@ -35,7 +35,7 @@ export default class extends Endpoint { @Inject(DI.db) private db: DataSource, - @Inject('emojisRepository') + @Inject(DI.emojisRepository) private emojisRepository: typeof Emojis, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index cbdf902b6f04..90d843c66382 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -46,7 +46,7 @@ export default class extends Endpoint { @Inject(DI.db) private db: DataSource, - @Inject('emojisRepository') + @Inject(DI.emojisRepository) private emojisRepository: typeof Emojis, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts index e5cc46ed69a6..63483bcd7096 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFiles } from '@/models/index.js'; import { DriveService } from '@/services/DriveService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -22,7 +23,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private driveService: DriveService, diff --git a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts index e85c925a4d22..d6804fe74494 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Instances } from '@/models/index.js'; import { FetchInstanceMetadataService } from '@/services/FetchInstanceMetadataService.js'; import { UtilityService } from '@/services/UtilityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -23,7 +24,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('instancesRepository') + @Inject(DI.instancesRepository) private instancesRepository: typeof Instances, private utilityService: UtilityService, diff --git a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts index 6a6c930803ec..6d727fbec7ec 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Followings, Users } from '@/models/index.js'; import { UserFollowingService } from '@/services/UserFollowingService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -22,10 +23,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('notesRepository') + @Inject(DI.notesRepository) private followingsRepository: typeof Followings, private userFollowingService: UserFollowingService, diff --git a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts index 9c4a7aa3fac3..799b25aa8db8 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Instances } from '@/models/index.js'; import { UtilityService } from '@/services/UtilityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -23,7 +24,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('instancesRepository') + @Inject(DI.instancesRepository) private instancesRepository: typeof Instances, private utilityService: UtilityService, diff --git a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts index a68b98be356d..2208aad66a51 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserIps } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -21,7 +22,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userIpsRepository') + @Inject(DI.userIpsRepository) private userIpsRepository: typeof UserIps, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/admin/invite.ts b/packages/backend/src/server/api/endpoints/admin/invite.ts index b651a29d72cf..b66f42871adb 100644 --- a/packages/backend/src/server/api/endpoints/admin/invite.ts +++ b/packages/backend/src/server/api/endpoints/admin/invite.ts @@ -3,6 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { RegistrationTickets } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -35,7 +36,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('registrationTicketsRepository') + @Inject(DI.registrationTicketsRepository) private registrationTicketsRepository: typeof RegistrationTickets, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/admin/moderators/add.ts b/packages/backend/src/server/api/endpoints/admin/moderators/add.ts index be688fbb73ff..5c613096d5aa 100644 --- a/packages/backend/src/server/api/endpoints/admin/moderators/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/moderators/add.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -22,7 +23,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private globalEventService: GlobalEventService, diff --git a/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts b/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts index 4c5eb114e9a1..512152d5ad76 100644 --- a/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts +++ b/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -22,7 +23,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private globalEventService: GlobalEventService, diff --git a/packages/backend/src/server/api/endpoints/admin/promo/create.ts b/packages/backend/src/server/api/endpoints/admin/promo/create.ts index 9d63727f9e37..4d5e3b9fc713 100644 --- a/packages/backend/src/server/api/endpoints/admin/promo/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/promo/create.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { PromoNotes } from '@/models/index.js'; import { GetterService } from '@/server/api/common/GetterService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -38,7 +39,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('promoNotesRepository') + @Inject(DI.promoNotesRepository) private promoNotesRepository: typeof PromoNotes, private getterService: GetterService, diff --git a/packages/backend/src/server/api/endpoints/admin/reset-password.ts b/packages/backend/src/server/api/endpoints/admin/reset-password.ts index 45fcf477d0a6..0871e3fabbef 100644 --- a/packages/backend/src/server/api/endpoints/admin/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/admin/reset-password.ts @@ -2,7 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; import rndstr from 'rndstr'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users , UserProfiles } from '@/models/index.js'; +import type { Users, UserProfiles } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -36,10 +37,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, ) { super(meta, paramDef, async (ps) => { diff --git a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts index 75b7ded748ba..5db0188a2621 100644 --- a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts +++ b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts @@ -1,9 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users , AbuseUserReports } from '@/models/index.js'; +import type { Users, AbuseUserReports } from '@/models/index.js'; import { InstanceActorService } from '@/services/InstanceActorService.js'; import { QueueService } from '@/services/QueueService.js'; import { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -27,10 +28,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('abuseUserReportsRepository') + @Inject(DI.abuseUserReportsRepository) private abuseUserReportsRepository: typeof AbuseUserReports, private queueService: QueueService, diff --git a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts index 0de737615711..6ccf09a1fa38 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { ModerationLogs } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -63,7 +64,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('moderationLogsRepository') + @Inject(DI.moderationLogsRepository) private moderationLogsRepository: typeof ModerationLogs, private queryService: QueryService, diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index bb09320b0b95..9d90e4b1bc2a 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { Users , Signins, UserProfiles } from '@/models/index.js'; +import type { Users, Signins, UserProfiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -26,13 +27,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, - @Inject('signinsRepository') + @Inject(DI.signinsRepository) private signinsRepository: typeof Signins, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/admin/show-users.ts b/packages/backend/src/server/api/endpoints/admin/show-users.ts index c643685b1879..49aa776fb14e 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-users.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -42,7 +43,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/admin/silence-user.ts b/packages/backend/src/server/api/endpoints/admin/silence-user.ts index 506cddf22364..b7b4c0e4b41d 100644 --- a/packages/backend/src/server/api/endpoints/admin/silence-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/silence-user.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { ModerationLogService } from '@/services/ModerationLogService.js'; import type { Users } from '@/models/index.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -23,7 +24,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private moderationLogService: ModerationLogService, diff --git a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts index 9dbcbf81fadc..a0fc5c935e5b 100644 --- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts @@ -1,11 +1,12 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users , Followings , Notifications } from '@/models/index.js'; +import type { Users, Followings, Notifications } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ModerationLogService } from '@/services/ModerationLogService.js'; import { UserSuspendService } from '@/services/UserSuspendService.js'; import { UserFollowingService } from '@/services/UserFollowingService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -26,13 +27,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, - @Inject('notificationsRepository') + @Inject(DI.notificationsRepository) private notificationsRepository: typeof Notifications, private userFollowingService: UserFollowingService, diff --git a/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts b/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts index 7ce4cae8a445..53e4968b532a 100644 --- a/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { ModerationLogService } from '@/services/ModerationLogService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -23,7 +24,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private moderationLogService: ModerationLogService, diff --git a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts index c526cb70f556..51df8e7c5847 100644 --- a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; import { ModerationLogService } from '@/services/ModerationLogService.js'; import { UserSuspendService } from '@/services/UserSuspendService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -23,7 +24,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private userSuspendService: UserSuspendService, diff --git a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts index a986eb1a9565..bbe94da5e29a 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-user-note.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-user-note.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserProfiles, Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['admin'], @@ -22,10 +23,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index e0b90946bbab..76b8591b2949 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -1,9 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/services/IdService.js'; -import type { UserLists, UserGroupJoinings , Antennas } from '@/models/index.js'; +import type { UserLists, UserGroupJoinings, Antennas } from '@/models/index.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { AntennaEntityService } from '@/services/entities/AntennaEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -66,13 +67,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('antennasRepository') + @Inject(DI.antennasRepository) private antennasRepository: typeof Antennas, - @Inject('userListsRepository') + @Inject(DI.userListsRepository) private userListsRepository: typeof UserLists, - @Inject('userGroupJoiningsRepository') + @Inject(DI.userGroupJoiningsRepository) private userGroupJoiningsRepository: typeof UserGroupJoinings, private antennaEntityService: AntennaEntityService, diff --git a/packages/backend/src/server/api/endpoints/antennas/delete.ts b/packages/backend/src/server/api/endpoints/antennas/delete.ts index 5599c858a1d4..438555fd0539 100644 --- a/packages/backend/src/server/api/endpoints/antennas/delete.ts +++ b/packages/backend/src/server/api/endpoints/antennas/delete.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Antennas } from '@/models/index.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -32,7 +33,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('antennasRepository') + @Inject(DI.antennasRepository) private antennasRepository: typeof Antennas, private globalEventService: GlobalEventService, diff --git a/packages/backend/src/server/api/endpoints/antennas/list.ts b/packages/backend/src/server/api/endpoints/antennas/list.ts index 6b6cd874f2f6..7f29a4c3ee59 100644 --- a/packages/backend/src/server/api/endpoints/antennas/list.ts +++ b/packages/backend/src/server/api/endpoints/antennas/list.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Antennas } from '@/models/index.js'; import { AntennaEntityService } from '@/services/entities/AntennaEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['antennas', 'account'], @@ -31,7 +32,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('antennasRepository') + @Inject(DI.antennasRepository) private antennasRepository: typeof Antennas, private antennaEntityService: AntennaEntityService, diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index b2a5be4e1cff..07b31eaa81be 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -1,9 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Notes , AntennaNotes } from '@/models/index.js'; +import type { Notes, AntennaNotes } from '@/models/index.js'; import { Antennas } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { NoteReadService } from '@/services/NoteReadService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -49,10 +50,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('antennaNotesRepository') + @Inject(DI.antennaNotesRepository) private antennaNotesRepository: typeof AntennaNotes, private queryService: QueryService, diff --git a/packages/backend/src/server/api/endpoints/antennas/show.ts b/packages/backend/src/server/api/endpoints/antennas/show.ts index 6e3d0b6ad921..65d31043c5ad 100644 --- a/packages/backend/src/server/api/endpoints/antennas/show.ts +++ b/packages/backend/src/server/api/endpoints/antennas/show.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Antennas } from '@/models/index.js'; import { AntennaEntityService } from '@/services/entities/AntennaEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -38,7 +39,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('antennasRepository') + @Inject(DI.antennasRepository) private antennasRepository: typeof Antennas, private antennaEntityService: AntennaEntityService, diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts index 153db8684413..2d99193ee6f6 100644 --- a/packages/backend/src/server/api/endpoints/antennas/update.ts +++ b/packages/backend/src/server/api/endpoints/antennas/update.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Antennas , UserLists, UserGroupJoinings } from '@/models/index.js'; +import type { Antennas, UserLists, UserGroupJoinings } from '@/models/index.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { AntennaEntityService } from '@/services/entities/AntennaEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -72,13 +73,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('antennasRepository') + @Inject(DI.antennasRepository) private antennasRepository: typeof Antennas, - @Inject('userListsRepository') + @Inject(DI.userListsRepository) private userListsRepository: typeof UserLists, - @Inject('userGroupJoiningsRepository') + @Inject(DI.userGroupJoiningsRepository) private userGroupJoiningsRepository: typeof UserGroupJoinings, private antennaEntityService: AntennaEntityService, diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index 46933726cc23..b5c5289ddd69 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users , Notes } from '@/models/index.js'; +import type { Users, Notes } from '@/models/index.js'; import type { Note } from '@/models/entities/Note.js'; import type { CacheableLocalUser, User } from '@/models/entities/User.js'; import { isActor, isPost, getApId } from '@/services/remote/activitypub/type.js'; @@ -14,6 +14,7 @@ import { ApNoteService } from '@/services/remote/activitypub/models/ApNoteServic import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { UtilityService } from '@/services/UtilityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -83,10 +84,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private utilityService: UtilityService, diff --git a/packages/backend/src/server/api/endpoints/app/create.ts b/packages/backend/src/server/api/endpoints/app/create.ts index b3da01e6ee8d..2306c8c574f1 100644 --- a/packages/backend/src/server/api/endpoints/app/create.ts +++ b/packages/backend/src/server/api/endpoints/app/create.ts @@ -5,6 +5,7 @@ import { IdService } from '@/services/IdService.js'; import { unique } from '@/prelude/array.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; import { AppEntityService } from '@/services/entities/AppEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['app'], @@ -35,7 +36,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('appsRepository') + @Inject(DI.appsRepository) private appsRepository: typeof Apps, private appEntityService: AppEntityService, diff --git a/packages/backend/src/server/api/endpoints/app/show.ts b/packages/backend/src/server/api/endpoints/app/show.ts index 753cd0b0143e..bb3a821227ec 100644 --- a/packages/backend/src/server/api/endpoints/app/show.ts +++ b/packages/backend/src/server/api/endpoints/app/show.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Apps } from '@/models/index.js'; import { AppEntityService } from '@/services/entities/AppEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -34,7 +35,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('appsRepository') + @Inject(DI.appsRepository) private appsRepository: typeof Apps, private appEntityService: AppEntityService, diff --git a/packages/backend/src/server/api/endpoints/auth/accept.ts b/packages/backend/src/server/api/endpoints/auth/accept.ts index bde5ccd70b10..fa596568af66 100644 --- a/packages/backend/src/server/api/endpoints/auth/accept.ts +++ b/packages/backend/src/server/api/endpoints/auth/accept.ts @@ -1,9 +1,10 @@ import * as crypto from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { AuthSessions, Apps , AccessTokens } from '@/models/index.js'; +import type { AuthSessions, Apps, AccessTokens } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -34,13 +35,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('appsRepository') + @Inject(DI.appsRepository) private appsRepository: typeof Apps, - @Inject('authSessionsRepository') + @Inject(DI.authSessionsRepository) private authSessionsRepository: typeof AuthSessions, - @Inject('accessTokensRepository') + @Inject(DI.accessTokensRepository) private accessTokensRepository: typeof AccessTokens, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/auth/session/generate.ts b/packages/backend/src/server/api/endpoints/auth/session/generate.ts index a4c1a45d6672..5d438ff5989d 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/generate.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/generate.ts @@ -52,10 +52,10 @@ export default class extends Endpoint { @Inject(DI.config) private config: Config, - @Inject('appsRepository') + @Inject(DI.appsRepository) private appsRepository: typeof Apps, - @Inject('authSessionsRepository') + @Inject(DI.authSessionsRepository) private authSessionsRepository: typeof AuthSessions, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/auth/session/show.ts b/packages/backend/src/server/api/endpoints/auth/session/show.ts index da878d7e7ce5..d1614fc6b053 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/show.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/show.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { AuthSessions } from '@/models/index.js'; import { AuthSessionEntityService } from '@/services/entities/AuthSessionEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -51,7 +52,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('authSessionsRepository') + @Inject(DI.authSessionsRepository) private authSessionsRepository: typeof AuthSessions, private authSessionEntityService: AuthSessionEntityService, diff --git a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts index 828afaa99621..c1b50513f479 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users , Apps, AccessTokens , AuthSessions } from '@/models/index.js'; +import type { Users, Apps, AccessTokens, AuthSessions } from '@/models/index.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -60,16 +61,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('appsRepository') + @Inject(DI.appsRepository) private appsRepository: typeof Apps, - @Inject('authSessionsRepository') + @Inject(DI.authSessionsRepository) private authSessionsRepository: typeof AuthSessions, - @Inject('accessTokensRepository') + @Inject(DI.accessTokensRepository) private accessTokensRepository: typeof AccessTokens, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts index 031d9375af98..c48525d2b883 100644 --- a/packages/backend/src/server/api/endpoints/blocking/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking/create.ts @@ -1,9 +1,10 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users , Blockings } from '@/models/index.js'; +import type { Users, Blockings } from '@/models/index.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { UserBlockingService } from '@/services/UserBlockingService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; @@ -58,10 +59,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('blockingsRepository') + @Inject(DI.blockingsRepository) private blockingsRepository: typeof Blockings, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts index d1f91d2096cf..b8ca72cf7f06 100644 --- a/packages/backend/src/server/api/endpoints/blocking/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts @@ -1,9 +1,10 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users , Blockings } from '@/models/index.js'; +import type { Users, Blockings } from '@/models/index.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { UserBlockingService } from '@/services/UserBlockingService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; @@ -58,10 +59,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('blockingsRepository') + @Inject(DI.blockingsRepository) private blockingsRepository: typeof Blockings, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/blocking/list.ts b/packages/backend/src/server/api/endpoints/blocking/list.ts index a09d2d9d7a1a..76e854244fbe 100644 --- a/packages/backend/src/server/api/endpoints/blocking/list.ts +++ b/packages/backend/src/server/api/endpoints/blocking/list.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Blockings } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { BlockingEntityService } from '@/services/entities/BlockingEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['account'], @@ -36,7 +37,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('blockingsRepository') + @Inject(DI.blockingsRepository) private blockingsRepository: typeof Blockings, private blockingEntityService: BlockingEntityService, diff --git a/packages/backend/src/server/api/endpoints/channels/create.ts b/packages/backend/src/server/api/endpoints/channels/create.ts index 70af5a97e6fe..405bba91b4d9 100644 --- a/packages/backend/src/server/api/endpoints/channels/create.ts +++ b/packages/backend/src/server/api/endpoints/channels/create.ts @@ -4,6 +4,7 @@ import type { Channels, DriveFiles } from '@/models/index.js'; import type { Channel } from '@/models/entities/Channel.js'; import { IdService } from '@/services/IdService.js'; import { ChannelEntityService } from '@/services/entities/ChannelEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -42,10 +43,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, - @Inject('channelsRepository') + @Inject(DI.channelsRepository) private channelsRepository: typeof Channels, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/channels/featured.ts b/packages/backend/src/server/api/endpoints/channels/featured.ts index d3ce826296d3..f52ef6121353 100644 --- a/packages/backend/src/server/api/endpoints/channels/featured.ts +++ b/packages/backend/src/server/api/endpoints/channels/featured.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Channels } from '@/models/index.js'; import { ChannelEntityService } from '@/services/entities/ChannelEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['channels'], @@ -29,7 +30,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('channelsRepository') + @Inject(DI.channelsRepository) private channelsRepository: typeof Channels, private channelEntityService: ChannelEntityService, diff --git a/packages/backend/src/server/api/endpoints/channels/follow.ts b/packages/backend/src/server/api/endpoints/channels/follow.ts index 799cf0b2ed80..547d9f3d6c0e 100644 --- a/packages/backend/src/server/api/endpoints/channels/follow.ts +++ b/packages/backend/src/server/api/endpoints/channels/follow.ts @@ -4,6 +4,7 @@ import type { ChannelFollowings } from '@/models/index.js'; import { Channels } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -34,7 +35,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('channelFollowingsRepository') + @Inject(DI.channelFollowingsRepository) private channelFollowingsRepository: typeof ChannelFollowings, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/channels/followed.ts b/packages/backend/src/server/api/endpoints/channels/followed.ts index 0384c001a140..ebb26cfbe8cb 100644 --- a/packages/backend/src/server/api/endpoints/channels/followed.ts +++ b/packages/backend/src/server/api/endpoints/channels/followed.ts @@ -4,6 +4,7 @@ import type { ChannelFollowings } from '@/models/index.js'; import { Channels } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { ChannelEntityService } from '@/services/entities/ChannelEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['channels', 'account'], @@ -37,7 +38,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('channelFollowingsRepository') + @Inject(DI.channelFollowingsRepository) private channelFollowingsRepository: typeof ChannelFollowings, private channelEntityService: ChannelEntityService, diff --git a/packages/backend/src/server/api/endpoints/channels/owned.ts b/packages/backend/src/server/api/endpoints/channels/owned.ts index 3642d4ae1902..274b1bf9a728 100644 --- a/packages/backend/src/server/api/endpoints/channels/owned.ts +++ b/packages/backend/src/server/api/endpoints/channels/owned.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Channels } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { ChannelEntityService } from '@/services/entities/ChannelEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['channels', 'account'], @@ -36,7 +37,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('channelsRepository') + @Inject(DI.channelsRepository) private channelsRepository: typeof Channels, private channelEntityService: ChannelEntityService, diff --git a/packages/backend/src/server/api/endpoints/channels/show.ts b/packages/backend/src/server/api/endpoints/channels/show.ts index a04d8d868fa6..ec996ee36b20 100644 --- a/packages/backend/src/server/api/endpoints/channels/show.ts +++ b/packages/backend/src/server/api/endpoints/channels/show.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Channels } from '@/models/index.js'; import { ChannelEntityService } from '@/services/entities/ChannelEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -36,7 +37,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('channelsRepository') + @Inject(DI.channelsRepository) private channelsRepository: typeof Channels, private channelEntityService: ChannelEntityService, diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index d0ad7f0686d1..a2d4779984cb 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -5,6 +5,7 @@ import { Channels } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import ActiveUsersChart from '@/services/chart/charts/active-users.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -48,7 +49,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/api/endpoints/channels/unfollow.ts b/packages/backend/src/server/api/endpoints/channels/unfollow.ts index 81e5d7635314..4e2d29b16259 100644 --- a/packages/backend/src/server/api/endpoints/channels/unfollow.ts +++ b/packages/backend/src/server/api/endpoints/channels/unfollow.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { ChannelFollowings } from '@/models/index.js'; import { Channels } from '@/models/index.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -33,7 +34,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('channelFollowingsRepository') + @Inject(DI.channelFollowingsRepository) private channelFollowingsRepository: typeof ChannelFollowings, private globalEventService: GlobalEventService, diff --git a/packages/backend/src/server/api/endpoints/channels/update.ts b/packages/backend/src/server/api/endpoints/channels/update.ts index 88397b516833..b6b0c8db721b 100644 --- a/packages/backend/src/server/api/endpoints/channels/update.ts +++ b/packages/backend/src/server/api/endpoints/channels/update.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFiles , Channels } from '@/models/index.js'; +import type { DriveFiles, Channels } from '@/models/index.js'; import { ChannelEntityService } from '@/services/entities/ChannelEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -53,10 +54,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('channelsRepository') + @Inject(DI.channelsRepository) private channelsRepository: typeof Channels, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private channelEntityService: ChannelEntityService, diff --git a/packages/backend/src/server/api/endpoints/clips/create.ts b/packages/backend/src/server/api/endpoints/clips/create.ts index a33d9981609f..0ffe6887dbe3 100644 --- a/packages/backend/src/server/api/endpoints/clips/create.ts +++ b/packages/backend/src/server/api/endpoints/clips/create.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/services/IdService.js'; import type { Clips } from '@/models/index.js'; import { ClipEntityService } from '@/services/entities/ClipEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['clips'], @@ -32,7 +33,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('clipsRepository') + @Inject(DI.clipsRepository) private clipsRepository: typeof Clips, private clipEntityService: ClipEntityService, diff --git a/packages/backend/src/server/api/endpoints/clips/delete.ts b/packages/backend/src/server/api/endpoints/clips/delete.ts index cf70fcdaf977..6346aa5935b0 100644 --- a/packages/backend/src/server/api/endpoints/clips/delete.ts +++ b/packages/backend/src/server/api/endpoints/clips/delete.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Clips } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -31,7 +32,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('clipsRepository') + @Inject(DI.clipsRepository) private clipsRepository: typeof Clips, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/clips/list.ts b/packages/backend/src/server/api/endpoints/clips/list.ts index 6c659f04ea41..3d9c431d786b 100644 --- a/packages/backend/src/server/api/endpoints/clips/list.ts +++ b/packages/backend/src/server/api/endpoints/clips/list.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Clips } from '@/models/index.js'; import { ClipEntityService } from '@/services/entities/ClipEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['clips', 'account'], @@ -31,7 +32,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('clipsRepository') + @Inject(DI.clipsRepository) private clipsRepository: typeof Clips, private clipEntityService: ClipEntityService, diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts index 421dde429dd7..a420213f20d3 100644 --- a/packages/backend/src/server/api/endpoints/clips/notes.ts +++ b/packages/backend/src/server/api/endpoints/clips/notes.ts @@ -1,9 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Notes , Clips } from '@/models/index.js'; +import type { Notes, Clips } from '@/models/index.js'; import { ClipNotes } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -47,10 +48,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('clipsRepository') + @Inject(DI.clipsRepository) private clipsRepository: typeof Clips, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/api/endpoints/clips/remove-note.ts b/packages/backend/src/server/api/endpoints/clips/remove-note.ts index 8d56da7f41b9..c67e20a1f49a 100644 --- a/packages/backend/src/server/api/endpoints/clips/remove-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/remove-note.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { ClipNotes, Clips } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; @@ -39,10 +40,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('clipsRepository') + @Inject(DI.clipsRepository) private clipsRepository: typeof Clips, - @Inject('clipNotesRepository') + @Inject(DI.clipNotesRepository) private clipNotesRepository: typeof ClipNotes, private getterService: GetterService, diff --git a/packages/backend/src/server/api/endpoints/clips/show.ts b/packages/backend/src/server/api/endpoints/clips/show.ts index 17fcdd35af32..10cc58daaef3 100644 --- a/packages/backend/src/server/api/endpoints/clips/show.ts +++ b/packages/backend/src/server/api/endpoints/clips/show.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Clips } from '@/models/index.js'; import { ClipEntityService } from '@/services/entities/ClipEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -38,7 +39,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('clipsRepository') + @Inject(DI.clipsRepository) private clipsRepository: typeof Clips, private clipEntityService: ClipEntityService, diff --git a/packages/backend/src/server/api/endpoints/clips/update.ts b/packages/backend/src/server/api/endpoints/clips/update.ts index adace87d7c89..f01ccaf11a57 100644 --- a/packages/backend/src/server/api/endpoints/clips/update.ts +++ b/packages/backend/src/server/api/endpoints/clips/update.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Clips } from '@/models/index.js'; import { ClipEntityService } from '@/services/entities/ClipEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -41,7 +42,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('clipsRepository') + @Inject(DI.clipsRepository) private clipsRepository: typeof Clips, private clipEntityService: ClipEntityService, diff --git a/packages/backend/src/server/api/endpoints/drive/files.ts b/packages/backend/src/server/api/endpoints/drive/files.ts index 11e4a7f3359a..d78fb6881878 100644 --- a/packages/backend/src/server/api/endpoints/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/drive/files.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFiles } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['drive'], @@ -38,7 +39,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private driveFileEntityService: DriveFileEntityService, diff --git a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts index 7f75668ae359..d087a354504f 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Notes , DriveFiles } from '@/models/index.js'; +import type { Notes, DriveFiles } from '@/models/index.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -44,10 +45,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts index 8bb5dd21cb38..8f876e9a59ea 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFiles } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['drive'], @@ -29,7 +30,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/drive/files/create.ts b/packages/backend/src/server/api/endpoints/drive/files/create.ts index d082dfacda1f..980e04fbc244 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/create.ts @@ -7,6 +7,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; import { MetaService } from '@/services/MetaService.js'; import { DriveService } from '@/services/DriveService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -68,7 +69,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private driveFileEntityService: DriveFileEntityService, diff --git a/packages/backend/src/server/api/endpoints/drive/files/delete.ts b/packages/backend/src/server/api/endpoints/drive/files/delete.ts index 309a4820fbf0..86943ded3f62 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/delete.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/delete.ts @@ -4,6 +4,7 @@ import type { DriveFiles } from '@/models/index.js'; import { Users } from '@/models/index.js'; import { DriveService } from '@/services/DriveService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -42,7 +43,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private driveService: DriveService, diff --git a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts index 86ef4372eb6c..25777831af9d 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { DriveFiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['drive'], @@ -35,7 +36,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private driveFileEntityService: DriveFileEntityService, diff --git a/packages/backend/src/server/api/endpoints/drive/files/find.ts b/packages/backend/src/server/api/endpoints/drive/files/find.ts index 5b31bb348b91..9dcc44c4cbfc 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/find.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/find.ts @@ -3,6 +3,7 @@ import { IsNull } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFiles } from '@/models/index.js'; import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -37,7 +38,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private driveFileEntityService: DriveFileEntityService, diff --git a/packages/backend/src/server/api/endpoints/drive/files/show.ts b/packages/backend/src/server/api/endpoints/drive/files/show.ts index cb03a5c0666d..bb3c559d7c7e 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/show.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/show.ts @@ -4,6 +4,7 @@ import type { DriveFiles } from '@/models/index.js'; import { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -58,7 +59,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private driveFileEntityService: DriveFileEntityService, diff --git a/packages/backend/src/server/api/endpoints/drive/files/update.ts b/packages/backend/src/server/api/endpoints/drive/files/update.ts index a6108e9823a6..1d2acd0bfe0b 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts @@ -1,10 +1,11 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { DriveFiles , DriveFolders } from '@/models/index.js'; +import type { DriveFiles, DriveFolders } from '@/models/index.js'; import { Users } from '@/models/index.js'; import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -65,10 +66,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, - @Inject('driveFoldersRepository') + @Inject(DI.driveFoldersRepository) private driveFoldersRepository: typeof DriveFolders, private driveFileEntityService: DriveFileEntityService, diff --git a/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts b/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts index 5c58017525a6..f14ff1d7095e 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts @@ -6,6 +6,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; import { DriveService } from '@/services/DriveService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['drive'], @@ -39,7 +40,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private driveFileEntityService: DriveFileEntityService, diff --git a/packages/backend/src/server/api/endpoints/drive/folders.ts b/packages/backend/src/server/api/endpoints/drive/folders.ts index 736a174eb71d..1a35314252bd 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFolders } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { DriveFolderEntityService } from '@/services/entities/DriveFolderEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['drive'], @@ -37,7 +38,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFoldersRepository') + @Inject(DI.driveFoldersRepository) private driveFoldersRepository: typeof DriveFolders, private driveFolderEntityService: DriveFolderEntityService, diff --git a/packages/backend/src/server/api/endpoints/drive/folders/create.ts b/packages/backend/src/server/api/endpoints/drive/folders/create.ts index d511835cda29..fca99b085d7d 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/create.ts @@ -4,6 +4,7 @@ import type { DriveFolders } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; import { DriveFolderEntityService } from '@/services/entities/DriveFolderEntityService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -41,7 +42,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFoldersRepository') + @Inject(DI.driveFoldersRepository) private driveFoldersRepository: typeof DriveFolders, private driveFolderEntityService: DriveFolderEntityService, diff --git a/packages/backend/src/server/api/endpoints/drive/folders/delete.ts b/packages/backend/src/server/api/endpoints/drive/folders/delete.ts index d61d48be7684..88ebb186c4a9 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/delete.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/delete.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { publishDriveStream } from '@/services/stream.js'; import type { DriveFolders, DriveFiles } from '@/models/index.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -39,10 +39,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, - @Inject('driveFoldersRepository') + @Inject(DI.driveFoldersRepository) private driveFoldersRepository: typeof DriveFolders, private globalEventService: GlobalEventService, diff --git a/packages/backend/src/server/api/endpoints/drive/folders/find.ts b/packages/backend/src/server/api/endpoints/drive/folders/find.ts index 6737f835e7cb..75231efafdb5 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/find.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/find.ts @@ -3,6 +3,7 @@ import { IsNull } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFolders } from '@/models/index.js'; import { DriveFolderEntityService } from '@/services/entities/DriveFolderEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['drive'], @@ -35,7 +36,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFoldersRepository') + @Inject(DI.driveFoldersRepository) private driveFoldersRepository: typeof DriveFolders, private driveFolderEntityService: DriveFolderEntityService, diff --git a/packages/backend/src/server/api/endpoints/drive/folders/show.ts b/packages/backend/src/server/api/endpoints/drive/folders/show.ts index 69294b1031cc..c993f1687a74 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/show.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/show.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFolders } from '@/models/index.js'; import { DriveFolderEntityService } from '@/services/entities/DriveFolderEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -38,7 +39,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFoldersRepository') + @Inject(DI.driveFoldersRepository) private driveFoldersRepository: typeof DriveFolders, private driveFolderEntityService: DriveFolderEntityService, diff --git a/packages/backend/src/server/api/endpoints/drive/folders/update.ts b/packages/backend/src/server/api/endpoints/drive/folders/update.ts index 07013b010ce8..ea22f539e8b1 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/update.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFolders } from '@/models/index.js'; import { DriveFolderEntityService } from '@/services/entities/DriveFolderEntityService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -53,7 +54,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFoldersRepository') + @Inject(DI.driveFoldersRepository) private driveFoldersRepository: typeof DriveFolders, private driveFolderEntityService: DriveFolderEntityService, diff --git a/packages/backend/src/server/api/endpoints/drive/stream.ts b/packages/backend/src/server/api/endpoints/drive/stream.ts index 673cf1d4699b..6f9f74089fa1 100644 --- a/packages/backend/src/server/api/endpoints/drive/stream.ts +++ b/packages/backend/src/server/api/endpoints/drive/stream.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFiles } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['drive'], @@ -37,7 +38,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private driveFileEntityService: DriveFileEntityService, diff --git a/packages/backend/src/server/api/endpoints/federation/followers.ts b/packages/backend/src/server/api/endpoints/federation/followers.ts index 10c81a0c2a8b..d3db3b4d7ea7 100644 --- a/packages/backend/src/server/api/endpoints/federation/followers.ts +++ b/packages/backend/src/server/api/endpoints/federation/followers.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Followings } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { FollowingEntityService } from '@/services/entities/FollowingEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['federation'], @@ -35,7 +36,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, private followingEntityService: FollowingEntityService, diff --git a/packages/backend/src/server/api/endpoints/federation/following.ts b/packages/backend/src/server/api/endpoints/federation/following.ts index 6f29dc65f4fe..d8f8637310be 100644 --- a/packages/backend/src/server/api/endpoints/federation/following.ts +++ b/packages/backend/src/server/api/endpoints/federation/following.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Followings } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { FollowingEntityService } from '@/services/entities/FollowingEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['federation'], @@ -35,7 +36,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, private followingEntityService: FollowingEntityService, diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts index a109e6210ff4..f8f16b30cb5a 100644 --- a/packages/backend/src/server/api/endpoints/federation/instances.ts +++ b/packages/backend/src/server/api/endpoints/federation/instances.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Instances } from '@/models/index.js'; import { InstanceEntityService } from '@/services/entities/InstanceEntityService.js'; import { MetaService } from '@/services/MetaService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['federation'], @@ -41,7 +42,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('instancesRepository') + @Inject(DI.instancesRepository) private instancesRepository: typeof Instances, private instanceEntityService: InstanceEntityService, diff --git a/packages/backend/src/server/api/endpoints/federation/show-instance.ts b/packages/backend/src/server/api/endpoints/federation/show-instance.ts index 8b9d170e33f9..c883d8798992 100644 --- a/packages/backend/src/server/api/endpoints/federation/show-instance.ts +++ b/packages/backend/src/server/api/endpoints/federation/show-instance.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Instances } from '@/models/index.js'; import { InstanceEntityService } from '@/services/entities/InstanceEntityService.js'; import { UtilityService } from '@/services/UtilityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['federation'], @@ -31,7 +32,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('instancesRepository') + @Inject(DI.instancesRepository) private instancesRepository: typeof Instances, private utilityService: UtilityService, diff --git a/packages/backend/src/server/api/endpoints/federation/stats.ts b/packages/backend/src/server/api/endpoints/federation/stats.ts index 437e244d6706..22c2c4e55c92 100644 --- a/packages/backend/src/server/api/endpoints/federation/stats.ts +++ b/packages/backend/src/server/api/endpoints/federation/stats.ts @@ -4,6 +4,7 @@ import type { Followings, Instances } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { InstanceEntityService } from '@/services/entities/InstanceEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['federation'], @@ -26,10 +27,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('instancesRepository') + @Inject(DI.instancesRepository) private instancesRepository: typeof Instances, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, private instanceEntityService: InstanceEntityService, diff --git a/packages/backend/src/server/api/endpoints/federation/users.ts b/packages/backend/src/server/api/endpoints/federation/users.ts index 4979008108ff..e50d2e31da28 100644 --- a/packages/backend/src/server/api/endpoints/federation/users.ts +++ b/packages/backend/src/server/api/endpoints/federation/users.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['federation'], @@ -35,7 +36,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts index c4c93800c946..78eb1ad0daaa 100644 --- a/packages/backend/src/server/api/endpoints/following/create.ts +++ b/packages/backend/src/server/api/endpoints/following/create.ts @@ -1,10 +1,11 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users , Followings } from '@/models/index.js'; +import type { Users, Followings } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { UserFollowingService } from '@/services/UserFollowingService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; @@ -71,10 +72,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/following/delete.ts b/packages/backend/src/server/api/endpoints/following/delete.ts index c5f95a4f9a7e..cbbfb725cb18 100644 --- a/packages/backend/src/server/api/endpoints/following/delete.ts +++ b/packages/backend/src/server/api/endpoints/following/delete.ts @@ -1,9 +1,10 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users , Followings } from '@/models/index.js'; +import type { Users, Followings } from '@/models/index.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { UserFollowingService } from '@/services/UserFollowingService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; @@ -58,10 +59,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/following/invalidate.ts b/packages/backend/src/server/api/endpoints/following/invalidate.ts index 27f221a960bb..e5bb15a5d540 100644 --- a/packages/backend/src/server/api/endpoints/following/invalidate.ts +++ b/packages/backend/src/server/api/endpoints/following/invalidate.ts @@ -1,9 +1,10 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users , Followings } from '@/models/index.js'; +import type { Users, Followings } from '@/models/index.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { UserFollowingService } from '@/services/UserFollowingService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; @@ -58,10 +59,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts index 16401d59610e..2c624b51d753 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts @@ -5,6 +5,7 @@ import { IdentifiableError } from '@/misc/identifiable-error.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { GetterService } from '@/server/api/common/GetterService.js'; import { UserFollowingService } from '@/services/UserFollowingService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -47,7 +48,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/following/requests/list.ts b/packages/backend/src/server/api/endpoints/following/requests/list.ts index 644dcd3e1d70..6f882085da2f 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/list.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/list.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { FollowRequests } from '@/models/index.js'; import { FollowRequestEntityService } from '@/services/entities/FollowRequestEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['following', 'account'], @@ -47,7 +48,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('followRequestsRepository') + @Inject(DI.followRequestsRepository) private followRequestsRepository: typeof FollowRequests, private followRequestEntityService: FollowRequestEntityService, diff --git a/packages/backend/src/server/api/endpoints/gallery/featured.ts b/packages/backend/src/server/api/endpoints/gallery/featured.ts index 435c6ea52c07..4818d39978ac 100644 --- a/packages/backend/src/server/api/endpoints/gallery/featured.ts +++ b/packages/backend/src/server/api/endpoints/gallery/featured.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { GalleryPosts } from '@/models/index.js'; import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['gallery'], @@ -29,7 +30,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('galleryPostsRepository') + @Inject(DI.galleryPostsRepository) private galleryPostsRepository: typeof GalleryPosts, private galleryPostEntityService: GalleryPostEntityService, diff --git a/packages/backend/src/server/api/endpoints/gallery/popular.ts b/packages/backend/src/server/api/endpoints/gallery/popular.ts index b1345c00b207..8bb3d806fd44 100644 --- a/packages/backend/src/server/api/endpoints/gallery/popular.ts +++ b/packages/backend/src/server/api/endpoints/gallery/popular.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { GalleryPosts } from '@/models/index.js'; import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['gallery'], @@ -29,7 +30,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('galleryPostsRepository') + @Inject(DI.galleryPostsRepository) private galleryPostsRepository: typeof GalleryPosts, private galleryPostEntityService: GalleryPostEntityService, diff --git a/packages/backend/src/server/api/endpoints/gallery/posts.ts b/packages/backend/src/server/api/endpoints/gallery/posts.ts index bbd5b83625ef..ddcbe20744a4 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { GalleryPosts } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['gallery'], @@ -32,7 +33,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('galleryPostsRepository') + @Inject(DI.galleryPostsRepository) private galleryPostsRepository: typeof GalleryPosts, private galleryPostEntityService: GalleryPostEntityService, diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts index d6247b4f05e1..a9b2f4cabfc0 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts @@ -7,6 +7,7 @@ import { GalleryPost } from '@/models/entities/GalleryPost.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import { IdService } from '@/services/IdService.js'; import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -49,7 +50,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('galleryPostsRepository') + @Inject(DI.galleryPostsRepository) private galleryPostsRepository: typeof GalleryPosts, private galleryPostEntityService: GalleryPostEntityService, diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts b/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts index 952211c0c1e0..f095a645fdf0 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { GalleryPosts } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -31,7 +32,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('galleryPostsRepository') + @Inject(DI.galleryPostsRepository) private galleryPostsRepository: typeof GalleryPosts, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts index 7ad453eef3f7..3f486cd234dc 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { GalleryLikes , GalleryPosts } from '@/models/index.js'; +import type { GalleryLikes, GalleryPosts } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -44,10 +45,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('galleryPostsRepository') + @Inject(DI.galleryPostsRepository) private galleryPostsRepository: typeof GalleryPosts, - @Inject('galleryLikesRepository') + @Inject(DI.galleryLikesRepository) private galleryLikesRepository: typeof GalleryLikes, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts b/packages/backend/src/server/api/endpoints/gallery/posts/show.ts index a247f224f130..19e68c276b37 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/show.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { GalleryPosts } from '@/models/index.js'; import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -36,7 +37,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('galleryPostsRepository') + @Inject(DI.galleryPostsRepository) private galleryPostsRepository: typeof GalleryPosts, private galleryPostEntityService: GalleryPostEntityService, diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts index e04c9610ecd3..0e245de93629 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { GalleryPosts, GalleryLikes } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -37,10 +38,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('galleryPostsRepository') + @Inject(DI.galleryPostsRepository) private galleryPostsRepository: typeof GalleryPosts, - @Inject('galleryLikesRepository') + @Inject(DI.galleryLikesRepository) private galleryLikesRepository: typeof GalleryLikes, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts index 2517c5e8b62d..a9826583b8b9 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts @@ -5,6 +5,7 @@ import type { DriveFiles, GalleryPosts } from '@/models/index.js'; import { GalleryPost } from '@/models/entities/GalleryPost.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -48,10 +49,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('galleryPostsRepository') + @Inject(DI.galleryPostsRepository) private galleryPostsRepository: typeof GalleryPosts, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private galleryPostEntityService: GalleryPostEntityService, diff --git a/packages/backend/src/server/api/endpoints/get-online-users-count.ts b/packages/backend/src/server/api/endpoints/get-online-users-count.ts index dde957c71b2c..86342be131d3 100644 --- a/packages/backend/src/server/api/endpoints/get-online-users-count.ts +++ b/packages/backend/src/server/api/endpoints/get-online-users-count.ts @@ -3,6 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { USER_ONLINE_THRESHOLD } from '@/const.js'; import type { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['meta'], @@ -20,7 +21,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, ) { super(meta, paramDef, async () => { diff --git a/packages/backend/src/server/api/endpoints/hashtags/list.ts b/packages/backend/src/server/api/endpoints/hashtags/list.ts index 12b3ee1c358e..4fdc43382ed5 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/list.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/list.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Hashtags } from '@/models/index.js'; import { HashtagEntityService } from '@/services/entities/HashtagEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['hashtags'], @@ -35,7 +36,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('hashtagsRepository') + @Inject(DI.hashtagsRepository) private hashtagsRepository: typeof Hashtags, private hashtagEntityService: HashtagEntityService, diff --git a/packages/backend/src/server/api/endpoints/hashtags/search.ts b/packages/backend/src/server/api/endpoints/hashtags/search.ts index 0cacffe81200..5536a90b0564 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/search.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/search.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Hashtags } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['hashtags'], @@ -31,7 +32,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('hashtagsRepository') + @Inject(DI.hashtagsRepository) private hashtagsRepository: typeof Hashtags, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/hashtags/show.ts b/packages/backend/src/server/api/endpoints/hashtags/show.ts index 4dd474faa3b9..2ebf929edabb 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/show.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/show.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Hashtags } from '@/models/index.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { HashtagEntityService } from '@/services/entities/HashtagEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -37,7 +38,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('hashtagsRepository') + @Inject(DI.hashtagsRepository) private hashtagsRepository: typeof Hashtags, private hashtagEntityService: HashtagEntityService, diff --git a/packages/backend/src/server/api/endpoints/hashtags/trend.ts b/packages/backend/src/server/api/endpoints/hashtags/trend.ts index 6c184d54a167..31f5112e2138 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/trend.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/trend.ts @@ -6,6 +6,7 @@ import type { Note } from '@/models/entities/Note.js'; import { safeForSql } from '@/misc/safe-for-sql.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { MetaService } from '@/services/MetaService.js'; +import { DI } from '@/di-symbols.js'; /* トレンドに載るためには「『直近a分間のユニーク投稿数が今からa分前~今からb分前の間のユニーク投稿数のn倍以上』のハッシュタグの上位5位以内に入る」ことが必要 @@ -64,7 +65,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private metaService: MetaService, diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts index f4aa093620b1..5faf4386bbb7 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/users.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Users } from '@/models/index.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: false, @@ -36,7 +37,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index ece030e9f173..8bb2d332cccc 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['account'], @@ -25,7 +26,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/i/2fa/done.ts b/packages/backend/src/server/api/endpoints/i/2fa/done.ts index 4621448a95bc..85ee133a65b0 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/done.ts @@ -2,6 +2,7 @@ import * as speakeasy from 'speakeasy'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UserProfiles } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -21,7 +22,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts index 17b5ea15fbf0..3f41230f702e 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts @@ -4,7 +4,7 @@ import * as cbor from 'cbor'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { - Users , + Users, UserProfiles } from '@/models/index.js'; import { UserSecurityKeys, @@ -43,7 +43,7 @@ export default class extends Endpoint { @Inject(DI.config) private config: Config, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts index da6a49d6934f..2011c283a710 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UserProfiles } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -20,7 +21,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts index 8cf2aedf629a..57d35244a2f5 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts @@ -3,9 +3,10 @@ import * as crypto from 'node:crypto'; import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UserProfiles , AttestationChallenges } from '@/models/index.js'; +import type { UserProfiles, AttestationChallenges } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; import { TwoFactorAuthenticationService } from '@/services/TwoFactorAuthenticationService.js'; +import { DI } from '@/di-symbols.js'; const randomBytes = promisify(crypto.randomBytes); @@ -27,10 +28,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, - @Inject('attestationChallengesRepository') + @Inject(DI.attestationChallengesRepository) private attestationChallengesRepository: typeof AttestationChallenges, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register.ts b/packages/backend/src/server/api/endpoints/i/2fa/register.ts index d09f8eb81cb2..b2a71f93ed7e 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts @@ -28,7 +28,7 @@ export default class extends Endpoint { @Inject(DI.config) private config: Config, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts index 8ad1e0cd96cd..8344b63b5c73 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts @@ -1,10 +1,10 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users , UserProfiles , UserSecurityKeys } from '@/models/index.js'; -import { publishMainStream } from '@/services/stream.js'; +import type { Users, UserProfiles, UserSecurityKeys } from '@/models/index.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -25,10 +25,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userSecurityKeysRepository') + @Inject(DI.userSecurityKeysRepository) private userSecurityKeysRepository: typeof UserSecurityKeys, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts index 25232065c4c4..e6a6a6204e76 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts @@ -2,6 +2,7 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UserProfiles } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -21,7 +22,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/i/apps.ts b/packages/backend/src/server/api/endpoints/i/apps.ts index b8bcbbcf29cb..cba93db56625 100644 --- a/packages/backend/src/server/api/endpoints/i/apps.ts +++ b/packages/backend/src/server/api/endpoints/i/apps.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { AccessTokens } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -20,7 +21,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('accessTokensRepository') + @Inject(DI.accessTokensRepository) private accessTokensRepository: typeof AccessTokens, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts index f5579d741d55..7f423bf9ca13 100644 --- a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts +++ b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { AccessTokens } from '@/models/index.js'; import { Apps } from '@/models/index.js'; import { AppEntityService } from '@/services/entities/AppEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -24,7 +25,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('accessTokensRepository') + @Inject(DI.accessTokensRepository) private accessTokensRepository: typeof AccessTokens, private appEntityService: AppEntityService, diff --git a/packages/backend/src/server/api/endpoints/i/change-password.ts b/packages/backend/src/server/api/endpoints/i/change-password.ts index beadcb626a6e..b09d277c48aa 100644 --- a/packages/backend/src/server/api/endpoints/i/change-password.ts +++ b/packages/backend/src/server/api/endpoints/i/change-password.ts @@ -2,6 +2,7 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UserProfiles } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -22,7 +23,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/i/delete-account.ts b/packages/backend/src/server/api/endpoints/i/delete-account.ts index 4b555376ba2c..741703e6dee9 100644 --- a/packages/backend/src/server/api/endpoints/i/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts @@ -1,8 +1,9 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; -import type { Users , UserProfiles } from '@/models/index.js'; +import type { Users, UserProfiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DeleteAccountService } from '@/services/DeleteAccountService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -22,10 +23,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, private deleteAccountService: DeleteAccountService, diff --git a/packages/backend/src/server/api/endpoints/i/favorites.ts b/packages/backend/src/server/api/endpoints/i/favorites.ts index 30fb5b1bc2ba..4a661037c46e 100644 --- a/packages/backend/src/server/api/endpoints/i/favorites.ts +++ b/packages/backend/src/server/api/endpoints/i/favorites.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { NoteFavorites } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { NoteFavoriteEntityService } from '@/services/entities/NoteFavoriteEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['account', 'notes', 'favorites'], @@ -36,7 +37,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('noteFavoritesRepository') + @Inject(DI.noteFavoritesRepository) private noteFavoritesRepository: typeof NoteFavorites, private noteFavoriteEntityService: NoteFavoriteEntityService, diff --git a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts index e905e5449282..c753aef1523f 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { GalleryLikes } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { GalleryLikeEntityService } from '@/services/entities/GalleryLikeEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['account', 'gallery'], @@ -47,7 +48,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('galleryLikesRepository') + @Inject(DI.galleryLikesRepository) private galleryLikesRepository: typeof GalleryLikes, private galleryLikeEntityService: GalleryLikeEntityService, diff --git a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts index d4eb46a9f31d..b811590eb471 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { GalleryPosts } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['account', 'gallery'], @@ -36,7 +37,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('galleryPostsRepository') + @Inject(DI.galleryPostsRepository) private galleryPostsRepository: typeof GalleryPosts, private galleryPostEntityService: GalleryPostEntityService, diff --git a/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts b/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts index 85b107f1fdd6..3368c76ae527 100644 --- a/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts +++ b/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { MutedNotes } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['account'], @@ -31,7 +32,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('mutedNotesRepository') + @Inject(DI.mutedNotesRepository) private mutedNotesRepository: typeof MutedNotes, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index 996ae21b43b0..f7441e54325b 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -1,6 +1,6 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { Users , Followings, Mutings, UserProfiles } from '@/models/index.js'; +import type { Users, Followings, Mutings, UserProfiles } from '@/models/index.js'; import { Notifications } from '@/models/index.js'; import { notificationTypes } from '@/types.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -8,6 +8,7 @@ import { QueryService } from '@/services/QueryService.js'; import { NoteReadService } from '@/services/NoteReadService.js'; import { NotificationEntityService } from '@/services/entities/NotificationEntityService.js'; import { NotificationService } from '@/services/NotificationService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['account', 'notifications'], @@ -55,16 +56,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, - @Inject('mutingsRepository') + @Inject(DI.mutingsRepository) private mutingsRepository: typeof Mutings, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, private notificationEntityService: NotificationEntityService, diff --git a/packages/backend/src/server/api/endpoints/i/page-likes.ts b/packages/backend/src/server/api/endpoints/i/page-likes.ts index 03a55e9723f9..191570c76d7d 100644 --- a/packages/backend/src/server/api/endpoints/i/page-likes.ts +++ b/packages/backend/src/server/api/endpoints/i/page-likes.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { PageLikes } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { PageLikeEntityService } from '@/services/entities/PageLikeEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['account', 'pages'], @@ -46,7 +47,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('pageLikesRepository') + @Inject(DI.pageLikesRepository) private pageLikesRepository: typeof PageLikes, private pageLikeEntityService: PageLikeEntityService, diff --git a/packages/backend/src/server/api/endpoints/i/pages.ts b/packages/backend/src/server/api/endpoints/i/pages.ts index 13761b2f8553..24c594395f4f 100644 --- a/packages/backend/src/server/api/endpoints/i/pages.ts +++ b/packages/backend/src/server/api/endpoints/i/pages.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Pages } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { PageEntityService } from '@/services/entities/PageEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['account', 'pages'], @@ -36,7 +37,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('pagesRepository') + @Inject(DI.pagesRepository) private pagesRepository: typeof Pages, private pageEntityService: PageEntityService, diff --git a/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts b/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts index cfb9580a2c7b..754ed7056837 100644 --- a/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts +++ b/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { MessagingMessages, UserGroupJoinings } from '@/models/index.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['account', 'messaging'], @@ -21,10 +22,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('messagingMessagesRepository') + @Inject(DI.messagingMessagesRepository) private messagingMessagesRepository: typeof MessagingMessages, - @Inject('userGroupJoiningsRepository') + @Inject(DI.userGroupJoiningsRepository) private userGroupJoiningsRepository: typeof UserGroupJoinings, private globalEventService: GlobalEventService, diff --git a/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts b/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts index 872c01f53acb..6cfd29aa5681 100644 --- a/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts +++ b/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { NoteUnreads } from '@/models/index.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['account'], @@ -21,7 +22,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('noteUnreadsRepository') + @Inject(DI.noteUnreadsRepository) private noteUnreadsRepository: typeof NoteUnreads, private globalEventService: GlobalEventService, diff --git a/packages/backend/src/server/api/endpoints/i/read-announcement.ts b/packages/backend/src/server/api/endpoints/i/read-announcement.ts index 0607a41fdb86..3b999a5dc938 100644 --- a/packages/backend/src/server/api/endpoints/i/read-announcement.ts +++ b/packages/backend/src/server/api/endpoints/i/read-announcement.ts @@ -1,9 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/services/IdService.js'; -import type { Users , AnnouncementReads, Announcements } from '@/models/index.js'; +import type { Users, AnnouncementReads, Announcements } from '@/models/index.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -34,10 +35,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('announcementsRepository') + @Inject(DI.announcementsRepository) private announcementsRepository: typeof Announcements, - @Inject('announcementReadsRepository') + @Inject(DI.announcementReadsRepository) private announcementReadsRepository: typeof AnnouncementReads, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts index ff7f6599f51c..40095d4a217f 100644 --- a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts +++ b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts @@ -1,9 +1,10 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users , UserProfiles } from '@/models/index.js'; +import type { Users, UserProfiles } from '@/models/index.js'; import generateUserToken from '@/misc/generate-native-user-token.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -23,10 +24,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, private globalEventService: GlobalEventService, diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts index fc66cabc1d9b..f9d4ffede847 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { RegistryItems } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -22,7 +23,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('registryItemsRepository') + @Inject(DI.registryItemsRepository) private registryItemsRepository: typeof RegistryItems, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts index 64f8397355de..59e02714638b 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { RegistryItems } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -32,7 +33,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('registryItemsRepository') + @Inject(DI.registryItemsRepository) private registryItemsRepository: typeof RegistryItems, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/i/registry/get.ts b/packages/backend/src/server/api/endpoints/i/registry/get.ts index 9c625d6aba2d..f3c8d5a1deb7 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { RegistryItems } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -32,7 +33,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('registryItemsRepository') + @Inject(DI.registryItemsRepository) private registryItemsRepository: typeof RegistryItems, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts index 4ce992a63efc..c0a32ac64dc9 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { RegistryItems } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -22,7 +23,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('registryItemsRepository') + @Inject(DI.registryItemsRepository) private registryItemsRepository: typeof RegistryItems, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys.ts b/packages/backend/src/server/api/endpoints/i/registry/keys.ts index fa28c40ffc5f..89bf033564b0 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/keys.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/keys.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { RegistryItems } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -22,7 +23,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('registryItemsRepository') + @Inject(DI.registryItemsRepository) private registryItemsRepository: typeof RegistryItems, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/i/registry/remove.ts b/packages/backend/src/server/api/endpoints/i/registry/remove.ts index b1177495f296..ccdb4dbbd479 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/remove.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/remove.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { RegistryItems } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -32,7 +33,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('registryItemsRepository') + @Inject(DI.registryItemsRepository) private registryItemsRepository: typeof RegistryItems, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/i/registry/scopes.ts b/packages/backend/src/server/api/endpoints/i/registry/scopes.ts index 99c7ec24c4ea..28ab2671e7cf 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/scopes.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/scopes.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { RegistryItems } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -18,7 +19,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('registryItemsRepository') + @Inject(DI.registryItemsRepository) private registryItemsRepository: typeof RegistryItems, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/i/registry/set.ts b/packages/backend/src/server/api/endpoints/i/registry/set.ts index a151c90a8407..cdc31c695d4f 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/set.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/set.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; -import { publishMainStream } from '@/services/stream.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { RegistryItems } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -27,7 +27,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('registryItemsRepository') + @Inject(DI.registryItemsRepository) private registryItemsRepository: typeof RegistryItems, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/i/revoke-token.ts b/packages/backend/src/server/api/endpoints/i/revoke-token.ts index 5ffade6ad60f..0b2ff48d333d 100644 --- a/packages/backend/src/server/api/endpoints/i/revoke-token.ts +++ b/packages/backend/src/server/api/endpoints/i/revoke-token.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { AccessTokens } from '@/models/index.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -21,7 +22,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('accessTokensRepository') + @Inject(DI.accessTokensRepository) private accessTokensRepository: typeof AccessTokens, private globalEventService: GlobalEventService, diff --git a/packages/backend/src/server/api/endpoints/i/signin-history.ts b/packages/backend/src/server/api/endpoints/i/signin-history.ts index 3ddc5e848a7e..a09fc4eaa53f 100644 --- a/packages/backend/src/server/api/endpoints/i/signin-history.ts +++ b/packages/backend/src/server/api/endpoints/i/signin-history.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Signins } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { SigninEntityService } from '@/services/entities/SigninEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, @@ -24,7 +25,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('signinsRepository') + @Inject(DI.signinsRepository) private signinsRepository: typeof Signins, private signinEntityService: SigninEntityService, diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts index 80a6f202b456..12fa51dbeed6 100644 --- a/packages/backend/src/server/api/endpoints/i/update-email.ts +++ b/packages/backend/src/server/api/endpoints/i/update-email.ts @@ -53,10 +53,10 @@ export default class extends Endpoint { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index c8ff35becaca..41c180910752 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -3,7 +3,7 @@ import * as mfm from 'mfm-js'; import { Inject, Injectable } from '@nestjs/common'; import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js'; import { extractHashtags } from '@/misc/extract-hashtags.js'; -import type { Users , DriveFiles , UserProfiles } from '@/models/index.js'; +import type { Users, DriveFiles, UserProfiles } from '@/models/index.js'; import { Pages } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import { birthdaySchema, descriptionSchema, locationSchema, nameSchema } from '@/models/entities/User.js'; @@ -17,6 +17,7 @@ import { GlobalEventService } from '@/services/GlobalEventService.js'; import { UserFollowingService } from '@/services/UserFollowingService.js'; import { AccountUpdateService } from '@/services/AccountUpdateService.js'; import { HashtagService } from '@/services/HashtagService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -129,13 +130,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/i/user-group-invites.ts b/packages/backend/src/server/api/endpoints/i/user-group-invites.ts index b9577987ab3d..9972a106dadb 100644 --- a/packages/backend/src/server/api/endpoints/i/user-group-invites.ts +++ b/packages/backend/src/server/api/endpoints/i/user-group-invites.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UserGroupInvitations } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { UserGroupInvitationEntityService } from '@/services/entities/UserGroupInvitationEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['account', 'groups'], @@ -47,7 +48,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userGroupInvitationsRepository') + @Inject(DI.userGroupInvitationsRepository) private userGroupInvitationsRepository: typeof UserGroupInvitations, private userGroupInvitationEntityService: UserGroupInvitationEntityService, diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts index 418bdf59bcb3..5fa67bb82ad2 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts @@ -4,6 +4,7 @@ import { IdService } from '@/services/IdService.js'; import type { Webhooks } from '@/models/index.js'; import { webhookEventTypes } from '@/models/entities/Webhook.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['webhooks'], @@ -32,7 +33,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('webhooksRepository') + @Inject(DI.webhooksRepository) private webhooksRepository: typeof Webhooks, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts b/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts index 900f74d5932f..6199a695b3a2 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Webhooks } from '@/models/index.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -34,7 +35,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('webhooksRepository') + @Inject(DI.webhooksRepository) private webhooksRepository: typeof Webhooks, private globalEventService: GlobalEventService, diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/list.ts b/packages/backend/src/server/api/endpoints/i/webhooks/list.ts index 5a1e2213505c..cbec4a693dca 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/list.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/list.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Webhooks } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['webhooks', 'account'], @@ -20,7 +21,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('webhooksRepository') + @Inject(DI.webhooksRepository) private webhooksRepository: typeof Webhooks, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/show.ts b/packages/backend/src/server/api/endpoints/i/webhooks/show.ts index 54d8c0a37ddd..0140e2e7cc6e 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/show.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/show.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Webhooks } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -31,7 +32,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('webhooksRepository') + @Inject(DI.webhooksRepository) private webhooksRepository: typeof Webhooks, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/update.ts b/packages/backend/src/server/api/endpoints/i/webhooks/update.ts index b195921e8d32..5898fc898ed6 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/update.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/update.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Webhooks } from '@/models/index.js'; -import { publishInternalEvent } from '@/services/stream.js'; import { webhookEventTypes } from '@/models/entities/Webhook.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -44,7 +44,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('webhooksRepository') + @Inject(DI.webhooksRepository) private webhooksRepository: typeof Webhooks, private globalEventService: GlobalEventService, diff --git a/packages/backend/src/server/api/endpoints/messaging/history.ts b/packages/backend/src/server/api/endpoints/messaging/history.ts index 9b37c1dc4d79..93115d9a52c7 100644 --- a/packages/backend/src/server/api/endpoints/messaging/history.ts +++ b/packages/backend/src/server/api/endpoints/messaging/history.ts @@ -2,8 +2,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Brackets } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; -import type { Mutings , UserGroupJoinings , MessagingMessages } from '@/models/index.js'; +import type { Mutings, UserGroupJoinings, MessagingMessages } from '@/models/index.js'; import { MessagingMessageEntityService } from '@/services/entities/MessagingMessageEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['messaging'], @@ -36,13 +37,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('messagingMessagesRepository') + @Inject(DI.messagingMessagesRepository) private messagingMessagesRepository: typeof MessagingMessages, - @Inject('mutingsRepository') + @Inject(DI.mutingsRepository) private mutingsRepository: typeof Mutings, - @Inject('userGroupJoiningsRepository') + @Inject(DI.userGroupJoiningsRepository) private userGroupJoiningsRepository: typeof UserGroupJoinings, private messagingMessageEntityService: MessagingMessageEntityService, diff --git a/packages/backend/src/server/api/endpoints/messaging/messages.ts b/packages/backend/src/server/api/endpoints/messaging/messages.ts index 5d8256ef93db..fa8fafa77015 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages.ts @@ -1,12 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { Brackets } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Users , MessagingMessages, UserGroupJoinings } from '@/models/index.js'; +import type { Users, MessagingMessages, UserGroupJoinings } from '@/models/index.js'; import { UserGroups } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { MessagingMessageEntityService } from '@/services/entities/MessagingMessageEntityService.js'; import { MessagingService } from '@/services/MessagingService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; @@ -76,10 +77,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('messagingMessagesRepository') + @Inject(DI.messagingMessagesRepository) private messagingMessagesRepository: typeof MessagingMessages, - @Inject('userGroupJoiningsRepository') + @Inject(DI.userGroupJoiningsRepository) private userGroupJoiningsRepository: typeof UserGroupJoinings, private messagingMessageEntityService: MessagingMessageEntityService, diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts index 1d74e1d6e5ed..8eb795f38632 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts @@ -1,13 +1,12 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { Blockings , UserGroupJoinings , DriveFiles , UserGroups } from '@/models/index.js'; +import type { Blockings, UserGroupJoinings, DriveFiles, UserGroups } from '@/models/index.js'; import { MessagingMessages } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import type { UserGroup } from '@/models/entities/UserGroup.js'; -import { createMessage } from '@/services/messages/create.js'; import { GetterService } from '@/server/api/common/GetterService.js'; import { MessagingService } from '@/services/MessagingService.js'; -import { getUser } from '../../../common/getters.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -94,16 +93,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userGroupsRepository') + @Inject(DI.userGroupsRepository) private userGroupsRepository: typeof UserGroups, - @Inject('userGroupJoiningsRepository') + @Inject(DI.userGroupJoiningsRepository) private userGroupJoiningsRepository: typeof UserGroupJoinings, - @Inject('blockingsRepository') + @Inject(DI.blockingsRepository) private blockingsRepository: typeof Blockings, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private getterService: GetterService, diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts b/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts index baeb01b79fa8..9a6acf8ae466 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts @@ -2,8 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { MessagingMessages } from '@/models/index.js'; -import { deleteMessage } from '@/services/messages/delete.js'; import { MessagingService } from '@/services/MessagingService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -40,7 +40,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('messagingMessagesRepository') + @Inject(DI.messagingMessagesRepository) private messagingMessagesRepository: typeof MessagingMessages, private messagingService: MessagingService, diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/read.ts b/packages/backend/src/server/api/endpoints/messaging/messages/read.ts index b7000ea69789..eb995420c1f7 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/read.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/read.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { MessagingMessages } from '@/models/index.js'; import { MessagingService } from '@/services/MessagingService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -32,7 +33,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('messagingMessagesRepository') + @Inject(DI.messagingMessagesRepository) private messagingMessagesRepository: typeof MessagingMessages, private messagingService: MessagingService, diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 42ae52eb287e..ec3bd40252b3 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -314,7 +314,7 @@ export default class extends Endpoint { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts index 06ce35a002ab..bf6048f3762e 100644 --- a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts +++ b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { AccessTokens } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['auth'], @@ -41,7 +42,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('accessTokensRepository') + @Inject(DI.accessTokensRepository) private accessTokensRepository: typeof AccessTokens, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/mute/create.ts b/packages/backend/src/server/api/endpoints/mute/create.ts index 85c1f5ec2060..4d7aff38ee79 100644 --- a/packages/backend/src/server/api/endpoints/mute/create.ts +++ b/packages/backend/src/server/api/endpoints/mute/create.ts @@ -4,6 +4,7 @@ import { IdService } from '@/services/IdService.js'; import type { Mutings } from '@/models/index.js'; import type { Muting } from '@/models/entities/Muting.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; @@ -52,7 +53,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('mutingsRepository') + @Inject(DI.mutingsRepository) private mutingsRepository: typeof Mutings, private globalEventService: GlobalEventService, diff --git a/packages/backend/src/server/api/endpoints/mute/delete.ts b/packages/backend/src/server/api/endpoints/mute/delete.ts index e37fbba0296d..cf4bc5cbfd4f 100644 --- a/packages/backend/src/server/api/endpoints/mute/delete.ts +++ b/packages/backend/src/server/api/endpoints/mute/delete.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Mutings } from '@/models/index.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; @@ -45,7 +46,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('mutingsRepository') + @Inject(DI.mutingsRepository) private mutingsRepository: typeof Mutings, private globalEventService: GlobalEventService, diff --git a/packages/backend/src/server/api/endpoints/mute/list.ts b/packages/backend/src/server/api/endpoints/mute/list.ts index 73401365eeb2..2da5af9e252f 100644 --- a/packages/backend/src/server/api/endpoints/mute/list.ts +++ b/packages/backend/src/server/api/endpoints/mute/list.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Mutings } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { MutingEntityService } from '@/services/entities/MutingEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['account'], @@ -36,7 +37,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('mutingsRepository') + @Inject(DI.mutingsRepository) private mutingsRepository: typeof Mutings, private mutingEntityService: MutingEntityService, diff --git a/packages/backend/src/server/api/endpoints/my/apps.ts b/packages/backend/src/server/api/endpoints/my/apps.ts index 5aae9cb909fc..3a9923dd81ad 100644 --- a/packages/backend/src/server/api/endpoints/my/apps.ts +++ b/packages/backend/src/server/api/endpoints/my/apps.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { Apps } from '@/models/index.js'; import { AppEntityService } from '@/services/entities/AppEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['account', 'app'], @@ -32,7 +33,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('appsRepository') + @Inject(DI.appsRepository) private appsRepository: typeof Apps, private appEntityService: AppEntityService, diff --git a/packages/backend/src/server/api/endpoints/notes.ts b/packages/backend/src/server/api/endpoints/notes.ts index 1a5fa4f2505d..857067881032 100644 --- a/packages/backend/src/server/api/endpoints/notes.ts +++ b/packages/backend/src/server/api/endpoints/notes.ts @@ -3,6 +3,7 @@ import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['notes'], @@ -37,7 +38,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index 275a7fb89e27..3a8972d8b77e 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -4,6 +4,7 @@ import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['notes'], @@ -36,7 +37,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/api/endpoints/notes/clips.ts b/packages/backend/src/server/api/endpoints/notes/clips.ts index bce0bf4f1700..e8636a59e435 100644 --- a/packages/backend/src/server/api/endpoints/notes/clips.ts +++ b/packages/backend/src/server/api/endpoints/notes/clips.ts @@ -3,6 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { ClipNotes, Clips } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ClipEntityService } from '@/services/entities/ClipEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; @@ -42,10 +43,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('clipsRepository') + @Inject(DI.clipsRepository) private clipsRepository: typeof Clips, - @Inject('clipNotesRepository') + @Inject(DI.clipNotesRepository) private clipNotesRepository: typeof ClipNotes, private clipEntityService: ClipEntityService, diff --git a/packages/backend/src/server/api/endpoints/notes/conversation.ts b/packages/backend/src/server/api/endpoints/notes/conversation.ts index 374e2bc66b31..8043b8d23c6b 100644 --- a/packages/backend/src/server/api/endpoints/notes/conversation.ts +++ b/packages/backend/src/server/api/endpoints/notes/conversation.ts @@ -3,6 +3,7 @@ import type { Note } from '@/models/entities/Note.js'; import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; @@ -44,7 +45,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 8b51384f8274..f640a48d6b17 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -2,7 +2,7 @@ import ms from 'ms'; import { In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { User } from '@/models/entities/User.js'; -import type { Users , Notes , Blockings } from '@/models/index.js'; +import type { Users, Notes, Blockings } from '@/models/index.js'; import { DriveFiles, Channels } from '@/models/index.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { Note } from '@/models/entities/Note.js'; @@ -11,6 +11,7 @@ import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { NoteCreateService } from '@/services/NoteCreateService.js'; +import { DI } from '@/di-symbols.js'; import { noteVisibilities } from '../../../../types.js'; import { ApiError } from '../../error.js'; @@ -167,13 +168,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('blockingsRepository') + @Inject(DI.blockingsRepository) private blockingsRepository: typeof Blockings, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/api/endpoints/notes/delete.ts b/packages/backend/src/server/api/endpoints/notes/delete.ts index 15520231f60f..62e7bfc8539f 100644 --- a/packages/backend/src/server/api/endpoints/notes/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/delete.ts @@ -3,6 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteDeleteService } from '@/services/NoteDeleteService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; @@ -46,7 +47,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private getterService: GetterService, diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts index 51ef02ee6619..f2f8a4d2a8ec 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts @@ -3,6 +3,7 @@ import type { NoteFavorites } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/common/GetterService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -39,7 +40,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('noteFavoritesRepository') + @Inject(DI.noteFavoritesRepository) private noteFavoritesRepository: typeof NoteFavorites, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts index 08650e6e0959..1b9f8ba1ffef 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { NoteFavorites } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/common/GetterService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -38,7 +39,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('noteFavoritesRepository') + @Inject(DI.noteFavoritesRepository) private noteFavoritesRepository: typeof NoteFavorites, private getterService: GetterService, diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index 313e6ddabd61..a78db1c625e5 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -3,6 +3,7 @@ import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['notes'], @@ -33,7 +34,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index 8f5baa93e114..c26cb908551b 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -5,6 +5,7 @@ import { QueryService } from '@/services/QueryService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { MetaService } from '@/services/MetaService.js'; import ActiveUsersChart from '@/services/chart/charts/active-users.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -50,7 +51,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 1a3d7b77d53d..210fc1f668d9 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -1,11 +1,12 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { Notes , Followings } from '@/models/index.js'; +import type { Notes, Followings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import ActiveUsersChart from '@/services/chart/charts/active-users.js'; import { MetaService } from '@/services/MetaService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -56,10 +57,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 324b9d2c8bc9..c334dea615ac 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -1,11 +1,12 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { Users , Notes } from '@/models/index.js'; +import type { Users, Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { MetaService } from '@/services/MetaService.js'; import ActiveUsersChart from '@/services/chart/charts/active-users.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -55,7 +56,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index 615e400577b7..05fb67e2df6e 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -1,11 +1,12 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { Notes , Followings } from '@/models/index.js'; +import type { Notes, Followings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { MetaService } from '@/services/MetaService.js'; import { NoteReadService } from '@/services/NoteReadService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['notes'], @@ -39,10 +40,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts index 189bb8a22b40..94ce7b4bc1fc 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts @@ -1,8 +1,9 @@ import { Brackets, In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { Notes , Mutings , Polls, PollVotes } from '@/models/index.js'; +import type { Notes, Mutings, Polls, PollVotes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['notes'], @@ -33,16 +34,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('pollsRepository') + @Inject(DI.pollsRepository) private pollsRepository: typeof Polls, - @Inject('pollVotesRepository') + @Inject(DI.pollVotesRepository) private pollVotesRepository: typeof PollVotes, - @Inject('mutingsRepository') + @Inject(DI.mutingsRepository) private mutingsRepository: typeof Mutings, private noteEntityService: NoteEntityService, 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 3159a4ea53c5..a00f7627e8c9 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -1,6 +1,6 @@ import { Not } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { Users , Blockings , Polls , PollVotes } from '@/models/index.js'; +import type { Users, Blockings, Polls, PollVotes } from '@/models/index.js'; import type { IRemoteUser } from '@/models/entities/User.js'; import { IdService } from '@/services/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -10,6 +10,7 @@ import { PollService } from '@/services/PollService.js'; import { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { CreateNotificationService } from '@/services/CreateNotificationService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -73,16 +74,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('blockingsRepository') + @Inject(DI.blockingsRepository) private blockingsRepository: typeof Blockings, - @Inject('pollsRepository') + @Inject(DI.pollsRepository) private pollsRepository: typeof Polls, - @Inject('pollVotesRepository') + @Inject(DI.pollVotesRepository) private pollVotesRepository: typeof PollVotes, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts index f123b8b64988..5b70fc504b34 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts @@ -4,6 +4,7 @@ import type { NoteReactions } from '@/models/index.js'; import type { NoteReaction } from '@/models/entities/NoteReaction.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteReactionEntityService } from '@/services/entities/NoteReactionEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import type { FindOptionsWhere } from 'typeorm'; @@ -51,7 +52,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('noteReactionsRepository') + @Inject(DI.noteReactionsRepository) private noteReactionsRepository: typeof NoteReactions, private noteReactionEntityService: NoteReactionEntityService, diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index c2c4c4ab0cf2..81c22dbec280 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -3,6 +3,7 @@ import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; @@ -45,7 +46,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index 9703c6b79d9e..dafcfdedba35 100644 --- a/packages/backend/src/server/api/endpoints/notes/replies.ts +++ b/packages/backend/src/server/api/endpoints/notes/replies.ts @@ -3,6 +3,7 @@ import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['notes'], @@ -35,7 +36,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts index d59d5f38cf8a..7428675445eb 100644 --- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts +++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts @@ -6,6 +6,7 @@ import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['notes', 'hashtags'], @@ -68,7 +69,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index 8809ebfdc75a..dad654393d53 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -54,7 +54,7 @@ export default class extends Endpoint { @Inject(DI.config) private config: Config, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts index d4e90b9824c2..cedbd03a8fd1 100644 --- a/packages/backend/src/server/api/endpoints/notes/show.ts +++ b/packages/backend/src/server/api/endpoints/notes/show.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; @@ -37,7 +38,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/api/endpoints/notes/state.ts b/packages/backend/src/server/api/endpoints/notes/state.ts index b9703e2898be..0c6699b94976 100644 --- a/packages/backend/src/server/api/endpoints/notes/state.ts +++ b/packages/backend/src/server/api/endpoints/notes/state.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { Notes , NoteThreadMutings } from '@/models/index.js'; +import type { Notes, NoteThreadMutings } from '@/models/index.js'; import { NoteFavorites } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['notes'], @@ -40,10 +41,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('noteThreadMutingsRepository') + @Inject(DI.noteThreadMutingsRepository) private noteThreadMutingsRepository: typeof NoteThreadMutings, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts index b7e9316bc832..d13fdc68f543 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts @@ -1,9 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { Notes , NoteThreadMutings } from '@/models/index.js'; +import type { Notes, NoteThreadMutings } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/common/GetterService.js'; import { NoteReadService } from '@/services/NoteReadService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -34,10 +35,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('noteThreadMutingsRepository') + @Inject(DI.noteThreadMutingsRepository) private noteThreadMutingsRepository: typeof NoteThreadMutings, private getterService: GetterService, diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts index d05376dacc2a..eb87b48452c3 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { NoteThreadMutings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/common/GetterService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -32,7 +33,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('noteThreadMutingsRepository') + @Inject(DI.noteThreadMutingsRepository) private noteThreadMutingsRepository: typeof NoteThreadMutings, private getterService: GetterService, diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 28a884a4cc27..1fd9c45cbc93 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -1,11 +1,12 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { Notes , Followings } from '@/models/index.js'; +import type { Notes, Followings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import ActiveUsersChart from '@/services/chart/charts/active-users.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { MetaService } from '@/services/MetaService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['notes'], @@ -47,10 +48,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts index ee7d6f29c8cf..a2fca95be913 100644 --- a/packages/backend/src/server/api/endpoints/notes/translate.ts +++ b/packages/backend/src/server/api/endpoints/notes/translate.ts @@ -4,7 +4,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { Config } from '@/config.js'; -import { DI } from '@/di-symbols.js'; +import { DI, DI } from '@/di-symbols.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import { MetaService } from '@/services/MetaService.js'; import { HttpRequestService } from '@/services/HttpRequestService.js'; @@ -46,7 +46,7 @@ export default class extends Endpoint { @Inject(DI.config) private config: Config, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/api/endpoints/notes/unrenote.ts b/packages/backend/src/server/api/endpoints/notes/unrenote.ts index 63339f1efc5c..76c7fdeb404e 100644 --- a/packages/backend/src/server/api/endpoints/notes/unrenote.ts +++ b/packages/backend/src/server/api/endpoints/notes/unrenote.ts @@ -1,8 +1,9 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import type { Users , Notes } from '@/models/index.js'; +import type { Users, Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteDeleteService } from '@/services/NoteDeleteService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; @@ -40,10 +41,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private getterService: GetterService, diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 100da776c3bd..f6fabd72571c 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -1,10 +1,11 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { Notes , UserLists, UserListJoinings } from '@/models/index.js'; +import type { Notes, UserLists, UserListJoinings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; import ActiveUsersChart from '@/services/chart/charts/active-users.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -56,13 +57,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('userListsRepository') + @Inject(DI.userListsRepository) private userListsRepository: typeof UserLists, - @Inject('userListJoiningsRepository') + @Inject(DI.userListJoiningsRepository) private userListJoiningsRepository: typeof UserListJoinings, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts index 2f0aeb95ee55..98879a12babd 100644 --- a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts @@ -3,6 +3,7 @@ import type { Notifications } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { PushNotificationService } from '@/services/PushNotificationService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['notifications', 'account'], @@ -22,7 +23,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notificationsRepository') + @Inject(DI.notificationsRepository) private notificationsRepository: typeof Notifications, private globalEventService: GlobalEventService, diff --git a/packages/backend/src/server/api/endpoints/page-push.ts b/packages/backend/src/server/api/endpoints/page-push.ts index 2aa872897867..22fcaf25e95a 100644 --- a/packages/backend/src/server/api/endpoints/page-push.ts +++ b/packages/backend/src/server/api/endpoints/page-push.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { Users , Pages } from '@/models/index.js'; +import type { Users, Pages } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../error.js'; export const meta = { @@ -32,7 +33,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('pagesRepository') + @Inject(DI.pagesRepository) private pagesRepository: typeof Pages, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/pages/create.ts b/packages/backend/src/server/api/endpoints/pages/create.ts index 498e43d6d4f2..15be200843bc 100644 --- a/packages/backend/src/server/api/endpoints/pages/create.ts +++ b/packages/backend/src/server/api/endpoints/pages/create.ts @@ -6,6 +6,7 @@ import { IdService } from '@/services/IdService.js'; import { Page } from '@/models/entities/Page.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { PageEntityService } from '@/services/entities/PageEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -65,7 +66,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('pagesRepository') + @Inject(DI.pagesRepository) private pagesRepository: typeof Pages, private pageEntityService: PageEntityService, diff --git a/packages/backend/src/server/api/endpoints/pages/delete.ts b/packages/backend/src/server/api/endpoints/pages/delete.ts index fc8f3deb206a..b0ab13622551 100644 --- a/packages/backend/src/server/api/endpoints/pages/delete.ts +++ b/packages/backend/src/server/api/endpoints/pages/delete.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Pages } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -37,7 +38,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('pagesRepository') + @Inject(DI.pagesRepository) private pagesRepository: typeof Pages, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/pages/featured.ts b/packages/backend/src/server/api/endpoints/pages/featured.ts index e47115b168dd..2f7e6068568c 100644 --- a/packages/backend/src/server/api/endpoints/pages/featured.ts +++ b/packages/backend/src/server/api/endpoints/pages/featured.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Pages } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { PageEntityService } from '@/services/entities/PageEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['pages'], @@ -29,7 +30,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('pagesRepository') + @Inject(DI.pagesRepository) private pagesRepository: typeof Pages, private pageEntityService: PageEntityService, diff --git a/packages/backend/src/server/api/endpoints/pages/like.ts b/packages/backend/src/server/api/endpoints/pages/like.ts index 63137ff2c93e..a2b19634c8a5 100644 --- a/packages/backend/src/server/api/endpoints/pages/like.ts +++ b/packages/backend/src/server/api/endpoints/pages/like.ts @@ -1,7 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { Pages , PageLikes } from '@/models/index.js'; +import type { Pages, PageLikes } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -44,10 +45,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('pagesRepository') + @Inject(DI.pagesRepository) private pagesRepository: typeof Pages, - @Inject('pageLikesRepository') + @Inject(DI.pageLikesRepository) private pageLikesRepository: typeof PageLikes, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/pages/show.ts b/packages/backend/src/server/api/endpoints/pages/show.ts index 4d231094aa6a..6a2051f2958d 100644 --- a/packages/backend/src/server/api/endpoints/pages/show.ts +++ b/packages/backend/src/server/api/endpoints/pages/show.ts @@ -1,9 +1,10 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { Users , Pages } from '@/models/index.js'; +import type { Users, Pages } from '@/models/index.js'; import type { Page } from '@/models/entities/Page.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { PageEntityService } from '@/services/entities/PageEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -49,10 +50,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('pagesRepository') + @Inject(DI.pagesRepository) private pagesRepository: typeof Pages, private pageEntityService: PageEntityService, diff --git a/packages/backend/src/server/api/endpoints/pages/unlike.ts b/packages/backend/src/server/api/endpoints/pages/unlike.ts index fd4e77c94ec6..432ebcd83562 100644 --- a/packages/backend/src/server/api/endpoints/pages/unlike.ts +++ b/packages/backend/src/server/api/endpoints/pages/unlike.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Pages, PageLikes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -37,10 +38,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('pagesRepository') + @Inject(DI.pagesRepository) private pagesRepository: typeof Pages, - @Inject('pageLikesRepository') + @Inject(DI.pageLikesRepository) private pageLikesRepository: typeof PageLikes, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/pages/update.ts b/packages/backend/src/server/api/endpoints/pages/update.ts index 4e446b129e24..21c75740b84d 100644 --- a/packages/backend/src/server/api/endpoints/pages/update.ts +++ b/packages/backend/src/server/api/endpoints/pages/update.ts @@ -1,8 +1,9 @@ import ms from 'ms'; import { Not } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { Pages , DriveFiles } from '@/models/index.js'; +import type { Pages, DriveFiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -69,10 +70,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('pagesRepository') + @Inject(DI.pagesRepository) private pagesRepository: typeof Pages, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/pinned-users.ts b/packages/backend/src/server/api/endpoints/pinned-users.ts index 31a509e2a5b4..301cfdf90ceb 100644 --- a/packages/backend/src/server/api/endpoints/pinned-users.ts +++ b/packages/backend/src/server/api/endpoints/pinned-users.ts @@ -6,6 +6,7 @@ import type { User } from '@/models/entities/User.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { MetaService } from '@/services/MetaService.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['users'], @@ -33,7 +34,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private metaService: MetaService, diff --git a/packages/backend/src/server/api/endpoints/promo/read.ts b/packages/backend/src/server/api/endpoints/promo/read.ts index 741bd2b7a92c..2ae8979dbd7a 100644 --- a/packages/backend/src/server/api/endpoints/promo/read.ts +++ b/packages/backend/src/server/api/endpoints/promo/read.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { PromoReads } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; @@ -31,7 +32,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('promoReadsRepository') + @Inject(DI.promoReadsRepository) private promoReadsRepository: typeof PromoReads, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/request-reset-password.ts b/packages/backend/src/server/api/endpoints/request-reset-password.ts index 05cfb08ff1bb..eadc662b8b83 100644 --- a/packages/backend/src/server/api/endpoints/request-reset-password.ts +++ b/packages/backend/src/server/api/endpoints/request-reset-password.ts @@ -7,7 +7,7 @@ import { UserProfiles, PasswordResetRequests } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/services/IdService.js'; import { Config } from '@/config.js'; -import { DI } from '@/di-symbols.js'; +import { DI, DI } from '@/di-symbols.js'; import { EmailService } from '@/services/EmailService.js'; import { ApiError } from '../error.js'; @@ -44,7 +44,7 @@ export default class extends Endpoint { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/reset-password.ts b/packages/backend/src/server/api/endpoints/reset-password.ts index b297c3f7da52..37938f4a5b5f 100644 --- a/packages/backend/src/server/api/endpoints/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/reset-password.ts @@ -1,8 +1,8 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; -import { publishMainStream } from '@/services/stream.js'; -import type { Users , UserProfiles, PasswordResetRequests } from '@/models/index.js'; +import type { Users, UserProfiles, PasswordResetRequests } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../error.js'; export const meta = { @@ -30,10 +30,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('passwordResetRequestsRepository') + @Inject(DI.passwordResetRequestsRepository) private passwordResetRequestsRepository: typeof PasswordResetRequests, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/stats.ts b/packages/backend/src/server/api/endpoints/stats.ts index 55c39d3d2e9a..2a0bc689679d 100644 --- a/packages/backend/src/server/api/endpoints/stats.ts +++ b/packages/backend/src/server/api/endpoints/stats.ts @@ -3,6 +3,7 @@ import { IsNull } from 'typeorm'; import type { Notes, Users } from '@/models/index.js'; import { Instances, NoteReactions } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: false, @@ -55,10 +56,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, ) { super(meta, paramDef, async () => { diff --git a/packages/backend/src/server/api/endpoints/sw/register.ts b/packages/backend/src/server/api/endpoints/sw/register.ts index 18eb799d7305..00f517e9b8f7 100644 --- a/packages/backend/src/server/api/endpoints/sw/register.ts +++ b/packages/backend/src/server/api/endpoints/sw/register.ts @@ -3,6 +3,7 @@ import { IdService } from '@/services/IdService.js'; import type { SwSubscriptions } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { MetaService } from '@/services/MetaService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['account'], @@ -42,7 +43,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('swSubscriptionsRepository') + @Inject(DI.swSubscriptionsRepository) private swSubscriptionsRepository: typeof SwSubscriptions, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/sw/unregister.ts b/packages/backend/src/server/api/endpoints/sw/unregister.ts index d2cb75681799..59795f98ff24 100644 --- a/packages/backend/src/server/api/endpoints/sw/unregister.ts +++ b/packages/backend/src/server/api/endpoints/sw/unregister.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { SwSubscriptions } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['account'], @@ -22,7 +23,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('swSubscriptionsRepository') + @Inject(DI.swSubscriptionsRepository) private swSubscriptionsRepository: typeof SwSubscriptions, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/username/available.ts b/packages/backend/src/server/api/endpoints/username/available.ts index 6ba5ce9cea18..af60787477ac 100644 --- a/packages/backend/src/server/api/endpoints/username/available.ts +++ b/packages/backend/src/server/api/endpoints/username/available.ts @@ -1,8 +1,9 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { UsedUsernames , Users } from '@/models/index.js'; +import type { UsedUsernames, Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { localUsernameSchema } from '@/models/entities/User.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['users'], @@ -33,10 +34,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('usedUsernamesRepository') + @Inject(DI.usedUsernamesRepository) private usedUsernamesRepository: typeof UsedUsernames, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/users.ts b/packages/backend/src/server/api/endpoints/users.ts index 744fb36acd16..1546dd8dbfa2 100644 --- a/packages/backend/src/server/api/endpoints/users.ts +++ b/packages/backend/src/server/api/endpoints/users.ts @@ -3,6 +3,7 @@ import type { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['users'], @@ -42,7 +43,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/clips.ts b/packages/backend/src/server/api/endpoints/users/clips.ts index 12a91fd7a106..b09960838a1c 100644 --- a/packages/backend/src/server/api/endpoints/users/clips.ts +++ b/packages/backend/src/server/api/endpoints/users/clips.ts @@ -3,6 +3,7 @@ import type { Clips } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { ClipEntityService } from '@/services/entities/ClipEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['users', 'clips'], @@ -35,7 +36,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('clipsRepository') + @Inject(DI.clipsRepository) private clipsRepository: typeof Clips, private clipEntityService: ClipEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index 771f8044f877..5ef5b423b524 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -1,10 +1,11 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { Users , Followings, UserProfiles } from '@/models/index.js'; +import type { Users, Followings, UserProfiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { FollowingEntityService } from '@/services/entities/FollowingEntityService.js'; import { UtilityService } from '@/services/UtilityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -71,13 +72,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, private utilityService: UtilityService, diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index 62e4318a81e8..37d8dab115a9 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -1,10 +1,11 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { Users , Followings , UserProfiles } from '@/models/index.js'; +import type { Users, Followings, UserProfiles } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { FollowingEntityService } from '@/services/entities/FollowingEntityService.js'; import { UtilityService } from '@/services/UtilityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -71,13 +72,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, private utilityService: UtilityService, diff --git a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts index 5a371642d897..260c144e1cb5 100644 --- a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts @@ -3,6 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { GalleryPosts } from '@/models/index.js'; import { QueryService } from '@/services/QueryService.js'; import { GalleryPostEntityService } from '@/services/entities/GalleryPostEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['users', 'gallery'], @@ -35,7 +36,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('galleryPostsRepository') + @Inject(DI.galleryPostsRepository) private galleryPostsRepository: typeof GalleryPosts, private galleryPostEntityService: GalleryPostEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts index 36af0af05d0b..063fbcf80d1b 100644 --- a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts +++ b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts @@ -4,6 +4,7 @@ import { maximum } from '@/prelude/array.js'; import type { Notes, Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; @@ -56,10 +57,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/groups/create.ts b/packages/backend/src/server/api/endpoints/users/groups/create.ts index 2b07dd461047..ee2510937d37 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/create.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/create.ts @@ -5,6 +5,7 @@ import type { UserGroup } from '@/models/entities/UserGroup.js'; import type { UserGroupJoining } from '@/models/entities/UserGroupJoining.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserGroupEntityService } from '@/services/entities/UserGroupEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['groups'], @@ -34,10 +35,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userGroupsRepository') + @Inject(DI.userGroupsRepository) private userGroupsRepository: typeof UserGroups, - @Inject('userGroupJoiningsRepository') + @Inject(DI.userGroupJoiningsRepository) private userGroupJoiningsRepository: typeof UserGroupJoinings, private userGroupEntityService: UserGroupEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/groups/delete.ts b/packages/backend/src/server/api/endpoints/users/groups/delete.ts index 6ce2c8534edb..cd7035b9abb7 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/delete.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/delete.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserGroups } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -33,7 +34,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userGroupsRepository') + @Inject(DI.userGroupsRepository) private userGroupsRepository: typeof UserGroups, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts b/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts index 641322479d1a..586214108472 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { UserGroupInvitations , UserGroupJoinings } from '@/models/index.js'; +import type { UserGroupInvitations, UserGroupJoinings } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; import type { UserGroupJoining } from '@/models/entities/UserGroupJoining.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../../error.js'; export const meta = { @@ -35,10 +36,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userGroupInvitationsRepository') + @Inject(DI.userGroupInvitationsRepository) private userGroupInvitationsRepository: typeof UserGroupInvitations, - @Inject('userGroupJoiningsRepository') + @Inject(DI.userGroupJoiningsRepository) private userGroupJoiningsRepository: typeof UserGroupJoinings, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts b/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts index 2c8e4b732564..eb05b3ddf8b4 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserGroupInvitations } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../../error.js'; export const meta = { @@ -33,7 +34,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userGroupInvitationsRepository') + @Inject(DI.userGroupInvitationsRepository) private userGroupInvitationsRepository: typeof UserGroupInvitations, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/users/groups/invite.ts b/packages/backend/src/server/api/endpoints/users/groups/invite.ts index 4523e273a545..a245e3b3808e 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invite.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invite.ts @@ -5,6 +5,7 @@ import type { UserGroupInvitation } from '@/models/entities/UserGroupInvitation. import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/common/GetterService.js'; import { CreateNotificationService } from '@/services/CreateNotificationService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -56,13 +57,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userGroupsRepository') + @Inject(DI.userGroupsRepository) private userGroupsRepository: typeof UserGroups, - @Inject('userGroupInvitationsRepository') + @Inject(DI.userGroupInvitationsRepository) private userGroupInvitationsRepository: typeof UserGroupInvitations, - @Inject('userGroupJoiningsRepository') + @Inject(DI.userGroupJoiningsRepository) private userGroupJoiningsRepository: typeof UserGroupJoinings, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/users/groups/joined.ts b/packages/backend/src/server/api/endpoints/users/groups/joined.ts index 763f78d5b0d3..751281c919ff 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/joined.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/joined.ts @@ -3,6 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserGroupEntityService } from '@/services/entities/UserGroupEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['groups', 'account'], @@ -34,10 +35,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userGroupsRepository') + @Inject(DI.userGroupsRepository) private userGroupsRepository: typeof UserGroups, - @Inject('userGroupJoiningsRepository') + @Inject(DI.userGroupJoiningsRepository) private userGroupJoiningsRepository: typeof UserGroupJoinings, private userGroupEntityService: UserGroupEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/groups/leave.ts b/packages/backend/src/server/api/endpoints/users/groups/leave.ts index 60e95eb3a105..8be2665bd2de 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/leave.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/leave.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -39,10 +40,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userGroupsRepository') + @Inject(DI.userGroupsRepository) private userGroupsRepository: typeof UserGroups, - @Inject('userGroupJoiningsRepository') + @Inject(DI.userGroupJoiningsRepository) private userGroupJoiningsRepository: typeof UserGroupJoinings, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/users/groups/owned.ts b/packages/backend/src/server/api/endpoints/users/groups/owned.ts index e6264f595a25..1c0c7a3b65ef 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/owned.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/owned.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserGroups } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserGroupEntityService } from '@/services/entities/UserGroupEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['groups', 'account'], @@ -33,7 +34,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userGroupsRepository') + @Inject(DI.userGroupsRepository) private userGroupsRepository: typeof UserGroups, private userGroupEntityService: UserGroupEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/groups/pull.ts b/packages/backend/src/server/api/endpoints/users/groups/pull.ts index 54331920839a..50f64406779a 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/pull.ts @@ -2,8 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/common/GetterService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; -import { getUser } from '../../../common/getters.js'; export const meta = { tags: ['groups', 'users'], @@ -48,10 +48,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userGroupsRepository') + @Inject(DI.userGroupsRepository) private userGroupsRepository: typeof UserGroups, - @Inject('userGroupJoiningsRepository') + @Inject(DI.userGroupJoiningsRepository) private userGroupJoiningsRepository: typeof UserGroupJoinings, private getterService: GetterService, diff --git a/packages/backend/src/server/api/endpoints/users/groups/show.ts b/packages/backend/src/server/api/endpoints/users/groups/show.ts index 49c7c1b89687..537d58d95af0 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/show.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/show.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserGroupEntityService } from '@/services/entities/UserGroupEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -40,10 +41,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userGroupsRepository') + @Inject(DI.userGroupsRepository) private userGroupsRepository: typeof UserGroups, - @Inject('userGroupJoiningsRepository') + @Inject(DI.userGroupJoiningsRepository) private userGroupJoiningsRepository: typeof UserGroupJoinings, private userGroupEntityService: UserGroupEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/groups/transfer.ts b/packages/backend/src/server/api/endpoints/users/groups/transfer.ts index dfad41de8eaa..49315a7c1326 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/transfer.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/transfer.ts @@ -3,6 +3,7 @@ import type { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserGroupEntityService } from '@/services/entities/UserGroupEntityService.js'; import { GetterService } from '@/server/api/common/GetterService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -54,10 +55,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userGroupsRepository') + @Inject(DI.userGroupsRepository) private userGroupsRepository: typeof UserGroups, - @Inject('userGroupJoiningsRepository') + @Inject(DI.userGroupJoiningsRepository) private userGroupJoiningsRepository: typeof UserGroupJoinings, private userGroupEntityService: UserGroupEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/groups/update.ts b/packages/backend/src/server/api/endpoints/users/groups/update.ts index deef462fd007..19dd03cf3a19 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/update.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/update.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserGroups } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserGroupEntityService } from '@/services/entities/UserGroupEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -41,7 +42,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userGroupsRepository') + @Inject(DI.userGroupsRepository) private userGroupsRepository: typeof UserGroups, private userGroupEntityService: UserGroupEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/lists/create.ts b/packages/backend/src/server/api/endpoints/users/lists/create.ts index 7ed71893fad9..72d798374035 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts @@ -4,6 +4,7 @@ import { IdService } from '@/services/IdService.js'; import type { UserList } from '@/models/entities/UserList.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserListEntityService } from '@/services/entities/UserListEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['lists'], @@ -33,7 +34,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userListsRepository') + @Inject(DI.userListsRepository) private userListsRepository: typeof UserLists, private userListEntityService: UserListEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/lists/delete.ts b/packages/backend/src/server/api/endpoints/users/lists/delete.ts index 9053965aa2d0..b3d16dd5b0c2 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/delete.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/delete.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserLists } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -33,7 +34,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userListsRepository') + @Inject(DI.userListsRepository) private userListsRepository: typeof UserLists, ) { super(meta, paramDef, async (ps, me) => { diff --git a/packages/backend/src/server/api/endpoints/users/lists/list.ts b/packages/backend/src/server/api/endpoints/users/lists/list.ts index f36e32ae8837..81b69c0832ab 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/list.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/list.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserLists } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserListEntityService } from '@/services/entities/UserListEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['lists', 'account'], @@ -33,7 +34,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userListsRepository') + @Inject(DI.userListsRepository) private userListsRepository: typeof UserLists, private userListEntityService: UserListEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/lists/pull.ts b/packages/backend/src/server/api/endpoints/users/lists/pull.ts index 5665af37aaa8..793292ed54d0 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/pull.ts @@ -5,6 +5,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { GetterService } from '@/server/api/common/GetterService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -44,10 +45,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userListsRepository') + @Inject(DI.userListsRepository) private userListsRepository: typeof UserLists, - @Inject('userListJoiningsRepository') + @Inject(DI.userListJoiningsRepository) private userListJoiningsRepository: typeof UserListJoinings, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/lists/push.ts b/packages/backend/src/server/api/endpoints/users/lists/push.ts index 99248c411269..347e1532a94e 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/push.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/push.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { UserLists, UserListJoinings , Blockings } from '@/models/index.js'; +import type { UserLists, UserListJoinings, Blockings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/common/GetterService.js'; import { UserListService } from '@/services/UserListService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -54,13 +55,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userListsRepository') + @Inject(DI.userListsRepository) private userListsRepository: typeof UserLists, - @Inject('userListJoiningsRepository') + @Inject(DI.userListJoiningsRepository) private userListJoiningsRepository: typeof UserListJoinings, - @Inject('blockingsRepository') + @Inject(DI.blockingsRepository) private blockingsRepository: typeof Blockings, private getterService: GetterService, diff --git a/packages/backend/src/server/api/endpoints/users/lists/show.ts b/packages/backend/src/server/api/endpoints/users/lists/show.ts index eba72cc41d89..356517457875 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/show.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/show.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserLists } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserListEntityService } from '@/services/entities/UserListEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -40,7 +41,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userListsRepository') + @Inject(DI.userListsRepository) private userListsRepository: typeof UserLists, private userListEntityService: UserListEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/lists/update.ts b/packages/backend/src/server/api/endpoints/users/lists/update.ts index 28d751277619..f3d726c8158b 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/update.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/update.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UserLists } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserListEntityService } from '@/services/entities/UserListEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -41,7 +42,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userListsRepository') + @Inject(DI.userListsRepository) private userListsRepository: typeof UserLists, private userListEntityService: UserListEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index e987d8387cc0..8eef36b8b060 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -4,6 +4,7 @@ import type { Notes } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; @@ -55,7 +56,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts index 6c7d83a8d715..9425c1d32e1b 100644 --- a/packages/backend/src/server/api/endpoints/users/reactions.ts +++ b/packages/backend/src/server/api/endpoints/users/reactions.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { UserProfiles , NoteReactions } from '@/models/index.js'; +import type { UserProfiles, NoteReactions } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { NoteReactionEntityService } from '@/services/entities/NoteReactionEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -48,10 +49,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, - @Inject('noteReactionsRepository') + @Inject(DI.noteReactionsRepository) private noteReactionsRepository: typeof NoteReactions, private noteReactionEntityService: NoteReactionEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/recommendation.ts b/packages/backend/src/server/api/endpoints/users/recommendation.ts index e40e9ba3ea1a..293ccee07e71 100644 --- a/packages/backend/src/server/api/endpoints/users/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/users/recommendation.ts @@ -1,9 +1,10 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import type { Users , Followings } from '@/models/index.js'; +import type { Users, Followings } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/services/QueryService.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['users'], @@ -38,10 +39,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/relation.ts b/packages/backend/src/server/api/endpoints/users/relation.ts index 87165a6fd704..b323b19c2c8b 100644 --- a/packages/backend/src/server/api/endpoints/users/relation.ts +++ b/packages/backend/src/server/api/endpoints/users/relation.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['users'], @@ -117,7 +118,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts index a5b017dced0b..ed4589e45b4a 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -1,11 +1,12 @@ import * as sanitizeHtml from 'sanitize-html'; import { Inject, Injectable } from '@nestjs/common'; -import type { Users , AbuseUserReports } from '@/models/index.js'; +import type { Users, AbuseUserReports } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { MetaService } from '@/services/MetaService.js'; import { EmailService } from '@/services/EmailService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; @@ -50,10 +51,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('abuseUserReportsRepository') + @Inject(DI.abuseUserReportsRepository) private abuseUserReportsRepository: typeof AbuseUserReports, private idService: IdService, diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index 6c9d0f641554..39977984081e 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -1,10 +1,11 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { Users , Followings } from '@/models/index.js'; +import type { Users, Followings } from '@/models/index.js'; import { USER_ACTIVE_THRESHOLD } from '@/const.js'; import type { User } from '@/models/entities/User.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['users'], @@ -44,10 +45,10 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index 9d0e9c2360fd..b7e85dc08133 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -5,6 +5,7 @@ import { UserProfiles } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { DI } from '@/di-symbols.js'; export const meta = { tags: ['users'], @@ -40,7 +41,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts index 0300f59a624c..f43600256820 100644 --- a/packages/backend/src/server/api/endpoints/users/show.ts +++ b/packages/backend/src/server/api/endpoints/users/show.ts @@ -5,6 +5,7 @@ import type { User } from '@/models/entities/User.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; import { ResolveUserService } from '@/services/remote/ResolveUserService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { ApiLoggerService } from '../../ApiLoggerService.js'; import type { FindOptionsWhere } from 'typeorm'; @@ -84,7 +85,7 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/endpoints/users/stats.ts b/packages/backend/src/server/api/endpoints/users/stats.ts index 82d01354a102..0f9b7afdada4 100644 --- a/packages/backend/src/server/api/endpoints/users/stats.ts +++ b/packages/backend/src/server/api/endpoints/users/stats.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { Users , Notes , Followings , DriveFiles, NoteFavorites, NoteReactions, PageLikes, PollVotes } from '@/models/index.js'; +import type { Users, Notes, Followings, DriveFiles, NoteFavorites, NoteReactions, PageLikes, PollVotes } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; +import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -121,28 +122,28 @@ export const paramDef = { @Injectable() export default class extends Endpoint { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, - @Inject('noteReactionsRepository') + @Inject(DI.noteReactionsRepository) private noteReactionsRepository: typeof NoteReactions, - @Inject('pageLikesRepository') + @Inject(DI.pageLikesRepository) private pageLikesRepository: typeof PageLikes, - @Inject('noteFavoritesRepository') + @Inject(DI.noteFavoritesRepository) private noteFavoritesRepository: typeof NoteFavorites, - @Inject('pollVotesRepository') + @Inject(DI.pollVotesRepository) private pollVotesRepository: typeof PollVotes, private driveFileEntityService: DriveFileEntityService, diff --git a/packages/backend/src/server/api/integration/DiscordServerService.ts b/packages/backend/src/server/api/integration/DiscordServerService.ts index 1d4dc6f253ac..ad5bec66511c 100644 --- a/packages/backend/src/server/api/integration/DiscordServerService.ts +++ b/packages/backend/src/server/api/integration/DiscordServerService.ts @@ -5,7 +5,7 @@ import { OAuth2 } from 'oauth'; import { v4 as uuid } from 'uuid'; import { IsNull } from 'typeorm'; import { Config } from '@/config.js'; -import type { UserProfiles , Users } from '@/models/index.js'; +import type { UserProfiles, Users } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { HttpRequestService } from '@/services/HttpRequestService.js'; import type { ILocalUser } from '@/models/entities/User.js'; @@ -24,10 +24,10 @@ export class DiscordServerService { @Inject(DI.redis) private redisClient: Redis, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/integration/GithubServerService.ts b/packages/backend/src/server/api/integration/GithubServerService.ts index 739a385df34f..25904f522a58 100644 --- a/packages/backend/src/server/api/integration/GithubServerService.ts +++ b/packages/backend/src/server/api/integration/GithubServerService.ts @@ -24,10 +24,10 @@ export class GithubServerService { @Inject(DI.redis) private redisClient: Redis, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/integration/TwitterServerService.ts b/packages/backend/src/server/api/integration/TwitterServerService.ts index c48e9148a099..fdd60e5c249b 100644 --- a/packages/backend/src/server/api/integration/TwitterServerService.ts +++ b/packages/backend/src/server/api/integration/TwitterServerService.ts @@ -25,10 +25,10 @@ export class TwitterServerService { @Inject(DI.redis) private redisClient: Redis, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/stream/channels/messaging.ts b/packages/backend/src/server/api/stream/channels/messaging.ts index 50e43acd57f8..4def51690243 100644 --- a/packages/backend/src/server/api/stream/channels/messaging.ts +++ b/packages/backend/src/server/api/stream/channels/messaging.ts @@ -1,9 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { UserGroupJoinings , Users , MessagingMessages } from '@/models/index.js'; +import type { UserGroupJoinings, Users, MessagingMessages } from '@/models/index.js'; import type { User, ILocalUser, IRemoteUser } from '@/models/entities/User.js'; import type { UserGroup } from '@/models/entities/UserGroup.js'; import { MessagingService } from '@/services/MessagingService.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; +import { DI } from '@/di-symbols.js'; import Channel from '../channel.js'; import type { StreamMessages } from '../types.js'; @@ -123,13 +124,13 @@ export class MessagingChannelService { public readonly requireCredential = MessagingChannel.requireCredential; constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userGroupJoiningsRepository') + @Inject(DI.userGroupJoiningsRepository) private userGroupJoiningsRepository: typeof UserGroupJoinings, - @Inject('messagingMessagesRepository') + @Inject(DI.messagingMessagesRepository) private messagingMessagesRepository: typeof MessagingMessages, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts index ed87bf7b5cdb..6eb3bc386a43 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -4,6 +4,7 @@ import type { User } from '@/models/entities/User.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import type { Packed } from '@/misc/schema.js'; import { NoteEntityService } from '@/services/entities/NoteEntityService.js'; +import { DI } from '@/di-symbols.js'; import Channel from '../channel.js'; class UserListChannel extends Channel { @@ -106,10 +107,10 @@ export class UserListChannelService { public readonly requireCredential = UserListChannel.requireCredential; constructor( - @Inject('userListsRepository') + @Inject(DI.userListsRepository) private userListsRepository: typeof UserLists, - @Inject('userListJoiningsRepository') + @Inject(DI.userListJoiningsRepository) private userListJoiningsRepository: typeof UserListJoinings, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 71ea3c453060..a82cd1c654d6 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -14,7 +14,7 @@ import { BullAdapter } from '@bull-board/api/bullAdapter.js'; import { KoaAdapter } from '@bull-board/koa'; import { In, IsNull } from 'typeorm'; import { Config } from '@/config.js'; -import type { Pages , Channels, Clips, GalleryPosts , Notes, UserProfiles, Users } from '@/models/index.js'; +import type { Pages, Channels, Clips, GalleryPosts, Notes, UserProfiles, Users } from '@/models/index.js'; import { getNoteSummary } from '@/misc/get-note-summary.js'; import { DI } from '@/di-symbols.js'; import * as Acct from '@/misc/acct.js'; @@ -44,25 +44,25 @@ export class ClientServerService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('galleryPostsRepository') + @Inject(DI.galleryPostsRepository) private galleryPostsRepository: typeof GalleryPosts, - @Inject('channelsRepository') + @Inject(DI.channelsRepository) private channelsRepository: typeof Channels, - @Inject('clipsRepository') + @Inject(DI.clipsRepository) private clipsRepository: typeof Clips, - @Inject('pagesRepository') + @Inject(DI.pagesRepository) private pagesRepository: typeof Pages, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts index 201582ece127..88da4ce3984b 100644 --- a/packages/backend/src/server/web/FeedService.ts +++ b/packages/backend/src/server/web/FeedService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, IsNull } from 'typeorm'; import { Feed } from 'feed'; import { DI } from '@/di-symbols.js'; -import type { DriveFiles, Notes, UserProfiles , Users } from '@/models/index.js'; +import type { DriveFiles, Notes, UserProfiles, Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type { User } from '@/models/entities/User.js'; import { UserEntityService } from '@/services/entities/UserEntityService.js'; @@ -14,16 +14,16 @@ export class FeedService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private userEntityService: UserEntityService, diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts index a3ceb851aedd..55bec5a8efd1 100644 --- a/packages/backend/src/server/web/UrlPreviewService.ts +++ b/packages/backend/src/server/web/UrlPreviewService.ts @@ -17,7 +17,7 @@ export class UrlPreviewService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private metaService: MetaService, diff --git a/packages/backend/src/services/AccountUpdateService.ts b/packages/backend/src/services/AccountUpdateService.ts index f4694f78e985..6d0a503a36ef 100644 --- a/packages/backend/src/services/AccountUpdateService.ts +++ b/packages/backend/src/services/AccountUpdateService.ts @@ -14,7 +14,7 @@ export class AccountUpdateService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/AntennaService.ts b/packages/backend/src/services/AntennaService.ts index ca7ded26807f..a1194ff72e55 100644 --- a/packages/backend/src/services/AntennaService.ts +++ b/packages/backend/src/services/AntennaService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import Redis from 'ioredis'; -import type { UserGroupJoinings, UserListJoinings , AntennaNotes, Mutings, Notes, Users , Blockings, Antennas } from '@/models/index.js'; +import type { UserGroupJoinings, UserListJoinings, AntennaNotes, Mutings, Notes, Users, Blockings, Antennas } from '@/models/index.js'; import type { Antenna } from '@/models/entities/Antenna.js'; import type { Note } from '@/models/entities/Note.js'; import type { User } from '@/models/entities/User.js'; @@ -24,25 +24,25 @@ export class AntennaService implements OnApplicationShutdown { @Inject(DI.redisSubscriber) private redisSubscriber: Redis.Redis, - @Inject('mutingsRepository') + @Inject(DI.mutingsRepository) private mutingsRepository: typeof Mutings, - @Inject('blockingsRepository') + @Inject(DI.blockingsRepository) private blockingsRepository: typeof Blockings, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('antennaNotesRepository') + @Inject(DI.antennaNotesRepository) private antennaNotesRepository: typeof AntennaNotes, - @Inject('antennasRepository') + @Inject(DI.antennasRepository) private antennasRepository: typeof Antennas, - @Inject('userGroupJoiningsRepository') + @Inject(DI.userGroupJoiningsRepository) private userGroupJoiningsRepository: typeof UserGroupJoinings, - @Inject('userListJoiningsRepository') + @Inject(DI.userListJoiningsRepository) private userListJoiningsRepository: typeof UserListJoinings, private utilityService: UtilityService, diff --git a/packages/backend/src/services/CreateNotificationService.ts b/packages/backend/src/services/CreateNotificationService.ts index c78bc784d9fc..2f09c9d3d942 100644 --- a/packages/backend/src/services/CreateNotificationService.ts +++ b/packages/backend/src/services/CreateNotificationService.ts @@ -1,25 +1,26 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { Mutings, Notifications, UserProfiles , Users } from '@/models/index.js'; +import type { Mutings, Notifications, UserProfiles, Users } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import type { Notification } from '@/models/entities/Notification.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import { IdService } from '@/services/IdService.js'; +import { DI } from '@/di-symbols.js'; import { NotificationEntityService } from './entities/NotificationEntityService.js'; import { PushNotificationService } from './PushNotificationService.js'; @Injectable() export class CreateNotificationService { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, - @Inject('notificationsRepository') + @Inject(DI.notificationsRepository) private notificationsRepository: typeof Notifications, - @Inject('mutingsRepository') + @Inject(DI.mutingsRepository) private mutingsRepository: typeof Mutings, private notificationEntityService: NotificationEntityService, diff --git a/packages/backend/src/services/CreateSystemUserService.ts b/packages/backend/src/services/CreateSystemUserService.ts index 873f6a0b8f5c..e37a254443ae 100644 --- a/packages/backend/src/services/CreateSystemUserService.ts +++ b/packages/backend/src/services/CreateSystemUserService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; import { v4 as uuid } from 'uuid'; -import { IsNull , DataSource } from 'typeorm'; +import { IsNull, DataSource } from 'typeorm'; import { genRsaKeyPair } from '@/misc/gen-key-pair.js'; import { User } from '@/models/entities/User.js'; import { UserProfile } from '@/models/entities/UserProfile.js'; diff --git a/packages/backend/src/services/CustomEmojiService.ts b/packages/backend/src/services/CustomEmojiService.ts index 502a4847c071..b33bc3c75d7c 100644 --- a/packages/backend/src/services/CustomEmojiService.ts +++ b/packages/backend/src/services/CustomEmojiService.ts @@ -32,7 +32,7 @@ export class CustomEmojiService { @Inject(DI.db) private db: DataSource, - @Inject('emojisRepository') + @Inject(DI.emojisRepository) private emojisRepository: typeof Emojis, private idService: IdService, diff --git a/packages/backend/src/services/DeleteAccountService.ts b/packages/backend/src/services/DeleteAccountService.ts index f7c1c56e9a1e..d312fb497423 100644 --- a/packages/backend/src/services/DeleteAccountService.ts +++ b/packages/backend/src/services/DeleteAccountService.ts @@ -3,11 +3,12 @@ import type { Users } from '@/models/index.js'; import { QueueService } from '@/services/QueueService.js'; import { UserSuspendService } from '@/services/UserSuspendService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; @Injectable() export class DeleteAccountService { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private userSuspendService: UserSuspendService, diff --git a/packages/backend/src/services/DriveService.ts b/packages/backend/src/services/DriveService.ts index a25d381cdf81..33e0cca51070 100644 --- a/packages/backend/src/services/DriveService.ts +++ b/packages/backend/src/services/DriveService.ts @@ -4,7 +4,7 @@ import { v4 as uuid } from 'uuid'; import sharp from 'sharp'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { DriveFiles , Users , DriveFolders , UserProfiles } from '@/models/index.js'; +import type { DriveFiles, Users, DriveFolders, UserProfiles } from '@/models/index.js'; import { Config } from '@/config.js'; import Logger from '@/Logger.js'; import type { IRemoteUser, User } from '@/models/entities/User.js'; @@ -81,16 +81,16 @@ export class DriveService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, - @Inject('driveFoldersRepository') + @Inject(DI.driveFoldersRepository) private driveFoldersRepository: typeof DriveFolders, private fileInfoService: FileInfoService, diff --git a/packages/backend/src/services/EmailService.ts b/packages/backend/src/services/EmailService.ts index 67bd926a6d22..accaf80c34e7 100644 --- a/packages/backend/src/services/EmailService.ts +++ b/packages/backend/src/services/EmailService.ts @@ -15,7 +15,7 @@ export class EmailService { @Inject(DI.config) private config: Config, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, private metaService: MetaService, diff --git a/packages/backend/src/services/FederatedInstanceService.ts b/packages/backend/src/services/FederatedInstanceService.ts index ce6659c0ba34..056f2ebfdd4f 100644 --- a/packages/backend/src/services/FederatedInstanceService.ts +++ b/packages/backend/src/services/FederatedInstanceService.ts @@ -3,6 +3,7 @@ import type { Instances } from '@/models/index.js'; import type { Instance } from '@/models/entities/Instance.js'; import { Cache } from '@/misc/cache.js'; import { IdService } from '@/services/IdService.js'; +import { DI } from '@/di-symbols.js'; import { UtilityService } from './UtilityService.js'; @Injectable() @@ -10,7 +11,7 @@ export class FederatedInstanceService { #cache: Cache; constructor( - @Inject('instancesRepository') + @Inject(DI.instancesRepository) private instancesRepository: typeof Instances, private utilityService: UtilityService, diff --git a/packages/backend/src/services/FetchInstanceMetadataService.ts b/packages/backend/src/services/FetchInstanceMetadataService.ts index 0917b73c2eb8..d10126b93d95 100644 --- a/packages/backend/src/services/FetchInstanceMetadataService.ts +++ b/packages/backend/src/services/FetchInstanceMetadataService.ts @@ -7,6 +7,7 @@ import type { Instance } from '@/models/entities/Instance.js'; import type { Instances } from '@/models/index.js'; import { AppLockService } from '@/services/AppLockService.js'; import Logger from '@/logger.js'; +import { DI } from '@/di-symbols.js'; import { HttpRequestService } from './HttpRequestService.js'; import type { DOMWindow } from 'jsdom'; @@ -33,7 +34,7 @@ type NodeInfo = { @Injectable() export class FetchInstanceMetadataService { constructor( - @Inject('instancesRepository') + @Inject(DI.instancesRepository) private instancesRepository: typeof Instances, private appLockService: AppLockService, diff --git a/packages/backend/src/services/HashtagService.ts b/packages/backend/src/services/HashtagService.ts index 1816e9820b8b..f1406414e366 100644 --- a/packages/backend/src/services/HashtagService.ts +++ b/packages/backend/src/services/HashtagService.ts @@ -10,10 +10,10 @@ import HashtagChart from '@/services/chart/charts/hashtag.js'; @Injectable() export class HashtagService { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('hashtagsRepository') + @Inject(DI.hashtagsRepository) private hashtagsRepository: typeof Hashtags, private idService: IdService, diff --git a/packages/backend/src/services/InstanceActorService.ts b/packages/backend/src/services/InstanceActorService.ts index d4b2b29bf8ca..47d63c2a1dba 100644 --- a/packages/backend/src/services/InstanceActorService.ts +++ b/packages/backend/src/services/InstanceActorService.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; - import { IsNull } from 'typeorm'; import type { ILocalUser } from '@/models/entities/User.js'; import type { Users } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; +import { DI } from '@/di-symbols.js'; import { CreateSystemUserService } from './CreateSystemUserService.js'; const ACTOR_USERNAME = 'instance.actor' as const; @@ -13,7 +13,7 @@ export class InstanceActorService { #cache: Cache; constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private createSystemUserService: CreateSystemUserService, diff --git a/packages/backend/src/services/MessagingService.ts b/packages/backend/src/services/MessagingService.ts index 0a319ed6679f..2b80eccf3123 100644 --- a/packages/backend/src/services/MessagingService.ts +++ b/packages/backend/src/services/MessagingService.ts @@ -24,10 +24,10 @@ export class MessagingService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('messagingMessagesRepository') + @Inject(DI.messagingMessagesRepository) private messagingMessagesRepository: typeof MessagingMessages, private userEntityService: UserEntityService, @@ -115,12 +115,12 @@ export class MessagingService { const note = { id: message.id, createdAt: message.createdAt, - fileIds: message.fileId ? [ message.fileId ] : [], + fileIds: message.fileId ? [message.fileId] : [], text: message.text, userId: message.userId, visibility: 'specified', - mentions: [ recipientUser ].map(u => u.id), - mentionedRemoteUsers: JSON.stringify([ recipientUser ].map(u => ({ + mentions: [recipientUser].map(u => u.id), + mentionedRemoteUsers: JSON.stringify([recipientUser].map(u => ({ uri: u.uri, username: u.username, host: u.host, diff --git a/packages/backend/src/services/ModerationLogService.ts b/packages/backend/src/services/ModerationLogService.ts index f5792fb66a7a..4e450cb0b224 100644 --- a/packages/backend/src/services/ModerationLogService.ts +++ b/packages/backend/src/services/ModerationLogService.ts @@ -7,7 +7,7 @@ import { IdService } from '@/services/IdService.js'; @Injectable() export class ModerationLogService { constructor( - @Inject('moderationLogsRepository') + @Inject(DI.moderationLogsRepository) private moderationLogsRepository: typeof ModerationLogs, private idService: IdService, diff --git a/packages/backend/src/services/NoteCreateService.ts b/packages/backend/src/services/NoteCreateService.ts index 5a82d48e1fbe..befb6d20b7bf 100644 --- a/packages/backend/src/services/NoteCreateService.ts +++ b/packages/backend/src/services/NoteCreateService.ts @@ -6,7 +6,7 @@ import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mf import { extractHashtags } from '@/misc/extract-hashtags.js'; import type { IMentionedRemoteUsers } from '@/models/entities/Note.js'; import { Note } from '@/models/entities/Note.js'; -import type { Notes , Users } from '@/models/index.js'; +import type { Notes, Users } from '@/models/index.js'; import { Mutings, Instances, UserProfiles, MutedNotes, Channels, ChannelFollowings, NoteThreadMutings } from '@/models/index.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { App } from '@/models/entities/App.js'; @@ -135,10 +135,10 @@ export class NoteCreateService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/NoteDeleteService.ts b/packages/backend/src/services/NoteDeleteService.ts index f612afd2dad7..d6b81e5f5320 100644 --- a/packages/backend/src/services/NoteDeleteService.ts +++ b/packages/backend/src/services/NoteDeleteService.ts @@ -23,7 +23,7 @@ export class NoteDeleteService { @Inject(DI.config) private config: Config, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/NotePiningService.ts b/packages/backend/src/services/NotePiningService.ts index 16db1939415b..f1816ce9faf7 100644 --- a/packages/backend/src/services/NotePiningService.ts +++ b/packages/backend/src/services/NotePiningService.ts @@ -19,13 +19,13 @@ export class NotePiningService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('userNotePiningsRepository') + @Inject(DI.userNotePiningsRepository) private userNotePiningsRepository: typeof UserNotePinings, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/NoteReadService.ts b/packages/backend/src/services/NoteReadService.ts index 30830088f86b..bb6b67329655 100644 --- a/packages/backend/src/services/NoteReadService.ts +++ b/packages/backend/src/services/NoteReadService.ts @@ -1,8 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, IsNull, Not } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { AntennaNotes, ChannelFollowings, Followings, Mutings, NoteThreadMutings , NoteUnreads , Users } from '@/models/index.js'; - +import type { AntennaNotes, ChannelFollowings, Followings, Mutings, NoteThreadMutings, NoteUnreads, Users } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import type { Channel } from '@/models/entities/Channel.js'; import type { Packed } from '@/misc/schema.js'; @@ -16,25 +15,25 @@ import { AntennaService } from './AntennaService.js'; @Injectable() export class NoteReadService { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('noteUnreadsRepository') + @Inject(DI.noteUnreadsRepository) private noteUnreadsRepository: typeof NoteUnreads, - @Inject('mutingsRepository') + @Inject(DI.mutingsRepository) private mutingsRepository: typeof Mutings, - @Inject('noteThreadMutingsRepository') + @Inject(DI.noteThreadMutingsRepository) private noteThreadMutingsRepository: typeof NoteThreadMutings, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, - @Inject('channelFollowingsRepository') + @Inject(DI.channelFollowingsRepository) private channelFollowingsRepository: typeof ChannelFollowings, - @Inject('antennaNotesRepository') + @Inject(DI.antennaNotesRepository) private antennaNotesRepository: typeof AntennaNotes, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/NotificationService.ts b/packages/backend/src/services/NotificationService.ts index d84e280827d7..daa7e0825949 100644 --- a/packages/backend/src/services/NotificationService.ts +++ b/packages/backend/src/services/NotificationService.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { Notifications , Users } from '@/models/index.js'; +import type { Notifications, Users } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; +import type { Notification } from '@/models/entities/Notification.js'; import { UserEntityService } from './entities/UserEntityService.js'; import { GlobalEventService } from './GlobalEventService.js'; import { PushNotificationService } from './PushNotificationService.js'; @@ -10,7 +11,7 @@ import { PushNotificationService } from './PushNotificationService.js'; @Injectable() export class NotificationService { constructor( - @Inject('notificationsRepository') + @Inject(DI.notificationsRepository) private notificationsRepository: typeof Notifications, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/PollService.ts b/packages/backend/src/services/PollService.ts index 59b4fdf1d953..61dbda458975 100644 --- a/packages/backend/src/services/PollService.ts +++ b/packages/backend/src/services/PollService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { Not } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { Notes, Users , Blockings } from '@/models/index.js'; -import { Polls , PollVotes } from '@/models/index.js'; +import type { Notes, Users, Blockings } from '@/models/index.js'; +import { Polls, PollVotes } from '@/models/index.js'; import type { Note } from '@/models/entities/Note.js'; import { RelayService } from '@/services/RelayService.js'; import type { CacheableUser } from '@/models/entities/User.js'; @@ -16,19 +16,19 @@ import { ApDeliverManagerService } from './remote/activitypub/ApDeliverManagerSe @Injectable() export class PollService { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('pollsRepository') + @Inject(DI.pollsRepository) private pollsRepository: typeof Polls, - @Inject('pollVotesRepository') + @Inject(DI.pollVotesRepository) private pollVotesRepository: typeof PollVotes, - @Inject('blockingsRepository') + @Inject(DI.blockingsRepository) private blockingsRepository: typeof Blockings, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/ProxyAccountService.ts b/packages/backend/src/services/ProxyAccountService.ts index 8c4093e2602e..3f68d3e89e7a 100644 --- a/packages/backend/src/services/ProxyAccountService.ts +++ b/packages/backend/src/services/ProxyAccountService.ts @@ -1,12 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; import type { Users } from '@/models/index.js'; import type { ILocalUser, User } from '@/models/entities/User.js'; +import { DI } from '@/di-symbols.js'; import { MetaService } from './MetaService.js'; @Injectable() export class ProxyAccountService { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private metaService: MetaService, diff --git a/packages/backend/src/services/PushNotificationService.ts b/packages/backend/src/services/PushNotificationService.ts index 94e7f177fa42..0932c67970a6 100644 --- a/packages/backend/src/services/PushNotificationService.ts +++ b/packages/backend/src/services/PushNotificationService.ts @@ -45,7 +45,7 @@ export class PushNotificationService { @Inject(DI.config) private config: Config, - @Inject('swSubscriptionsRepository') + @Inject(DI.swSubscriptionsRepository) private swSubscriptionsRepository: typeof SwSubscriptions, private metaService: MetaService, diff --git a/packages/backend/src/services/QueryService.ts b/packages/backend/src/services/QueryService.ts index c085ad6fc022..6d53e05cddac 100644 --- a/packages/backend/src/services/QueryService.ts +++ b/packages/backend/src/services/QueryService.ts @@ -1,32 +1,32 @@ import { Inject, Injectable } from '@nestjs/common'; import { Brackets } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { NoteThreadMutings , Blockings , ChannelFollowings , MutedNotes , Followings , Mutings , UserProfiles } from '@/models/index.js'; +import type { NoteThreadMutings, Blockings, ChannelFollowings, MutedNotes, Followings, Mutings, UserProfiles } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import type { SelectQueryBuilder } from 'typeorm'; @Injectable() export class QueryService { constructor( - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, - @Inject('channelFollowingsRepository') + @Inject(DI.channelFollowingsRepository) private channelFollowingsRepository: typeof ChannelFollowings, - @Inject('mutedNotesRepository') + @Inject(DI.mutedNotesRepository) private mutedNotesRepository: typeof MutedNotes, - @Inject('blockingsRepository') + @Inject(DI.blockingsRepository) private blockingsRepository: typeof Blockings, - @Inject('noteThreadMutingsRepository') + @Inject(DI.noteThreadMutingsRepository) private noteThreadMutingsRepository: typeof NoteThreadMutings, - @Inject('mutingsRepository') + @Inject(DI.mutingsRepository) private mutingsRepository: typeof Mutings, ) { } diff --git a/packages/backend/src/services/ReactionService.ts b/packages/backend/src/services/ReactionService.ts index 266e56a4d5c9..a2053b63531b 100644 --- a/packages/backend/src/services/ReactionService.ts +++ b/packages/backend/src/services/ReactionService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; import { Emojis } from '@/models/index.js'; -import type { Blockings, NoteReactions , Users , Notes } from '@/models/index.js'; +import type { Blockings, NoteReactions, Users, Notes } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import type { IRemoteUser, User } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; @@ -54,19 +54,19 @@ type DecodedReaction = { @Injectable() export class ReactionService { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('blockingsRepository') + @Inject(DI.blockingsRepository) private blockingsRepository: typeof Blockings, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('noteReactionsRepository') + @Inject(DI.noteReactionsRepository) private noteReactionsRepository: typeof NoteReactions, - @Inject('emojisRepository') + @Inject(DI.emojisRepository) private emojisRepository: typeof Emojis, private utilityService: UtilityService, diff --git a/packages/backend/src/services/RelayService.ts b/packages/backend/src/services/RelayService.ts index 9f9ddd11c8c2..38ad375a099e 100644 --- a/packages/backend/src/services/RelayService.ts +++ b/packages/backend/src/services/RelayService.ts @@ -8,6 +8,7 @@ import type { Relay } from '@/models/entities/Relay.js'; import { QueueService } from '@/services/QueueService.js'; import { CreateSystemUserService } from '@/services/CreateSystemUserService.js'; import { ApRendererService } from '@/services/remote/activitypub/ApRendererService.js'; +import { DI } from '@/di-symbols.js'; const ACTOR_USERNAME = 'relay.actor' as const; @@ -16,10 +17,10 @@ export class RelayService { #relaysCache: Cache; constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('relaysRepository') + @Inject(DI.relaysRepository) private relaysRepository: typeof Relays, private idService: IdService, diff --git a/packages/backend/src/services/SignupService.ts b/packages/backend/src/services/SignupService.ts index 7a42be9b57d7..604830eabe19 100644 --- a/packages/backend/src/services/SignupService.ts +++ b/packages/backend/src/services/SignupService.ts @@ -25,10 +25,10 @@ export class SignupService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('usedUsernamesRepository') + @Inject(DI.usedUsernamesRepository) private usedUsernamesRepository: typeof UsedUsernames, private utilityService: UtilityService, diff --git a/packages/backend/src/services/TwoFactorAuthenticationService.ts b/packages/backend/src/services/TwoFactorAuthenticationService.ts index 4d98e2fa95f3..aeea6ea45c70 100644 --- a/packages/backend/src/services/TwoFactorAuthenticationService.ts +++ b/packages/backend/src/services/TwoFactorAuthenticationService.ts @@ -108,7 +108,7 @@ export class TwoFactorAuthenticationService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, ) { } diff --git a/packages/backend/src/services/UserBlockingService.ts b/packages/backend/src/services/UserBlockingService.ts index ae1a656688e1..e5a67b70e1d4 100644 --- a/packages/backend/src/services/UserBlockingService.ts +++ b/packages/backend/src/services/UserBlockingService.ts @@ -1,12 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { FollowRequests , Followings , UserLists , UserListJoinings , Users , Blockings } from '@/models/index.js'; +import type { FollowRequests, Followings, UserLists, UserListJoinings, Users, Blockings } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; import type { CacheableUser, User } from '@/models/entities/User.js'; import type { Blocking } from '@/models/entities/Blocking.js'; import { QueueService } from '@/services/QueueService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import PerUserFollowingChart from '@/services/chart/charts/per-user-following.js'; +import { DI } from '@/di-symbols.js'; import { UserEntityService } from './entities/UserEntityService.js'; import { WebhookService } from './WebhookService.js'; import { ApRendererService } from './remote/activitypub/ApRendererService.js'; @@ -14,22 +15,22 @@ import { ApRendererService } from './remote/activitypub/ApRendererService.js'; @Injectable() export class UserBlockingService { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, - @Inject('followRequestsRepository') + @Inject(DI.followRequestsRepository) private followRequestsRepository: typeof FollowRequests, - @Inject('blockingsRepository') + @Inject(DI.blockingsRepository) private blockingsRepository: typeof Blockings, - @Inject('userListsRepository') + @Inject(DI.userListsRepository) private userListsRepository: typeof UserLists, - @Inject('userListJoiningsRepository') + @Inject(DI.userListJoiningsRepository) private userListJoiningsRepository: typeof UserListJoinings, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/UserCacheService.ts b/packages/backend/src/services/UserCacheService.ts index 753d5cfbbcf7..fa6b7ccff49e 100644 --- a/packages/backend/src/services/UserCacheService.ts +++ b/packages/backend/src/services/UserCacheService.ts @@ -18,7 +18,7 @@ export class UserCacheService implements OnApplicationShutdown { @Inject(DI.redisSubscriber) private redisSubscriber: Redis.Redis, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/UserFollowingService.ts b/packages/backend/src/services/UserFollowingService.ts index be9613061111..69067bf7ea6b 100644 --- a/packages/backend/src/services/UserFollowingService.ts +++ b/packages/backend/src/services/UserFollowingService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { Users , Followings, FollowRequests , UserProfiles , Instances , Blockings } from '@/models/index.js'; +import type { Users, Followings, FollowRequests, UserProfiles, Instances, Blockings } from '@/models/index.js'; import type { CacheableUser, ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { QueueService } from '@/services/QueueService.js'; @@ -12,6 +12,7 @@ import InstanceChart from '@/services/chart/charts/instance.js'; import { FederatedInstanceService } from '@/services/FederatedInstanceService.js'; import { WebhookService } from '@/services/WebhookService.js'; import { CreateNotificationService } from '@/services/CreateNotificationService.js'; +import { DI } from '@/di-symbols.js'; import Logger from '../logger.js'; import { UserEntityService } from './entities/UserEntityService.js'; import { ApRendererService } from './remote/activitypub/ApRendererService.js'; @@ -34,22 +35,22 @@ type Both = Local | Remote; @Injectable() export class UserFollowingService { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, - @Inject('followRequestsRepository') + @Inject(DI.followRequestsRepository) private followRequestsRepository: typeof FollowRequests, - @Inject('blockingsRepository') + @Inject(DI.blockingsRepository) private blockingsRepository: typeof Blockings, - @Inject('instancesRepository') + @Inject(DI.instancesRepository) private instancesRepository: typeof Instances, private userEntityService: UserEntityService, @@ -86,7 +87,7 @@ export class UserFollowingService { if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee) && blocked) { // リモートフォローを受けてブロックしていた場合は、エラーにするのではなくRejectを送り返しておしまい。 const content = this.apRendererService.renderActivity(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower, followee, requestId), followee)); - this.queueService.deliver(followee , content, follower.inbox); + this.queueService.deliver(followee, content, follower.inbox); return; } else if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee) && blocking) { // リモートフォローを受けてブロックされているはずの場合だったら、ブロック解除しておく。 diff --git a/packages/backend/src/services/UserKeypairStoreService.ts b/packages/backend/src/services/UserKeypairStoreService.ts index a732ace8d4d7..b4e18523ad87 100644 --- a/packages/backend/src/services/UserKeypairStoreService.ts +++ b/packages/backend/src/services/UserKeypairStoreService.ts @@ -3,13 +3,14 @@ import type { User } from '@/models/entities/User.js'; import type { UserKeypairs } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; import type { UserKeypair } from '@/models/entities/UserKeypair.js'; +import { DI } from '@/di-symbols.js'; @Injectable() export class UserKeypairStoreService { #cache: Cache; constructor( - @Inject('userKeypairsRepository') + @Inject(DI.userKeypairsRepository) private userKeypairsRepository: typeof UserKeypairs, ) { this.#cache = new Cache(Infinity); diff --git a/packages/backend/src/services/UserListService.ts b/packages/backend/src/services/UserListService.ts index b5ada69634f7..edcddcae9584 100644 --- a/packages/backend/src/services/UserListService.ts +++ b/packages/backend/src/services/UserListService.ts @@ -1,21 +1,22 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { UserListJoinings , Users } from '@/models/index.js'; +import type { UserListJoinings, Users } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import type { UserList } from '@/models/entities/UserList.js'; import type { UserListJoining } from '@/models/entities/UserListJoining.js'; import { IdService } from '@/services/IdService.js'; import { UserFollowingService } from '@/services/UserFollowingService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; import { UserEntityService } from './entities/UserEntityService.js'; import { ProxyAccountService } from './ProxyAccountService.js'; @Injectable() export class UserListService { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userListJoiningsRepository') + @Inject(DI.userListJoiningsRepository) private userListJoiningsRepository: typeof UserListJoinings, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/UserMutingService.ts b/packages/backend/src/services/UserMutingService.ts index d1941e4adce1..1973926da2fa 100644 --- a/packages/backend/src/services/UserMutingService.ts +++ b/packages/backend/src/services/UserMutingService.ts @@ -1,17 +1,18 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { Users , Mutings } from '@/models/index.js'; +import type { Users, Mutings } from '@/models/index.js'; import { IdService } from '@/services/IdService.js'; import { QueueService } from '@/services/QueueService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; import type { User } from '@/models/entities/User.js'; +import { DI } from '@/di-symbols.js'; @Injectable() export class UserMutingService { constructor( - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('mutingsRepository') + @Inject(DI.mutingsRepository) private mutingsRepository: typeof Mutings, private idService: IdService, diff --git a/packages/backend/src/services/UserSuspendService.ts b/packages/backend/src/services/UserSuspendService.ts index d5ec83da1cb6..6c176ef6b66e 100644 --- a/packages/backend/src/services/UserSuspendService.ts +++ b/packages/backend/src/services/UserSuspendService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Not, IsNull } from 'typeorm'; -import type { Followings , Users } from '@/models/index.js'; +import type { Followings, Users } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import { QueueService } from '@/services/QueueService.js'; import { GlobalEventService } from '@/services/GlobalEventService.js'; @@ -15,10 +15,10 @@ export class UserSuspendService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/WebhookService.ts b/packages/backend/src/services/WebhookService.ts index 36b910bae0ad..c47bb48bd0cc 100644 --- a/packages/backend/src/services/WebhookService.ts +++ b/packages/backend/src/services/WebhookService.ts @@ -14,7 +14,7 @@ export class WebhookService implements OnApplicationShutdown { @Inject(DI.redisSubscriber) private redisSubscriber: Redis.Redis, - @Inject('webhooksRepository') + @Inject(DI.webhooksRepository) private webhooksRepository: typeof Webhooks, ) { this.onMessage = this.onMessage.bind(this); diff --git a/packages/backend/src/services/chart/charts/drive.ts b/packages/backend/src/services/chart/charts/drive.ts index a1132b25113b..9ae0ebcae089 100644 --- a/packages/backend/src/services/chart/charts/drive.ts +++ b/packages/backend/src/services/chart/charts/drive.ts @@ -1,5 +1,5 @@ import { Injectable, Inject } from '@nestjs/common'; -import { Not, IsNull , DataSource } from 'typeorm'; +import { Not, IsNull, DataSource } from 'typeorm'; import { DriveFiles } from '@/models/index.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import { AppLockService } from '@/services/AppLockService.js'; diff --git a/packages/backend/src/services/chart/charts/notes.ts b/packages/backend/src/services/chart/charts/notes.ts index 34ae6b24f64f..c78a5a53d808 100644 --- a/packages/backend/src/services/chart/charts/notes.ts +++ b/packages/backend/src/services/chart/charts/notes.ts @@ -1,5 +1,5 @@ import { Injectable, Inject } from '@nestjs/common'; -import { Not, IsNull , DataSource } from 'typeorm'; +import { Not, IsNull, DataSource } from 'typeorm'; import { Notes } from '@/models/index.js'; import type { Note } from '@/models/entities/Note.js'; import { AppLockService } from '@/services/AppLockService.js'; diff --git a/packages/backend/src/services/chart/charts/per-user-following.ts b/packages/backend/src/services/chart/charts/per-user-following.ts index b18a770169ce..0fbea0f8ad00 100644 --- a/packages/backend/src/services/chart/charts/per-user-following.ts +++ b/packages/backend/src/services/chart/charts/per-user-following.ts @@ -1,5 +1,5 @@ import { Injectable, Inject } from '@nestjs/common'; -import { Not, IsNull , DataSource } from 'typeorm'; +import { Not, IsNull, DataSource } from 'typeorm'; import { Followings, Users } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import { AppLockService } from '@/services/AppLockService.js'; diff --git a/packages/backend/src/services/chart/charts/test-grouped.ts b/packages/backend/src/services/chart/charts/test-grouped.ts index 5ba4a51d1c69..77e776d130ce 100644 --- a/packages/backend/src/services/chart/charts/test-grouped.ts +++ b/packages/backend/src/services/chart/charts/test-grouped.ts @@ -1,5 +1,5 @@ import { Injectable, Inject } from '@nestjs/common'; -import { DataSource , DataSource } from 'typeorm'; +import { DataSource, DataSource } from 'typeorm'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; diff --git a/packages/backend/src/services/chart/charts/test-intersection.ts b/packages/backend/src/services/chart/charts/test-intersection.ts index 36405c11e1d7..8b34a883ed0b 100644 --- a/packages/backend/src/services/chart/charts/test-intersection.ts +++ b/packages/backend/src/services/chart/charts/test-intersection.ts @@ -1,5 +1,5 @@ import { Injectable, Inject } from '@nestjs/common'; -import { DataSource , DataSource } from 'typeorm'; +import { DataSource, DataSource } from 'typeorm'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; diff --git a/packages/backend/src/services/chart/charts/test-unique.ts b/packages/backend/src/services/chart/charts/test-unique.ts index 0d72f740ad15..e0064dedc70e 100644 --- a/packages/backend/src/services/chart/charts/test-unique.ts +++ b/packages/backend/src/services/chart/charts/test-unique.ts @@ -1,5 +1,5 @@ import { Injectable, Inject } from '@nestjs/common'; -import { DataSource , DataSource } from 'typeorm'; +import { DataSource, DataSource } from 'typeorm'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; diff --git a/packages/backend/src/services/chart/charts/test.ts b/packages/backend/src/services/chart/charts/test.ts index 72c2062b66a9..28e4d38c7a8a 100644 --- a/packages/backend/src/services/chart/charts/test.ts +++ b/packages/backend/src/services/chart/charts/test.ts @@ -1,5 +1,5 @@ import { Injectable, Inject } from '@nestjs/common'; -import { DataSource , DataSource } from 'typeorm'; +import { DataSource, DataSource } from 'typeorm'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; diff --git a/packages/backend/src/services/chart/charts/users.ts b/packages/backend/src/services/chart/charts/users.ts index 19d88af3188f..84f778dd4b5e 100644 --- a/packages/backend/src/services/chart/charts/users.ts +++ b/packages/backend/src/services/chart/charts/users.ts @@ -1,5 +1,5 @@ import { Injectable, Inject } from '@nestjs/common'; -import { Not, IsNull , DataSource } from 'typeorm'; +import { Not, IsNull, DataSource } from 'typeorm'; import { Users } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import { AppLockService } from '@/services/AppLockService.js'; diff --git a/packages/backend/src/services/entities/AbuseUserReportEntityService.ts b/packages/backend/src/services/entities/AbuseUserReportEntityService.ts index f8fc34216c55..5b2a97795f7c 100644 --- a/packages/backend/src/services/entities/AbuseUserReportEntityService.ts +++ b/packages/backend/src/services/entities/AbuseUserReportEntityService.ts @@ -8,7 +8,7 @@ import { UserEntityService } from './UserEntityService.js'; @Injectable() export class AbuseUserReportEntityService { constructor( - @Inject('abuseUserReportsRepository') + @Inject(DI.abuseUserReportsRepository) private abuseUserReportsRepository: typeof AbuseUserReports, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/entities/AntennaEntityService.ts b/packages/backend/src/services/entities/AntennaEntityService.ts index ecfe6e41f6ff..818f1a6fa7f5 100644 --- a/packages/backend/src/services/entities/AntennaEntityService.ts +++ b/packages/backend/src/services/entities/AntennaEntityService.ts @@ -8,13 +8,13 @@ import type { Antenna } from '@/models/entities/Antenna.js'; @Injectable() export class AntennaEntityService { constructor( - @Inject('antennasRepository') + @Inject(DI.antennasRepository) private antennasRepository: typeof Antennas, - @Inject('antennaNotesRepository') + @Inject(DI.antennaNotesRepository) private antennaNotesRepository: typeof AntennaNotes, - @Inject('userGroupJoiningsRepository') + @Inject(DI.userGroupJoiningsRepository) private userGroupJoiningsRepository: typeof UserGroupJoinings, ) { } diff --git a/packages/backend/src/services/entities/AppEntityService.ts b/packages/backend/src/services/entities/AppEntityService.ts index ca172f3f0c94..ff55685a4880 100644 --- a/packages/backend/src/services/entities/AppEntityService.ts +++ b/packages/backend/src/services/entities/AppEntityService.ts @@ -10,10 +10,10 @@ import { UserEntityService } from './UserEntityService.js'; @Injectable() export class AppEntityService { constructor( - @Inject('appsRepository') + @Inject(DI.appsRepository) private appsRepository: typeof Apps, - @Inject('accessTokensRepository') + @Inject(DI.accessTokensRepository) private accessTokensRepository: typeof AccessTokens, ) { } diff --git a/packages/backend/src/services/entities/AuthSessionEntityService.ts b/packages/backend/src/services/entities/AuthSessionEntityService.ts index d6b131e38fe2..49e924943621 100644 --- a/packages/backend/src/services/entities/AuthSessionEntityService.ts +++ b/packages/backend/src/services/entities/AuthSessionEntityService.ts @@ -12,7 +12,7 @@ import { AppEntityService } from './AppEntityService.js'; @Injectable() export class AuthSessionEntityService { constructor( - @Inject('authSessionsRepository') + @Inject(DI.authSessionsRepository) private authSessionsRepository: typeof AuthSessions, private appEntityService: AppEntityService, diff --git a/packages/backend/src/services/entities/BlockingEntityService.ts b/packages/backend/src/services/entities/BlockingEntityService.ts index 61671f8e5aa1..0d760d94057c 100644 --- a/packages/backend/src/services/entities/BlockingEntityService.ts +++ b/packages/backend/src/services/entities/BlockingEntityService.ts @@ -10,7 +10,7 @@ import { UserEntityService } from './UserEntityService.js'; @Injectable() export class BlockingEntityService { constructor( - @Inject('blockingsRepository') + @Inject(DI.blockingsRepository) private blockingsRepository: typeof Blockings, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/entities/ChannelEntityService.ts b/packages/backend/src/services/entities/ChannelEntityService.ts index 8e04d0e5c689..014332f8e9d9 100644 --- a/packages/backend/src/services/entities/ChannelEntityService.ts +++ b/packages/backend/src/services/entities/ChannelEntityService.ts @@ -12,16 +12,16 @@ import { DriveFileEntityService } from './DriveFileEntityService.js'; @Injectable() export class ChannelEntityService { constructor( - @Inject('channelsRepository') + @Inject(DI.channelsRepository) private channelsRepository: typeof Channels, - @Inject('channelFollowingsRepository') + @Inject(DI.channelFollowingsRepository) private channelFollowingsRepository: typeof ChannelFollowings, - @Inject('noteUnreadsRepository') + @Inject(DI.noteUnreadsRepository) private noteUnreadsRepository: typeof NoteUnreads, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/entities/ClipEntityService.ts b/packages/backend/src/services/entities/ClipEntityService.ts index 05a5534c7f84..2276035540dd 100644 --- a/packages/backend/src/services/entities/ClipEntityService.ts +++ b/packages/backend/src/services/entities/ClipEntityService.ts @@ -11,7 +11,7 @@ import { UserEntityService } from './UserEntityService.js'; @Injectable() export class ClipEntityService { constructor( - @Inject('clipsRepository') + @Inject(DI.clipsRepository) private clipsRepository: typeof Clips, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/entities/DriveFileEntityService.ts b/packages/backend/src/services/entities/DriveFileEntityService.ts index bb445b685abb..f831762e4b70 100644 --- a/packages/backend/src/services/entities/DriveFileEntityService.ts +++ b/packages/backend/src/services/entities/DriveFileEntityService.ts @@ -2,7 +2,7 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import { DataSource, In } from 'typeorm'; import * as mfm from 'mfm-js'; import { DI } from '@/di-symbols.js'; -import type { Notes , DriveFiles } from '@/models/index.js'; +import type { Notes, DriveFiles } from '@/models/index.js'; import { Config } from '@/config.js'; import type { Packed } from '@/misc/schema.js'; import { awaitAll } from '@/prelude/await-all.js'; @@ -28,10 +28,10 @@ export class DriveFileEntityService { @Inject(DI.db) private db: DataSource, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, // 循環参照のため / for circular dependency diff --git a/packages/backend/src/services/entities/DriveFolderEntityService.ts b/packages/backend/src/services/entities/DriveFolderEntityService.ts index 2446ee571025..b356ef213eee 100644 --- a/packages/backend/src/services/entities/DriveFolderEntityService.ts +++ b/packages/backend/src/services/entities/DriveFolderEntityService.ts @@ -11,10 +11,10 @@ import { UserEntityService } from './UserEntityService.js'; @Injectable() export class DriveFolderEntityService { constructor( - @Inject('driveFoldersRepository') + @Inject(DI.driveFoldersRepository) private driveFoldersRepository: typeof DriveFolders, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, ) { } diff --git a/packages/backend/src/services/entities/EmojiEntityService.ts b/packages/backend/src/services/entities/EmojiEntityService.ts index b216c39a6500..1f9f3e163c74 100644 --- a/packages/backend/src/services/entities/EmojiEntityService.ts +++ b/packages/backend/src/services/entities/EmojiEntityService.ts @@ -11,7 +11,7 @@ import { UserEntityService } from './UserEntityService.js'; @Injectable() export class EmojiEntityService { constructor( - @Inject('emojisRepository') + @Inject(DI.emojisRepository) private emojisRepository: typeof Emojis, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/entities/FollowRequestEntityService.ts b/packages/backend/src/services/entities/FollowRequestEntityService.ts index a484d318601b..53662aec5155 100644 --- a/packages/backend/src/services/entities/FollowRequestEntityService.ts +++ b/packages/backend/src/services/entities/FollowRequestEntityService.ts @@ -11,7 +11,7 @@ import { UserEntityService } from './UserEntityService.js'; @Injectable() export class FollowRequestEntityService { constructor( - @Inject('followRequestsRepository') + @Inject(DI.followRequestsRepository) private followRequestsRepository: typeof FollowRequests, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/entities/FollowingEntityService.ts b/packages/backend/src/services/entities/FollowingEntityService.ts index e028b204145a..93a4ab293e9d 100644 --- a/packages/backend/src/services/entities/FollowingEntityService.ts +++ b/packages/backend/src/services/entities/FollowingEntityService.ts @@ -35,7 +35,7 @@ type RemoteFolloweeFollowing = Following & { @Injectable() export class FollowingEntityService { constructor( - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/entities/GalleryLikeEntityService.ts b/packages/backend/src/services/entities/GalleryLikeEntityService.ts index cc0dd818909a..3b8b88b15697 100644 --- a/packages/backend/src/services/entities/GalleryLikeEntityService.ts +++ b/packages/backend/src/services/entities/GalleryLikeEntityService.ts @@ -13,7 +13,7 @@ import { GalleryPostEntityService } from './GalleryPostEntityService.js'; @Injectable() export class GalleryLikeEntityService { constructor( - @Inject('galleryLikesRepository') + @Inject(DI.galleryLikesRepository) private galleryLikesRepository: typeof GalleryLikes, private galleryPostEntityService: GalleryPostEntityService, diff --git a/packages/backend/src/services/entities/GalleryPostEntityService.ts b/packages/backend/src/services/entities/GalleryPostEntityService.ts index 9f99a6ce0b6c..209ec3f5c6ff 100644 --- a/packages/backend/src/services/entities/GalleryPostEntityService.ts +++ b/packages/backend/src/services/entities/GalleryPostEntityService.ts @@ -12,10 +12,10 @@ import { DriveFileEntityService } from './DriveFileEntityService.js'; @Injectable() export class GalleryPostEntityService { constructor( - @Inject('galleryPostsRepository') + @Inject(DI.galleryPostsRepository) private galleryPostsRepository: typeof GalleryPosts, - @Inject('galleryLikesRepository') + @Inject(DI.galleryLikesRepository) private galleryLikesRepository: typeof GalleryLikes, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/entities/HashtagEntityService.ts b/packages/backend/src/services/entities/HashtagEntityService.ts index 1b923f97fa1a..0f6cd562144c 100644 --- a/packages/backend/src/services/entities/HashtagEntityService.ts +++ b/packages/backend/src/services/entities/HashtagEntityService.ts @@ -11,7 +11,7 @@ import { UserEntityService } from './UserEntityService.js'; @Injectable() export class HashtagEntityService { constructor( - @Inject('hashtagsRepository') + @Inject(DI.hashtagsRepository) private hashtagsRepository: typeof Hashtags, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/entities/InstanceEntityService.ts b/packages/backend/src/services/entities/InstanceEntityService.ts index edb733c6123c..bb3e23aa34a6 100644 --- a/packages/backend/src/services/entities/InstanceEntityService.ts +++ b/packages/backend/src/services/entities/InstanceEntityService.ts @@ -12,7 +12,7 @@ import { UserEntityService } from './UserEntityService.js'; @Injectable() export class InstanceEntityService { constructor( - @Inject('instancesRepository') + @Inject(DI.instancesRepository) private instancesRepository: typeof Instances, private metaService: MetaService, diff --git a/packages/backend/src/services/entities/MessagingMessageEntityService.ts b/packages/backend/src/services/entities/MessagingMessageEntityService.ts index dd9cb1036332..3dabdc8d0ad5 100644 --- a/packages/backend/src/services/entities/MessagingMessageEntityService.ts +++ b/packages/backend/src/services/entities/MessagingMessageEntityService.ts @@ -13,7 +13,7 @@ import { UserGroupEntityService } from './UserGroupEntityService.js'; @Injectable() export class MessagingMessageEntityService { constructor( - @Inject('messagingMessagesRepository') + @Inject(DI.messagingMessagesRepository) private messagingMessagesRepository: typeof MessagingMessages, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/entities/ModerationLogEntityService.ts b/packages/backend/src/services/entities/ModerationLogEntityService.ts index 3129e3bcef30..16ea59bff2de 100644 --- a/packages/backend/src/services/entities/ModerationLogEntityService.ts +++ b/packages/backend/src/services/entities/ModerationLogEntityService.ts @@ -11,7 +11,7 @@ import { UserEntityService } from './UserEntityService.js'; @Injectable() export class ModerationLogEntityService { constructor( - @Inject('moderationLogsRepository') + @Inject(DI.moderationLogsRepository) private moderationLogsRepository: typeof ModerationLogs, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/entities/MutingEntityService.ts b/packages/backend/src/services/entities/MutingEntityService.ts index af9a5d6c1f5b..7b705dff922c 100644 --- a/packages/backend/src/services/entities/MutingEntityService.ts +++ b/packages/backend/src/services/entities/MutingEntityService.ts @@ -11,7 +11,7 @@ import { UserEntityService } from './UserEntityService.js'; @Injectable() export class MutingEntityService { constructor( - @Inject('mutingsRepository') + @Inject(DI.mutingsRepository) private mutingsRepository: typeof Mutings, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/entities/NoteEntityService.ts b/packages/backend/src/services/entities/NoteEntityService.ts index b0f6a4f62ed9..d9713cca646e 100644 --- a/packages/backend/src/services/entities/NoteEntityService.ts +++ b/packages/backend/src/services/entities/NoteEntityService.ts @@ -3,7 +3,7 @@ import { DataSource, In } from 'typeorm'; import * as mfm from 'mfm-js'; import { ModuleRef } from '@nestjs/core'; import { DI } from '@/di-symbols.js'; -import type { Notes , Polls, PollVotes , DriveFiles , Channels , Followings , Users , NoteReactions } from '@/models/index.js'; +import type { Notes, Polls, PollVotes, DriveFiles, Channels, Followings, Users, NoteReactions } from '@/models/index.js'; import { Config } from '@/config.js'; import type { Packed } from '@/misc/schema.js'; import { nyaize } from '@/misc/nyaize.js'; @@ -30,28 +30,28 @@ export class NoteEntityService implements OnModuleInit { @Inject(DI.db) private db: DataSource, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, - @Inject('pollsRepository') + @Inject(DI.pollsRepository) private pollsRepository: typeof Polls, - @Inject('pollVotesRepository') + @Inject(DI.pollVotesRepository) private pollVotesRepository: typeof PollVotes, - @Inject('noteReactionsRepository') + @Inject(DI.noteReactionsRepository) private noteReactionsRepository: typeof NoteReactions, - @Inject('channelsRepository') + @Inject(DI.channelsRepository) private channelsRepository: typeof Channels, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, //private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/entities/NoteFavoriteEntityService.ts b/packages/backend/src/services/entities/NoteFavoriteEntityService.ts index f076a119333e..196e4d84a84c 100644 --- a/packages/backend/src/services/entities/NoteFavoriteEntityService.ts +++ b/packages/backend/src/services/entities/NoteFavoriteEntityService.ts @@ -12,7 +12,7 @@ import { NoteEntityService } from './NoteEntityService.js'; @Injectable() export class NoteFavoriteEntityService { constructor( - @Inject('noteFavoritesRepository') + @Inject(DI.noteFavoritesRepository) private noteFavoritesRepository: typeof NoteFavorites, private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/services/entities/NoteReactionEntityService.ts b/packages/backend/src/services/entities/NoteReactionEntityService.ts index 1f64ed27628d..1e9ed573378c 100644 --- a/packages/backend/src/services/entities/NoteReactionEntityService.ts +++ b/packages/backend/src/services/entities/NoteReactionEntityService.ts @@ -21,7 +21,7 @@ export class NoteReactionEntityService implements OnModuleInit { constructor( private moduleRef: ModuleRef, - @Inject('noteReactionsRepository') + @Inject(DI.noteReactionsRepository) private noteReactionsRepository: typeof NoteReactions, //private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/entities/NotificationEntityService.ts b/packages/backend/src/services/entities/NotificationEntityService.ts index ab1698d96873..b309d14a1ecc 100644 --- a/packages/backend/src/services/entities/NotificationEntityService.ts +++ b/packages/backend/src/services/entities/NotificationEntityService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; import { ModuleRef } from '@nestjs/core'; import { DI } from '@/di-symbols.js'; -import type { AccessTokens, NoteReactions , Notifications } from '@/models/index.js'; +import type { AccessTokens, NoteReactions, Notifications } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Notification } from '@/models/entities/Notification.js'; import type { NoteReaction } from '@/models/entities/NoteReaction.js'; @@ -24,13 +24,13 @@ export class NotificationEntityService implements OnModuleInit { constructor( private moduleRef: ModuleRef, - @Inject('notificationsRepository') + @Inject(DI.notificationsRepository) private notificationsRepository: typeof Notifications, - @Inject('noteReactionsRepository') + @Inject(DI.noteReactionsRepository) private noteReactionsRepository: typeof NoteReactions, - @Inject('accessTokensRepository') + @Inject(DI.accessTokensRepository) private accessTokensRepository: typeof AccessTokens, //private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/entities/PageEntityService.ts b/packages/backend/src/services/entities/PageEntityService.ts index a65625a5942f..8a318cd56a55 100644 --- a/packages/backend/src/services/entities/PageEntityService.ts +++ b/packages/backend/src/services/entities/PageEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { DriveFiles , Pages , PageLikes } from '@/models/index.js'; +import type { DriveFiles, Pages, PageLikes } from '@/models/index.js'; import { awaitAll } from '@/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; @@ -13,13 +13,13 @@ import { DriveFileEntityService } from './DriveFileEntityService.js'; @Injectable() export class PageEntityService { constructor( - @Inject('pagesRepository') + @Inject(DI.pagesRepository) private pagesRepository: typeof Pages, - @Inject('pageLikesRepository') + @Inject(DI.pageLikesRepository) private pageLikesRepository: typeof PageLikes, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/entities/PageLikeEntityService.ts b/packages/backend/src/services/entities/PageLikeEntityService.ts index be43d7bc3e28..0eaee33e213f 100644 --- a/packages/backend/src/services/entities/PageLikeEntityService.ts +++ b/packages/backend/src/services/entities/PageLikeEntityService.ts @@ -12,7 +12,7 @@ import { PageEntityService } from './PageEntityService.js'; @Injectable() export class PageLikeEntityService { constructor( - @Inject('pageLikesRepository') + @Inject(DI.pageLikesRepository) private pageLikesRepository: typeof PageLikes, private pageEntityService: PageEntityService, diff --git a/packages/backend/src/services/entities/SigninEntityService.ts b/packages/backend/src/services/entities/SigninEntityService.ts index 9cf762026b1d..421e0968382c 100644 --- a/packages/backend/src/services/entities/SigninEntityService.ts +++ b/packages/backend/src/services/entities/SigninEntityService.ts @@ -11,7 +11,7 @@ import { UserEntityService } from './UserEntityService.js'; @Injectable() export class SigninEntityService { constructor( - @Inject('signinsRepository') + @Inject(DI.signinsRepository) private signinsRepository: typeof Signins, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/entities/UserEntityService.ts b/packages/backend/src/services/entities/UserEntityService.ts index 49705966516d..7dcfc2586f0c 100644 --- a/packages/backend/src/services/entities/UserEntityService.ts +++ b/packages/backend/src/services/entities/UserEntityService.ts @@ -3,7 +3,7 @@ import { EntityRepository, Repository, In, Not } from 'typeorm'; import Ajv from 'ajv'; import { ModuleRef } from '@nestjs/core'; import { DI } from '@/di-symbols.js'; -import type { Pages , AntennaNotes, Instances, MessagingMessages, UserSecurityKeys , Blockings, Mutings , Followings, FollowRequests, Users, DriveFiles, NoteUnreads, ChannelFollowings, Notifications, UserNotePinings, UserProfiles , AnnouncementReads, Announcements, UserGroupJoinings } from '@/models/index.js'; +import type { Pages, AntennaNotes, Instances, MessagingMessages, UserSecurityKeys, Blockings, Mutings, Followings, FollowRequests, Users, DriveFiles, NoteUnreads, ChannelFollowings, Notifications, UserNotePinings, UserProfiles, AnnouncementReads, Announcements, UserGroupJoinings } from '@/models/index.js'; import { Config } from '@/config.js'; import type { Packed } from '@/misc/schema.js'; import type { Promiseable } from '@/prelude/await-all.js'; @@ -57,61 +57,61 @@ export class UserEntityService implements OnModuleInit { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userSecurityKeysRepository') + @Inject(DI.userSecurityKeysRepository) private userSecurityKeysRepository: typeof UserSecurityKeys, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, - @Inject('followRequestsRepository') + @Inject(DI.followRequestsRepository) private followRequestsRepository: typeof FollowRequests, - @Inject('blockingsRepository') + @Inject(DI.blockingsRepository) private blockingsRepository: typeof Blockings, - @Inject('mutingsRepository') + @Inject(DI.mutingsRepository) private mutingsRepository: typeof Mutings, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, - @Inject('noteUnreadsRepository') + @Inject(DI.noteUnreadsRepository) private noteUnreadsRepository: typeof NoteUnreads, - @Inject('channelFollowingsRepository') + @Inject(DI.channelFollowingsRepository) private channelFollowingsRepository: typeof ChannelFollowings, - @Inject('notificationsRepository') + @Inject(DI.notificationsRepository) private notificationsRepository: typeof Notifications, - @Inject('userNotePiningsRepository') + @Inject(DI.userNotePiningsRepository) private userNotePiningsRepository: typeof UserNotePinings, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, - @Inject('instancesRepository') + @Inject(DI.instancesRepository) private instancesRepository: typeof Instances, - @Inject('announcementReadsRepository') + @Inject(DI.announcementReadsRepository) private announcementReadsRepository: typeof AnnouncementReads, - @Inject('messagingMessagesRepository') + @Inject(DI.messagingMessagesRepository) private messagingMessagesRepository: typeof MessagingMessages, - @Inject('userGroupJoiningsRepository') + @Inject(DI.userGroupJoiningsRepository) private userGroupJoiningsRepository: typeof UserGroupJoinings, - @Inject('announcementsRepository') + @Inject(DI.announcementsRepository) private announcementsRepository: typeof Announcements, - @Inject('antennaNotesRepository') + @Inject(DI.antennaNotesRepository) private antennaNotesRepository: typeof AntennaNotes, - @Inject('pagesRepository') + @Inject(DI.pagesRepository) private pagesRepository: typeof Pages, //private noteEntityService: NoteEntityService, diff --git a/packages/backend/src/services/entities/UserGroupEntityService.ts b/packages/backend/src/services/entities/UserGroupEntityService.ts index 591da729c9e9..ddf1a7f6c960 100644 --- a/packages/backend/src/services/entities/UserGroupEntityService.ts +++ b/packages/backend/src/services/entities/UserGroupEntityService.ts @@ -11,10 +11,10 @@ import { UserEntityService } from './UserEntityService.js'; @Injectable() export class UserGroupEntityService { constructor( - @Inject('userGroupsRepository') + @Inject(DI.userGroupsRepository) private userGroupsRepository: typeof UserGroups, - @Inject('userGroupJoiningsRepository') + @Inject(DI.userGroupJoiningsRepository) private userGroupJoiningsRepository: typeof UserGroupJoinings, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/entities/UserGroupInvitationEntityService.ts b/packages/backend/src/services/entities/UserGroupInvitationEntityService.ts index 896ed84819d5..69e086ff20eb 100644 --- a/packages/backend/src/services/entities/UserGroupInvitationEntityService.ts +++ b/packages/backend/src/services/entities/UserGroupInvitationEntityService.ts @@ -12,7 +12,7 @@ import { UserGroupEntityService } from './UserGroupEntityService.js'; @Injectable() export class UserGroupInvitationEntityService { constructor( - @Inject('userGroupInvitationsRepository') + @Inject(DI.userGroupInvitationsRepository) private userGroupInvitationsRepository: typeof UserGroupInvitations, private userGroupEntityService: UserGroupEntityService, diff --git a/packages/backend/src/services/entities/UserListEntityService.ts b/packages/backend/src/services/entities/UserListEntityService.ts index 7c42c5789eec..f83b135c6aaf 100644 --- a/packages/backend/src/services/entities/UserListEntityService.ts +++ b/packages/backend/src/services/entities/UserListEntityService.ts @@ -11,10 +11,10 @@ import { UserEntityService } from './UserEntityService.js'; @Injectable() export class UserListEntityService { constructor( - @Inject('userListsRepository') + @Inject(DI.userListsRepository) private userListsRepository: typeof UserLists, - @Inject('userListJoiningsRepository') + @Inject(DI.userListJoiningsRepository) private userListJoiningsRepository: typeof UserListJoinings, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/remote/ResolveUserService.ts b/packages/backend/src/services/remote/ResolveUserService.ts index 83c35f425ca8..dac877c43611 100644 --- a/packages/backend/src/services/remote/ResolveUserService.ts +++ b/packages/backend/src/services/remote/ResolveUserService.ts @@ -20,7 +20,7 @@ export class ResolveUserService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, private utilityService: UtilityService, diff --git a/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts b/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts index fe3f877ce9fc..3d318ab098ba 100644 --- a/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts +++ b/packages/backend/src/services/remote/activitypub/ApDbResolverService.ts @@ -39,13 +39,13 @@ export class ApDbResolverService { @Inject(DI.config) private config: Config, - @Inject('messagingMessagesRepository') + @Inject(DI.messagingMessagesRepository) private messagingMessagesRepository: typeof MessagingMessages, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('userPublickeysRepository') + @Inject(DI.userPublickeysRepository) private userPublickeysRepository: typeof UserPublickeys, private userCacheService: UserCacheService, diff --git a/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts b/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts index edcb120b2327..b55a13d5eb39 100644 --- a/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts +++ b/packages/backend/src/services/remote/activitypub/ApDeliverManagerService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, Not } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { Followings , Users } from '@/models/index.js'; +import type { Followings, Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; import { QueueService } from '@/services/QueueService.js'; @@ -32,10 +32,10 @@ export class ApDeliverManagerService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/remote/activitypub/ApInboxService.ts b/packages/backend/src/services/remote/activitypub/ApInboxService.ts index c8d260aed01c..493fee9afe40 100644 --- a/packages/backend/src/services/remote/activitypub/ApInboxService.ts +++ b/packages/backend/src/services/remote/activitypub/ApInboxService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { AbuseUserReports , Followings, FollowRequests, MessagingMessages, Notes , Users } from '@/models/index.js'; +import type { AbuseUserReports, Followings, FollowRequests, MessagingMessages, Notes, Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type { CacheableRemoteUser } from '@/models/entities/User.js'; import { UserFollowingService } from '@/services/UserFollowingService.js'; @@ -41,22 +41,22 @@ export class ApInboxService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, - @Inject('messagingMessagesRepository') + @Inject(DI.messagingMessagesRepository) private messagingMessagesRepository: typeof MessagingMessages, - @Inject('abuseUserReportsRepository') + @Inject(DI.abuseUserReportsRepository) private abuseUserReportsRepository: typeof AbuseUserReports, - @Inject('followRequestsRepository') + @Inject(DI.followRequestsRepository) private followRequestsRepository: typeof FollowRequests, private userEntityService: UserEntityService, diff --git a/packages/backend/src/services/remote/activitypub/ApRendererService.ts b/packages/backend/src/services/remote/activitypub/ApRendererService.ts index 52a6cfd0d53a..69b59eb60127 100644 --- a/packages/backend/src/services/remote/activitypub/ApRendererService.ts +++ b/packages/backend/src/services/remote/activitypub/ApRendererService.ts @@ -4,7 +4,7 @@ import { In, IsNull } from 'typeorm'; import { v4 as uuid } from 'uuid'; import * as mfm from 'mfm-js'; import { DI } from '@/di-symbols.js'; -import type { UserProfiles , Polls , DriveFiles, Emojis, Notes, Users } from '@/models/index.js'; +import type { UserProfiles, Polls, DriveFiles, Emojis, Notes, Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; import type { IMentionedRemoteUsers, Note } from '@/models/entities/Note.js'; @@ -32,26 +32,26 @@ export class ApRendererService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, - @Inject('emojisRepository') + @Inject(DI.emojisRepository) private emojisRepository: typeof Emojis, - @Inject('pollsRepository') + @Inject(DI.pollsRepository) private pollsRepository: typeof Polls, private userEntityService: UserEntityService, -private driveFileEntityService: DriveFileEntityService, + private driveFileEntityService: DriveFileEntityService, private ldSignatureService: LdSignatureService, private userKeypairStoreService: UserKeypairStoreService, private apMfmService: ApMfmService, @@ -565,7 +565,7 @@ private driveFileEntityService: DriveFileEntityService, id: `${this.config.url}/users/${user.id}#updates/${new Date().getTime()}`, actor: `${this.config.url}/users/${user.id}`, type: 'Update', - to: [ 'https://www.w3.org/ns/activitystreams#Public' ], + to: ['https://www.w3.org/ns/activitystreams#Public'], object, published: new Date().toISOString(), } as any; diff --git a/packages/backend/src/services/remote/activitypub/ApResolverService.ts b/packages/backend/src/services/remote/activitypub/ApResolverService.ts index 7ca5eef7bce0..ee0e2b0bdd81 100644 --- a/packages/backend/src/services/remote/activitypub/ApResolverService.ts +++ b/packages/backend/src/services/remote/activitypub/ApResolverService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { ILocalUser } from '@/models/entities/User.js'; import { InstanceActorService } from '@/services/InstanceActorService.js'; -import type { Notes , Polls , NoteReactions , Users } from '@/models/index.js'; +import type { Notes, Polls, NoteReactions, Users } from '@/models/index.js'; import { Config } from '@/config.js'; import { MetaService } from '@/services/MetaService.js'; import { HttpRequestService } from '@/services/HttpRequestService.js'; @@ -19,16 +19,16 @@ export class ApResolverService { @Inject(DI.config) private config: Config, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('pollsRepository') + @Inject(DI.pollsRepository) private pollsRepository: typeof Polls, - @Inject('noteReactionsRepository') + @Inject(DI.noteReactionsRepository) private noteReactionsRepository: typeof NoteReactions, private utilityService: UtilityService, diff --git a/packages/backend/src/services/remote/activitypub/models/ApImageService.ts b/packages/backend/src/services/remote/activitypub/models/ApImageService.ts index 5ea446346e52..d259f2f8219f 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApImageService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApImageService.ts @@ -20,7 +20,7 @@ export class ApImageService { @Inject(DI.config) private config: Config, - @Inject('driveFilesRepository') + @Inject(DI.driveFilesRepository) private driveFilesRepository: typeof DriveFiles, private metaService: MetaService, diff --git a/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts b/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts index 30a01ca8c28f..3c000d54bf6c 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApMentionService.ts @@ -8,7 +8,7 @@ import type { CacheableUser } from '@/models/entities/User.js'; import { isMention } from '../type.js'; import { ApResolverService } from '../ApResolverService.js'; import { ApPersonService } from './ApPersonService.js'; -import type { IObject , IApMention } from '../type.js'; +import type { IObject, IApMention } from '../type.js'; @Injectable() export class ApMentionService { diff --git a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts index b730ffd34346..aa17ed4ece7c 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApNoteService.ts @@ -1,7 +1,7 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { DI } from '@/di-symbols.js'; -import type { MessagingMessages , Polls , Emojis, Users } from '@/models/index.js'; +import type { MessagingMessages, Polls, Emojis, Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type { CacheableRemoteUser } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; @@ -40,13 +40,13 @@ export class ApNoteService { @Inject(DI.config) private config: Config, - @Inject('pollsRepository') + @Inject(DI.pollsRepository) private pollsRepository: typeof Polls, - @Inject('emojisRepository') + @Inject(DI.emojisRepository) private emojisRepository: typeof Emojis, - @Inject('messagingMessagesRepository') + @Inject(DI.messagingMessagesRepository) private messagingMessagesRepository: typeof MessagingMessages, private idService: IdService, diff --git a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts index 33572fcee23c..6a2405f74332 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApPersonService.ts @@ -3,7 +3,7 @@ import promiseLimit from 'promise-limit'; import { DataSource } from 'typeorm'; import { ModuleRef } from '@nestjs/core'; import { DI } from '@/di-symbols.js'; -import type { Followings , Instances, UserProfiles, UserPublickeys, Users } from '@/models/index.js'; +import type { Followings, Instances, UserProfiles, UserPublickeys, Users } from '@/models/index.js'; import { Config } from '@/config.js'; import type { CacheableUser, IRemoteUser } from '@/models/entities/User.js'; import { User } from '@/models/entities/User.js'; @@ -34,12 +34,11 @@ import { extractApHashtags } from './tag.js'; import type { OnModuleInit } from '@nestjs/common'; import type { ApNoteService } from './ApNoteService.js'; import type { ApMfmService } from '../ApMfmService.js'; -import type { ApResolverService , Resolver } from '../ApResolverService.js'; +import type { ApResolverService, Resolver } from '../ApResolverService.js'; import type { ApLoggerService } from '../ApLoggerService.js'; // eslint-disable-next-line @typescript-eslint/consistent-type-imports import type { ApImageService } from './ApImageService.js'; - -import type { IActor, IObject , IApPropertyValue } from '../type.js'; +import type { IActor, IObject, IApPropertyValue } from '../type.js'; const nameLength = 128; const summaryLength = 2048; @@ -103,19 +102,19 @@ export class ApPersonService implements OnModuleInit { @Inject(DI.db) private db: DataSource, - @Inject('usersRepository') + @Inject(DI.usersRepository) private usersRepository: typeof Users, - @Inject('userProfilesRepository') + @Inject(DI.userProfilesRepository) private userProfilesRepository: typeof UserProfiles, - @Inject('userPublickeysRepository') + @Inject(DI.userPublickeysRepository) private userPublickeysRepository: typeof UserPublickeys, - @Inject('instancesRepository') + @Inject(DI.instancesRepository) private instancesRepository: typeof Instances, - @Inject('followingsRepository') + @Inject(DI.followingsRepository) private followingsRepository: typeof Followings, //private utilityService: UtilityService, diff --git a/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts b/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts index afb9860ecd9a..e40b7cec0d9a 100644 --- a/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts +++ b/packages/backend/src/services/remote/activitypub/models/ApQuestionService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { Notes , Polls } from '@/models/index.js'; +import type { Notes, Polls } from '@/models/index.js'; import { Config } from '@/config.js'; import type { IPoll } from '@/models/entities/Poll.js'; import type Logger from '@/logger.js'; @@ -8,7 +8,7 @@ import { isQuestion } from '../type.js'; import { ApLoggerService } from '../ApLoggerService.js'; import { ApResolverService } from '../ApResolverService.js'; import type { Resolver } from '../ApResolverService.js'; -import type { IObject , IQuestion } from '../type.js'; +import type { IObject, IQuestion } from '../type.js'; @Injectable() export class ApQuestionService { @@ -18,10 +18,10 @@ export class ApQuestionService { @Inject(DI.config) private config: Config, - @Inject('notesRepository') + @Inject(DI.notesRepository) private notesRepository: typeof Notes, - @Inject('pollsRepository') + @Inject(DI.pollsRepository) private pollsRepository: typeof Polls, private apResolverService: ApResolverService, From 244ee18abe3484d870ee4d9aaf88504db1f37332 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 17 Sep 2022 05:24:13 +0900 Subject: [PATCH 180/180] :100: --- .../src/services/chart/charts/active-users.ts | 2 +- .../src/services/chart/charts/ap-request.ts | 2 +- .../src/services/chart/charts/drive.ts | 2 +- .../src/services/chart/charts/federation.ts | 28 +++++++++++-------- .../src/services/chart/charts/hashtag.ts | 8 ++++-- .../src/services/chart/charts/instance.ts | 26 ++++++++++++----- .../src/services/chart/charts/notes.ts | 11 +++++--- .../services/chart/charts/per-user-drive.ts | 13 ++++++--- .../chart/charts/per-user-following.ts | 8 ++++-- .../services/chart/charts/per-user-notes.ts | 2 +- .../chart/charts/per-user-reactions.ts | 6 ++-- .../src/services/chart/charts/test-grouped.ts | 2 +- .../chart/charts/test-intersection.ts | 2 +- .../src/services/chart/charts/test-unique.ts | 2 +- .../backend/src/services/chart/charts/test.ts | 2 +- .../src/services/chart/charts/users.ts | 6 ++-- 16 files changed, 78 insertions(+), 44 deletions(-) diff --git a/packages/backend/src/services/chart/charts/active-users.ts b/packages/backend/src/services/chart/charts/active-users.ts index 4084f890bf85..f7b2b5b6da3e 100644 --- a/packages/backend/src/services/chart/charts/active-users.ts +++ b/packages/backend/src/services/chart/charts/active-users.ts @@ -23,7 +23,7 @@ export default class ActiveUsersChart extends Chart { private appLockService: AppLockService, ) { - super(db, appLockService.getChartInsertLock, name, schema); + super(db, (k) => appLockService.getChartInsertLock(k), name, schema); } protected async tickMajor(): Promise>> { diff --git a/packages/backend/src/services/chart/charts/ap-request.ts b/packages/backend/src/services/chart/charts/ap-request.ts index d990132ad5bf..ecd78528ea66 100644 --- a/packages/backend/src/services/chart/charts/ap-request.ts +++ b/packages/backend/src/services/chart/charts/ap-request.ts @@ -18,7 +18,7 @@ export default class ApRequestChart extends Chart { private appLockService: AppLockService, ) { - super(db, appLockService.getChartInsertLock, name, schema); + super(db, (k) => appLockService.getChartInsertLock(k), name, schema); } protected async tickMajor(): Promise>> { diff --git a/packages/backend/src/services/chart/charts/drive.ts b/packages/backend/src/services/chart/charts/drive.ts index 9ae0ebcae089..4f88b73eeddb 100644 --- a/packages/backend/src/services/chart/charts/drive.ts +++ b/packages/backend/src/services/chart/charts/drive.ts @@ -20,7 +20,7 @@ export default class DriveChart extends Chart { private appLockService: AppLockService, ) { - super(db, appLockService.getChartInsertLock, name, schema); + super(db, (k) => appLockService.getChartInsertLock(k), name, schema); } protected async tickMajor(): Promise>> { diff --git a/packages/backend/src/services/chart/charts/federation.ts b/packages/backend/src/services/chart/charts/federation.ts index 75123d92026b..9deeb9dafb8b 100644 --- a/packages/backend/src/services/chart/charts/federation.ts +++ b/packages/backend/src/services/chart/charts/federation.ts @@ -1,6 +1,6 @@ import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; -import { Followings, Instances } from '@/models/index.js'; +import type { Followings, Instances } from '@/models/index.js'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { MetaService } from '@/services/MetaService.js'; @@ -18,10 +18,16 @@ export default class FederationChart extends Chart { @Inject(DI.db) private db: DataSource, + @Inject(DI.followingsRepository) + private followingsRepository: typeof Followings, + + @Inject(DI.instancesRepository) + private instancesRepository: typeof Instances, + private metaService: MetaService, private appLockService: AppLockService, ) { - super(db, appLockService.getChartInsertLock, name, schema); + super(db, (k) => appLockService.getChartInsertLock(k), name, schema); } protected async tickMajor(): Promise>> { @@ -32,38 +38,38 @@ export default class FederationChart extends Chart { protected async tickMinor(): Promise>> { const meta = await this.metaService.fetch(); - const suspendedInstancesQuery = Instances.createQueryBuilder('instance') + const suspendedInstancesQuery = this.instancesRepository.createQueryBuilder('instance') .select('instance.host') .where('instance.isSuspended = true'); - const pubsubSubQuery = Followings.createQueryBuilder('f') + const pubsubSubQuery = this.followingsRepository.createQueryBuilder('f') .select('f.followerHost') .where('f.followerHost IS NOT NULL'); - const subInstancesQuery = Followings.createQueryBuilder('f') + const subInstancesQuery = this.followingsRepository.createQueryBuilder('f') .select('f.followeeHost') .where('f.followeeHost IS NOT NULL'); - const pubInstancesQuery = Followings.createQueryBuilder('f') + const pubInstancesQuery = this.followingsRepository.createQueryBuilder('f') .select('f.followerHost') .where('f.followerHost IS NOT NULL'); const [sub, pub, pubsub, subActive, pubActive] = await Promise.all([ - Followings.createQueryBuilder('following') + this.followingsRepository.createQueryBuilder('following') .select('COUNT(DISTINCT following.followeeHost)') .where('following.followeeHost IS NOT NULL') .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'following.followeeHost NOT IN (:...blocked)', { blocked: meta.blockedHosts }) .andWhere(`following.followeeHost NOT IN (${ suspendedInstancesQuery.getQuery() })`) .getRawOne() .then(x => parseInt(x.count, 10)), - Followings.createQueryBuilder('following') + this.followingsRepository.createQueryBuilder('following') .select('COUNT(DISTINCT following.followerHost)') .where('following.followerHost IS NOT NULL') .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'following.followerHost NOT IN (:...blocked)', { blocked: meta.blockedHosts }) .andWhere(`following.followerHost NOT IN (${ suspendedInstancesQuery.getQuery() })`) .getRawOne() .then(x => parseInt(x.count, 10)), - Followings.createQueryBuilder('following') + this.followingsRepository.createQueryBuilder('following') .select('COUNT(DISTINCT following.followeeHost)') .where('following.followeeHost IS NOT NULL') .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'following.followeeHost NOT IN (:...blocked)', { blocked: meta.blockedHosts }) @@ -72,7 +78,7 @@ export default class FederationChart extends Chart { .setParameters(pubsubSubQuery.getParameters()) .getRawOne() .then(x => parseInt(x.count, 10)), - Instances.createQueryBuilder('instance') + this.instancesRepository.createQueryBuilder('instance') .select('COUNT(instance.id)') .where(`instance.host IN (${ subInstancesQuery.getQuery() })`) .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'instance.host NOT IN (:...blocked)', { blocked: meta.blockedHosts }) @@ -80,7 +86,7 @@ export default class FederationChart extends Chart { .andWhere('instance.lastCommunicatedAt > :gt', { gt: new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)) }) .getRawOne() .then(x => parseInt(x.count, 10)), - Instances.createQueryBuilder('instance') + this.instancesRepository.createQueryBuilder('instance') .select('COUNT(instance.id)') .where(`instance.host IN (${ pubInstancesQuery.getQuery() })`) .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'instance.host NOT IN (:...blocked)', { blocked: meta.blockedHosts }) diff --git a/packages/backend/src/services/chart/charts/hashtag.ts b/packages/backend/src/services/chart/charts/hashtag.ts index a30520dce006..af4e3bbbc7c1 100644 --- a/packages/backend/src/services/chart/charts/hashtag.ts +++ b/packages/backend/src/services/chart/charts/hashtag.ts @@ -4,6 +4,7 @@ import type { User } from '@/models/entities/User.js'; import { Users } from '@/models/index.js'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; import Chart from '../core.js'; import { name, schema } from './entities/hashtag.js'; import type { KVs } from '../core.js'; @@ -19,8 +20,9 @@ export default class HashtagChart extends Chart { private db: DataSource, private appLockService: AppLockService, + private userEntityService: UserEntityService, ) { - super(db, appLockService.getChartInsertLock, name, schema, true); + super(db, (k) => appLockService.getChartInsertLock(k), name, schema, true); } protected async tickMajor(): Promise>> { @@ -33,8 +35,8 @@ export default class HashtagChart extends Chart { public async update(hashtag: string, user: { id: User['id'], host: User['host'] }): Promise { await this.commit({ - 'local.users': Users.isLocalUser(user) ? [user.id] : [], - 'remote.users': Users.isLocalUser(user) ? [] : [user.id], + 'local.users': this.userEntityService.isLocalUser(user) ? [user.id] : [], + 'remote.users': this.userEntityService.isLocalUser(user) ? [] : [user.id], }, hashtag); } } diff --git a/packages/backend/src/services/chart/charts/instance.ts b/packages/backend/src/services/chart/charts/instance.ts index 98bbd7e5246d..5bbb68c15660 100644 --- a/packages/backend/src/services/chart/charts/instance.ts +++ b/packages/backend/src/services/chart/charts/instance.ts @@ -1,6 +1,6 @@ import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; -import { DriveFiles, Followings, Users, Notes } from '@/models/index.js'; +import type { DriveFiles, Followings, Users, Notes } from '@/models/index.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { Note } from '@/models/entities/Note.js'; import { AppLockService } from '@/services/AppLockService.js'; @@ -20,10 +20,22 @@ export default class InstanceChart extends Chart { @Inject(DI.db) private db: DataSource, + @Inject(DI.usersRepository) + private usersRepository: typeof Users, + + @Inject(DI.notesRepository) + private notesRepository: typeof Notes, + + @Inject(DI.driveFilesRepository) + private driveFilesRepository: typeof DriveFiles, + + @Inject(DI.followingsRepository) + private followingsRepository: typeof Followings, + private utilityService: UtilityService, private appLockService: AppLockService, ) { - super(db, appLockService.getChartInsertLock, name, schema, true); + super(db, (k) => appLockService.getChartInsertLock(k), name, schema, true); } protected async tickMajor(group: string): Promise>> { @@ -34,11 +46,11 @@ export default class InstanceChart extends Chart { followersCount, driveFiles, ] = await Promise.all([ - Notes.countBy({ userHost: group }), - Users.countBy({ host: group }), - Followings.countBy({ followerHost: group }), - Followings.countBy({ followeeHost: group }), - DriveFiles.countBy({ userHost: group }), + this.notesRepository.countBy({ userHost: group }), + this.usersRepository.countBy({ host: group }), + this.followingsRepository.countBy({ followerHost: group }), + this.followingsRepository.countBy({ followeeHost: group }), + this.driveFilesRepository.countBy({ userHost: group }), ]); return { diff --git a/packages/backend/src/services/chart/charts/notes.ts b/packages/backend/src/services/chart/charts/notes.ts index c78a5a53d808..2631254f8cc8 100644 --- a/packages/backend/src/services/chart/charts/notes.ts +++ b/packages/backend/src/services/chart/charts/notes.ts @@ -1,6 +1,6 @@ import { Injectable, Inject } from '@nestjs/common'; import { Not, IsNull, DataSource } from 'typeorm'; -import { Notes } from '@/models/index.js'; +import type { Notes } from '@/models/index.js'; import type { Note } from '@/models/entities/Note.js'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; @@ -18,15 +18,18 @@ export default class NotesChart extends Chart { @Inject(DI.db) private db: DataSource, + @Inject(DI.notesRepository) + private notesRepository: typeof Notes, + private appLockService: AppLockService, ) { - super(db, appLockService.getChartInsertLock, name, schema); + super(db, (k) => appLockService.getChartInsertLock(k), name, schema); } protected async tickMajor(): Promise>> { const [localCount, remoteCount] = await Promise.all([ - Notes.countBy({ userHost: IsNull() }), - Notes.countBy({ userHost: Not(IsNull()) }), + this.notesRepository.countBy({ userHost: IsNull() }), + this.notesRepository.countBy({ userHost: Not(IsNull()) }), ]); return { diff --git a/packages/backend/src/services/chart/charts/per-user-drive.ts b/packages/backend/src/services/chart/charts/per-user-drive.ts index f469d5d416a5..1722fa8f7383 100644 --- a/packages/backend/src/services/chart/charts/per-user-drive.ts +++ b/packages/backend/src/services/chart/charts/per-user-drive.ts @@ -1,9 +1,10 @@ import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; -import { DriveFiles } from '@/models/index.js'; +import type { DriveFiles } from '@/models/index.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; +import { DriveFileEntityService } from '@/services/entities/DriveFileEntityService.js'; import Chart from '../core.js'; import { name, schema } from './entities/per-user-drive.js'; import type { KVs } from '../core.js'; @@ -18,15 +19,19 @@ export default class PerUserDriveChart extends Chart { @Inject(DI.db) private db: DataSource, + @Inject(DI.driveFilesRepository) + private driveFilesRepository: typeof DriveFiles, + private appLockService: AppLockService, + private driveFileEntityService: DriveFileEntityService, ) { - super(db, appLockService.getChartInsertLock, name, schema, true); + super(db, (k) => appLockService.getChartInsertLock(k), name, schema, true); } protected async tickMajor(group: string): Promise>> { const [count, size] = await Promise.all([ - DriveFiles.countBy({ userId: group }), - DriveFiles.calcDriveUsageOf(group), + this.driveFilesRepository.countBy({ userId: group }), + this.driveFileEntityService.calcDriveUsageOf(group), ]); return { diff --git a/packages/backend/src/services/chart/charts/per-user-following.ts b/packages/backend/src/services/chart/charts/per-user-following.ts index 0fbea0f8ad00..62e37e59f7ec 100644 --- a/packages/backend/src/services/chart/charts/per-user-following.ts +++ b/packages/backend/src/services/chart/charts/per-user-following.ts @@ -4,6 +4,7 @@ import { Followings, Users } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; import Chart from '../core.js'; import { name, schema } from './entities/per-user-following.js'; import type { KVs } from '../core.js'; @@ -19,8 +20,9 @@ export default class PerUserFollowingChart extends Chart { private db: DataSource, private appLockService: AppLockService, + private userEntityService: UserEntityService, ) { - super(db, appLockService.getChartInsertLock, name, schema, true); + super(db, (k) => appLockService.getChartInsertLock(k), name, schema, true); } protected async tickMajor(group: string): Promise>> { @@ -49,8 +51,8 @@ export default class PerUserFollowingChart extends Chart { } public async update(follower: { id: User['id']; host: User['host']; }, followee: { id: User['id']; host: User['host']; }, isFollow: boolean): Promise { - const prefixFollower = Users.isLocalUser(follower) ? 'local' : 'remote'; - const prefixFollowee = Users.isLocalUser(followee) ? 'local' : 'remote'; + const prefixFollower = this.userEntityService.isLocalUser(follower) ? 'local' : 'remote'; + const prefixFollowee = this.userEntityService.isLocalUser(followee) ? 'local' : 'remote'; this.commit({ [`${prefixFollower}.followings.total`]: isFollow ? 1 : -1, diff --git a/packages/backend/src/services/chart/charts/per-user-notes.ts b/packages/backend/src/services/chart/charts/per-user-notes.ts index 5e2bb811a10d..8d6a4ca817b0 100644 --- a/packages/backend/src/services/chart/charts/per-user-notes.ts +++ b/packages/backend/src/services/chart/charts/per-user-notes.ts @@ -21,7 +21,7 @@ export default class PerUserNotesChart extends Chart { private appLockService: AppLockService, ) { - super(db, appLockService.getChartInsertLock, name, schema, true); + super(db, (k) => appLockService.getChartInsertLock(k), name, schema, true); } protected async tickMajor(group: string): Promise>> { diff --git a/packages/backend/src/services/chart/charts/per-user-reactions.ts b/packages/backend/src/services/chart/charts/per-user-reactions.ts index 46a049c1b5c5..4c3ec67cfbe3 100644 --- a/packages/backend/src/services/chart/charts/per-user-reactions.ts +++ b/packages/backend/src/services/chart/charts/per-user-reactions.ts @@ -5,6 +5,7 @@ import type { Note } from '@/models/entities/Note.js'; import { Users } from '@/models/index.js'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; import Chart from '../core.js'; import { name, schema } from './entities/per-user-reactions.js'; import type { KVs } from '../core.js'; @@ -20,8 +21,9 @@ export default class PerUserReactionsChart extends Chart { private db: DataSource, private appLockService: AppLockService, + private userEntityService: UserEntityService, ) { - super(db, appLockService.getChartInsertLock, name, schema, true); + super(db, (k) => appLockService.getChartInsertLock(k), name, schema, true); } protected async tickMajor(group: string): Promise>> { @@ -33,7 +35,7 @@ export default class PerUserReactionsChart extends Chart { } public async update(user: { id: User['id'], host: User['host'] }, note: Note): Promise { - const prefix = Users.isLocalUser(user) ? 'local' : 'remote'; + const prefix = this.userEntityService.isLocalUser(user) ? 'local' : 'remote'; this.commit({ [`${prefix}.count`]: 1, }, note.userId); diff --git a/packages/backend/src/services/chart/charts/test-grouped.ts b/packages/backend/src/services/chart/charts/test-grouped.ts index 77e776d130ce..1745f4deb7cb 100644 --- a/packages/backend/src/services/chart/charts/test-grouped.ts +++ b/packages/backend/src/services/chart/charts/test-grouped.ts @@ -20,7 +20,7 @@ export default class TestGroupedChart extends Chart { private appLockService: AppLockService, ) { - super(db, appLockService.getChartInsertLock, name, schema, true); + super(db, (k) => appLockService.getChartInsertLock(k), name, schema, true); } protected async tickMajor(group: string): Promise>> { diff --git a/packages/backend/src/services/chart/charts/test-intersection.ts b/packages/backend/src/services/chart/charts/test-intersection.ts index 8b34a883ed0b..7a94775fd5de 100644 --- a/packages/backend/src/services/chart/charts/test-intersection.ts +++ b/packages/backend/src/services/chart/charts/test-intersection.ts @@ -18,7 +18,7 @@ export default class TestIntersectionChart extends Chart { private appLockService: AppLockService, ) { - super(db, appLockService.getChartInsertLock, name, schema); + super(db, (k) => appLockService.getChartInsertLock(k), name, schema); } protected async tickMajor(): Promise>> { diff --git a/packages/backend/src/services/chart/charts/test-unique.ts b/packages/backend/src/services/chart/charts/test-unique.ts index e0064dedc70e..d12bc7157236 100644 --- a/packages/backend/src/services/chart/charts/test-unique.ts +++ b/packages/backend/src/services/chart/charts/test-unique.ts @@ -18,7 +18,7 @@ export default class TestUniqueChart extends Chart { private appLockService: AppLockService, ) { - super(db, appLockService.getChartInsertLock, name, schema); + super(db, (k) => appLockService.getChartInsertLock(k), name, schema); } protected async tickMajor(): Promise>> { diff --git a/packages/backend/src/services/chart/charts/test.ts b/packages/backend/src/services/chart/charts/test.ts index 28e4d38c7a8a..29e99711b288 100644 --- a/packages/backend/src/services/chart/charts/test.ts +++ b/packages/backend/src/services/chart/charts/test.ts @@ -20,7 +20,7 @@ export default class TestChart extends Chart { private appLockService: AppLockService, ) { - super(db, appLockService.getChartInsertLock, name, schema); + super(db, (k) => appLockService.getChartInsertLock(k), name, schema); } protected async tickMajor(): Promise>> { diff --git a/packages/backend/src/services/chart/charts/users.ts b/packages/backend/src/services/chart/charts/users.ts index 84f778dd4b5e..aa31a5cfddda 100644 --- a/packages/backend/src/services/chart/charts/users.ts +++ b/packages/backend/src/services/chart/charts/users.ts @@ -4,6 +4,7 @@ import { Users } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import { AppLockService } from '@/services/AppLockService.js'; import { DI } from '@/di-symbols.js'; +import { UserEntityService } from '@/services/entities/UserEntityService.js'; import Chart from '../core.js'; import { name, schema } from './entities/users.js'; import type { KVs } from '../core.js'; @@ -19,8 +20,9 @@ export default class UsersChart extends Chart { private db: DataSource, private appLockService: AppLockService, + private userEntityService: UserEntityService, ) { - super(db, appLockService.getChartInsertLock, name, schema); + super(db, (k) => appLockService.getChartInsertLock(k), name, schema); } protected async tickMajor(): Promise>> { @@ -40,7 +42,7 @@ export default class UsersChart extends Chart { } public async update(user: { id: User['id'], host: User['host'] }, isAdditional: boolean): Promise { - const prefix = Users.isLocalUser(user) ? 'local' : 'remote'; + const prefix = this.userEntityService.isLocalUser(user) ? 'local' : 'remote'; await this.commit({ [`${prefix}.total`]: isAdditional ? 1 : -1,