From b75184ec8e3436200bacdcd832e3324702553d20 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 18 Sep 2022 03:27:08 +0900 Subject: なんかもうめっちゃ変えた MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/NoteCreateService.ts | 742 +++++++++++++++++++++++++ 1 file changed, 742 insertions(+) create mode 100644 packages/backend/src/core/NoteCreateService.ts (limited to 'packages/backend/src/core/NoteCreateService.ts') diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts new file mode 100644 index 0000000000..83c080893d --- /dev/null +++ b/packages/backend/src/core/NoteCreateService.ts @@ -0,0 +1,742 @@ +import * as mfm from 'mfm-js'; +import { Not, In, DataSource } 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'; +import type { IMentionedRemoteUsers } from '@/models/entities/Note.js'; +import { Note } from '@/models/entities/Note.js'; +import { ChannelFollowingsRepository, ChannelsRepository, InstancesRepository, MutedNotesRepository, MutingsRepository, NotesRepository, NoteThreadMutingsRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; +import type { App } from '@/models/entities/App.js'; +import { concat } from '@/misc/prelude/array.js'; +import { IdService } from '@/core/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 { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; +import { checkWordMute } from '@/misc/check-word-mute.js'; +import type { Channel } from '@/models/entities/Channel.js'; +import { normalizeForSearch } from '@/misc/normalize-for-search.js'; +import { Cache } from '@/misc/cache.js'; +import type { UserProfile } from '@/models/entities/UserProfile.js'; +import { RelayService } from '@/core/RelayService.js'; +import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; +import { DI } from '@/di-symbols.js'; +import { Config } from '@/config.js'; +import NotesChart from '@/core/chart/charts/notes.js'; +import PerUserNotesChart from '@/core/chart/charts/per-user-notes.js'; +import InstanceChart from '@/core/chart/charts/instance.js'; +import ActiveUsersChart from '@/core/chart/charts/active-users.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { CreateNotificationService } from '@/core/CreateNotificationService.js'; +import { WebhookService } from '@/core/WebhookService.js'; +import { HashtagService } from '@/core/HashtagService.js'; +import { AntennaService } from '@/core/AntennaService.js'; +import { QueueService } from '@/core/QueueService.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'; +import { ApDeliverManagerService } from './remote/activitypub/ApDeliverManagerService.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( + private mutingsRepository: MutingsRepository, + private createNotificationService: CreateNotificationService, + 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 this.mutingsRepository.findBy({ + muterId: x.target, + }); + + const mentioneesMutedUserIds = mentioneeMutes.map(m => m.muteeId); + + // 通知される側のユーザーが通知する側のユーザーをミュートしていない限りは通知する + if (!mentioneesMutedUserIds.includes(this.notifier.id)) { + this.createNotificationService.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; +}; + +@Injectable() +export class NoteCreateService { + constructor( + @Inject(DI.config) + private config: Config, + + @Inject(DI.db) + private db: DataSource, + + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + + @Inject(DI.mutingsRepository) + private mutingsRepository: MutingsRepository, + + @Inject(DI.instancesRepository) + private instancesRepository: InstancesRepository, + + @Inject(DI.userProfilesRepository) + private userProfilesRepository: UserProfilesRepository, + + @Inject(DI.mutedNotesRepository) + private mutedNotesRepository: MutedNotesRepository, + + @Inject(DI.channelsRepository) + private channelsRepository: ChannelsRepository, + + @Inject(DI.channelFollowingsRepository) + private channelFollowingsRepository: ChannelFollowingsRepository, + + @Inject(DI.noteThreadMutingsRepository) + private noteThreadMutingsRepository: NoteThreadMutingsRepository, + + 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 apDeliverManagerService: ApDeliverManagerService, + private apRendererService: ApRendererService, + private notesChart: NotesChart, + private perUserNotesChart: PerUserNotesChart, + private activeUsersChart: ActiveUsersChart, + private instanceChart: InstanceChart, + ) {} + + 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 this.channelsRepository.findOneBy({ id: data.reply.channelId }); + } else { + data.channel = null; + } + } + + // チャンネル内にリプライしたら対象のスコープに合わせる + // (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで) + if (data.reply && (data.channel == null) && data.reply.channelId) { + data.channel = await this.channelsRepository.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 this.usersRepository.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 this.usersRepository.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: this.idService.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 this.userProfilesRepository.findBy({ userId: In(insert.mentions) }); + 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 { + uri: u.uri, + url: url == null ? undefined : url, + username: u.username, + host: u.host, + } as IMentionedRemoteUsers[0]; + })); + } + + // 投稿を作成 + try { + if (insert.hasPoll) { + // Start transaction + await this.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[]) { + // 統計を更新 + this.notesChart.update(note, true); + this.perUserNotesChart.update(user, note, true); + + // Register host + if (this.userEntityService.isRemoteUser(user)) { + this.federatedInstanceService.registerOrFetchInstanceDoc(user.host).then(i => { + this.instancesRepository.increment({ id: i.id }, 'notesCount', 1); + this.instanceChart.updateNote(i.host, note, true); + }); + } + + // ハッシュタグ更新 + if (data.visibility === 'public' || data.visibility === 'home') { + this.hashtagService.updateHashtags(user, tags); + } + + // Increment notes count (user) + this.#incNotesCountOfUser(user); + + // Word mute + mutedWordsCache.fetch(null, () => this.userProfilesRepository.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) { + this.mutedNotesRepository.insert({ + id: this.idService.genId(), + userId: u.userId, + noteId: note.id, + reason: 'word', + }); + } + }); + } + }); + + // Antenna + for (const antenna of (await this.antennaService.getAntennas())) { + this.antennaService.checkHitAntenna(antenna, note, user).then(hit => { + if (hit) { + this.antennaService.addNoteToAntenna(antenna, note, user); + } + }); + } + + // Channel + if (note.channelId) { + this.channelFollowingsRepository.findBy({ followeeId: note.channelId }).then(followings => { + for (const following of followings) { + this.noteReadService.insertNoteUnread(following.followerId, note, { + isSpecified: false, + isMentioned: false, + }); + } + }); + } + + if (data.reply) { + this.#saveReply(data.reply, note); + } + + // この投稿を除く指定したユーザーによる指定したノートのリノートが存在しないとき + if (data.renote && (await this.noteEntityService.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(); + this.queueService.endedPollNotificationQueue.add({ + noteId: note.id, + }, { + delay, + removeOnComplete: true, + }); + } + + if (!silent) { + if (this.userEntityService.isLocalUser(user)) this.activeUsersChart.write(user); + + // 未読通知を作成 + if (data.visibility === 'specified') { + if (data.visibleUsers == null) throw new Error('invalid param'); + + for (const u of data.visibleUsers) { + // ローカルユーザーのみ + if (!this.userEntityService.isLocalUser(u)) continue; + + this.noteReadService.insertNoteUnread(u.id, note, { + isSpecified: true, + isMentioned: false, + }); + } + } else { + for (const u of mentionedUsers) { + // ローカルユーザーのみ + if (!this.userEntityService.isLocalUser(u)) continue; + + this.noteReadService.insertNoteUnread(u.id, note, { + isSpecified: false, + isMentioned: true, + }); + } + } + + // Pack the note + const noteObj = await this.noteEntityService.pack(note); + + this.globalEventServie.publishNotesStream(noteObj); + + this.webhookService.getActiveWebhooks().then(webhooks => { + webhooks = webhooks.filter(x => x.userId === user.id && x.on.includes('note')); + for (const webhook of webhooks) { + this.queueService.webhookDeliver(webhook, 'note', { + note: noteObj, + }); + } + }); + + const nm = new NotificationManager(this.mutingsRepository, this.createNotificationService, user, note); + const nmRelatedPromises = []; + + await this.#createMentionedEvents(mentionedUsers, note, nm); + + // If has in reply to note + if (data.reply) { + // 通知 + if (data.reply.userHost === null) { + const threadMuted = await this.noteThreadMutingsRepository.findOneBy({ + userId: data.reply.userId, + threadId: data.reply.threadId ?? data.reply.id, + }); + + if (!threadMuted) { + nm.push(data.reply.userId, 'reply'); + this.globalEventServie.publishMainStream(data.reply.userId, 'reply', noteObj); + + const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === data.reply!.userId && x.on.includes('reply')); + for (const webhook of webhooks) { + this.queueService.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); + } + + // Publish event + if ((user.id !== data.renote.userId) && data.renote.userHost === null) { + this.globalEventServie.publishMainStream(data.renote.userId, 'renote', noteObj); + + const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === data.renote!.userId && x.on.includes('renote')); + for (const webhook of webhooks) { + this.queueService.webhookDeliver(webhook, 'renote', { + note: noteObj, + }); + } + } + } + + Promise.all(nmRelatedPromises).then(() => { + nm.deliver(); + }); + + //#region AP deliver + if (this.userEntityService.isLocalUser(user)) { + (async () => { + const noteActivity = await this.#renderNoteOrRenoteActivity(data, note); + const dm = this.apDeliverManagerService.createDeliverManager(user, noteActivity); + + // メンションされたリモートユーザーに配送 + 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.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.userEntityService.isRemoteUser(u)) dm.addDirectRecipe(u); + } + + // フォロワーに配送 + if (['public', 'home', 'followers'].includes(note.visibility)) { + dm.addFollowersRecipe(); + } + + if (['public'].includes(note.visibility)) { + this.relayService.deliverToRelays(user, noteActivity); + } + + dm.execute(); + })(); + } + //#endregion + } + + if (data.channel) { + this.channelsRepository.increment({ id: data.channel.id }, 'notesCount', 1); + this.channelsRepository.update(data.channel.id, { + lastNotedAt: new Date(), + }); + + this.notesRepository.countBy({ + userId: user.id, + channelId: data.channel.id, + }).then(count => { + // この処理が行われるのはノート作成後なので、ノートが一つしかなかったら最初の投稿だと判断できる + // TODO: とはいえノートを削除して何回も投稿すればその分だけインクリメントされる雑さもあるのでどうにかしたい + if (count === 1) { + this.channelsRepository.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 => this.userEntityService.isLocalUser(u))) { + const threadMuted = await this.noteThreadMutingsRepository.findOneBy({ + userId: u.id, + threadId: note.threadId ?? note.id, + }); + + if (threadMuted) { + continue; + } + + const detailPackedNote = await this.noteEntityService.pack(note, u, { + detail: true, + }); + + this.globalEventServie.publishMainStream(u.id, 'mention', detailPackedNote); + + const webhooks = (await this.webhookService.getActiveWebhooks()).filter(x => x.userId === u.id && x.on.includes('mention')); + for (const webhook of webhooks) { + this.queueService.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) + ? 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 this.apRendererService.renderActivity(content); + } + + #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(), + body: { + text: normalizeForSearch(note.text), + userId: note.userId, + userHost: note.userHost, + }, + });*/ + } + + #incNotesCountOfUser(user: { id: User['id']; }) { + this.usersRepository.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 => + this.resolveUserService.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; + } +} -- cgit v1.2.3-freya From a2eac9fff67f811ed4ac1a80a88fd1f0eafae6c8 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 19 Sep 2022 03:11:50 +0900 Subject: test --- packages/backend/src/core/AiService.ts | 10 +- packages/backend/src/core/AntennaService.ts | 28 ++-- packages/backend/src/core/AppLockService.ts | 10 +- packages/backend/src/core/CaptchaService.ts | 6 +- .../backend/src/core/CreateNotificationService.ts | 8 +- packages/backend/src/core/CustomEmojiService.ts | 26 ++-- packages/backend/src/core/DownloadService.ts | 20 +-- packages/backend/src/core/DriveService.ts | 82 ++++++------ packages/backend/src/core/EmailService.ts | 8 +- .../backend/src/core/FederatedInstanceService.ts | 10 +- .../src/core/FetchInstanceMetadataService.ts | 52 ++++---- packages/backend/src/core/FileInfoService.ts | 26 ++-- packages/backend/src/core/HttpRequestService.ts | 14 +- packages/backend/src/core/IdService.ts | 6 +- packages/backend/src/core/InstanceActorService.ts | 10 +- packages/backend/src/core/LoggerService.ts | 6 +- packages/backend/src/core/MessagingService.ts | 4 +- packages/backend/src/core/MetaService.ts | 16 +-- packages/backend/src/core/NoteCreateService.ts | 36 +++--- packages/backend/src/core/NoteDeleteService.ts | 14 +- packages/backend/src/core/NotificationService.ts | 8 +- packages/backend/src/core/RelayService.ts | 12 +- packages/backend/src/core/UserBlockingService.ts | 16 +-- packages/backend/src/core/UserFollowingService.ts | 38 +++--- .../backend/src/core/UserKeypairStoreService.ts | 6 +- packages/backend/src/core/WebhookService.ts | 24 ++-- packages/backend/src/core/chart/core.ts | 12 +- .../backend/src/core/entities/NoteEntityService.ts | 12 +- .../backend/src/core/entities/UserEntityService.ts | 6 +- .../backend/src/core/remote/ResolveUserService.ts | 34 ++--- .../backend/src/core/remote/WebfingerService.ts | 4 +- .../core/remote/activitypub/ApAudienceService.ts | 14 +- .../core/remote/activitypub/ApDbResolverService.ts | 12 +- .../src/core/remote/activitypub/ApInboxService.ts | 144 ++++++++++----------- .../core/remote/activitypub/ApRendererService.ts | 6 +- .../core/remote/activitypub/ApRequestService.ts | 32 ++--- .../remote/activitypub/models/ApImageService.ts | 6 +- .../remote/activitypub/models/ApNoteService.ts | 22 ++-- .../remote/activitypub/models/ApPersonService.ts | 26 ++-- .../remote/activitypub/models/ApQuestionService.ts | 6 +- packages/backend/src/daemons/JanitorService.ts | 6 +- packages/backend/src/daemons/QueueStatsService.ts | 6 +- packages/backend/src/daemons/ServerStatsService.ts | 6 +- .../backend/src/queue/QueueProcessorService.ts | 16 +-- .../CheckExpiredMutingsProcessorService.ts | 8 +- .../processors/CleanChartsProcessorService.ts | 8 +- .../src/queue/processors/CleanProcessorService.ts | 8 +- .../processors/CleanRemoteFilesProcessorService.ts | 8 +- .../processors/DeleteAccountProcessorService.ts | 10 +- .../processors/DeleteDriveFilesProcessorService.ts | 8 +- .../queue/processors/DeleteFileProcessorService.ts | 4 +- .../queue/processors/DeliverProcessorService.ts | 20 +-- .../EndedPollNotificationProcessorService.ts | 4 +- .../processors/ExportBlockingProcessorService.ts | 14 +- .../ExportCustomEmojisProcessorService.ts | 16 +-- .../processors/ExportFollowingProcessorService.ts | 14 +- .../processors/ExportMutingProcessorService.ts | 14 +- .../processors/ExportNotesProcessorService.ts | 14 +- .../processors/ExportUserListsProcessorService.ts | 14 +- .../processors/ImportBlockingProcessorService.ts | 12 +- .../ImportCustomEmojisProcessorService.ts | 14 +- .../processors/ImportFollowingProcessorService.ts | 12 +- .../processors/ImportMutingProcessorService.ts | 12 +- .../processors/ImportUserListsProcessorService.ts | 10 +- .../src/queue/processors/InboxProcessorService.ts | 6 +- .../processors/ResyncChartsProcessorService.ts | 8 +- .../queue/processors/TickChartsProcessorService.ts | 8 +- .../processors/WebhookDeliverProcessorService.ts | 6 +- .../backend/src/server/ActivityPubServerService.ts | 64 ++++----- packages/backend/src/server/FileServerService.ts | 14 +- .../backend/src/server/MediaProxyServerService.ts | 10 +- packages/backend/src/server/ServerService.ts | 12 +- packages/backend/src/server/api/ApiCallService.ts | 28 ++-- .../backend/src/server/api/AuthenticateService.ts | 6 +- .../backend/src/server/api/RateLimiterService.ts | 8 +- .../src/server/api/endpoints/admin/suspend-user.ts | 8 +- .../backend/src/server/api/endpoints/ap/show.ts | 12 +- .../server/api/integration/DiscordServerService.ts | 14 +- .../server/api/integration/GithubServerService.ts | 14 +- .../server/api/integration/TwitterServerService.ts | 14 +- .../backend/src/server/web/ClientServerService.ts | 4 +- .../backend/src/server/web/UrlPreviewService.ts | 16 +-- 82 files changed, 671 insertions(+), 671 deletions(-) (limited to 'packages/backend/src/core/NoteCreateService.ts') diff --git a/packages/backend/src/core/AiService.ts b/packages/backend/src/core/AiService.ts index 1cfc3382a9..e6102a1b91 100644 --- a/packages/backend/src/core/AiService.ts +++ b/packages/backend/src/core/AiService.ts @@ -15,7 +15,7 @@ let isSupportedCpu: undefined | boolean = undefined; @Injectable() export class AiService { - #model: nsfw.NSFWJS; + private model: nsfw.NSFWJS; constructor( @Inject(DI.config) @@ -26,7 +26,7 @@ export class AiService { public async detectSensitive(path: string): Promise { try { if (isSupportedCpu === undefined) { - const cpuFlags = await this.#getCpuFlags(); + const cpuFlags = await this.getCpuFlags(); isSupportedCpu = REQUIRED_CPU_FLAGS.every(required => cpuFlags.includes(required)); } @@ -37,12 +37,12 @@ export class AiService { const tf = await import('@tensorflow/tfjs-node'); - if (this.#model == null) this.#model = await nsfw.load(`file://${_dirname}/../../nsfw-model/`, { size: 299 }); + 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); + const predictions = await this.model.classify(image); return predictions; } finally { image.dispose(); @@ -53,7 +53,7 @@ export class AiService { } } - async #getCpuFlags(): Promise { + private async getCpuFlags(): Promise { const str = await si.cpuFlags(); return str.split(/\s+/); } diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts index 8993880a06..e0af033952 100644 --- a/packages/backend/src/core/AntennaService.ts +++ b/packages/backend/src/core/AntennaService.ts @@ -16,9 +16,9 @@ import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() export class AntennaService implements OnApplicationShutdown { - #antennasFetched: boolean; - #antennas: Antenna[]; - #blockingCache: Cache; + private antennasFetched: boolean; + private antennas: Antenna[]; + private blockingCache: Cache; constructor( @Inject(DI.redisSubscriber) @@ -49,9 +49,9 @@ export class AntennaService implements OnApplicationShutdown { private idService: IdService, private globalEventServie: GlobalEventService, ) { - this.#antennasFetched = false; - this.#antennas = []; - this.#blockingCache = new Cache(1000 * 60 * 5); + this.antennasFetched = false; + this.antennas = []; + this.blockingCache = new Cache(1000 * 60 * 5); this.redisSubscriber.on('message', this.onRedisMessage); } @@ -67,13 +67,13 @@ export class AntennaService implements OnApplicationShutdown { const { type, body } = obj.message; switch (type) { case 'antennaCreated': - this.#antennas.push(body); + this.antennas.push(body); break; case 'antennaUpdated': - this.#antennas[this.#antennas.findIndex(a => a.id === body.id)] = body; + this.antennas[this.antennas.findIndex(a => a.id === body.id)] = body; break; case 'antennaDeleted': - this.#antennas = this.#antennas.filter(a => a.id !== body.id); + this.antennas = this.antennas.filter(a => a.id !== body.id); break; default: break; @@ -137,7 +137,7 @@ export class AntennaService implements OnApplicationShutdown { 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))); + 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') { @@ -218,11 +218,11 @@ export class AntennaService implements OnApplicationShutdown { } public async getAntennas() { - if (!this.#antennasFetched) { - this.#antennas = await this.antennasRepository.find(); - this.#antennasFetched = true; + if (!this.antennasFetched) { + this.antennas = await this.antennasRepository.find(); + this.antennasFetched = true; } - return this.#antennas; + return this.antennas; } } diff --git a/packages/backend/src/core/AppLockService.ts b/packages/backend/src/core/AppLockService.ts index f3c345493b..04b3d8b112 100644 --- a/packages/backend/src/core/AppLockService.ts +++ b/packages/backend/src/core/AppLockService.ts @@ -11,13 +11,13 @@ const retryDelay = 100; @Injectable() export class AppLockService { - #lock: (key: string, timeout?: number) => Promise<() => void>; + private lock: (key: string, timeout?: number) => Promise<() => void>; constructor( @Inject(DI.redis) private redisClient: Redis.Redis, ) { - this.#lock = promisify(redisLock(this.redisClient, retryDelay)); + this.lock = promisify(redisLock(this.redisClient, retryDelay)); } /** @@ -27,14 +27,14 @@ export class AppLockService { * @returns Unlock function */ public getApLock(uri: string, timeout = 30 * 1000): Promise<() => void> { - return this.#lock(`ap-object:${uri}`, timeout); + return this.lock(`ap-object:${uri}`, timeout); } public getFetchInstanceMetadataLock(host: string, timeout = 30 * 1000): Promise<() => void> { - return this.#lock(`instance:${host}`, timeout); + return this.lock(`instance:${host}`, timeout); } public getChartInsertLock(lockKey: string, timeout = 30 * 1000): Promise<() => void> { - return this.#lock(`chart-insert:${lockKey}`, timeout); + return this.lock(`chart-insert:${lockKey}`, timeout); } } diff --git a/packages/backend/src/core/CaptchaService.ts b/packages/backend/src/core/CaptchaService.ts index 891e5315c2..b1b52fd6a9 100644 --- a/packages/backend/src/core/CaptchaService.ts +++ b/packages/backend/src/core/CaptchaService.ts @@ -19,7 +19,7 @@ export class CaptchaService { ) { } - async #getCaptchaResponse(url: string, secret: string, response: string): Promise { + private async getCaptchaResponse(url: string, secret: string, response: string): Promise { const params = new URLSearchParams({ secret, response, @@ -46,7 +46,7 @@ export class CaptchaService { } public async verifyRecaptcha(secret: string, response: string): Promise { - const result = await this.#getCaptchaResponse('https://www.recaptcha.net/recaptcha/api/siteverify', secret, response).catch(e => { + const result = await this.getCaptchaResponse('https://www.recaptcha.net/recaptcha/api/siteverify', secret, response).catch(e => { throw `recaptcha-request-failed: ${e}`; }); @@ -57,7 +57,7 @@ export class CaptchaService { } public async verifyHcaptcha(secret: string, response: string): Promise { - const result = await this.#getCaptchaResponse('https://hcaptcha.com/siteverify', secret, response).catch(e => { + const result = await this.getCaptchaResponse('https://hcaptcha.com/siteverify', secret, response).catch(e => { throw `hcaptcha-request-failed: ${e}`; }); diff --git a/packages/backend/src/core/CreateNotificationService.ts b/packages/backend/src/core/CreateNotificationService.ts index 10aa2c5df5..525fac6d92 100644 --- a/packages/backend/src/core/CreateNotificationService.ts +++ b/packages/backend/src/core/CreateNotificationService.ts @@ -78,8 +78,8 @@ export class CreateNotificationService { this.globalEventServie.publishMainStream(notifieeId, 'unreadNotification', 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! })); + 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; @@ -90,7 +90,7 @@ export class CreateNotificationService { // TODO: locale ファイルをクライアント用とサーバー用で分けたい - async #emailNotificationFollow(userId: User['id'], follower: User) { + private async emailNotificationFollow(userId: User['id'], follower: User) { /* const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); if (!userProfile.email || !userProfile.emailNotificationTypes.includes('follow')) return; @@ -101,7 +101,7 @@ export class CreateNotificationService { */ } - async #emailNotificationReceiveFollowRequest(userId: User['id'], follower: User) { + private async emailNotificationReceiveFollowRequest(userId: User['id'], follower: User) { /* const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); if (!userProfile.email || !userProfile.emailNotificationTypes.includes('receiveFollowRequest')) return; diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 83e28baea4..32dad70d1c 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -23,7 +23,7 @@ type PopulatedEmoji = { @Injectable() export class CustomEmojiService { - #cache: Cache; + private cache: Cache; constructor( @Inject(DI.config) @@ -40,7 +40,7 @@ export class CustomEmojiService { private utilityService: UtilityService, private reactionService: ReactionService, ) { - this.#cache = new Cache(1000 * 60 * 60 * 12); + this.cache = new Cache(1000 * 60 * 60 * 12); } public async add(data: { @@ -67,7 +67,7 @@ export class CustomEmojiService { return emoji; } - #normalizeHost(src: string | undefined, noteUserHost: string | null): string | null { + private normalizeHost(src: string | undefined, noteUserHost: string | null): string | null { // クエリに使うホスト let host = src === '.' ? null // .はローカルホスト (ここがマッチするのはリアクションのみ) : src === undefined ? noteUserHost // ノートなどでホスト省略表記の場合はローカルホスト (ここがリアクションにマッチすることはない) @@ -79,14 +79,14 @@ export class CustomEmojiService { return host; } - #parseEmojiStr(emojiName: string, noteUserHost: string | null) { + private 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)); + const host = this.utilityService.toPunyNullable(this.normalizeHost(match[2], noteUserHost)); return { name, host }; } @@ -98,7 +98,7 @@ export class CustomEmojiService { * @returns 絵文字情報, nullは未マッチを意味する */ public async populateEmoji(emojiName: string, noteUserHost: string | null): Promise { - const { name, host } = this.#parseEmojiStr(emojiName, noteUserHost); + const { name, host } = this.parseEmojiStr(emojiName, noteUserHost); if (name == null) return null; const queryOrNull = async () => (await this.emojisRepository.findOneBy({ @@ -106,7 +106,7 @@ export class CustomEmojiService { host: host ?? IsNull(), })) ?? null; - const emoji = await this.#cache.fetch(`${name} ${host}`, queryOrNull); + const emoji = await this.cache.fetch(`${name} ${host}`, queryOrNull); if (emoji == null) return null; @@ -132,20 +132,20 @@ export class CustomEmojiService { 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))); + .map(e => this.parseEmojiStr(e, note.userHost))); if (note.renote) { emojis = emojis.concat(note.renote.emojis - .map(e => this.#parseEmojiStr(e, note.renote!.userHost))); + .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))); + .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))); + .map(e => this.parseEmojiStr(e, note.userHost))); } } return emojis.filter(x => x.name != null) as { name: string; host: string | null; }[]; @@ -155,7 +155,7 @@ export class CustomEmojiService { * 与えられた絵文字のリストをデータベースから取得し、キャッシュに追加します */ public async prefetchEmojis(emojis: { name: string; host: string | null; }[]): Promise { - const notCachedEmojis = emojis.filter(emoji => this.#cache.get(`${emoji.name} ${emoji.host}`) == null); + 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) { @@ -169,7 +169,7 @@ export class CustomEmojiService { select: ['name', 'host', 'originalUrl', 'publicUrl'], }) : []; for (const emoji of _emojis) { - this.#cache.set(`${emoji.name} ${emoji.host}`, emoji); + this.cache.set(`${emoji.name} ${emoji.host}`, emoji); } } } diff --git a/packages/backend/src/core/DownloadService.ts b/packages/backend/src/core/DownloadService.ts index 9c5c45ed2c..81939d5f51 100644 --- a/packages/backend/src/core/DownloadService.ts +++ b/packages/backend/src/core/DownloadService.ts @@ -18,7 +18,7 @@ const pipeline = util.promisify(stream.pipeline); @Injectable() export class DownloadService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -27,11 +27,11 @@ export class DownloadService { private httpRequestService: HttpRequestService, private loggerService: LoggerService, ) { - this.#logger = this.loggerService.getLogger('download'); + this.logger = this.loggerService.getLogger('download'); } public async downloadUrl(url: string, path: string): Promise { - this.#logger.info(`Downloading ${chalk.cyan(url)} ...`); + this.logger.info(`Downloading ${chalk.cyan(url)} ...`); const timeout = 30 * 1000; const operationTimeout = 60 * 1000; @@ -60,8 +60,8 @@ export class DownloadService { }, }).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}`); + if (this.isPrivateIp(res.ip)) { + this.logger.warn(`Blocked address: ${res.ip}`); req.destroy(); } } @@ -70,13 +70,13 @@ export class DownloadService { if (contentLength != null) { const size = Number(contentLength); if (size > maxSize) { - this.#logger.warn(`maxSize exceeded (${size} > ${maxSize}) on response`); + 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`); + this.logger.warn(`maxSize exceeded (${progress.transferred} > ${maxSize}) on downloadProgress`); req.destroy(); } }); @@ -91,14 +91,14 @@ export class DownloadService { } } - this.#logger.succ(`Download finished: ${chalk.cyan(url)}`); + 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}`); + this.logger.info(`text file: Temp file is ${path}`); try { // write content at URL to temp file @@ -112,7 +112,7 @@ export class DownloadService { } } - #isPrivateIp(ip: string): boolean { + private isPrivateIp(ip: string): boolean { for (const net of this.config.allowedPrivateNetworks ?? []) { const cidr = new IPCIDR(net); if (cidr.contains(ip)) { diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index e601122fca..467f3c1cde 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -74,8 +74,8 @@ type UploadFromUrlArgs = { @Injectable() export class DriveService { - #registerLogger: Logger; - #downloaderLogger: Logger; + private registerLogger: Logger; + private downloaderLogger: Logger; constructor( @Inject(DI.config) @@ -110,8 +110,8 @@ export class DriveService { private instanceChart: InstanceChart, ) { const logger = new Logger('drive', 'blue'); - this.#registerLogger = logger.createSubLogger('register', 'yellow'); - this.#downloaderLogger = logger.createSubLogger('downloader'); + this.registerLogger = logger.createSubLogger('register', 'yellow'); + this.downloaderLogger = logger.createSubLogger('downloader'); } /*** @@ -122,7 +122,7 @@ export class DriveService { * @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 { + private 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); @@ -161,25 +161,25 @@ export class DriveService { //#endregion //#region Uploads - this.#registerLogger.info(`uploading original: ${key}`); + this.registerLogger.info(`uploading original: ${key}`); const uploads = [ - this.#upload(key, fs.createReadStream(path), type, name), + 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)); + 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)); + this.registerLogger.info(`uploading thumbnail: ${thumbnailKey}`); + uploads.push(this.upload(thumbnailKey, alts.thumbnail.data, alts.thumbnail.type)); } await Promise.all(uploads); @@ -211,12 +211,12 @@ export class DriveService { if (alts.thumbnail) { thumbnailUrl = this.internalStorageService.saveFromBuffer(thumbnailAccessKey, alts.thumbnail.data); - this.#registerLogger.info(`thumbnail stored: ${thumbnailAccessKey}`); + this.registerLogger.info(`thumbnail stored: ${thumbnailAccessKey}`); } if (alts.webpublic) { webpublicUrl = this.internalStorageService.saveFromBuffer(webpublicAccessKey, alts.webpublic.data); - this.#registerLogger.info(`web stored: ${webpublicAccessKey}`); + this.registerLogger.info(`web stored: ${webpublicAccessKey}`); } file.storedInternal = true; @@ -251,7 +251,7 @@ export class DriveService { thumbnail, }; } catch (err) { - this.#registerLogger.warn(`GenerateVideoThumbnail failed: ${err}`); + this.registerLogger.warn(`GenerateVideoThumbnail failed: ${err}`); return { webpublic: null, thumbnail: null, @@ -260,7 +260,7 @@ export class DriveService { } 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)'); + this.registerLogger.debug('web image and thumbnail not created (not an required file)'); return { webpublic: null, thumbnail: null, @@ -290,7 +290,7 @@ export class DriveService { metadata.height && metadata.height <= 2048 ); } catch (err) { - this.#registerLogger.warn(`sharp failed: ${err}`); + this.registerLogger.warn(`sharp failed: ${err}`); return { webpublic: null, thumbnail: null, @@ -301,7 +301,7 @@ export class DriveService { let webpublic: IImage | null = null; if (generateWeb && !satisfyWebpublic) { - this.#registerLogger.info('creating web image'); + this.registerLogger.info('creating web image'); try { if (['image/jpeg', 'image/webp'].includes(type)) { @@ -311,14 +311,14 @@ export class DriveService { } 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)'); + 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); + 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)'); + if (satisfyWebpublic) this.registerLogger.info('web image not created (original satisfies webpublic)'); + else this.registerLogger.info('web image not created (from remote)'); } // #endregion webpublic @@ -329,10 +329,10 @@ export class DriveService { 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)'); + this.registerLogger.debug('thumbnail not created (not an required file)'); } } catch (err) { - this.#registerLogger.warn('thumbnail not created (an error occured)', err as Error); + this.registerLogger.warn('thumbnail not created (an error occured)', err as Error); } // #endregion thumbnail @@ -345,7 +345,7 @@ export class DriveService { /** * Upload to ObjectStorage */ - async #upload(key: string, stream: fs.ReadStream | Buffer, type: string, filename?: string) { + private 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'; @@ -369,10 +369,10 @@ export class DriveService { }); const result = await upload.promise(); - if (result) this.#registerLogger.debug(`Uploaded: ${result.Bucket}/${result.Key} => ${result.Location}`); + if (result) this.registerLogger.debug(`Uploaded: ${result.Bucket}/${result.Key} => ${result.Location}`); } - async #deleteOldFile(user: IRemoteUser) { + private async deleteOldFile(user: IRemoteUser) { const q = this.driveFilesRepository.createQueryBuilder('file') .where('file.userId = :userId', { userId: user.id }) .andWhere('file.isLink = FALSE'); @@ -430,7 +430,7 @@ export class DriveService { sensitiveThresholdForPorn: 0.75, enableSensitiveMediaDetectionForVideos: instance.enableSensitiveMediaDetectionForVideos, }); - this.#registerLogger.info(`${JSON.stringify(info)}`); + this.registerLogger.info(`${JSON.stringify(info)}`); // 現状 false positive が多すぎて実用に耐えない //if (info.porn && instance.disallowUploadWhenPredictedAsPorn) { @@ -448,7 +448,7 @@ export class DriveService { }); if (much) { - this.#registerLogger.info(`file with same hash is found: ${much.id}`); + this.registerLogger.info(`file with same hash is found: ${much.id}`); return much; } } @@ -463,11 +463,11 @@ export class DriveService { 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`); + 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})`); + this.registerLogger.debug(`drive usage is ${usage} (max: ${driveCapacity})`); // If usage limit exceeded if (usage + info.size > driveCapacity) { @@ -475,7 +475,7 @@ export class DriveService { throw new IdentifiableError('c6244ed2-a39a-4e1c-bf93-f0fbd7764fa6', 'No free space.'); } else { // (アバターまたはバナーを含まず)最も古いファイルを削除する - this.#deleteOldFile(await this.usersRepository.findOneByOrFail({ id: user.id }) as IRemoteUser); + this.deleteOldFile(await this.usersRepository.findOneByOrFail({ id: user.id }) as IRemoteUser); } } } @@ -566,22 +566,22 @@ export class DriveService { } catch (err) { // duplicate key error (when already registered) if (isDuplicateKeyValueError(err)) { - this.#registerLogger.info(`already registered ${file.uri}`); + 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); + this.registerLogger.error(err as Error); throw err; } } } else { - file = await (this.#save(file, path, detectedName, info.type.mime, info.md5, info.size)); + file = await (this.save(file, path, detectedName, info.type.mime, info.md5, info.size)); } - this.#registerLogger.succ(`drive file has been created ${file.id}`); + this.registerLogger.succ(`drive file has been created ${file.id}`); if (user) { this.driveFileEntityService.pack(file, { self: true }).then(packedFile => { @@ -624,7 +624,7 @@ export class DriveService { } } - this.#deletePostProcess(file, isExpired); + this.deletePostProcess(file, isExpired); } public async deleteFileSync(file: DriveFile, isExpired = false) { @@ -654,10 +654,10 @@ export class DriveService { await Promise.all(promises); } - this.#deletePostProcess(file, isExpired); + this.deletePostProcess(file, isExpired); } - async #deletePostProcess(file: DriveFile, isExpired = false) { + private async deletePostProcess(file: DriveFile, isExpired = false) { // リモートファイル期限切れ削除後は直リンクにする if (isExpired && file.userHost !== null && file.uri != null) { this.driveFilesRepository.update(file.id, { @@ -725,10 +725,10 @@ export class DriveService { 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}`); + this.downloaderLogger.succ(`Got: ${driveFile.id}`); return driveFile!; } catch (err) { - this.#downloaderLogger.error(`Failed to create drive file: ${err}`, { + this.downloaderLogger.error(`Failed to create drive file: ${err}`, { url: url, e: err, }); diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts index a593922acc..521ab7fd81 100644 --- a/packages/backend/src/core/EmailService.ts +++ b/packages/backend/src/core/EmailService.ts @@ -10,7 +10,7 @@ import { LoggerService } from '@/core/LoggerService.js'; @Injectable() export class EmailService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -22,7 +22,7 @@ export class EmailService { private metaService: MetaService, private loggerService: LoggerService, ) { - this.#logger = this.loggerService.getLogger('email'); + this.logger = this.loggerService.getLogger('email'); } public async sendEmail(to: string, subject: string, html: string, text: string) { @@ -134,9 +134,9 @@ export class EmailService { `, }); - this.#logger.info(`Message sent: ${info.messageId}`); + this.logger.info(`Message sent: ${info.messageId}`); } catch (err) { - this.#logger.error(err as Error); + this.logger.error(err as Error); throw err; } } diff --git a/packages/backend/src/core/FederatedInstanceService.ts b/packages/backend/src/core/FederatedInstanceService.ts index 24bedd8192..a4894a4376 100644 --- a/packages/backend/src/core/FederatedInstanceService.ts +++ b/packages/backend/src/core/FederatedInstanceService.ts @@ -8,7 +8,7 @@ import { UtilityService } from './UtilityService.js'; @Injectable() export class FederatedInstanceService { - #cache: Cache; + private cache: Cache; constructor( @Inject(DI.instancesRepository) @@ -17,13 +17,13 @@ export class FederatedInstanceService { private utilityService: UtilityService, private idService: IdService, ) { - this.#cache = new Cache(1000 * 60 * 60); + this.cache = new Cache(1000 * 60 * 60); } public async registerOrFetchInstanceDoc(host: string): Promise { host = this.utilityService.toPuny(host); - const cached = this.#cache.get(host); + const cached = this.cache.get(host); if (cached) return cached; const index = await this.instancesRepository.findOneBy({ host }); @@ -36,10 +36,10 @@ export class FederatedInstanceService { lastCommunicatedAt: new Date(), }).then(x => this.instancesRepository.findOneByOrFail(x.identifiers[0])); - this.#cache.set(host, i); + this.cache.set(host, i); return i; } else { - this.#cache.set(host, index); + this.cache.set(host, index); return index; } } diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts index 4414d83942..376617914e 100644 --- a/packages/backend/src/core/FetchInstanceMetadataService.ts +++ b/packages/backend/src/core/FetchInstanceMetadataService.ts @@ -32,7 +32,7 @@ type NodeInfo = { @Injectable() export class FetchInstanceMetadataService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.instancesRepository) @@ -42,7 +42,7 @@ export class FetchInstanceMetadataService { private httpRequestService: HttpRequestService, private loggerService: LoggerService, ) { - this.#logger = this.loggerService.getLogger('metadata', 'cyan'); + this.logger = this.loggerService.getLogger('metadata', 'cyan'); } public async fetchInstanceMetadata(instance: Instance, force = false): Promise { @@ -57,24 +57,24 @@ export class FetchInstanceMetadataService { } } - this.#logger.info(`Fetching metadata of ${instance.host} ...`); + this.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), + 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), + 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), ]); - this.#logger.succ(`Successfuly fetched metadata of ${instance.host}`); + this.logger.succ(`Successfuly fetched metadata of ${instance.host}`); const updates = { infoUpdatedAt: new Date(), @@ -96,16 +96,16 @@ export class FetchInstanceMetadataService { await this.instancesRepository.update(instance.id, updates); - this.#logger.succ(`Successfuly updated metadata of ${instance.host}`); + this.logger.succ(`Successfuly updated metadata of ${instance.host}`); } catch (e) { - this.#logger.error(`Failed to update metadata of ${instance.host}: ${e}`); + this.logger.error(`Failed to update metadata of ${instance.host}: ${e}`); } finally { unlock(); } } - async #fetchNodeinfo(instance: Instance): Promise { - this.#logger.info(`Fetching nodeinfo of ${instance.host} ...`); + private async fetchNodeinfo(instance: Instance): Promise { + this.logger.info(`Fetching nodeinfo of ${instance.host} ...`); try { const wellknown = await this.httpRequestService.getJson('https://' + instance.host + '/.well-known/nodeinfo') @@ -137,18 +137,18 @@ export class FetchInstanceMetadataService { throw err.statusCode ?? err.message; }); - this.#logger.succ(`Successfuly fetched nodeinfo of ${instance.host}`); + this.logger.succ(`Successfuly fetched nodeinfo of ${instance.host}`); return info as NodeInfo; } catch (err) { - this.#logger.error(`Failed to fetch nodeinfo of ${instance.host}: ${err}`); + this.logger.error(`Failed to fetch nodeinfo of ${instance.host}: ${err}`); throw err; } } - async #fetchDom(instance: Instance): Promise { - this.#logger.info(`Fetching HTML of ${instance.host} ...`); + private async fetchDom(instance: Instance): Promise { + this.logger.info(`Fetching HTML of ${instance.host} ...`); const url = 'https://' + instance.host; @@ -160,7 +160,7 @@ export class FetchInstanceMetadataService { return doc; } - async #fetchManifest(instance: Instance): Promise | null> { + private async fetchManifest(instance: Instance): Promise | null> { const url = 'https://' + instance.host; const manifestUrl = url + '/manifest.json'; @@ -170,7 +170,7 @@ export class FetchInstanceMetadataService { return manifest; } - async #fetchFaviconUrl(instance: Instance, doc: DOMWindow['document'] | null): Promise { + private async fetchFaviconUrl(instance: Instance, doc: DOMWindow['document'] | null): Promise { const url = 'https://' + instance.host; if (doc) { @@ -197,7 +197,7 @@ export class FetchInstanceMetadataService { return null; } - async #fetchIconUrl(instance: Instance, doc: DOMWindow['document'] | null, manifest: Record | null): Promise { + private 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; @@ -225,7 +225,7 @@ export class FetchInstanceMetadataService { return null; } - async #getThemeColor(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record | null): Promise { + private 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) { @@ -236,7 +236,7 @@ export class FetchInstanceMetadataService { return null; } - async #getSiteName(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record | null): Promise { + private 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; @@ -258,7 +258,7 @@ export class FetchInstanceMetadataService { return null; } - async #getDescription(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record | null): Promise { + private 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; diff --git a/packages/backend/src/core/FileInfoService.ts b/packages/backend/src/core/FileInfoService.ts index 45c94dafdc..fd8a4fdd3a 100644 --- a/packages/backend/src/core/FileInfoService.ts +++ b/packages/backend/src/core/FileInfoService.ts @@ -61,7 +61,7 @@ export class FileInfoService { const warnings = [] as string[]; const size = await this.getFileSize(path); - const md5 = await this.#calcHash(path); + const md5 = await this.calcHash(path); let type = await this.detectType(path); @@ -71,7 +71,7 @@ export class FileInfoService { 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 => { + const imageSize = await this.detectImageSize(path).catch(e => { warnings.push(`detectImageSize failed: ${e}`); return undefined; }); @@ -98,7 +98,7 @@ export class FileInfoService { 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 => { + blurhash = await this.getBlurhash(path).catch(e => { warnings.push(`getBlurhash failed: ${e}`); return undefined; }); @@ -108,7 +108,7 @@ export class FileInfoService { let porn = false; if (!opts.skipSensitiveDetection) { - await this.#detectSensitivity( + await this.detectSensitivity( path, type.mime, opts.sensitiveThreshold ?? 0.5, @@ -135,7 +135,7 @@ export class FileInfoService { }; } - async #detectSensitivity(source: string, mime: string, sensitiveThreshold: number, sensitiveThresholdForPorn: number, analyzeVideo: boolean): Promise<[sensitive: boolean, porn: boolean]> { + private async detectSensitivity(source: string, mime: string, sensitiveThreshold: number, sensitiveThresholdForPorn: number, analyzeVideo: boolean): Promise<[sensitive: boolean, porn: boolean]> { let sensitive = false; let porn = false; @@ -204,7 +204,7 @@ export class FileInfoService { let frameIndex = 0; let targetIndex = 0; let nextIndex = 1; - for await (const path of this.#asyncIterateFrames(outDir, command)) { + for await (const path of this.asyncIterateFrames(outDir, command)) { try { const index = frameIndex++; if (index !== targetIndex) { @@ -230,7 +230,7 @@ export class FileInfoService { return [sensitive, porn]; } - async *#asyncIterateFrames(cwd: string, command: FFmpeg.FfmpegCommand): AsyncGenerator { + private async *asyncIterateFrames(cwd: string, command: FFmpeg.FfmpegCommand): AsyncGenerator { const watcher = new FSWatcher({ cwd, disableGlobbing: true, @@ -245,7 +245,7 @@ export class FileInfoService { const current = `${i}.png`; const next = `${i + 1}.png`; const framePath = join(cwd, current); - if (await this.#exists(join(cwd, next))) { + if (await this.exists(join(cwd, next))) { yield framePath; } else if (!finished) { // eslint-disable-line @typescript-eslint/no-unnecessary-condition watcher.add(next); @@ -261,7 +261,7 @@ export class FileInfoService { command.once('error', reject); }); yield framePath; - } else if (await this.#exists(framePath)) { + } else if (await this.exists(framePath)) { yield framePath; } else { return; @@ -269,7 +269,7 @@ export class FileInfoService { } } - #exists(path: string): Promise { + private exists(path: string): Promise { return fs.promises.access(path).then(() => true, () => false); } @@ -333,7 +333,7 @@ export class FileInfoService { /** * Calculate MD5 hash */ - async #calcHash(path: string): Promise { + private async calcHash(path: string): Promise { const hash = crypto.createHash('md5').setEncoding('hex'); await pipeline(fs.createReadStream(path), hash); return hash.read(); @@ -342,7 +342,7 @@ export class FileInfoService { /** * Detect dimensions of image */ - async #detectImageSize(path: string): Promise<{ + private async detectImageSize(path: string): Promise<{ width: number; height: number; wUnits: string; @@ -358,7 +358,7 @@ export class FileInfoService { /** * Calculate average color of image */ - #getBlurhash(path: string): Promise { + private getBlurhash(path: string): Promise { return new Promise((resolve, reject) => { sharp(path) .raw() diff --git a/packages/backend/src/core/HttpRequestService.ts b/packages/backend/src/core/HttpRequestService.ts index 21cde12536..f4c00cd259 100644 --- a/packages/backend/src/core/HttpRequestService.ts +++ b/packages/backend/src/core/HttpRequestService.ts @@ -15,12 +15,12 @@ export class HttpRequestService { /** * Get http non-proxy agent */ - #http: http.Agent; + private http: http.Agent; /** * Get https non-proxy agent */ - #https: https.Agent; + private https: https.Agent; /** * Get http proxy or non-proxy agent @@ -42,13 +42,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, @@ -65,7 +65,7 @@ export class HttpRequestService { scheduling: 'lifo', proxy: config.proxy, }) - : this.#http; + : this.http; this.httpsAgent = config.proxy ? new HttpsProxyAgent({ @@ -76,7 +76,7 @@ export class HttpRequestService { scheduling: 'lifo', proxy: config.proxy, }) - : this.#https; + : this.https; } /** @@ -86,7 +86,7 @@ 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; } diff --git a/packages/backend/src/core/IdService.ts b/packages/backend/src/core/IdService.ts index b3b0d63627..345b72bac5 100644 --- a/packages/backend/src/core/IdService.ts +++ b/packages/backend/src/core/IdService.ts @@ -9,19 +9,19 @@ import { genObjectId } from '@/misc/id/object-id.js'; @Injectable() export class IdService { - #metohd: string; + private metohd: string; constructor( @Inject(DI.config) private config: Config, ) { - this.#metohd = config.id.toLowerCase(); + this.metohd = config.id.toLowerCase(); } public genId(date?: Date): string { if (!date || (date > new Date())) date = new Date(); - switch (this.#metohd) { + switch (this.metohd) { case 'aid': return genAid(date); case 'meid': return genMeid(date); case 'meidg': return genMeidg(date); diff --git a/packages/backend/src/core/InstanceActorService.ts b/packages/backend/src/core/InstanceActorService.ts index 3a93a49c7b..57d55870b1 100644 --- a/packages/backend/src/core/InstanceActorService.ts +++ b/packages/backend/src/core/InstanceActorService.ts @@ -10,7 +10,7 @@ const ACTOR_USERNAME = 'instance.actor' as const; @Injectable() export class InstanceActorService { - #cache: Cache; + private cache: Cache; constructor( @Inject(DI.usersRepository) @@ -18,11 +18,11 @@ export class InstanceActorService { private createSystemUserService: CreateSystemUserService, ) { - this.#cache = new Cache(Infinity); + this.cache = new Cache(Infinity); } public async getInstanceActor(): Promise { - const cached = this.#cache.get(null); + const cached = this.cache.get(null); if (cached) return cached; const user = await this.usersRepository.findOneBy({ @@ -31,11 +31,11 @@ export class InstanceActorService { }) as ILocalUser | undefined; if (user) { - this.#cache.set(null, user); + this.cache.set(null, user); return user; } else { const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME) as ILocalUser; - this.#cache.set(null, created); + this.cache.set(null, created); return created; } } diff --git a/packages/backend/src/core/LoggerService.ts b/packages/backend/src/core/LoggerService.ts index d844b38841..558e3016dc 100644 --- a/packages/backend/src/core/LoggerService.ts +++ b/packages/backend/src/core/LoggerService.ts @@ -6,14 +6,14 @@ import Logger from '@/logger.js'; @Injectable() export class LoggerService { - #syslogClient; + private syslogClient; constructor( @Inject(DI.config) private config: Config, ) { if (this.config.syslog) { - this.#syslogClient = new SyslogPro.RFC5424({ + this.syslogClient = new SyslogPro.RFC5424({ applacationName: 'Misskey', timestamp: true, encludeStructuredData: true, @@ -28,6 +28,6 @@ export class LoggerService { } public getLogger(domain: string, color?: string | undefined, store?: boolean) { - return new Logger(domain, color, store, this.#syslogClient); + return new Logger(domain, color, store, this.syslogClient); } } diff --git a/packages/backend/src/core/MessagingService.ts b/packages/backend/src/core/MessagingService.ts index 669089e1e5..1819b32a45 100644 --- a/packages/backend/src/core/MessagingService.ts +++ b/packages/backend/src/core/MessagingService.ts @@ -142,10 +142,10 @@ export class MessagingService { public async deleteMessage(message: MessagingMessage) { await this.messagingMessagesRepository.delete(message.id); - this.#postDeleteMessage(message); + this.postDeleteMessage(message); } - async #postDeleteMessage(message: MessagingMessage) { + private async postDeleteMessage(message: MessagingMessage) { if (message.recipientId) { const user = await this.usersRepository.findOneByOrFail({ id: message.userId }); const recipient = await this.usersRepository.findOneByOrFail({ id: message.recipientId }); diff --git a/packages/backend/src/core/MetaService.ts b/packages/backend/src/core/MetaService.ts index b5bd423765..4099e340be 100644 --- a/packages/backend/src/core/MetaService.ts +++ b/packages/backend/src/core/MetaService.ts @@ -7,24 +7,24 @@ import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() export class MetaService implements OnApplicationShutdown { - #cache: Meta | undefined; - #intervalId: NodeJS.Timer; + private cache: Meta | undefined; + private intervalId: NodeJS.Timer; constructor( @Inject(DI.db) private db: DataSource, ) { if (process.env.NODE_ENV !== 'test') { - this.#intervalId = setInterval(() => { + this.intervalId = setInterval(() => { this.fetch(true).then(meta => { - this.#cache = meta; + this.cache = meta; }); }, 1000 * 10); } } async fetch(noCache = false): Promise { - if (!noCache && this.#cache) return this.#cache; + if (!noCache && this.cache) return this.cache; return await this.db.transaction(async transactionalEntityManager => { // 過去のバグでレコードが複数出来てしまっている可能性があるので新しいIDを優先する @@ -37,7 +37,7 @@ export class MetaService implements OnApplicationShutdown { const meta = metas[0]; if (meta) { - this.#cache = meta; + this.cache = meta; return meta; } else { // metaが空のときfetchMetaが同時に呼ばれるとここが同時に呼ばれてしまうことがあるのでフェイルセーフなupsertを使う @@ -51,13 +51,13 @@ export class MetaService implements OnApplicationShutdown { ) .then((x) => transactionalEntityManager.findOneByOrFail(Meta, x.identifiers[0])); - this.#cache = saved; + this.cache = saved; return saved; } }); } public onApplicationShutdown(signal?: string | undefined) { - clearInterval(this.#intervalId); + clearInterval(this.intervalId); } } diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 83c080893d..5acc07fba6 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -277,7 +277,7 @@ export class NoteCreateService { 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); @@ -300,14 +300,14 @@ export class NoteCreateService { } } - const note = await this.#insertNote(user, data, tags, emojis, mentionedUsers); + const note = await this.insertNote(user, data, tags, emojis, mentionedUsers); - setImmediate(() => this.#postNoteCreated(note, user, data, silent, tags!, 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[]) { + private async insertNote(user: { id: User['id']; host: User['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]) { const insert = new Note({ id: this.idService.genId(data.createdAt!), createdAt: data.createdAt!, @@ -403,7 +403,7 @@ export class NoteCreateService { } } - async #postNoteCreated(note: Note, user: { + private async postNoteCreated(note: Note, user: { id: User['id']; username: User['username']; host: User['host']; @@ -428,7 +428,7 @@ export class NoteCreateService { } // Increment notes count (user) - this.#incNotesCountOfUser(user); + this.incNotesCountOfUser(user); // Word mute mutedWordsCache.fetch(null, () => this.userProfilesRepository.find({ @@ -473,12 +473,12 @@ export class NoteCreateService { } if (data.reply) { - this.#saveReply(data.reply, note); + this.saveReply(data.reply, note); } // この投稿を除く指定したユーザーによる指定したノートのリノートが存在しないとき if (data.renote && (await this.noteEntityService.countSameRenotes(user.id, data.renote.id, note.id) === 0)) { - this.#incRenoteCount(data.renote); + this.incRenoteCount(data.renote); } if (data.poll && data.poll.expiresAt) { @@ -536,7 +536,7 @@ export class NoteCreateService { const nm = new NotificationManager(this.mutingsRepository, this.createNotificationService, user, note); const nmRelatedPromises = []; - await this.#createMentionedEvents(mentionedUsers, note, nm); + await this.createMentionedEvents(mentionedUsers, note, nm); // If has in reply to note if (data.reply) { @@ -590,7 +590,7 @@ export class NoteCreateService { //#region AP deliver if (this.userEntityService.isLocalUser(user)) { (async () => { - const noteActivity = await this.#renderNoteOrRenoteActivity(data, note); + const noteActivity = await this.renderNoteOrRenoteActivity(data, note); const dm = this.apDeliverManagerService.createDeliverManager(user, noteActivity); // メンションされたリモートユーザーに配送 @@ -644,10 +644,10 @@ export class NoteCreateService { } // Register to search database - this.#index(note); + this.index(note); } - #incRenoteCount(renote: Note) { + private incRenoteCount(renote: Note) { this.notesRepository.createQueryBuilder().update() .set({ renoteCount: () => '"renoteCount" + 1', @@ -657,7 +657,7 @@ export class NoteCreateService { .execute(); } - async #createMentionedEvents(mentionedUsers: MinimumUser[], note: Note, nm: NotificationManager) { + private async createMentionedEvents(mentionedUsers: MinimumUser[], note: Note, nm: NotificationManager) { for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) { const threadMuted = await this.noteThreadMutingsRepository.findOneBy({ userId: u.id, @@ -686,11 +686,11 @@ export class NoteCreateService { } } - #saveReply(reply: Note, note: Note) { + private saveReply(reply: Note, note: Note) { this.notesRepository.increment({ id: reply.id }, 'repliesCount', 1); } - async #renderNoteOrRenoteActivity(data: Option, note: Note) { + private 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) @@ -700,7 +700,7 @@ export class NoteCreateService { return this.apRendererService.renderActivity(content); } - #index(note: Note) { + private index(note: Note) { if (note.text == null || this.config.elasticsearch == null) return; /* es!.index({ @@ -714,7 +714,7 @@ export class NoteCreateService { });*/ } - #incNotesCountOfUser(user: { id: User['id']; }) { + private incNotesCountOfUser(user: { id: User['id']; }) { this.usersRepository.createQueryBuilder().update() .set({ updatedAt: new Date(), @@ -724,7 +724,7 @@ export class NoteCreateService { .execute(); } - async #extractMentionedUsers(user: { host: User['host']; }, tokens: mfm.MfmNode[]): Promise { + private async extractMentionedUsers(user: { host: User['host']; }, tokens: mfm.MfmNode[]): Promise { if (tokens == null) return []; const mentions = extractMentions(tokens); diff --git a/packages/backend/src/core/NoteDeleteService.ts b/packages/backend/src/core/NoteDeleteService.ts index 9153418beb..6365286f84 100644 --- a/packages/backend/src/core/NoteDeleteService.ts +++ b/packages/backend/src/core/NoteDeleteService.ts @@ -79,16 +79,16 @@ export class NoteDeleteService { ? 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); + 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 + 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 (!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); + this.deliverToConcerned(cascadingNote.user, cascadingNote, content); } //#endregion @@ -110,7 +110,7 @@ export class NoteDeleteService { }); } - async #findCascadingNotes(note: Note) { + private async findCascadingNotes(note: Note) { const cascadingNotes: Note[] = []; const recursive = async (noteId: string) => { @@ -132,7 +132,7 @@ export class NoteDeleteService { return cascadingNotes.filter(note => note.userHost === null); // filter out non-local users } - async #getMentionedRemoteUsers(note: Note) { + private async getMentionedRemoteUsers(note: Note) { const where = [] as any[]; // mention / reply / dm @@ -157,10 +157,10 @@ export class NoteDeleteService { }) as IRemoteUser[]; } - async #deliverToConcerned(user: { id: ILocalUser['id']; host: null; }, note: Note, content: any) { + private async deliverToConcerned(user: { id: ILocalUser['id']; host: null; }, note: Note, content: any) { this.apDeliverManagerService.deliverToFollowers(user, content); this.relayService.deliverToRelays(user, content); - const remoteUsers = await this.#getMentionedRemoteUsers(note); + const remoteUsers = await this.getMentionedRemoteUsers(note); for (const remoteUser of remoteUsers) { this.apDeliverManagerService.deliverToUser(user, content, remoteUser); } diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts index 44957d62d3..ca9e60889d 100644 --- a/packages/backend/src/core/NotificationService.ts +++ b/packages/backend/src/core/NotificationService.ts @@ -38,8 +38,8 @@ export class NotificationService { if (result.affected === 0) return; - if (!await this.userEntityService.getHasUnreadNotification(userId)) return this.#postReadAllNotifications(userId); - else return this.#postReadNotifications(userId, notificationIds); + if (!await this.userEntityService.getHasUnreadNotification(userId)) return this.postReadAllNotifications(userId); + else return this.postReadNotifications(userId, notificationIds); } public async readNotificationByQuery( @@ -55,12 +55,12 @@ export class NotificationService { return this.readNotification(userId, notificationIds); } - #postReadAllNotifications(userId: User['id']) { + private postReadAllNotifications(userId: User['id']) { this.globalEventService.publishMainStream(userId, 'readAllNotifications'); return this.pushNotificationService.pushNotification(userId, 'readAllNotifications', undefined); } - #postReadNotifications(userId: User['id'], notificationIds: Notification['id'][]) { + private postReadNotifications(userId: User['id'], notificationIds: Notification['id'][]) { this.globalEventService.publishMainStream(userId, 'readNotifications', notificationIds); return this.pushNotificationService.pushNotification(userId, 'readNotifications', { notificationIds }); } diff --git a/packages/backend/src/core/RelayService.ts b/packages/backend/src/core/RelayService.ts index c1d85e8b48..688ea03d34 100644 --- a/packages/backend/src/core/RelayService.ts +++ b/packages/backend/src/core/RelayService.ts @@ -14,7 +14,7 @@ const ACTOR_USERNAME = 'relay.actor' as const; @Injectable() export class RelayService { - #relaysCache: Cache; + private relaysCache: Cache; constructor( @Inject(DI.usersRepository) @@ -28,10 +28,10 @@ export class RelayService { private createSystemUserService: CreateSystemUserService, private apRendererService: ApRendererService, ) { - this.#relaysCache = new Cache(1000 * 60 * 10); + this.relaysCache = new Cache(1000 * 60 * 10); } - async #getRelayActor(): Promise { + private async getRelayActor(): Promise { const user = await this.usersRepository.findOneBy({ host: IsNull(), username: ACTOR_USERNAME, @@ -50,7 +50,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 this.apRendererService.renderFollowRelay(relay, relayActor); const activity = this.apRendererService.renderActivity(follow); this.queueService.deliver(relayActor, activity, relay.inbox); @@ -67,7 +67,7 @@ export class RelayService { throw new Error('relay not found'); } - const relayActor = await this.#getRelayActor(); + const relayActor = await this.getRelayActor(); const follow = this.apRendererService.renderFollowRelay(relay, relayActor); const undo = this.apRendererService.renderUndo(follow, relayActor); const activity = this.apRendererService.renderActivity(undo); @@ -100,7 +100,7 @@ export class RelayService { 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({ + const relays = await this.relaysCache.fetch(null, () => this.relaysRepository.findBy({ status: 'accepted', })); if (relays.length === 0) return; diff --git a/packages/backend/src/core/UserBlockingService.ts b/packages/backend/src/core/UserBlockingService.ts index a9396fcbba..9efb021f64 100644 --- a/packages/backend/src/core/UserBlockingService.ts +++ b/packages/backend/src/core/UserBlockingService.ts @@ -44,11 +44,11 @@ export class UserBlockingService { public 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), + this.cancelRequest(blocker, blockee), + this.cancelRequest(blockee, blocker), + this.unFollow(blocker, blockee), + this.unFollow(blockee, blocker), + this.removeFromList(blockee, blocker), ]); const blocking = { @@ -68,7 +68,7 @@ export class UserBlockingService { } } - async #cancelRequest(follower: User, followee: User) { + private async cancelRequest(follower: User, followee: User) { const request = await this.followRequestsRepository.findOneBy({ followeeId: followee.id, followerId: follower.id, @@ -118,7 +118,7 @@ export class UserBlockingService { } } - async #unFollow(follower: User, followee: User) { + private async unFollow(follower: User, followee: User) { const following = await this.followingsRepository.findOneBy({ followerId: follower.id, followeeId: followee.id, @@ -159,7 +159,7 @@ export class UserBlockingService { } } - async #removeFromList(listOwner: User, user: User) { + private async removeFromList(listOwner: User, user: User) { const userLists = await this.userListsRepository.findBy({ userId: listOwner.id, }); diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index 6aae5a6b99..ff86d4343f 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/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 { } } - async #insertFollowingDoc( + private 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 { } } - async #decrementFollowing( + private 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)); @@ -477,13 +477,13 @@ export class UserFollowingService { */ public async rejectFollowRequest(user: Local, follower: Both): Promise { if (this.userEntityService.isRemoteUser(follower)) { - this.#deliverReject(user, follower); + this.deliverReject(user, follower); } - await this.#removeFollowRequest(user, follower); + await this.removeFollowRequest(user, follower); if (this.userEntityService.isLocalUser(follower)) { - this.#publishUnfollow(user, follower); + this.publishUnfollow(user, follower); } } @@ -492,13 +492,13 @@ export class UserFollowingService { */ public async rejectFollow(user: Local, follower: Both): Promise { if (this.userEntityService.isRemoteUser(follower)) { - this.#deliverReject(user, follower); + this.deliverReject(user, follower); } - await this.#removeFollow(user, follower); + await this.removeFollow(user, follower); if (this.userEntityService.isLocalUser(follower)) { - this.#publishUnfollow(user, follower); + this.publishUnfollow(user, follower); } } @@ -506,15 +506,15 @@ export class UserFollowingService { * 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); + 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 { + private async removeFollowRequest(followee: Both, follower: Both): Promise { const request = await this.followRequestsRepository.findOneBy({ followeeId: followee.id, followerId: follower.id, @@ -528,7 +528,7 @@ export class UserFollowingService { /** * Remove follow record */ - async #removeFollow(followee: Both, follower: Both): Promise { + private async removeFollow(followee: Both, follower: Both): Promise { const following = await this.followingsRepository.findOneBy({ followeeId: followee.id, followerId: follower.id, @@ -537,13 +537,13 @@ export class UserFollowingService { if (!following) return; await this.followingsRepository.delete(following.id); - this.#decrementFollowing(follower, followee); + this.decrementFollowing(follower, followee); } /** * Deliver Reject to remote */ - async #deliverReject(followee: Local, follower: Remote): Promise { + private async deliverReject(followee: Local, follower: Remote): Promise { const request = await this.followRequestsRepository.findOneBy({ followeeId: followee.id, followerId: follower.id, @@ -556,7 +556,7 @@ export class UserFollowingService { /** * Publish unfollow to local */ - async #publishUnfollow(followee: Both, follower: Local): Promise { + private async publishUnfollow(followee: Both, follower: Local): Promise { const packedFollowee = await this.userEntityService.pack(followee.id, follower, { detail: true, }); diff --git a/packages/backend/src/core/UserKeypairStoreService.ts b/packages/backend/src/core/UserKeypairStoreService.ts index f093d2ea91..e53f37b714 100644 --- a/packages/backend/src/core/UserKeypairStoreService.ts +++ b/packages/backend/src/core/UserKeypairStoreService.ts @@ -7,16 +7,16 @@ import { DI } from '@/di-symbols.js'; @Injectable() export class UserKeypairStoreService { - #cache: Cache; + private cache: Cache; constructor( @Inject(DI.userKeypairsRepository) private userKeypairsRepository: UserKeypairsRepository, ) { - this.#cache = new Cache(Infinity); + this.cache = new Cache(Infinity); } public async getUserKeypair(userId: User['id']): Promise { - return await this.#cache.fetch(userId, () => this.userKeypairsRepository.findOneByOrFail({ userId: userId })); + return await this.cache.fetch(userId, () => this.userKeypairsRepository.findOneByOrFail({ userId: userId })); } } diff --git a/packages/backend/src/core/WebhookService.ts b/packages/backend/src/core/WebhookService.ts index 3ed615ca0b..1d74290dd9 100644 --- a/packages/backend/src/core/WebhookService.ts +++ b/packages/backend/src/core/WebhookService.ts @@ -7,8 +7,8 @@ import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() export class WebhookService implements OnApplicationShutdown { - #webhooksFetched = false; - #webhooks: Webhook[] = []; + private webhooksFetched = false; + private webhooks: Webhook[] = []; constructor( @Inject(DI.redisSubscriber) @@ -22,14 +22,14 @@ export class WebhookService implements OnApplicationShutdown { } public async getActiveWebhooks() { - if (!this.#webhooksFetched) { - this.#webhooks = await this.webhooksRepository.findBy({ + if (!this.webhooksFetched) { + this.webhooks = await this.webhooksRepository.findBy({ active: true, }); - this.#webhooksFetched = true; + this.webhooksFetched = true; } - return this.#webhooks; + return this.webhooks; } private async onMessage(_, data) { @@ -40,23 +40,23 @@ export class WebhookService implements OnApplicationShutdown { switch (type) { case 'webhookCreated': if (body.active) { - this.#webhooks.push(body); + this.webhooks.push(body); } break; case 'webhookUpdated': if (body.active) { - const i = this.#webhooks.findIndex(a => a.id === body.id); + const i = this.webhooks.findIndex(a => a.id === body.id); if (i > -1) { - this.#webhooks[i] = body; + this.webhooks[i] = body; } else { - this.#webhooks.push(body); + this.webhooks.push(body); } } else { - this.#webhooks = this.#webhooks.filter(a => a.id !== body.id); + this.webhooks = this.webhooks.filter(a => a.id !== body.id); } break; case 'webhookDeleted': - this.#webhooks = this.#webhooks.filter(a => a.id !== body.id); + this.webhooks = this.webhooks.filter(a => a.id !== body.id); break; default: break; diff --git a/packages/backend/src/core/chart/core.ts b/packages/backend/src/core/chart/core.ts index 93a6c3ce05..cf5aa48884 100644 --- a/packages/backend/src/core/chart/core.ts +++ b/packages/backend/src/core/chart/core.ts @@ -109,7 +109,7 @@ export function getJsonSchema(schema: S): ToJsonSchema { - #logger: Logger; + private logger: Logger; public schema: T; @@ -242,7 +242,7 @@ export default abstract class Chart { this.name = name; this.schema = schema; this.lock = lock; - this.#logger = logger; + this.logger = logger; const { hour, day } = Chart.schemaToEntity(name, schema, grouped); this.repositoryForHour = db.getRepository<{ id: number; group?: string | null; date: number; }>(hour); @@ -333,7 +333,7 @@ export default abstract class Chart { // 初期ログデータを作成 data = this.getNewLog(null); - this.#logger.info(`${this.name + (group ? `:${group}` : '')}(${span}): Initial commit created`); + this.logger.info(`${this.name + (group ? `:${group}` : '')}(${span}): Initial commit created`); } const date = Chart.dateToTimestamp(current); @@ -363,7 +363,7 @@ export default abstract class Chart { ...columns, }).then(x => repository.findOneByOrFail(x.identifiers[0])) as RawRecord; - this.#logger.info(`${this.name + (group ? `:${group}` : '')}(${span}): New commit created`); + this.logger.info(`${this.name + (group ? `:${group}` : '')}(${span}): New commit created`); return log; } finally { @@ -382,7 +382,7 @@ export default abstract class Chart { public async save(): Promise { if (this.buffer.length === 0) { - this.#logger.info(`${this.name}: Write skipped`); + this.logger.info(`${this.name}: Write skipped`); return; } @@ -481,7 +481,7 @@ export default abstract class Chart { .execute(), ]); - this.#logger.info(`${this.name + (logHour.group ? `:${logHour.group}` : '')}: Updated`); + this.logger.info(`${this.name + (logHour.group ? `:${logHour.group}` : '')}: Updated`); // TODO: この一連の処理が始まった後に新たにbufferに入ったものは消さないようにする this.buffer = this.buffer.filter(q => q.group != null && (q.group !== logHour.group)); diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index eab233bbef..669680758d 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -68,7 +68,7 @@ export class NoteEntityService implements OnModuleInit { this.reactionService = this.moduleRef.get('ReactionService'); } - async #hideNote(packedNote: Packed<'Note'>, meId: User['id'] | null) { + private async hideNote(packedNote: Packed<'Note'>, meId: User['id'] | null) { // TODO: isVisibleForMe を使うようにしても良さそう(型違うけど) let hide = false; @@ -128,7 +128,7 @@ export class NoteEntityService implements OnModuleInit { } } - async #populatePoll(note: Note, meId: User['id'] | null) { + private 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, @@ -166,7 +166,7 @@ export class NoteEntityService implements OnModuleInit { }; } - async #populateMyReaction(note: Note, meId: User['id'], _hint_?: { + private async populateMyReaction(note: Note, meId: User['id'], _hint_?: { myReactions: Map; }) { if (_hint_?.myReactions) { @@ -319,10 +319,10 @@ export class NoteEntityService implements OnModuleInit { _hint_: options?._hint_, }) : undefined, - poll: note.hasPoll ? this.#populatePoll(note, meId) : undefined, + poll: note.hasPoll ? this.populatePoll(note, meId) : undefined, ...(meId ? { - myReaction: this.#populateMyReaction(note, meId, options?._hint_), + myReaction: this.populateMyReaction(note, meId, options?._hint_), } : {}), } : {}), }); @@ -339,7 +339,7 @@ export class NoteEntityService implements OnModuleInit { } if (!opts.skipHide) { - await this.#hideNote(packed, meId); + await this.hideNote(packed, meId); } return packed; diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 48d8af83fb..343e42df07 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -48,7 +48,7 @@ export class UserEntityService implements OnModuleInit { private pageEntityService: PageEntityService; private customEmojiService: CustomEmojiService; private antennaService: AntennaService; - #userInstanceCache: Cache; + private userInstanceCache: Cache; constructor( private moduleRef: ModuleRef, @@ -119,7 +119,7 @@ export class UserEntityService implements OnModuleInit { //private customEmojiService: CustomEmojiService, //private antennaService: AntennaService, ) { - this.#userInstanceCache = new Cache(1000 * 60 * 60 * 3); + this.userInstanceCache = new Cache(1000 * 60 * 60 * 3); } onModuleInit() { @@ -384,7 +384,7 @@ export class UserEntityService implements OnModuleInit { isModerator: user.isModerator ?? falsy, isBot: user.isBot ?? falsy, isCat: user.isCat ?? falsy, - instance: user.host ? this.#userInstanceCache.fetch(user.host, + instance: user.host ? this.userInstanceCache.fetch(user.host, () => this.instancesRepository.findOneBy({ host: user.host! }), v => v != null, ).then(instance => instance ? { diff --git a/packages/backend/src/core/remote/ResolveUserService.ts b/packages/backend/src/core/remote/ResolveUserService.ts index 4ef91bc8e6..b45168fb02 100644 --- a/packages/backend/src/core/remote/ResolveUserService.ts +++ b/packages/backend/src/core/remote/ResolveUserService.ts @@ -14,7 +14,7 @@ import { ApPersonService } from './activitypub/models/ApPersonService.js'; @Injectable() export class ResolveUserService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -28,14 +28,14 @@ export class ResolveUserService { private remoteLoggerService: RemoteLoggerService, private apPersonService: ApPersonService, ) { - this.#logger = this.remoteLoggerService.logger.createSubLogger('resolve-user'); + this.logger = this.remoteLoggerService.logger.createSubLogger('resolve-user'); } public async resolveUser(username: string, host: string | null): Promise { const usernameLower = username.toLowerCase(); if (host == null) { - this.#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'); @@ -48,7 +48,7 @@ export class ResolveUserService { host = this.utilityService.toPuny(host); if (this.config.host === host) { - this.#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'); @@ -63,9 +63,9 @@ export class ResolveUserService { const acctLower = `${usernameLower}@${host}`; if (user == null) { - const self = await this.#resolveSelf(acctLower); + const self = await this.resolveSelf(acctLower); - this.#logger.succ(`return new remote user: ${chalk.magenta(acctLower)}`); + this.logger.succ(`return new remote user: ${chalk.magenta(acctLower)}`); return await this.apPersonService.createPerson(self.href); } @@ -76,13 +76,13 @@ export class ResolveUserService { lastFetchedAt: new Date(), }); - this.#logger.info(`try resync: ${acctLower}`); - const self = await this.#resolveSelf(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. - this.#logger.info(`uri missmatch: ${acctLower}`); - this.#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); @@ -97,12 +97,12 @@ export class ResolveUserService { uri: self.href, }); } else { - this.#logger.info(`uri is fine: ${acctLower}`); + this.logger.info(`uri is fine: ${acctLower}`); } await this.apPersonService.updatePerson(self.href); - this.#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'); @@ -112,19 +112,19 @@ export class ResolveUserService { }); } - this.#logger.info(`return existing remote user: ${acctLower}`); + this.logger.info(`return existing remote user: ${acctLower}`); return user; } - async #resolveSelf(acctLower: string) { - this.#logger.info(`WebFinger for ${chalk.yellow(acctLower)}`); + private async resolveSelf(acctLower: string) { + this.logger.info(`WebFinger for ${chalk.yellow(acctLower)}`); const finger = await this.webfingerService.webfinger(acctLower).catch(err => { - this.#logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: ${ err.statusCode ?? err.message }`); + 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) { - this.#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/core/remote/WebfingerService.ts b/packages/backend/src/core/remote/WebfingerService.ts index 24ccaf528b..ab46314792 100644 --- a/packages/backend/src/core/remote/WebfingerService.ts +++ b/packages/backend/src/core/remote/WebfingerService.ts @@ -26,12 +26,12 @@ export class WebfingerService { } public async webfinger(query: string): Promise { - const url = this.#genUrl(query); + const url = this.genUrl(query); return await this.httpRequestService.getJson(url, 'application/jrd+json, application/json') as IWebFinger; } - #genUrl(query: string): string { + private genUrl(query: string): string { if (query.match(/^https?:\/\//)) { const u = new URL(query); return `${u.protocol}//${u.hostname}/.well-known/webfinger?` + urlQuery({ resource: query }); diff --git a/packages/backend/src/core/remote/activitypub/ApAudienceService.ts b/packages/backend/src/core/remote/activitypub/ApAudienceService.ts index 178631ac55..744017aa3a 100644 --- a/packages/backend/src/core/remote/activitypub/ApAudienceService.ts +++ b/packages/backend/src/core/remote/activitypub/ApAudienceService.ts @@ -25,8 +25,8 @@ export class ApAudienceService { } 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 toGroups = this.groupingAudience(getApIds(to), actor); + const ccGroups = this.groupingAudience(getApIds(cc), actor); const others = unique(concat([toGroups.other, ccGroups.other])); @@ -66,7 +66,7 @@ export class ApAudienceService { }; } - #groupingAudience(ids: string[], actor: CacheableRemoteUser) { + private groupingAudience(ids: string[], actor: CacheableRemoteUser) { const groups = { public: [] as string[], followers: [] as string[], @@ -74,9 +74,9 @@ export class ApAudienceService { }; for (const id of ids) { - if (this.#isPublic(id)) { + if (this.isPublic(id)) { groups.public.push(id); - } else if (this.#isFollowers(id, actor)) { + } else if (this.isFollowers(id, actor)) { groups.followers.push(id); } else { groups.other.push(id); @@ -88,7 +88,7 @@ export class ApAudienceService { return groups; } - #isPublic(id: string) { + private isPublic(id: string) { return [ 'https://www.w3.org/ns/activitystreams#Public', 'as#Public', @@ -96,7 +96,7 @@ export class ApAudienceService { ].includes(id); } - #isFollowers(id: string, actor: CacheableRemoteUser) { + private isFollowers(id: string, actor: CacheableRemoteUser) { return ( id === (actor.followersUri ?? `${actor.uri}/followers`) ); diff --git a/packages/backend/src/core/remote/activitypub/ApDbResolverService.ts b/packages/backend/src/core/remote/activitypub/ApDbResolverService.ts index 37f58c6b0c..6f197985da 100644 --- a/packages/backend/src/core/remote/activitypub/ApDbResolverService.ts +++ b/packages/backend/src/core/remote/activitypub/ApDbResolverService.ts @@ -31,8 +31,8 @@ export type UriParseResult = { @Injectable() export class ApDbResolverService { - #publicKeyCache: Cache; - #publicKeyByUserIdCache: Cache; + private publicKeyCache: Cache; + private publicKeyByUserIdCache: Cache; constructor( @Inject(DI.config) @@ -53,8 +53,8 @@ export class ApDbResolverService { private userCacheService: UserCacheService, private apPersonService: ApPersonService, ) { - this.#publicKeyCache = new Cache(Infinity); - this.#publicKeyByUserIdCache = new Cache(Infinity); + this.publicKeyCache = new Cache(Infinity); + this.publicKeyByUserIdCache = new Cache(Infinity); } public parseUri(value: string | IObject): UriParseResult { @@ -140,7 +140,7 @@ export class ApDbResolverService { user: CacheableRemoteUser; key: UserPublickey; } | null> { - const key = await this.#publicKeyCache.fetch(keyId, async () => { + const key = await this.publicKeyCache.fetch(keyId, async () => { const key = await this.userPublickeysRepository.findOneBy({ keyId, }); @@ -169,7 +169,7 @@ export class ApDbResolverService { if (user == null) return null; - const key = await this.#publicKeyByUserIdCache.fetch(user.id, () => this.userPublickeysRepository.findOneBy({ userId: user.id }), v => v != null); + const key = await this.publicKeyByUserIdCache.fetch(user.id, () => this.userPublickeysRepository.findOneBy({ userId: user.id }), v => v != null); return { user, diff --git a/packages/backend/src/core/remote/activitypub/ApInboxService.ts b/packages/backend/src/core/remote/activitypub/ApInboxService.ts index dc65f65ae1..0482e029d2 100644 --- a/packages/backend/src/core/remote/activitypub/ApInboxService.ts +++ b/packages/backend/src/core/remote/activitypub/ApInboxService.ts @@ -34,7 +34,7 @@ import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow @Injectable() export class ApInboxService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -81,7 +81,7 @@ export class ApInboxService { private queueService: QueueService, private messagingService: MessagingService, ) { - this.#logger = this.apLoggerService.logger; + this.logger = this.apLoggerService.logger; } public async performActivity(actor: CacheableRemoteUser, activity: IObject) { @@ -93,7 +93,7 @@ export class ApInboxService { await this.performOneActivity(actor, act); } catch (err) { if (err instanceof Error || typeof err === 'string') { - this.#logger.error(err); + this.logger.error(err); } } } @@ -115,39 +115,39 @@ export class ApInboxService { if (actor.isSuspended) return; if (isCreate(activity)) { - await this.#create(actor, activity); + await this.create(actor, activity); } else if (isDelete(activity)) { - await this.#delete(actor, activity); + await this.delete(actor, activity); } else if (isUpdate(activity)) { - await this.#update(actor, activity); + await this.update(actor, activity); } else if (isRead(activity)) { - await this.#read(actor, activity); + await this.read(actor, activity); } else if (isFollow(activity)) { - await this.#follow(actor, activity); + await this.follow(actor, activity); } else if (isAccept(activity)) { - await this.#accept(actor, activity); + await this.accept(actor, activity); } else if (isReject(activity)) { - await this.#reject(actor, activity); + await this.reject(actor, activity); } else if (isAdd(activity)) { - await this.#add(actor, activity).catch(err => this.#logger.error(err)); + await this.add(actor, activity).catch(err => this.logger.error(err)); } else if (isRemove(activity)) { - await this.#remove(actor, activity).catch(err => this.#logger.error(err)); + await this.remove(actor, activity).catch(err => this.logger.error(err)); } else if (isAnnounce(activity)) { - await this.#announce(actor, activity); + await this.announce(actor, activity); } else if (isLike(activity)) { - await this.#like(actor, activity); + await this.like(actor, activity); } else if (isUndo(activity)) { - await this.#undo(actor, activity); + await this.undo(actor, activity); } else if (isBlock(activity)) { - await this.#block(actor, activity); + await this.block(actor, activity); } else if (isFlag(activity)) { - await this.#flag(actor, activity); + await this.flag(actor, activity); } else { - this.#logger.warn(`unrecognized activity type: ${(activity as any).type}`); + this.logger.warn(`unrecognized activity type: ${(activity as any).type}`); } } - async #follow(actor: CacheableRemoteUser, activity: IFollow): Promise { + private async follow(actor: CacheableRemoteUser, activity: IFollow): Promise { const followee = await this.apDbResolverService.getUserFromApId(activity.object); if (followee == null) { @@ -162,7 +162,7 @@ export class ApInboxService { return 'ok'; } - async #like(actor: CacheableRemoteUser, activity: ILike): Promise { + private async like(actor: CacheableRemoteUser, activity: ILike): Promise { const targetUri = getApId(activity.object); const note = await this.apNoteService.fetchNote(targetUri); @@ -179,7 +179,7 @@ export class ApInboxService { }).then(() => 'ok'); } - async #read(actor: CacheableRemoteUser, activity: IRead): Promise { + private async read(actor: CacheableRemoteUser, activity: IRead): Promise { const id = await getApId(activity.object); if (!this.utilityService.isSelfHost(this.utilityService.extractDbHost(id))) { @@ -201,24 +201,24 @@ export class ApInboxService { return `ok: mark as read (${message.userId} => ${message.recipientId} ${message.id})`; } - async #accept(actor: CacheableRemoteUser, activity: IAccept): Promise { + private async accept(actor: CacheableRemoteUser, activity: IAccept): Promise { const uri = activity.id ?? activity; - this.#logger.info(`Accept: ${uri}`); + this.logger.info(`Accept: ${uri}`); const resolver = this.apResolverService.createResolver(); const object = await resolver.resolve(activity.object).catch(err => { - this.#logger.error(`Resolution failed: ${err}`); + this.logger.error(`Resolution failed: ${err}`); throw err; }); - if (isFollow(object)) return await this.#acceptFollow(actor, object); + if (isFollow(object)) return await this.acceptFollow(actor, object); return `skip: Unknown Accept type: ${getApType(object)}`; } - async #acceptFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { + private async acceptFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある const follower = await this.apDbResolverService.getUserFromApId(activity.actor); @@ -241,7 +241,7 @@ export class ApInboxService { return 'ok'; } - async #add(actor: CacheableRemoteUser, activity: IAdd): Promise { + private async add(actor: CacheableRemoteUser, activity: IAdd): Promise { if ('actor' in activity && actor.uri !== activity.actor) { throw new Error('invalid actor'); } @@ -260,17 +260,17 @@ export class ApInboxService { throw new Error(`unknown target: ${activity.target}`); } - async #announce(actor: CacheableRemoteUser, activity: IAnnounce): Promise { + private async announce(actor: CacheableRemoteUser, activity: IAnnounce): Promise { const uri = getApId(activity); - this.#logger.info(`Announce: ${uri}`); + this.logger.info(`Announce: ${uri}`); const targetUri = getApId(activity.object); - this.#announceNote(actor, activity, targetUri); + this.announceNote(actor, activity, targetUri); } - async #announceNote(actor: CacheableRemoteUser, activity: IAnnounce, targetUri: string): Promise { + private async announceNote(actor: CacheableRemoteUser, activity: IAnnounce, targetUri: string): Promise { const uri = getApId(activity); if (actor.isSuspended) { @@ -298,18 +298,18 @@ export class ApInboxService { // 対象が4xxならスキップ if (err instanceof StatusError) { if (err.isClientError) { - this.#logger.warn(`Ignored announce target ${targetUri} - ${err.statusCode}`); + this.logger.warn(`Ignored announce target ${targetUri} - ${err.statusCode}`); return; } - this.#logger.warn(`Error in announce target ${targetUri} - ${err.statusCode ?? err}`); + this.logger.warn(`Error in announce target ${targetUri} - ${err.statusCode ?? err}`); } throw err; } if (!await this.noteEntityService.isVisibleForMe(renote, actor.id)) return 'skip: invalid actor for this activity'; - this.#logger.info(`Creating the (Re)Note: ${uri}`); + this.logger.info(`Creating the (Re)Note: ${uri}`); const activityAudience = await this.apAudienceService.parseAudience(actor, activity.to, activity.cc); @@ -325,7 +325,7 @@ export class ApInboxService { } } - async #block(actor: CacheableRemoteUser, activity: IBlock): Promise { + private async block(actor: CacheableRemoteUser, activity: IBlock): Promise { // ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず const blockee = await this.apDbResolverService.getUserFromApId(activity.object); @@ -342,10 +342,10 @@ export class ApInboxService { return 'ok'; } - async #create(actor: CacheableRemoteUser, activity: ICreate): Promise { + private async create(actor: CacheableRemoteUser, activity: ICreate): Promise { const uri = getApId(activity); - this.#logger.info(`Create: ${uri}`); + this.logger.info(`Create: ${uri}`); // copy audiences between activity <=> object. if (typeof activity.object === 'object') { @@ -366,18 +366,18 @@ export class ApInboxService { const resolver = this.apResolverService.createResolver(); const object = await resolver.resolve(activity.object).catch(e => { - this.#logger.error(`Resolution failed: ${e}`); + this.logger.error(`Resolution failed: ${e}`); throw e; }); if (isPost(object)) { - this.#createNote(resolver, actor, object, false, activity); + this.createNote(resolver, actor, object, false, activity); } else { - this.#logger.warn(`Unknown type: ${getApType(object)}`); + this.logger.warn(`Unknown type: ${getApType(object)}`); } } - async #createNote(resolver: Resolver, actor: CacheableRemoteUser, note: IObject, silent = false, activity?: ICreate): Promise { + private async createNote(resolver: Resolver, actor: CacheableRemoteUser, note: IObject, silent = false, activity?: ICreate): Promise { const uri = getApId(note); if (typeof note === 'object') { @@ -411,7 +411,7 @@ export class ApInboxService { } } - async #delete(actor: CacheableRemoteUser, activity: IDelete): Promise { + private async delete(actor: CacheableRemoteUser, activity: IDelete): Promise { if ('actor' in activity && actor.uri !== activity.actor) { throw new Error('invalid actor'); } @@ -444,16 +444,16 @@ export class ApInboxService { } if (validPost.includes(formerType)) { - return await this.#deleteNote(actor, uri); + return await this.deleteNote(actor, uri); } else if (validActor.includes(formerType)) { - return await this.#deleteActor(actor, uri); + return await this.deleteActor(actor, uri); } else { return `Unknown type ${formerType}`; } } - async #deleteActor(actor: CacheableRemoteUser, uri: string): Promise { - this.#logger.info(`Deleting the Actor: ${uri}`); + private async deleteActor(actor: CacheableRemoteUser, uri: string): Promise { + this.logger.info(`Deleting the Actor: ${uri}`); if (actor.uri !== uri) { return `skip: delete actor ${actor.uri} !== ${uri}`; @@ -461,7 +461,7 @@ export class ApInboxService { const user = await this.usersRepository.findOneByOrFail({ id: actor.id }); if (user.isDeleted) { - this.#logger.info('skip: already deleted'); + this.logger.info('skip: already deleted'); } const job = await this.queueService.createDeleteAccountJob(actor); @@ -473,8 +473,8 @@ export class ApInboxService { return `ok: queued ${job.name} ${job.id}`; } - async #deleteNote(actor: CacheableRemoteUser, uri: string): Promise { - this.#logger.info(`Deleting the Note: ${uri}`); + private async deleteNote(actor: CacheableRemoteUser, uri: string): Promise { + this.logger.info(`Deleting the Note: ${uri}`); const unlock = await this.appLockService.getApLock(uri); @@ -505,7 +505,7 @@ export class ApInboxService { } } - async #flag(actor: CacheableRemoteUser, activity: IFlag): Promise { + private async flag(actor: CacheableRemoteUser, activity: IFlag): Promise { // objectは `(User|Note) | (User|Note)[]` だけど、全パターンDBスキーマと対応させられないので // 対象ユーザーは一番最初のユーザー として あとはコメントとして格納する const uris = getApIds(activity.object); @@ -529,24 +529,24 @@ export class ApInboxService { return 'ok'; } - async #reject(actor: CacheableRemoteUser, activity: IReject): Promise { + private async reject(actor: CacheableRemoteUser, activity: IReject): Promise { const uri = activity.id ?? activity; - this.#logger.info(`Reject: ${uri}`); + this.logger.info(`Reject: ${uri}`); const resolver = this.apResolverService.createResolver(); const object = await resolver.resolve(activity.object).catch(e => { - this.#logger.error(`Resolution failed: ${e}`); + this.logger.error(`Resolution failed: ${e}`); throw e; }); - if (isFollow(object)) return await this.#rejectFollow(actor, object); + if (isFollow(object)) return await this.rejectFollow(actor, object); return `skip: Unknown Reject type: ${getApType(object)}`; } - async #rejectFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { + private async rejectFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある const follower = await this.apDbResolverService.getUserFromApId(activity.actor); @@ -569,7 +569,7 @@ export class ApInboxService { return 'ok'; } - async #remove(actor: CacheableRemoteUser, activity: IRemove): Promise { + private async remove(actor: CacheableRemoteUser, activity: IRemove): Promise { if ('actor' in activity && actor.uri !== activity.actor) { throw new Error('invalid actor'); } @@ -588,32 +588,32 @@ export class ApInboxService { throw new Error(`unknown target: ${activity.target}`); } - async #undo(actor: CacheableRemoteUser, activity: IUndo): Promise { + private 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; - this.#logger.info(`Undo: ${uri}`); + this.logger.info(`Undo: ${uri}`); const resolver = this.apResolverService.createResolver(); const object = await resolver.resolve(activity.object).catch(e => { - this.#logger.error(`Resolution failed: ${e}`); + this.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); + 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 { + private async undoAccept(actor: CacheableRemoteUser, activity: IAccept): Promise { const follower = await this.apDbResolverService.getUserFromApId(activity.object); if (follower == null) { return 'skip: follower not found'; @@ -632,7 +632,7 @@ export class ApInboxService { return 'skip: フォローされていない'; } - async #undoAnnounce(actor: CacheableRemoteUser, activity: IAnnounce): Promise { + private async undoAnnounce(actor: CacheableRemoteUser, activity: IAnnounce): Promise { const uri = getApId(activity); const note = await this.notesRepository.findOneBy({ @@ -646,7 +646,7 @@ export class ApInboxService { return 'ok: deleted'; } - async #undoBlock(actor: CacheableRemoteUser, activity: IBlock): Promise { + private async undoBlock(actor: CacheableRemoteUser, activity: IBlock): Promise { const blockee = await this.apDbResolverService.getUserFromApId(activity.object); if (blockee == null) { @@ -661,7 +661,7 @@ export class ApInboxService { return 'ok'; } - async #undoFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { + private async undoFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { const followee = await this.apDbResolverService.getUserFromApId(activity.object); if (followee == null) { return 'skip: followee not found'; @@ -694,7 +694,7 @@ export class ApInboxService { return 'skip: リクエストもフォローもされていない'; } - async #undoLike(actor: CacheableRemoteUser, activity: ILike): Promise { + private async undoLike(actor: CacheableRemoteUser, activity: ILike): Promise { const targetUri = getApId(activity.object); const note = await this.apNoteService.fetchNote(targetUri); @@ -708,17 +708,17 @@ export class ApInboxService { return 'ok'; } - async #update(actor: CacheableRemoteUser, activity: IUpdate): Promise { + private async update(actor: CacheableRemoteUser, activity: IUpdate): Promise { if ('actor' in activity && actor.uri !== activity.actor) { return 'skip: invalid actor'; } - this.#logger.debug('Update'); + this.logger.debug('Update'); const resolver = this.apResolverService.createResolver(); const object = await resolver.resolve(activity.object).catch(e => { - this.#logger.error(`Resolution failed: ${e}`); + this.logger.error(`Resolution failed: ${e}`); throw e; }); diff --git a/packages/backend/src/core/remote/activitypub/ApRendererService.ts b/packages/backend/src/core/remote/activitypub/ApRendererService.ts index b065c1acdd..5a4cef63e0 100644 --- a/packages/backend/src/core/remote/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/remote/activitypub/ApRendererService.ts @@ -365,7 +365,7 @@ export class ApRendererService { text: apText, })); - const emojis = await this.#getEmojis(note.emojis); + const emojis = await this.getEmojis(note.emojis); const apemojis = emojis.map(emoji => this.renderEmoji(emoji)); const tag = [ @@ -448,7 +448,7 @@ export class ApRendererService { } } - const emojis = await this.#getEmojis(user.emojis); + const emojis = await this.getEmojis(user.emojis); const apemojis = emojis.map(emoji => this.renderEmoji(emoji)); const hashtagTags = (user.tags ?? []).map(tag => this.renderHashtag(tag)); @@ -687,7 +687,7 @@ export class ApRendererService { return page; } - async #getEmojis(names: string[]): Promise { + private async getEmojis(names: string[]): Promise { if (names == null || names.length === 0) return []; const emojis = await Promise.all( diff --git a/packages/backend/src/core/remote/activitypub/ApRequestService.ts b/packages/backend/src/core/remote/activitypub/ApRequestService.ts index a449a77dc8..2abaca06af 100644 --- a/packages/backend/src/core/remote/activitypub/ApRequestService.ts +++ b/packages/backend/src/core/remote/activitypub/ApRequestService.ts @@ -36,14 +36,14 @@ export class ApRequestService { ) { } - #createSignedPost(args: { key: PrivateKey, url: string, body: string, additionalHeaders: Record }): Signed { + private 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({ + headers: this.objectAssignWithLcKey({ 'Date': new Date().toUTCString(), 'Host': u.hostname, 'Content-Type': 'application/activity+json', @@ -51,7 +51,7 @@ export class ApRequestService { }, args.additionalHeaders), }; - const result = this.#signToRequest(request, args.key, ['(request-target)', 'date', 'host', 'digest']); + const result = this.signToRequest(request, args.key, ['(request-target)', 'date', 'host', 'digest']); return { request, @@ -61,20 +61,20 @@ export class ApRequestService { }; } - #createSignedGet(args: { key: PrivateKey, url: string, additionalHeaders: Record }): Signed { + private 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({ + 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']); + const result = this.signToRequest(request, args.key, ['(request-target)', 'date', 'host', 'accept']); return { request, @@ -84,12 +84,12 @@ export class ApRequestService { }; } - #signToRequest(request: Request, key: PrivateKey, includeHeaders: string[]): Signed { - const signingString = this.#genSigningString(request, includeHeaders); + private 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, { + request.headers = this.objectAssignWithLcKey(request.headers, { Signature: signatureHeader, }); @@ -101,8 +101,8 @@ export class ApRequestService { }; } - #genSigningString(request: Request, includeHeaders: string[]): string { - request.headers = this.#lcObjectKey(request.headers); + private genSigningString(request: Request, includeHeaders: string[]): string { + request.headers = this.lcObjectKey(request.headers); const results: string[] = []; @@ -117,14 +117,14 @@ export class ApRequestService { return results.join('\n'); } - #lcObjectKey(src: Record): Record { + private 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)); + private 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) { @@ -132,7 +132,7 @@ export class ApRequestService { const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); - const req = this.#createSignedPost({ + const req = this.createSignedPost({ key: { privateKeyPem: keypair.privateKey, keyId: `${this.config.url}/users/${user.id}#main-key`, @@ -160,7 +160,7 @@ export class ApRequestService { public async signedGet(url: string, user: { id: User['id'] }) { const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); - const req = this.#createSignedGet({ + const req = this.createSignedGet({ key: { privateKeyPem: keypair.privateKey, keyId: `${this.config.url}/users/${user.id}#main-key`, diff --git a/packages/backend/src/core/remote/activitypub/models/ApImageService.ts b/packages/backend/src/core/remote/activitypub/models/ApImageService.ts index d4e01923a2..da6ed61c56 100644 --- a/packages/backend/src/core/remote/activitypub/models/ApImageService.ts +++ b/packages/backend/src/core/remote/activitypub/models/ApImageService.ts @@ -14,7 +14,7 @@ import { ApLoggerService } from '../ApLoggerService.js'; @Injectable() export class ApImageService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -28,7 +28,7 @@ export class ApImageService { private driveService: DriveService, private apLoggerService: ApLoggerService, ) { - this.#logger = this.apLoggerService.logger; + this.logger = this.apLoggerService.logger; } /** @@ -46,7 +46,7 @@ export class ApImageService { throw new Error('invalid image: url not privided'); } - this.#logger.info(`Creating the Image: ${image.url}`); + this.logger.info(`Creating the Image: ${image.url}`); const instance = await this.metaService.fetch(); diff --git a/packages/backend/src/core/remote/activitypub/models/ApNoteService.ts b/packages/backend/src/core/remote/activitypub/models/ApNoteService.ts index c74949c595..1efe62333b 100644 --- a/packages/backend/src/core/remote/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/remote/activitypub/models/ApNoteService.ts @@ -35,7 +35,7 @@ import type { IObject, IPost } from '../type.js'; @Injectable() export class ApNoteService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -71,7 +71,7 @@ export class ApNoteService { private apDbResolverService: ApDbResolverService, private apLoggerService: ApLoggerService, ) { - this.#logger = this.apLoggerService.logger; + this.logger = this.apLoggerService.logger; } public validateNote(object: any, uri: string) { @@ -116,7 +116,7 @@ export class ApNoteService { const entryUri = getApId(value); const err = this.validateNote(object, entryUri); if (err) { - this.#logger.error(`${err.message}`, { + this.logger.error(`${err.message}`, { resolver: { history: resolver.getHistory(), }, @@ -128,9 +128,9 @@ export class ApNoteService { const note: IPost = object; - this.#logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`); + this.logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`); - this.#logger.info(`Creating the Note: ${note.id}`); + this.logger.info(`Creating the Note: ${note.id}`); // 投稿者をフェッチ const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as CacheableRemoteUser; @@ -174,7 +174,7 @@ export class ApNoteService { const reply: Note | null = note.inReplyTo ? await this.resolveNote(note.inReplyTo, resolver).then(x => { if (x == null) { - this.#logger.warn('Specified inReplyTo, but nout found'); + this.logger.warn('Specified inReplyTo, but nout found'); throw new Error('inReplyTo not found'); } else { return x; @@ -191,7 +191,7 @@ export class ApNoteService { } } - this.#logger.warn(`Error in inReplyTo ${note.inReplyTo} - ${err.statusCode ?? err}`); + this.logger.warn(`Error in inReplyTo ${note.inReplyTo} - ${err.statusCode ?? err}`); throw err; }) : null; @@ -255,9 +255,9 @@ export class ApNoteService { const tryCreateVote = async (name: string, index: number): Promise => { if (poll.expiresAt && Date.now() > new Date(poll.expiresAt).getTime()) { - this.#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) { - this.#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 this.pollService.vote(actor, reply, index); // リモートフォロワーにUpdate配信 @@ -272,7 +272,7 @@ export class ApNoteService { } const emojis = await this.extractEmojis(note.tag ?? [], actor.host).catch(e => { - this.#logger.info(`extractEmojis: ${e}`); + this.logger.info(`extractEmojis: ${e}`); return [] as Emoji[]; }); @@ -386,7 +386,7 @@ export class ApNoteService { return exists; } - this.#logger.info(`register emoji host=${host}, name=${name}`); + this.logger.info(`register emoji host=${host}, name=${name}`); return await this.emojisRepository.insert({ id: this.idService.genId(), diff --git a/packages/backend/src/core/remote/activitypub/models/ApPersonService.ts b/packages/backend/src/core/remote/activitypub/models/ApPersonService.ts index 1ca6463484..d088fa5554 100644 --- a/packages/backend/src/core/remote/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/remote/activitypub/models/ApPersonService.ts @@ -91,7 +91,7 @@ export class ApPersonService implements OnModuleInit { private usersChart: UsersChart; private instanceChart: InstanceChart; private apLoggerService: ApLoggerService; - #logger: Logger; + private logger: Logger; constructor( private moduleRef: ModuleRef, @@ -153,7 +153,7 @@ export class ApPersonService implements OnModuleInit { this.usersChart = this.moduleRef.get('UsersChart'); this.instanceChart = this.moduleRef.get('InstanceChart'); this.apLoggerService = this.moduleRef.get('ApLoggerService'); - this.#logger = this.apLoggerService.logger; + this.logger = this.apLoggerService.logger; } /** @@ -161,7 +161,7 @@ export class ApPersonService implements OnModuleInit { * @param x Fetched object * @param uri Fetch target URI */ - #validateActor(x: IObject, uri: string): IActor { + private validateActor(x: IObject, uri: string): IActor { const expectHost = this.utilityService.toPuny(new URL(uri).hostname); if (x == null) { @@ -264,9 +264,9 @@ export class ApPersonService implements OnModuleInit { const object = await resolver.resolve(uri) as any; - const person = this.#validateActor(object, uri); + const person = this.validateActor(object, uri); - this.#logger.info(`Creating the Person: ${person.id}`); + this.logger.info(`Creating the Person: ${person.id}`); const host = this.utilityService.toPuny(new URL(object.id).hostname); @@ -338,7 +338,7 @@ export class ApPersonService implements OnModuleInit { throw new Error('already registered'); } } else { - this.#logger.error(e instanceof Error ? e : new Error(e as string)); + this.logger.error(e instanceof Error ? e : new Error(e as string)); throw e; } } @@ -379,7 +379,7 @@ export class ApPersonService implements OnModuleInit { //#region カスタム絵文字取得 const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host).catch(err => { - this.#logger.info(`extractEmojis: ${err}`); + this.logger.info(`extractEmojis: ${err}`); return [] as Emoji[]; }); @@ -390,7 +390,7 @@ export class ApPersonService implements OnModuleInit { }); //#endregion - await this.updateFeatured(user!.id).catch(err => this.#logger.error(err)); + await this.updateFeatured(user!.id).catch(err => this.logger.error(err)); return user!; } @@ -422,9 +422,9 @@ export class ApPersonService implements OnModuleInit { const object = hint ?? await resolver.resolve(uri); - const person = this.#validateActor(object, uri); + const person = this.validateActor(object, uri); - this.#logger.info(`Updating the Person: ${person.id}`); + this.logger.info(`Updating the Person: ${person.id}`); // アバターとヘッダー画像をフェッチ const [avatar, banner] = await Promise.all([ @@ -438,7 +438,7 @@ export class ApPersonService implements OnModuleInit { // カスタム絵文字取得 const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], exist.host).catch(e => { - this.#logger.info(`extractEmojis: ${e}`); + this.logger.info(`extractEmojis: ${e}`); return [] as Emoji[]; }); @@ -503,7 +503,7 @@ export class ApPersonService implements OnModuleInit { followerSharedInbox: person.sharedInbox ?? (person.endpoints ? person.endpoints.sharedInbox : undefined), }); - await this.updateFeatured(exist.id).catch(err => this.#logger.error(err)); + await this.updateFeatured(exist.id).catch(err => this.logger.error(err)); } /** @@ -556,7 +556,7 @@ export class ApPersonService implements OnModuleInit { if (!this.userEntityService.isRemoteUser(user)) return; if (!user.featured) return; - this.#logger.info(`Updating the featured: ${user.uri}`); + this.logger.info(`Updating the featured: ${user.uri}`); const resolver = this.apResolverService.createResolver(); diff --git a/packages/backend/src/core/remote/activitypub/models/ApQuestionService.ts b/packages/backend/src/core/remote/activitypub/models/ApQuestionService.ts index 97f26d1681..2b89cb030b 100644 --- a/packages/backend/src/core/remote/activitypub/models/ApQuestionService.ts +++ b/packages/backend/src/core/remote/activitypub/models/ApQuestionService.ts @@ -12,7 +12,7 @@ import type { IObject, IQuestion } from '../type.js'; @Injectable() export class ApQuestionService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -27,7 +27,7 @@ export class ApQuestionService { private apResolverService: ApResolverService, private apLoggerService: ApLoggerService, ) { - this.#logger = this.apLoggerService.logger; + this.logger = this.apLoggerService.logger; } public async extractPollFromQuestion(source: string | IObject, resolver?: Resolver): Promise { @@ -82,7 +82,7 @@ export class ApQuestionService { // 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)}`); + this.logger.debug(`fetched question: ${JSON.stringify(question, null, 2)}`); if (question.type !== 'Question') throw new Error('object is not a Question'); diff --git a/packages/backend/src/daemons/JanitorService.ts b/packages/backend/src/daemons/JanitorService.ts index dacbb8cca6..a51f570725 100644 --- a/packages/backend/src/daemons/JanitorService.ts +++ b/packages/backend/src/daemons/JanitorService.ts @@ -8,7 +8,7 @@ const interval = 30 * 60 * 1000; @Injectable() export class JanitorService implements OnApplicationShutdown { - #intervalId: NodeJS.Timer; + private intervalId: NodeJS.Timer; constructor( @Inject(DI.attestationChallengesRepository) @@ -28,10 +28,10 @@ export class JanitorService implements OnApplicationShutdown { tick(); - this.#intervalId = setInterval(tick, interval); + this.intervalId = setInterval(tick, interval); } public onApplicationShutdown(signal?: string | undefined) { - clearInterval(this.#intervalId); + clearInterval(this.intervalId); } } diff --git a/packages/backend/src/daemons/QueueStatsService.ts b/packages/backend/src/daemons/QueueStatsService.ts index f05d3ce33f..931de19067 100644 --- a/packages/backend/src/daemons/QueueStatsService.ts +++ b/packages/backend/src/daemons/QueueStatsService.ts @@ -10,7 +10,7 @@ const interval = 10000; @Injectable() export class QueueStatsService implements OnApplicationShutdown { - #intervalId: NodeJS.Timer; + private intervalId: NodeJS.Timer; constructor( private queueService: QueueService, @@ -68,10 +68,10 @@ export class QueueStatsService implements OnApplicationShutdown { tick(); - this.#intervalId = setInterval(tick, interval); + this.intervalId = setInterval(tick, interval); } public onApplicationShutdown(signal?: string | undefined) { - clearInterval(this.#intervalId); + clearInterval(this.intervalId); } } diff --git a/packages/backend/src/daemons/ServerStatsService.ts b/packages/backend/src/daemons/ServerStatsService.ts index 3a6d408431..e40912442d 100644 --- a/packages/backend/src/daemons/ServerStatsService.ts +++ b/packages/backend/src/daemons/ServerStatsService.ts @@ -14,7 +14,7 @@ const round = (num: number) => Math.round(num * 10) / 10; @Injectable() export class ServerStatsService implements OnApplicationShutdown { - #intervalId: NodeJS.Timer; + private intervalId: NodeJS.Timer; constructor( ) { @@ -58,11 +58,11 @@ export class ServerStatsService implements OnApplicationShutdown { tick(); - this.#intervalId = setInterval(tick, interval); + this.intervalId = setInterval(tick, interval); } public onApplicationShutdown(signal?: string | undefined) { - clearInterval(this.#intervalId); + clearInterval(this.intervalId); } } diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts index 7be0f7b469..753df8cad6 100644 --- a/packages/backend/src/queue/QueueProcessorService.ts +++ b/packages/backend/src/queue/QueueProcessorService.ts @@ -16,7 +16,7 @@ import { QueueLoggerService } from './QueueLoggerService.js'; @Injectable() export class QueueProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -32,7 +32,7 @@ export class QueueProcessorService { private deliverProcessorService: DeliverProcessorService, private inboxProcessorService: InboxProcessorService, ) { - this.#logger = this.queueLoggerService.logger; + this.logger = this.queueLoggerService.logger; } public start() { @@ -44,12 +44,12 @@ export class QueueProcessorService { }; } - 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'); + 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}`)) diff --git a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts index 514dc1dcf3..17337837a3 100644 --- a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts +++ b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts @@ -10,7 +10,7 @@ import type Bull from 'bull'; @Injectable() export class CheckExpiredMutingsProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -22,11 +22,11 @@ export class CheckExpiredMutingsProcessorService { private globalEventService: GlobalEventService, private queueLoggerService: QueueLoggerService, ) { - this.#logger = 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 { - this.#logger.info('Checking expired mutings...'); + this.logger.info('Checking expired mutings...'); const expired = await this.mutingsRepository.createQueryBuilder('muting') .where('muting.expiresAt IS NOT NULL') @@ -44,7 +44,7 @@ export class CheckExpiredMutingsProcessorService { } } - this.#logger.succ('All expired mutings checked.'); + 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 index 0eaad9b9ed..6f2fb8dea0 100644 --- a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts @@ -20,7 +20,7 @@ import type Bull from 'bull'; @Injectable() export class CleanChartsProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -41,11 +41,11 @@ export class CleanChartsProcessorService { private queueLoggerService: QueueLoggerService, ) { - this.#logger = this.queueLoggerService.logger.createSubLogger('clean-charts'); + this.logger = this.queueLoggerService.logger.createSubLogger('clean-charts'); } public async process(job: Bull.Job>, done: () => void): Promise { - this.#logger.info('Clean charts...'); + this.logger.info('Clean charts...'); await Promise.all([ this.federationChart.clean(), @@ -62,7 +62,7 @@ export class CleanChartsProcessorService { this.apRequestChart.clean(), ]); - this.#logger.succ('All charts successfully cleaned.'); + 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 index 6150120806..830f0c56b6 100644 --- a/packages/backend/src/queue/processors/CleanProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanProcessorService.ts @@ -9,7 +9,7 @@ import type Bull from 'bull'; @Injectable() export class CleanProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -20,17 +20,17 @@ export class CleanProcessorService { private queueLoggerService: QueueLoggerService, ) { - this.#logger = this.queueLoggerService.logger.createSubLogger('clean'); + this.logger = this.queueLoggerService.logger.createSubLogger('clean'); } public async process(job: Bull.Job>, done: () => void): Promise { - this.#logger.info('Cleaning...'); + this.logger.info('Cleaning...'); this.userIpsRepository.delete({ createdAt: LessThan(new Date(Date.now() - (1000 * 60 * 60 * 24 * 90))), }); - this.#logger.succ('Cleaned.'); + this.logger.succ('Cleaned.'); done(); } } diff --git a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts index 8c53632563..c3c68be1bc 100644 --- a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts @@ -10,7 +10,7 @@ import type Bull from 'bull'; @Injectable() export class CleanRemoteFilesProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -22,11 +22,11 @@ export class CleanRemoteFilesProcessorService { private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { - this.#logger = 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 { - this.#logger.info('Deleting cached remote files...'); + this.logger.info('Deleting cached remote files...'); let deletedCount = 0; let cursor: any = null; @@ -63,7 +63,7 @@ export class CleanRemoteFilesProcessorService { job.progress(deletedCount / total); } - this.#logger.succ('All cahced remote files has been deleted.'); + this.logger.succ('All cahced remote files has been deleted.'); done(); } } diff --git a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts index a5255c5c05..ab82f87d5e 100644 --- a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts @@ -14,7 +14,7 @@ import type { DbUserDeleteJobData } from '../types.js'; @Injectable() export class DeleteAccountProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -36,11 +36,11 @@ export class DeleteAccountProcessorService { private emailService: EmailService, private queueLoggerService: QueueLoggerService, ) { - this.#logger = this.queueLoggerService.logger.createSubLogger('delete-account'); + this.logger = this.queueLoggerService.logger.createSubLogger('delete-account'); } public async process(job: Bull.Job): Promise { - this.#logger.info(`Deleting account of ${job.data.user.id} ...`); + this.logger.info(`Deleting account of ${job.data.user.id} ...`); const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); if (user == null) { @@ -71,7 +71,7 @@ export class DeleteAccountProcessorService { await this.notesRepository.delete(notes.map(note => note.id)); } - this.#logger.succ('All of notes deleted'); + this.logger.succ('All of notes deleted'); } { // Delete files @@ -100,7 +100,7 @@ export class DeleteAccountProcessorService { } } - this.#logger.succ('All of files deleted'); + this.logger.succ('All of files deleted'); } { // Send email notification diff --git a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts index 80814bb5a2..430fbf19e9 100644 --- a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts @@ -11,7 +11,7 @@ import type { DbUserJobData } from '../types.js'; @Injectable() export class DeleteDriveFilesProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -26,11 +26,11 @@ export class DeleteDriveFilesProcessorService { private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { - this.#logger = 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 { - this.#logger.info(`Deleting drive files of ${job.data.user.id} ...`); + 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) { @@ -72,7 +72,7 @@ export class DeleteDriveFilesProcessorService { job.progress(deletedCount / total); } - this.#logger.succ(`All drive files (${deletedCount}) of ${user.id} has been deleted.`); + this.logger.succ(`All drive files (${deletedCount}) of ${user.id} has been deleted.`); done(); } } diff --git a/packages/backend/src/queue/processors/DeleteFileProcessorService.ts b/packages/backend/src/queue/processors/DeleteFileProcessorService.ts index 55424f6444..72923b80a9 100644 --- a/packages/backend/src/queue/processors/DeleteFileProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteFileProcessorService.ts @@ -9,7 +9,7 @@ import type { ObjectStorageFileJobData } from '../types.js'; @Injectable() export class DeleteFileProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -18,7 +18,7 @@ export class DeleteFileProcessorService { private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { - this.#logger = 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 3403ec83a9..1bf51c1bc6 100644 --- a/packages/backend/src/queue/processors/DeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts @@ -21,9 +21,9 @@ import type { DeliverJobData } from '../types.js'; @Injectable() export class DeliverProcessorService { - #logger: Logger; - #suspendedHostsCache: Cache; - #latest: string | null; + private logger: Logger; + private suspendedHostsCache: Cache; + private latest: string | null; constructor( @Inject(DI.config) @@ -45,9 +45,9 @@ export class DeliverProcessorService { private federationChart: FederationChart, private queueLoggerService: QueueLoggerService, ) { - this.#logger = this.queueLoggerService.logger.createSubLogger('deliver'); - this.#suspendedHostsCache = new Cache(1000 * 60 * 60); - this.#latest = null; + this.logger = this.queueLoggerService.logger.createSubLogger('deliver'); + this.suspendedHostsCache = new Cache(1000 * 60 * 60); + this.latest = null; } public async process(job: Bull.Job): Promise { @@ -60,22 +60,22 @@ export class DeliverProcessorService { } // isSuspendedなら中断 - let suspendedHosts = this.#suspendedHostsCache.get(null); + let suspendedHosts = this.suspendedHostsCache.get(null); if (suspendedHosts == null) { suspendedHosts = await this.instancesRepository.find({ where: { isSuspended: true, }, }); - this.#suspendedHostsCache.set(null, suspendedHosts); + this.suspendedHostsCache.set(null, suspendedHosts); } if (suspendedHosts.map(x => x.host).includes(this.utilityService.toPuny(host))) { return 'skip (suspended)'; } try { - if (this.#latest !== (this.#latest = JSON.stringify(job.data.content, null, 2))) { - this.#logger.debug(`delivering ${this.#latest}`); + 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); diff --git a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts index b90c7be629..3e55a351a1 100644 --- a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts +++ b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts @@ -11,7 +11,7 @@ import type { EndedPollNotificationJobData } from '../types.js'; @Injectable() export class EndedPollNotificationProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -26,7 +26,7 @@ export class EndedPollNotificationProcessorService { private createNotificationService: CreateNotificationService, private queueLoggerService: QueueLoggerService, ) { - this.#logger = 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 9b520be06e..cbc483698f 100644 --- a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts @@ -16,7 +16,7 @@ import type { DbUserJobData } from '../types.js'; @Injectable() export class ExportBlockingProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -32,11 +32,11 @@ export class ExportBlockingProcessorService { private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { - this.#logger = this.queueLoggerService.logger.createSubLogger('export-blocking'); + this.logger = 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} ...`); + this.logger.info(`Exporting blocking of ${job.data.user.id} ...`); const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); if (user == null) { @@ -47,7 +47,7 @@ export class ExportBlockingProcessorService { // Create temp file const [path, cleanup] = await createTemp(); - this.#logger.info(`Temp file is ${path}`); + this.logger.info(`Temp file is ${path}`); try { const stream = fs.createWriteStream(path, { flags: 'a' }); @@ -84,7 +84,7 @@ export class ExportBlockingProcessorService { await new Promise((res, rej) => { stream.write(content + '\n', err => { if (err) { - this.#logger.error(err); + this.logger.error(err); rej(err); } else { res(); @@ -102,12 +102,12 @@ export class ExportBlockingProcessorService { } stream.end(); - this.#logger.succ(`Exported to: ${path}`); + 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}`); + this.logger.succ(`Exported to: ${driveFile.id}`); } finally { cleanup(); } diff --git a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts index 93341c2c63..c49a47561b 100644 --- a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts @@ -17,7 +17,7 @@ import type Bull from 'bull'; @Injectable() export class ExportCustomEmojisProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -33,11 +33,11 @@ export class ExportCustomEmojisProcessorService { private downloadService: DownloadService, private queueLoggerService: QueueLoggerService, ) { - this.#logger = 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 { - this.#logger.info('Exporting custom emojis ...'); + this.logger.info('Exporting custom emojis ...'); const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); if (user == null) { @@ -47,7 +47,7 @@ export class ExportCustomEmojisProcessorService { const [path, cleanup] = await createTempDir(); - this.#logger.info(`Temp dir is ${path}`); + this.logger.info(`Temp dir is ${path}`); const metaPath = path + '/meta.json'; @@ -59,7 +59,7 @@ export class ExportCustomEmojisProcessorService { return new Promise((res, rej) => { metaStream.write(text, err => { if (err) { - this.#logger.error(err); + this.logger.error(err); rej(err); } else { res(); @@ -90,7 +90,7 @@ export class ExportCustomEmojisProcessorService { await this.downloadService.downloadUrl(emoji.originalUrl, emojiPath); downloaded = true; } catch (e) { // TODO: 何度か再試行 - this.#logger.error(e instanceof Error ? e : new Error(e as string)); + this.logger.error(e instanceof Error ? e : new Error(e as string)); } if (!downloaded) { @@ -118,12 +118,12 @@ export class ExportCustomEmojisProcessorService { zlib: { level: 0 }, }); archiveStream.on('close', async () => { - this.#logger.succ(`Exported to: ${archivePath}`); + 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}`); + this.logger.succ(`Exported to: ${driveFile.id}`); cleanup(); archiveCleanup(); done(); diff --git a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts index 9946015ff7..4c6162432a 100644 --- a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts @@ -16,7 +16,7 @@ import type { DbUserJobData } from '../types.js'; @Injectable() export class ExportFollowingProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -35,11 +35,11 @@ export class ExportFollowingProcessorService { private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { - this.#logger = this.queueLoggerService.logger.createSubLogger('export-following'); + this.logger = 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} ...`); + this.logger.info(`Exporting following of ${job.data.user.id} ...`); const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); if (user == null) { @@ -50,7 +50,7 @@ export class ExportFollowingProcessorService { // Create temp file const [path, cleanup] = await createTemp(); - this.#logger.info(`Temp file is ${path}`); + this.logger.info(`Temp file is ${path}`); try { const stream = fs.createWriteStream(path, { flags: 'a' }); @@ -94,7 +94,7 @@ export class ExportFollowingProcessorService { await new Promise((res, rej) => { stream.write(content + '\n', err => { if (err) { - this.#logger.error(err); + this.logger.error(err); rej(err); } else { res(); @@ -105,12 +105,12 @@ export class ExportFollowingProcessorService { } stream.end(); - this.#logger.succ(`Exported to: ${path}`); + 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}`); + this.logger.succ(`Exported to: ${driveFile.id}`); } finally { cleanup(); } diff --git a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts index a34cea0f41..7781d2787f 100644 --- a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts @@ -15,7 +15,7 @@ import type { DbUserJobData } from '../types.js'; @Injectable() export class ExportMutingProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -34,11 +34,11 @@ export class ExportMutingProcessorService { private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { - this.#logger = this.queueLoggerService.logger.createSubLogger('export-muting'); + this.logger = 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} ...`); + this.logger.info(`Exporting muting of ${job.data.user.id} ...`); const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); if (user == null) { @@ -49,7 +49,7 @@ export class ExportMutingProcessorService { // Create temp file const [path, cleanup] = await createTemp(); - this.#logger.info(`Temp file is ${path}`); + this.logger.info(`Temp file is ${path}`); try { const stream = fs.createWriteStream(path, { flags: 'a' }); @@ -87,7 +87,7 @@ export class ExportMutingProcessorService { await new Promise((res, rej) => { stream.write(content + '\n', err => { if (err) { - this.#logger.error(err); + this.logger.error(err); rej(err); } else { res(); @@ -105,12 +105,12 @@ export class ExportMutingProcessorService { } stream.end(); - this.#logger.succ(`Exported to: ${path}`); + 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}`); + this.logger.succ(`Exported to: ${driveFile.id}`); } finally { cleanup(); } diff --git a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts index 24fcc1a8ad..62b3a53c49 100644 --- a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts @@ -16,7 +16,7 @@ import type { DbUserJobData } from '../types.js'; @Injectable() export class ExportNotesProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -34,11 +34,11 @@ export class ExportNotesProcessorService { private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { - this.#logger = this.queueLoggerService.logger.createSubLogger('export-notes'); + this.logger = 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} ...`); + this.logger.info(`Exporting notes of ${job.data.user.id} ...`); const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); if (user == null) { @@ -49,7 +49,7 @@ export class ExportNotesProcessorService { // Create temp file const [path, cleanup] = await createTemp(); - this.#logger.info(`Temp file is ${path}`); + this.logger.info(`Temp file is ${path}`); try { const stream = fs.createWriteStream(path, { flags: 'a' }); @@ -58,7 +58,7 @@ export class ExportNotesProcessorService { return new Promise((res, rej) => { stream.write(text, err => { if (err) { - this.#logger.error(err); + this.logger.error(err); rej(err); } else { res(); @@ -112,12 +112,12 @@ export class ExportNotesProcessorService { await write(']'); stream.end(); - this.#logger.succ(`Exported to: ${path}`); + 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}`); + this.logger.succ(`Exported to: ${driveFile.id}`); } finally { cleanup(); } diff --git a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts index a02e9bdee4..097835ac81 100644 --- a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts @@ -15,7 +15,7 @@ import type { DbUserJobData } from '../types.js'; @Injectable() export class ExportUserListsProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -34,11 +34,11 @@ export class ExportUserListsProcessorService { private driveService: DriveService, private queueLoggerService: QueueLoggerService, ) { - this.#logger = 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 { - this.#logger.info(`Exporting user lists of ${job.data.user.id} ...`); + 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) { @@ -53,7 +53,7 @@ export class ExportUserListsProcessorService { // Create temp file const [path, cleanup] = await createTemp(); - this.#logger.info(`Temp file is ${path}`); + this.logger.info(`Temp file is ${path}`); try { const stream = fs.createWriteStream(path, { flags: 'a' }); @@ -70,7 +70,7 @@ export class ExportUserListsProcessorService { await new Promise((res, rej) => { stream.write(content + '\n', err => { if (err) { - this.#logger.error(err); + this.logger.error(err); rej(err); } else { res(); @@ -81,12 +81,12 @@ export class ExportUserListsProcessorService { } stream.end(); - this.#logger.succ(`Exported to: ${path}`); + 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}`); + this.logger.succ(`Exported to: ${driveFile.id}`); } finally { cleanup(); } diff --git a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts index abae196299..44c8800a68 100644 --- a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts @@ -15,7 +15,7 @@ import type { DbUserImportJobData } from '../types.js'; @Injectable() export class ImportBlockingProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -36,11 +36,11 @@ export class ImportBlockingProcessorService { private downloadService: DownloadService, private queueLoggerService: QueueLoggerService, ) { - this.#logger = this.queueLoggerService.logger.createSubLogger('import-blocking'); + this.logger = 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} ...`); + this.logger.info(`Importing blocking of ${job.data.user.id} ...`); const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); if (user == null) { @@ -88,15 +88,15 @@ export class ImportBlockingProcessorService { // skip myself if (target.id === job.data.user.id) continue; - this.#logger.info(`Block[${linenum}] ${target.id} ...`); + 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.warn(`Error in line:${linenum} ${e}`); } } - this.#logger.succ('Imported'); + this.logger.succ('Imported'); done(); } } diff --git a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts index 6f86589aec..4919fb2f7b 100644 --- a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts @@ -17,7 +17,7 @@ import type { DbUserImportJobData } from '../types.js'; // TODO: 名前衝突時の動作を選べるようにする @Injectable() export class ImportCustomEmojisProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -40,11 +40,11 @@ export class ImportCustomEmojisProcessorService { private downloadService: DownloadService, private queueLoggerService: QueueLoggerService, ) { - this.#logger = 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 { - this.#logger.info('Importing custom emojis ...'); + this.logger.info('Importing custom emojis ...'); const file = await this.driveFilesRepository.findOneBy({ id: job.data.fileId, @@ -56,7 +56,7 @@ export class ImportCustomEmojisProcessorService { const [path, cleanup] = await createTempDir(); - this.#logger.info(`Temp dir is ${path}`); + this.logger.info(`Temp dir is ${path}`); const destPath = path + '/emojis.zip'; @@ -65,7 +65,7 @@ export class ImportCustomEmojisProcessorService { await this.downloadService.downloadUrl(file.url, destPath); } catch (e) { // TODO: 何度か再試行 if (e instanceof Error || typeof e === 'string') { - this.#logger.error(e); + this.logger.error(e); } throw e; } @@ -101,10 +101,10 @@ export class ImportCustomEmojisProcessorService { cleanup(); - this.#logger.succ('Imported'); + this.logger.succ('Imported'); done(); }); unzipStream.pipe(extractor); - this.#logger.succ(`Unzipping to ${outputPath}`); + this.logger.succ(`Unzipping to ${outputPath}`); } } diff --git a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts index 087e0baf96..5e49678d05 100644 --- a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts @@ -15,7 +15,7 @@ import type { DbUserImportJobData } from '../types.js'; @Injectable() export class ImportFollowingProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -33,11 +33,11 @@ export class ImportFollowingProcessorService { private downloadService: DownloadService, private queueLoggerService: QueueLoggerService, ) { - this.#logger = this.queueLoggerService.logger.createSubLogger('import-following'); + this.logger = 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} ...`); + this.logger.info(`Importing following of ${job.data.user.id} ...`); const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); if (user == null) { @@ -85,15 +85,15 @@ export class ImportFollowingProcessorService { // skip myself if (target.id === job.data.user.id) continue; - this.#logger.info(`Follow[${linenum}] ${target.id} ...`); + this.logger.info(`Follow[${linenum}] ${target.id} ...`); this.userFollowingService.follow(user, target); } catch (e) { - this.#logger.warn(`Error in line:${linenum} ${e}`); + this.logger.warn(`Error in line:${linenum} ${e}`); } } - this.#logger.succ('Imported'); + this.logger.succ('Imported'); done(); } } diff --git a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts index 404091e8ca..c613c7e74e 100644 --- a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts @@ -16,7 +16,7 @@ import type { DbUserImportJobData } from '../types.js'; @Injectable() export class ImportMutingProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -34,11 +34,11 @@ export class ImportMutingProcessorService { private downloadService: DownloadService, private queueLoggerService: QueueLoggerService, ) { - this.#logger = this.queueLoggerService.logger.createSubLogger('import-muting'); + this.logger = 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} ...`); + this.logger.info(`Importing muting of ${job.data.user.id} ...`); const user = await this.usersRepository.findOneBy({ id: job.data.user.id }); if (user == null) { @@ -86,15 +86,15 @@ export class ImportMutingProcessorService { // skip myself if (target.id === job.data.user.id) continue; - this.#logger.info(`Mute[${linenum}] ${target.id} ...`); + 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.warn(`Error in line:${linenum} ${e}`); } } - this.#logger.succ('Imported'); + this.logger.succ('Imported'); done(); } } diff --git a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts index aed1a4cde5..96c862e5c9 100644 --- a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts @@ -16,7 +16,7 @@ import type { DbUserImportJobData } from '../types.js'; @Injectable() export class ImportUserListsProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -41,11 +41,11 @@ export class ImportUserListsProcessorService { private downloadService: DownloadService, private queueLoggerService: QueueLoggerService, ) { - this.#logger = 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 { - this.#logger.info(`Importing user lists of ${job.data.user.id} ...`); + 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) { @@ -102,11 +102,11 @@ export class ImportUserListsProcessorService { this.userListService.push(target, list!); } catch (e) { - this.#logger.warn(`Error in line:${linenum} ${e}`); + this.logger.warn(`Error in line:${linenum} ${e}`); } } - this.#logger.succ('Imported'); + this.logger.succ('Imported'); done(); } } diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index 5733f5d0a9..4593b4fb61 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -31,7 +31,7 @@ import type { DeliverJobData, InboxJobData } from '../types.js'; // ユーザーのinboxにアクティビティが届いた時の処理 @Injectable() export class InboxProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -57,7 +57,7 @@ export class InboxProcessorService { private federationChart: FederationChart, private queueLoggerService: QueueLoggerService, ) { - this.#logger = this.queueLoggerService.logger.createSubLogger('inbox'); + this.logger = this.queueLoggerService.logger.createSubLogger('inbox'); } public async process(job: Bull.Job): Promise { @@ -67,7 +67,7 @@ export class InboxProcessorService { //#region Log const info = Object.assign({}, activity) as any; delete info['@context']; - this.#logger.debug(JSON.stringify(info, null, 2)); + this.logger.debug(JSON.stringify(info, null, 2)); //#endregion const host = this.utilityService.toPuny(new URL(signature.keyId).hostname); diff --git a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts index f976232a24..75d02d527a 100644 --- a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts @@ -20,7 +20,7 @@ import type Bull from 'bull'; @Injectable() export class ResyncChartsProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -41,11 +41,11 @@ export class ResyncChartsProcessorService { private queueLoggerService: QueueLoggerService, ) { - this.#logger = this.queueLoggerService.logger.createSubLogger('resync-charts'); + this.logger = this.queueLoggerService.logger.createSubLogger('resync-charts'); } public async process(job: Bull.Job>, done: () => void): Promise { - this.#logger.info('Resync charts...'); + this.logger.info('Resync charts...'); // TODO: ユーザーごとのチャートも更新する // TODO: インスタンスごとのチャートも更新する @@ -55,7 +55,7 @@ export class ResyncChartsProcessorService { this.usersChart.resync(), ]); - this.#logger.succ('All charts successfully resynced.'); + 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 index d1ca3c4576..e16956df0c 100644 --- a/packages/backend/src/queue/processors/TickChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/TickChartsProcessorService.ts @@ -20,7 +20,7 @@ import type Bull from 'bull'; @Injectable() export class TickChartsProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -41,11 +41,11 @@ export class TickChartsProcessorService { private queueLoggerService: QueueLoggerService, ) { - this.#logger = this.queueLoggerService.logger.createSubLogger('tick-charts'); + this.logger = this.queueLoggerService.logger.createSubLogger('tick-charts'); } public async process(job: Bull.Job>, done: () => void): Promise { - this.#logger.info('Tick charts...'); + this.logger.info('Tick charts...'); await Promise.all([ this.federationChart.tick(false), @@ -62,7 +62,7 @@ export class TickChartsProcessorService { this.apRequestChart.tick(false), ]); - this.#logger.succ('All charts successfully ticked.'); + 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 index 5723fe2eeb..27243be51b 100644 --- a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts @@ -12,7 +12,7 @@ import type { WebhookDeliverJobData } from '../types.js'; @Injectable() export class WebhookDeliverProcessorService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -24,12 +24,12 @@ export class WebhookDeliverProcessorService { private httpRequestService: HttpRequestService, private queueLoggerService: QueueLoggerService, ) { - this.#logger = this.queueLoggerService.logger.createSubLogger('webhook'); + this.logger = this.queueLoggerService.logger.createSubLogger('webhook'); } public async process(job: Bull.Job): Promise { try { - this.#logger.debug(`delivering ${job.data.webhookId}`); + this.logger.debug(`delivering ${job.data.webhookId}`); const res = await this.httpRequestService.getResponse({ url: job.data.to, diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 281da5175d..21ecc7177a 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -58,7 +58,7 @@ export class ActivityPubServerService { ) { } - #setResponseType(ctx: Router.RouterContext) { + private setResponseType(ctx: Router.RouterContext) { const accept = ctx.accepts(ACTIVITY_JSON, LD_JSON); if (accept === LD_JSON) { ctx.response.type = LD_JSON; @@ -71,7 +71,7 @@ export class ActivityPubServerService { * Pack Create or Announce Activity * @param note Note */ - async #packActivity(note: Note): Promise { + private 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); @@ -80,7 +80,7 @@ export class ActivityPubServerService { return this.apRendererService.renderCreate(await this.apRendererService.renderNote(note, false), note); } - #inbox(ctx: Router.RouterContext) { + private inbox(ctx: Router.RouterContext) { let signature; try { @@ -95,7 +95,7 @@ export class ActivityPubServerService { ctx.status = 202; } - async #followers(ctx: Router.RouterContext) { + private async followers(ctx: Router.RouterContext) { const userId = ctx.params.user; const cursor = ctx.request.query.cursor; @@ -169,17 +169,17 @@ export class ActivityPubServerService { ); ctx.body = this.apRendererService.renderActivity(rendered); - this.#setResponseType(ctx); + 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); + this.setResponseType(ctx); } } - async #following(ctx: Router.RouterContext) { + private async following(ctx: Router.RouterContext) { const userId = ctx.params.user; const cursor = ctx.request.query.cursor; @@ -253,17 +253,17 @@ export class ActivityPubServerService { ); ctx.body = this.apRendererService.renderActivity(rendered); - this.#setResponseType(ctx); + 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); + this.setResponseType(ctx); } } - async #featured(ctx: Router.RouterContext) { + private async featured(ctx: Router.RouterContext) { const userId = ctx.params.user; const user = await this.usersRepository.findOneBy({ @@ -293,10 +293,10 @@ export class ActivityPubServerService { ctx.body = this.apRendererService.renderActivity(rendered); ctx.set('Cache-Control', 'public, max-age=180'); - this.#setResponseType(ctx); + this.setResponseType(ctx); } - async #outbox(ctx: Router.RouterContext) { + private async outbox(ctx: Router.RouterContext) { const userId = ctx.params.user; const sinceId = ctx.request.query.since_id; @@ -344,7 +344,7 @@ export class ActivityPubServerService { if (sinceId) notes.reverse(); - const activities = await Promise.all(notes.map(note => this.#packActivity(note))); + const activities = await Promise.all(notes.map(note => this.packActivity(note))); const rendered = this.apRendererService.renderOrderedCollectionPage( `${partOf}?${url.query({ page: 'true', @@ -363,7 +363,7 @@ export class ActivityPubServerService { ); ctx.body = this.apRendererService.renderActivity(rendered); - this.#setResponseType(ctx); + this.setResponseType(ctx); } else { // index page const rendered = this.apRendererService.renderOrderedCollection(partOf, user.notesCount, @@ -372,11 +372,11 @@ export class ActivityPubServerService { ); ctx.body = this.apRendererService.renderActivity(rendered); ctx.set('Cache-Control', 'public, max-age=180'); - this.#setResponseType(ctx); + this.setResponseType(ctx); } } - async #userInfo(ctx: Router.RouterContext, user: User | null) { + private async userInfo(ctx: Router.RouterContext, user: User | null) { if (user == null) { ctx.status = 404; return; @@ -384,7 +384,7 @@ export class ActivityPubServerService { ctx.body = this.apRendererService.renderActivity(await this.apRendererService.renderPerson(user as ILocalUser)); ctx.set('Cache-Control', 'public, max-age=180'); - this.#setResponseType(ctx); + this.setResponseType(ctx); } public createRouter() { @@ -399,8 +399,8 @@ export class ActivityPubServerService { } // inbox - router.post('/inbox', json(), ctx => this.#inbox(ctx)); - router.post('/users/:user/inbox', json(), ctx => this.#inbox(ctx)); + 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) => { @@ -429,7 +429,7 @@ export class ActivityPubServerService { ctx.body = this.apRendererService.renderActivity(await this.apRendererService.renderNote(note, false)); ctx.set('Cache-Control', 'public, max-age=180'); - this.#setResponseType(ctx); + this.setResponseType(ctx); }); // note activity @@ -446,22 +446,22 @@ export class ActivityPubServerService { return; } - ctx.body = this.apRendererService.renderActivity(await this.#packActivity(note)); + ctx.body = this.apRendererService.renderActivity(await this.packActivity(note)); ctx.set('Cache-Control', 'public, max-age=180'); - this.#setResponseType(ctx); + this.setResponseType(ctx); }); // outbox - router.get('/users/:user/outbox', (ctx) => this.#outbox(ctx)); + router.get('/users/:user/outbox', (ctx) => this.outbox(ctx)); // followers - router.get('/users/:user/followers', (ctx) => this.#followers(ctx)); + router.get('/users/:user/followers', (ctx) => this.followers(ctx)); // following - router.get('/users/:user/following', (ctx) => this.#following(ctx)); + router.get('/users/:user/following', (ctx) => this.following(ctx)); // featured - router.get('/users/:user/collections/featured', (ctx) => this.#featured(ctx)); + router.get('/users/:user/collections/featured', (ctx) => this.featured(ctx)); // publickey router.get('/users/:user/publickey', async ctx => { @@ -482,7 +482,7 @@ export class ActivityPubServerService { 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); + this.setResponseType(ctx); } else { ctx.status = 400; } @@ -499,7 +499,7 @@ export class ActivityPubServerService { isSuspended: false, }); - await this.#userInfo(ctx, user); + await this.userInfo(ctx, user); }); router.get('/@:user', async (ctx, next) => { @@ -511,7 +511,7 @@ export class ActivityPubServerService { isSuspended: false, }); - await this.#userInfo(ctx, user); + await this.userInfo(ctx, user); }); //#endregion @@ -529,7 +529,7 @@ export class ActivityPubServerService { ctx.body = this.apRendererService.renderActivity(await this.apRendererService.renderEmoji(emoji)); ctx.set('Cache-Control', 'public, max-age=180'); - this.#setResponseType(ctx); + this.setResponseType(ctx); }); // like @@ -550,7 +550,7 @@ export class ActivityPubServerService { ctx.body = this.apRendererService.renderActivity(await this.apRendererService.renderLike(reaction, note)); ctx.set('Cache-Control', 'public, max-age=180'); - this.#setResponseType(ctx); + this.setResponseType(ctx); }); // follow @@ -576,7 +576,7 @@ export class ActivityPubServerService { ctx.body = this.apRendererService.renderActivity(this.apRendererService.renderFollow(follower, followee)); ctx.set('Cache-Control', 'public, max-age=180'); - this.#setResponseType(ctx); + this.setResponseType(ctx); }); return router; diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index e2b0ea5afe..becf0592d7 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -29,7 +29,7 @@ const assets = `${_dirname}/../../server/file/assets/`; @Injectable() export class FileServerService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -45,12 +45,12 @@ export class FileServerService { private internalStorageService: InternalStorageService, private loggerService: LoggerService, ) { - this.#logger = this.loggerService.getLogger('server', 'gray', false); + this.logger = this.loggerService.getLogger('server', 'gray', false); } public commonReadableHandlerGenerator(ctx: Koa.Context) { return (e: Error): void => { - this.#logger.error(e); + this.logger.error(e); ctx.status = 500; ctx.set('Cache-Control', 'max-age=300'); }; @@ -74,8 +74,8 @@ export class FileServerService { ctx.set('Cache-Control', 'max-age=31536000, immutable'); }); - router.get('/:key', ctx => this.#sendDriveFile(ctx)); - router.get('/:key/(.*)', ctx => this.#sendDriveFile(ctx)); + router.get('/:key', ctx => this.sendDriveFile(ctx)); + router.get('/:key/(.*)', ctx => this.sendDriveFile(ctx)); // Register router app.use(router.routes()); @@ -83,7 +83,7 @@ export class FileServerService { return app; } - async #sendDriveFile(ctx: Koa.Context) { + private async sendDriveFile(ctx: Koa.Context) { const key = ctx.params.key; // Fetch drive file @@ -139,7 +139,7 @@ export class FileServerService { 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) { - this.#logger.error(`${err}`); + this.logger.error(`${err}`); if (err instanceof StatusError && err.isClientError) { ctx.status = err.statusCode; diff --git a/packages/backend/src/server/MediaProxyServerService.ts b/packages/backend/src/server/MediaProxyServerService.ts index 5344d3a9f4..1e0385602c 100644 --- a/packages/backend/src/server/MediaProxyServerService.ts +++ b/packages/backend/src/server/MediaProxyServerService.ts @@ -19,7 +19,7 @@ import { LoggerService } from '@/core/LoggerService.js'; @Injectable() export class MediaProxyServerService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -30,7 +30,7 @@ export class MediaProxyServerService { private imageProcessingService: ImageProcessingService, private loggerService: LoggerService, ) { - this.#logger = this.loggerService.getLogger('server', 'gray', false); + this.logger = this.loggerService.getLogger('server', 'gray', false); } public createServer() { @@ -44,7 +44,7 @@ export class MediaProxyServerService { // Init router const router = new Router(); - router.get('/:url*', ctx => this.#handler(ctx)); + router.get('/:url*', ctx => this.handler(ctx)); // Register router app.use(router.routes()); @@ -52,7 +52,7 @@ export class MediaProxyServerService { return app; } - async #handler(ctx: Koa.Context) { + private async handler(ctx: Koa.Context) { const url = 'url' in ctx.query ? ctx.query.url : 'https://' + ctx.params.url; if (typeof url !== 'string') { @@ -126,7 +126,7 @@ export class MediaProxyServerService { ctx.set('Cache-Control', 'max-age=31536000, immutable'); ctx.body = image.data; } catch (err) { - this.#logger.error(`${err}`); + this.logger.error(`${err}`); if (err instanceof StatusError && (err.statusCode === 302 || err.isClientError)) { ctx.status = err.statusCode; diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 8349e1e9f5..14d5ed45ab 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -30,7 +30,7 @@ import { ClientServerService } from './web/ClientServerService.js'; @Injectable() export class ServerService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -54,7 +54,7 @@ export class ServerService { private globalEventService: GlobalEventService, private loggerService: LoggerService, ) { - this.#logger = this.loggerService.getLogger('server', 'gray', false); + this.logger = this.loggerService.getLogger('server', 'gray', false); } public launch() { @@ -65,7 +65,7 @@ export class ServerService { if (!['production', 'test'].includes(process.env.NODE_ENV ?? '')) { // Logger koa.use(koaLogger(str => { - this.#logger.info(str); + this.logger.info(str); })); // Delay @@ -157,13 +157,13 @@ export class ServerService { server.on('error', err => { switch ((err as any).code) { case 'EACCES': - this.#logger.error(`You do not have permission to listen on port ${this.config.port}.`); + this.logger.error(`You do not have permission to listen on port ${this.config.port}.`); break; case 'EADDRINUSE': - this.#logger.error(`Port ${this.config.port} is already in use by another process.`); + this.logger.error(`Port ${this.config.port} is already in use by another process.`); break; default: - this.#logger.error(err); + this.logger.error(err); break; } diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index f2ead3d4a1..d13b8d5ced 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -23,9 +23,9 @@ const accessDenied = { @Injectable() export class ApiCallService implements OnApplicationShutdown { - #logger: Logger; - #userIpHistories: Map>; - #userIpHistoriesClearIntervalId: NodeJS.Timer; + private logger: Logger; + private userIpHistories: Map>; + private userIpHistoriesClearIntervalId: NodeJS.Timer; constructor( @Inject(DI.userIpsRepository) @@ -36,11 +36,11 @@ export class ApiCallService implements OnApplicationShutdown { private rateLimiterService: RateLimiterService, private apiLoggerService: ApiLoggerService, ) { - this.#logger = this.apiLoggerService.logger; - this.#userIpHistories = new Map>(); + this.logger = this.apiLoggerService.logger; + this.userIpHistories = new Map>(); - this.#userIpHistoriesClearIntervalId = setInterval(() => { - this.#userIpHistories.clear(); + this.userIpHistoriesClearIntervalId = setInterval(() => { + this.userIpHistories.clear(); }, 1000 * 60 * 60); } @@ -76,7 +76,7 @@ export class ApiCallService implements OnApplicationShutdown { // Authentication this.authenticateService.authenticate(body['i']).then(([user, app]) => { // API invoking - this.#call(endpoint, exec, user, app, body, ctx).then((res: any) => { + 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}`); } @@ -90,10 +90,10 @@ export class ApiCallService implements OnApplicationShutdown { this.metaService.fetch().then(meta => { if (!meta.enableIpLogging) return; const ip = ctx.ip; - const ips = this.#userIpHistories.get(user.id); + const ips = this.userIpHistories.get(user.id); if (ips == null || !ips.has(ip)) { if (ips == null) { - this.#userIpHistories.set(user.id, new Set([ip])); + this.userIpHistories.set(user.id, new Set([ip])); } else { ips.add(ip); } @@ -123,7 +123,7 @@ export class ApiCallService implements OnApplicationShutdown { }); } - async #call( + private async call( ep: IEndpoint, exec: any, user: CacheableLocalUser | null | undefined, @@ -225,7 +225,7 @@ export class ApiCallService implements OnApplicationShutdown { if (err instanceof ApiError) { throw err; } else { - this.#logger.error(`Internal error occurred in ${ep.name}: ${err.message}`, { + this.logger.error(`Internal error occurred in ${ep.name}: ${err.message}`, { ep: ep.name, ps: data, e: { @@ -247,12 +247,12 @@ export class ApiCallService implements OnApplicationShutdown { const after = performance.now(); const time = after - before; if (time > 1000) { - this.#logger.warn(`SLOW API CALL DETECTED: ${ep.name} (${time}ms)`); + this.logger.warn(`SLOW API CALL DETECTED: ${ep.name} (${time}ms)`); } }); } public onApplicationShutdown(signal?: string | undefined) { - clearInterval(this.#userIpHistoriesClearIntervalId); + clearInterval(this.userIpHistoriesClearIntervalId); } } diff --git a/packages/backend/src/server/api/AuthenticateService.ts b/packages/backend/src/server/api/AuthenticateService.ts index b8bd09509a..29d6ba78f0 100644 --- a/packages/backend/src/server/api/AuthenticateService.ts +++ b/packages/backend/src/server/api/AuthenticateService.ts @@ -17,7 +17,7 @@ export class AuthenticationError extends Error { @Injectable() export class AuthenticateService { - #appCache: Cache; + private appCache: Cache; constructor( @Inject(DI.usersRepository) @@ -31,7 +31,7 @@ export class AuthenticateService { private userCacheService: UserCacheService, ) { - this.#appCache = new Cache(Infinity); + this.appCache = new Cache(Infinity); } public async authenticate(token: string | null): Promise<[CacheableLocalUser | null | undefined, AccessToken | null | undefined]> { @@ -71,7 +71,7 @@ export class AuthenticateService { }) as Promise); if (accessToken.appId) { - const app = await this.#appCache.fetch(accessToken.appId, + const app = await this.appCache.fetch(accessToken.appId, () => this.appsRepository.findOneByOrFail({ id: accessToken.appId! })); return [user, { diff --git a/packages/backend/src/server/api/RateLimiterService.ts b/packages/backend/src/server/api/RateLimiterService.ts index 61b7b1ff47..35f28bfd63 100644 --- a/packages/backend/src/server/api/RateLimiterService.ts +++ b/packages/backend/src/server/api/RateLimiterService.ts @@ -8,7 +8,7 @@ import type { IEndpointMeta } from './endpoints.js'; @Injectable() export class RateLimiterService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.redis) @@ -16,7 +16,7 @@ export class RateLimiterService { private loggerService: LoggerService, ) { - this.#logger = this.loggerService.getLogger('limiter'); + this.logger = this.loggerService.getLogger('limiter'); } public limit(limitation: IEndpointMeta['limit'] & { key: NonNullable }, actor: string) { @@ -37,7 +37,7 @@ export class RateLimiterService { return reject('ERR'); } - this.#logger.debug(`${actor} ${limitation.key} min remaining: ${info.remaining}`); + this.logger.debug(`${actor} ${limitation.key} min remaining: ${info.remaining}`); if (info.remaining === 0) { reject('BRIEF_REQUEST_INTERVAL'); @@ -65,7 +65,7 @@ export class RateLimiterService { return reject('ERR'); } - this.#logger.debug(`${actor} ${limitation.key} max remaining: ${info.remaining}`); + this.logger.debug(`${actor} ${limitation.key} max remaining: ${info.remaining}`); if (info.remaining === 0) { reject('RATE_LIMIT_EXCEEDED'); 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 fa057dadb6..d9266aac6c 100644 --- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts @@ -71,13 +71,13 @@ export default class extends Endpoint { (async () => { await this.userSuspendService.doPostSuspend(user).catch(e => {}); - await this.#unFollowAll(user).catch(e => {}); - await this.#readAllNotify(user).catch(e => {}); + await this.unFollowAll(user).catch(e => {}); + await this.readAllNotify(user).catch(e => {}); })(); }); } - async #unFollowAll(follower: User) { + private async unFollowAll(follower: User) { const followings = await this.followingsRepository.findBy({ followerId: follower.id, }); @@ -95,7 +95,7 @@ export default class extends Endpoint { } } - async #readAllNotify(notifier: User) { + private async readAllNotify(notifier: User) { await this.notificationsRepository.update({ notifierId: notifier.id, isRead: false, diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index eaf93ee977..e291b5908a 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -100,7 +100,7 @@ export default class extends Endpoint { private apNoteService: ApNoteService, ) { super(meta, paramDef, async (ps, me) => { - const object = await this.#fetchAny(ps.uri, me); + const object = await this.fetchAny(ps.uri, me); if (object) { return object; } else { @@ -112,12 +112,12 @@ export default class extends Endpoint { /*** * URIからUserかNoteを解決する */ - async #fetchAny(uri: string, me: CacheableLocalUser | null | undefined): Promise | null> { + private async fetchAny(uri: string, me: CacheableLocalUser | null | undefined): Promise | null> { // ブロックしてたら中断 const fetchedMeta = await this.metaService.fetch(); if (fetchedMeta.blockedHosts.includes(this.utilityService.extractDbHost(uri))) return null; - let local = await this.#mergePack(me, ...await Promise.all([ + let local = await this.mergePack(me, ...await Promise.all([ this.apDbResolverService.getUserFromApId(uri), this.apDbResolverService.getNoteFromApId(uri), ])); @@ -130,21 +130,21 @@ export default class extends Endpoint { // /@user のような正規id以外で取得できるURIが指定されていた場合、ここで初めて正規URIが確定する // これはDBに存在する可能性があるため再度DB検索 if (uri !== object.id) { - local = await this.#mergePack(me, ...await Promise.all([ + local = await this.mergePack(me, ...await Promise.all([ this.apDbResolverService.getUserFromApId(object.id), this.apDbResolverService.getNoteFromApId(object.id), ])); if (local != null) return local; } - return await this.#mergePack( + 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> { + private async mergePack(me: CacheableLocalUser | null | undefined, user: User | null | undefined, note: Note | null | undefined): Promise | null> { if (user != null) { return { type: 'User', diff --git a/packages/backend/src/server/api/integration/DiscordServerService.ts b/packages/backend/src/server/api/integration/DiscordServerService.ts index 604013c454..ea044c27d5 100644 --- a/packages/backend/src/server/api/integration/DiscordServerService.ts +++ b/packages/backend/src/server/api/integration/DiscordServerService.ts @@ -42,12 +42,12 @@ export class DiscordServerService { const router = new Router(); router.get('/disconnect/discord', async ctx => { - if (!this.#compareOrigin(ctx)) { + if (!this.compareOrigin(ctx)) { ctx.throw(400, 'invalid origin'); return; } - const userToken = this.#getUserToken(ctx); + const userToken = this.getUserToken(ctx); if (!userToken) { ctx.throw(400, 'signin required'); return; @@ -91,12 +91,12 @@ export class DiscordServerService { }; router.get('/connect/discord', async ctx => { - if (!this.#compareOrigin(ctx)) { + if (!this.compareOrigin(ctx)) { ctx.throw(400, 'invalid origin'); return; } - const userToken = this.#getUserToken(ctx); + const userToken = this.getUserToken(ctx); if (!userToken) { ctx.throw(400, 'signin required'); return; @@ -138,7 +138,7 @@ export class DiscordServerService { }); router.get('/dc/cb', async ctx => { - const userToken = this.#getUserToken(ctx); + const userToken = this.getUserToken(ctx); const oauth2 = await getOAuth2(); @@ -299,11 +299,11 @@ export class DiscordServerService { return router; } - #getUserToken(ctx: Koa.BaseContext): string | null { + private getUserToken(ctx: Koa.BaseContext): string | null { return ((ctx.headers['cookie'] ?? '').match(/igi=(\w+)/) ?? [null, null])[1]; } - #compareOrigin(ctx: Koa.BaseContext): boolean { + private compareOrigin(ctx: Koa.BaseContext): boolean { function normalizeUrl(url?: string): string { return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; } diff --git a/packages/backend/src/server/api/integration/GithubServerService.ts b/packages/backend/src/server/api/integration/GithubServerService.ts index 6864f21523..58b170d0e4 100644 --- a/packages/backend/src/server/api/integration/GithubServerService.ts +++ b/packages/backend/src/server/api/integration/GithubServerService.ts @@ -42,12 +42,12 @@ export class GithubServerService { const router = new Router(); router.get('/disconnect/github', async ctx => { - if (!this.#compareOrigin(ctx)) { + if (!this.compareOrigin(ctx)) { ctx.throw(400, 'invalid origin'); return; } - const userToken = this.#getUserToken(ctx); + const userToken = this.getUserToken(ctx); if (!userToken) { ctx.throw(400, 'signin required'); return; @@ -91,12 +91,12 @@ export class GithubServerService { }; router.get('/connect/github', async ctx => { - if (!this.#compareOrigin(ctx)) { + if (!this.compareOrigin(ctx)) { ctx.throw(400, 'invalid origin'); return; } - const userToken = this.#getUserToken(ctx); + const userToken = this.getUserToken(ctx); if (!userToken) { ctx.throw(400, 'signin required'); return; @@ -136,7 +136,7 @@ export class GithubServerService { }); router.get('/gh/cb', async ctx => { - const userToken = this.#getUserToken(ctx); + const userToken = this.getUserToken(ctx); const oauth2 = await getOath2(); @@ -271,11 +271,11 @@ export class GithubServerService { return router; } - #getUserToken(ctx: Koa.BaseContext): string | null { + private getUserToken(ctx: Koa.BaseContext): string | null { return ((ctx.headers['cookie'] ?? '').match(/igi=(\w+)/) ?? [null, null])[1]; } - #compareOrigin(ctx: Koa.BaseContext): boolean { + private compareOrigin(ctx: Koa.BaseContext): boolean { function normalizeUrl(url?: string): string { return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; } diff --git a/packages/backend/src/server/api/integration/TwitterServerService.ts b/packages/backend/src/server/api/integration/TwitterServerService.ts index 14b5f40ea5..a4a67f6c8c 100644 --- a/packages/backend/src/server/api/integration/TwitterServerService.ts +++ b/packages/backend/src/server/api/integration/TwitterServerService.ts @@ -42,12 +42,12 @@ export class TwitterServerService { const router = new Router(); router.get('/disconnect/twitter', async ctx => { - if (!this.#compareOrigin(ctx)) { + if (!this.compareOrigin(ctx)) { ctx.throw(400, 'invalid origin'); return; } - const userToken = this.#getUserToken(ctx); + const userToken = this.getUserToken(ctx); if (userToken == null) { ctx.throw(400, 'signin required'); return; @@ -90,12 +90,12 @@ export class TwitterServerService { }; router.get('/connect/twitter', async ctx => { - if (!this.#compareOrigin(ctx)) { + if (!this.compareOrigin(ctx)) { ctx.throw(400, 'invalid origin'); return; } - const userToken = this.#getUserToken(ctx); + const userToken = this.getUserToken(ctx); if (userToken == null) { ctx.throw(400, 'signin required'); return; @@ -125,7 +125,7 @@ export class TwitterServerService { }); router.get('/tw/cb', async ctx => { - const userToken = this.#getUserToken(ctx); + const userToken = this.getUserToken(ctx); const twAuth = await getTwAuth(); @@ -214,11 +214,11 @@ export class TwitterServerService { return router; } - #getUserToken(ctx: Koa.BaseContext): string | null { + private getUserToken(ctx: Koa.BaseContext): string | null { return ((ctx.headers['cookie'] ?? '').match(/igi=(\w+)/) ?? [null, null])[1]; } - #compareOrigin(ctx: Koa.BaseContext): boolean { + private compareOrigin(ctx: Koa.BaseContext): boolean { function normalizeUrl(url?: string): string { return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; } diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 67a7efaa25..85b31312b3 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -84,7 +84,7 @@ export class ClientServerService { ) { } - async #manifestHandler(ctx: Koa.Context) { + private async manifestHandler(ctx: Koa.Context) { // TODO //const res = structuredClone(manifest); const res = JSON.parse(JSON.stringify(manifest)); @@ -264,7 +264,7 @@ export class ClientServerService { }); // Manifest - router.get('/manifest.json', ctx => this.#manifestHandler(ctx)); + router.get('/manifest.json', ctx => this.manifestHandler(ctx)); router.get('/robots.txt', async ctx => { await send(ctx as any, '/robots.txt', { diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts index 726b158340..1cbb3f36c2 100644 --- a/packages/backend/src/server/web/UrlPreviewService.ts +++ b/packages/backend/src/server/web/UrlPreviewService.ts @@ -12,7 +12,7 @@ import type Koa from 'koa'; @Injectable() export class UrlPreviewService { - #logger: Logger; + private logger: Logger; constructor( @Inject(DI.config) @@ -25,10 +25,10 @@ export class UrlPreviewService { private httpRequestService: HttpRequestService, private loggerService: LoggerService, ) { - this.#logger = this.loggerService.getLogger('url-preview'); + this.logger = this.loggerService.getLogger('url-preview'); } - #wrap(url?: string): string | null { + private wrap(url?: string): string | null { return url != null ? url.match(/^https?:\/\//) ? `${this.config.url}/proxy/preview.webp?${query({ @@ -54,7 +54,7 @@ export class UrlPreviewService { const meta = await this.metaService.fetch(); - this.#logger.info(meta.summalyProxy + this.logger.info(meta.summalyProxy ? `(Proxy) Getting preview of ${url}@${lang} ...` : `Getting preview of ${url}@${lang} ...`); @@ -67,17 +67,17 @@ export class UrlPreviewService { lang: lang ?? 'ja-JP', }); - this.#logger.succ(`Got preview of ${url}: ${summary.title}`); + this.logger.succ(`Got preview of ${url}: ${summary.title}`); - summary.icon = this.#wrap(summary.icon); - summary.thumbnail = this.#wrap(summary.thumbnail); + 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}`); + this.logger.warn(`Failed to get preview of ${url}: ${err}`); ctx.status = 200; ctx.set('Cache-Control', 'max-age=86400, immutable'); ctx.body = '{}'; -- cgit v1.2.3-freya From 01d4d55e78fd977b9d44a68a2504e6091d346b7a Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 21 Sep 2022 05:33:11 +0900 Subject: fix import type --- packages/backend/src/core/AccountUpdateService.ts | 4 ++-- packages/backend/src/core/AiService.ts | 2 +- packages/backend/src/core/AntennaService.ts | 2 +- packages/backend/src/core/CaptchaService.ts | 2 +- packages/backend/src/core/CreateNotificationService.ts | 2 +- packages/backend/src/core/CustomEmojiService.ts | 4 ++-- packages/backend/src/core/DeleteAccountService.ts | 2 +- packages/backend/src/core/DownloadService.ts | 2 +- packages/backend/src/core/DriveService.ts | 4 ++-- packages/backend/src/core/EmailService.ts | 4 ++-- packages/backend/src/core/FederatedInstanceService.ts | 2 +- packages/backend/src/core/FetchInstanceMetadataService.ts | 2 +- packages/backend/src/core/GlobalEventService.ts | 2 +- packages/backend/src/core/HashtagService.ts | 2 +- packages/backend/src/core/HttpRequestService.ts | 2 +- packages/backend/src/core/IdService.ts | 2 +- packages/backend/src/core/ImageProcessingService.ts | 2 +- packages/backend/src/core/InstanceActorService.ts | 2 +- packages/backend/src/core/InternalStorageService.ts | 2 +- packages/backend/src/core/LoggerService.ts | 2 +- packages/backend/src/core/MessagingService.ts | 4 ++-- packages/backend/src/core/MfmService.ts | 2 +- packages/backend/src/core/ModerationLogService.ts | 2 +- packages/backend/src/core/NoteCreateService.ts | 4 ++-- packages/backend/src/core/NoteDeleteService.ts | 4 ++-- packages/backend/src/core/NotePiningService.ts | 4 ++-- packages/backend/src/core/NotificationService.ts | 2 +- packages/backend/src/core/PollService.ts | 2 +- packages/backend/src/core/ProxyAccountService.ts | 2 +- packages/backend/src/core/PushNotificationService.ts | 4 ++-- packages/backend/src/core/QueueService.ts | 4 ++-- packages/backend/src/core/ReactionService.ts | 2 +- packages/backend/src/core/RelayService.ts | 2 +- packages/backend/src/core/S3Service.ts | 2 +- packages/backend/src/core/SignupService.ts | 4 ++-- packages/backend/src/core/TwoFactorAuthenticationService.ts | 4 ++-- packages/backend/src/core/UserCacheService.ts | 2 +- packages/backend/src/core/UserKeypairStoreService.ts | 2 +- packages/backend/src/core/UserListService.ts | 2 +- packages/backend/src/core/UserMutingService.ts | 2 +- packages/backend/src/core/UserSuspendService.ts | 4 ++-- packages/backend/src/core/UtilityService.ts | 2 +- packages/backend/src/core/VideoProcessingService.ts | 2 +- packages/backend/src/core/WebhookService.ts | 2 +- packages/backend/src/core/chart/charts/federation.ts | 2 +- packages/backend/src/core/chart/charts/instance.ts | 2 +- packages/backend/src/core/chart/charts/notes.ts | 2 +- packages/backend/src/core/chart/charts/per-user-drive.ts | 2 +- packages/backend/src/core/chart/charts/per-user-following.ts | 2 +- packages/backend/src/core/chart/charts/per-user-notes.ts | 2 +- packages/backend/src/core/chart/charts/users.ts | 2 +- packages/backend/src/core/entities/AbuseUserReportEntityService.ts | 2 +- packages/backend/src/core/entities/AntennaEntityService.ts | 2 +- packages/backend/src/core/entities/AppEntityService.ts | 2 +- packages/backend/src/core/entities/AuthSessionEntityService.ts | 2 +- packages/backend/src/core/entities/BlockingEntityService.ts | 2 +- packages/backend/src/core/entities/ChannelEntityService.ts | 2 +- packages/backend/src/core/entities/ClipEntityService.ts | 2 +- packages/backend/src/core/entities/DriveFileEntityService.ts | 4 ++-- packages/backend/src/core/entities/DriveFolderEntityService.ts | 2 +- packages/backend/src/core/entities/EmojiEntityService.ts | 2 +- packages/backend/src/core/entities/FollowRequestEntityService.ts | 2 +- packages/backend/src/core/entities/FollowingEntityService.ts | 2 +- packages/backend/src/core/entities/GalleryLikeEntityService.ts | 2 +- packages/backend/src/core/entities/GalleryPostEntityService.ts | 2 +- packages/backend/src/core/entities/HashtagEntityService.ts | 2 +- packages/backend/src/core/entities/InstanceEntityService.ts | 2 +- packages/backend/src/core/entities/MessagingMessageEntityService.ts | 2 +- packages/backend/src/core/entities/ModerationLogEntityService.ts | 2 +- packages/backend/src/core/entities/MutingEntityService.ts | 2 +- packages/backend/src/core/entities/NoteEntityService.ts | 2 +- packages/backend/src/core/entities/NoteFavoriteEntityService.ts | 2 +- packages/backend/src/core/entities/NoteReactionEntityService.ts | 2 +- packages/backend/src/core/entities/NotificationEntityService.ts | 2 +- packages/backend/src/core/entities/PageEntityService.ts | 2 +- packages/backend/src/core/entities/PageLikeEntityService.ts | 2 +- packages/backend/src/core/entities/SigninEntityService.ts | 2 +- packages/backend/src/core/entities/UserEntityService.ts | 4 ++-- packages/backend/src/core/entities/UserGroupEntityService.ts | 2 +- .../backend/src/core/entities/UserGroupInvitationEntityService.ts | 2 +- packages/backend/src/core/entities/UserListEntityService.ts | 2 +- packages/backend/src/core/remote/ResolveUserService.ts | 4 ++-- packages/backend/src/core/remote/WebfingerService.ts | 2 +- packages/backend/src/core/remote/activitypub/ApDbResolverService.ts | 4 ++-- .../backend/src/core/remote/activitypub/ApDeliverManagerService.ts | 4 ++-- packages/backend/src/core/remote/activitypub/ApInboxService.ts | 2 +- packages/backend/src/core/remote/activitypub/ApMfmService.ts | 2 +- packages/backend/src/core/remote/activitypub/ApRendererService.ts | 2 +- packages/backend/src/core/remote/activitypub/ApRequestService.ts | 2 +- packages/backend/src/core/remote/activitypub/ApResolverService.ts | 4 ++-- .../backend/src/core/remote/activitypub/models/ApImageService.ts | 4 ++-- .../backend/src/core/remote/activitypub/models/ApMentionService.ts | 2 +- packages/backend/src/core/remote/activitypub/models/ApNoteService.ts | 4 ++-- .../backend/src/core/remote/activitypub/models/ApPersonService.ts | 4 ++-- .../backend/src/core/remote/activitypub/models/ApQuestionService.ts | 4 ++-- packages/backend/src/daemons/JanitorService.ts | 2 +- packages/backend/src/queue/DbQueueProcessorsService.ts | 2 +- packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts | 2 +- packages/backend/src/queue/QueueProcessorService.ts | 2 +- packages/backend/src/queue/SystemQueueProcessorsService.ts | 2 +- .../src/queue/processors/CheckExpiredMutingsProcessorService.ts | 4 ++-- packages/backend/src/queue/processors/CleanChartsProcessorService.ts | 2 +- packages/backend/src/queue/processors/CleanProcessorService.ts | 4 ++-- .../backend/src/queue/processors/CleanRemoteFilesProcessorService.ts | 4 ++-- .../backend/src/queue/processors/DeleteAccountProcessorService.ts | 4 ++-- .../backend/src/queue/processors/DeleteDriveFilesProcessorService.ts | 4 ++-- packages/backend/src/queue/processors/DeleteFileProcessorService.ts | 2 +- packages/backend/src/queue/processors/DeliverProcessorService.ts | 4 ++-- .../src/queue/processors/EndedPollNotificationProcessorService.ts | 4 ++-- .../backend/src/queue/processors/ExportBlockingProcessorService.ts | 5 ++--- .../src/queue/processors/ExportCustomEmojisProcessorService.ts | 4 ++-- .../backend/src/queue/processors/ExportFollowingProcessorService.ts | 4 ++-- .../backend/src/queue/processors/ExportMutingProcessorService.ts | 4 ++-- packages/backend/src/queue/processors/ExportNotesProcessorService.ts | 4 ++-- .../backend/src/queue/processors/ExportUserListsProcessorService.ts | 4 ++-- .../backend/src/queue/processors/ImportBlockingProcessorService.ts | 4 ++-- .../src/queue/processors/ImportCustomEmojisProcessorService.ts | 4 ++-- .../backend/src/queue/processors/ImportFollowingProcessorService.ts | 4 ++-- .../backend/src/queue/processors/ImportMutingProcessorService.ts | 4 ++-- .../backend/src/queue/processors/ImportUserListsProcessorService.ts | 4 ++-- packages/backend/src/queue/processors/InboxProcessorService.ts | 4 ++-- .../backend/src/queue/processors/ResyncChartsProcessorService.ts | 2 +- packages/backend/src/queue/processors/TickChartsProcessorService.ts | 2 +- .../backend/src/queue/processors/WebhookDeliverProcessorService.ts | 4 ++-- packages/backend/src/server/ActivityPubServerService.ts | 4 ++-- packages/backend/src/server/FileServerService.ts | 4 ++-- packages/backend/src/server/MediaProxyServerService.ts | 2 +- packages/backend/src/server/NodeinfoServerService.ts | 4 ++-- packages/backend/src/server/ServerService.ts | 4 ++-- packages/backend/src/server/WellKnownServerService.ts | 4 ++-- packages/backend/src/server/api/ApiCallService.ts | 2 +- packages/backend/src/server/api/ApiServerService.ts | 4 ++-- packages/backend/src/server/api/AuthenticateService.ts | 2 +- packages/backend/src/server/api/SigninApiService.ts | 4 ++-- packages/backend/src/server/api/SigninService.ts | 4 ++-- packages/backend/src/server/api/SignupApiService.ts | 4 ++-- packages/backend/src/server/api/StreamingApiServerService.ts | 4 ++-- packages/backend/src/server/api/common/GetterService.ts | 2 +- .../backend/src/server/api/endpoints/admin/abuse-user-reports.ts | 2 +- packages/backend/src/server/api/endpoints/admin/accounts/create.ts | 2 +- packages/backend/src/server/api/endpoints/admin/accounts/delete.ts | 2 +- packages/backend/src/server/api/endpoints/admin/ad/create.ts | 2 +- packages/backend/src/server/api/endpoints/admin/ad/delete.ts | 2 +- packages/backend/src/server/api/endpoints/admin/ad/list.ts | 2 +- packages/backend/src/server/api/endpoints/admin/ad/update.ts | 2 +- .../backend/src/server/api/endpoints/admin/announcements/create.ts | 2 +- .../backend/src/server/api/endpoints/admin/announcements/delete.ts | 2 +- .../backend/src/server/api/endpoints/admin/announcements/list.ts | 2 +- .../backend/src/server/api/endpoints/admin/announcements/update.ts | 2 +- packages/backend/src/server/api/endpoints/admin/delete-account.ts | 2 +- .../src/server/api/endpoints/admin/delete-all-files-of-a-user.ts | 2 +- .../src/server/api/endpoints/admin/drive-capacity-override.ts | 2 +- packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts | 2 +- packages/backend/src/server/api/endpoints/admin/drive/files.ts | 2 +- packages/backend/src/server/api/endpoints/admin/drive/show-file.ts | 2 +- .../backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts | 2 +- packages/backend/src/server/api/endpoints/admin/emoji/add.ts | 2 +- packages/backend/src/server/api/endpoints/admin/emoji/copy.ts | 2 +- packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts | 2 +- packages/backend/src/server/api/endpoints/admin/emoji/delete.ts | 2 +- packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts | 2 +- packages/backend/src/server/api/endpoints/admin/emoji/list.ts | 2 +- .../src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts | 2 +- .../backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts | 2 +- .../src/server/api/endpoints/admin/emoji/set-category-bulk.ts | 2 +- packages/backend/src/server/api/endpoints/admin/emoji/update.ts | 2 +- .../src/server/api/endpoints/admin/federation/delete-all-files.ts | 2 +- .../endpoints/admin/federation/refresh-remote-instance-metadata.ts | 2 +- .../server/api/endpoints/admin/federation/remove-all-following.ts | 2 +- .../src/server/api/endpoints/admin/federation/update-instance.ts | 2 +- packages/backend/src/server/api/endpoints/admin/get-user-ips.ts | 2 +- packages/backend/src/server/api/endpoints/admin/invite.ts | 2 +- packages/backend/src/server/api/endpoints/admin/meta.ts | 2 +- packages/backend/src/server/api/endpoints/admin/moderators/add.ts | 2 +- packages/backend/src/server/api/endpoints/admin/moderators/remove.ts | 2 +- packages/backend/src/server/api/endpoints/admin/promo/create.ts | 2 +- packages/backend/src/server/api/endpoints/admin/reset-password.ts | 2 +- .../src/server/api/endpoints/admin/resolve-abuse-user-report.ts | 2 +- .../backend/src/server/api/endpoints/admin/show-moderation-logs.ts | 2 +- packages/backend/src/server/api/endpoints/admin/show-user.ts | 2 +- packages/backend/src/server/api/endpoints/admin/show-users.ts | 2 +- packages/backend/src/server/api/endpoints/admin/silence-user.ts | 2 +- packages/backend/src/server/api/endpoints/admin/suspend-user.ts | 2 +- packages/backend/src/server/api/endpoints/admin/unsilence-user.ts | 2 +- packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts | 2 +- packages/backend/src/server/api/endpoints/admin/update-user-note.ts | 2 +- packages/backend/src/server/api/endpoints/announcements.ts | 2 +- packages/backend/src/server/api/endpoints/antennas/create.ts | 2 +- packages/backend/src/server/api/endpoints/antennas/delete.ts | 2 +- packages/backend/src/server/api/endpoints/antennas/list.ts | 2 +- packages/backend/src/server/api/endpoints/antennas/notes.ts | 2 +- packages/backend/src/server/api/endpoints/antennas/show.ts | 2 +- packages/backend/src/server/api/endpoints/antennas/update.ts | 2 +- packages/backend/src/server/api/endpoints/ap/show.ts | 2 +- packages/backend/src/server/api/endpoints/app/create.ts | 2 +- packages/backend/src/server/api/endpoints/app/show.ts | 2 +- packages/backend/src/server/api/endpoints/auth/accept.ts | 2 +- packages/backend/src/server/api/endpoints/auth/session/generate.ts | 4 ++-- packages/backend/src/server/api/endpoints/auth/session/show.ts | 2 +- packages/backend/src/server/api/endpoints/auth/session/userkey.ts | 2 +- packages/backend/src/server/api/endpoints/blocking/create.ts | 2 +- packages/backend/src/server/api/endpoints/blocking/delete.ts | 2 +- packages/backend/src/server/api/endpoints/blocking/list.ts | 2 +- packages/backend/src/server/api/endpoints/channels/create.ts | 2 +- packages/backend/src/server/api/endpoints/channels/featured.ts | 2 +- packages/backend/src/server/api/endpoints/channels/follow.ts | 2 +- packages/backend/src/server/api/endpoints/channels/followed.ts | 2 +- packages/backend/src/server/api/endpoints/channels/owned.ts | 2 +- packages/backend/src/server/api/endpoints/channels/show.ts | 2 +- packages/backend/src/server/api/endpoints/channels/timeline.ts | 2 +- packages/backend/src/server/api/endpoints/channels/unfollow.ts | 2 +- packages/backend/src/server/api/endpoints/channels/update.ts | 2 +- packages/backend/src/server/api/endpoints/clips/add-note.ts | 2 +- packages/backend/src/server/api/endpoints/clips/create.ts | 2 +- packages/backend/src/server/api/endpoints/clips/delete.ts | 2 +- packages/backend/src/server/api/endpoints/clips/list.ts | 2 +- packages/backend/src/server/api/endpoints/clips/notes.ts | 2 +- packages/backend/src/server/api/endpoints/clips/remove-note.ts | 2 +- packages/backend/src/server/api/endpoints/clips/show.ts | 2 +- packages/backend/src/server/api/endpoints/clips/update.ts | 2 +- packages/backend/src/server/api/endpoints/drive/files.ts | 2 +- .../backend/src/server/api/endpoints/drive/files/attached-notes.ts | 2 +- .../backend/src/server/api/endpoints/drive/files/check-existence.ts | 2 +- packages/backend/src/server/api/endpoints/drive/files/create.ts | 2 +- packages/backend/src/server/api/endpoints/drive/files/delete.ts | 2 +- .../backend/src/server/api/endpoints/drive/files/find-by-hash.ts | 2 +- packages/backend/src/server/api/endpoints/drive/files/find.ts | 2 +- packages/backend/src/server/api/endpoints/drive/files/show.ts | 2 +- packages/backend/src/server/api/endpoints/drive/files/update.ts | 2 +- .../backend/src/server/api/endpoints/drive/files/upload-from-url.ts | 2 +- packages/backend/src/server/api/endpoints/drive/folders.ts | 2 +- packages/backend/src/server/api/endpoints/drive/folders/create.ts | 2 +- packages/backend/src/server/api/endpoints/drive/folders/delete.ts | 2 +- packages/backend/src/server/api/endpoints/drive/folders/find.ts | 2 +- packages/backend/src/server/api/endpoints/drive/folders/show.ts | 2 +- packages/backend/src/server/api/endpoints/drive/folders/update.ts | 2 +- packages/backend/src/server/api/endpoints/drive/stream.ts | 2 +- packages/backend/src/server/api/endpoints/federation/followers.ts | 2 +- packages/backend/src/server/api/endpoints/federation/following.ts | 2 +- packages/backend/src/server/api/endpoints/federation/instances.ts | 2 +- .../backend/src/server/api/endpoints/federation/show-instance.ts | 2 +- packages/backend/src/server/api/endpoints/federation/stats.ts | 2 +- packages/backend/src/server/api/endpoints/federation/users.ts | 2 +- packages/backend/src/server/api/endpoints/fetch-rss.ts | 2 +- packages/backend/src/server/api/endpoints/following/create.ts | 2 +- packages/backend/src/server/api/endpoints/following/delete.ts | 2 +- packages/backend/src/server/api/endpoints/following/invalidate.ts | 2 +- .../backend/src/server/api/endpoints/following/requests/cancel.ts | 2 +- packages/backend/src/server/api/endpoints/following/requests/list.ts | 2 +- packages/backend/src/server/api/endpoints/gallery/featured.ts | 2 +- packages/backend/src/server/api/endpoints/gallery/popular.ts | 2 +- packages/backend/src/server/api/endpoints/gallery/posts.ts | 2 +- packages/backend/src/server/api/endpoints/gallery/posts/create.ts | 2 +- packages/backend/src/server/api/endpoints/gallery/posts/delete.ts | 2 +- packages/backend/src/server/api/endpoints/gallery/posts/like.ts | 2 +- packages/backend/src/server/api/endpoints/gallery/posts/show.ts | 2 +- packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts | 2 +- packages/backend/src/server/api/endpoints/gallery/posts/update.ts | 2 +- packages/backend/src/server/api/endpoints/get-online-users-count.ts | 2 +- packages/backend/src/server/api/endpoints/hashtags/list.ts | 2 +- packages/backend/src/server/api/endpoints/hashtags/search.ts | 2 +- packages/backend/src/server/api/endpoints/hashtags/show.ts | 2 +- packages/backend/src/server/api/endpoints/hashtags/trend.ts | 2 +- packages/backend/src/server/api/endpoints/hashtags/users.ts | 2 +- packages/backend/src/server/api/endpoints/i.ts | 2 +- packages/backend/src/server/api/endpoints/i/2fa/done.ts | 2 +- packages/backend/src/server/api/endpoints/i/2fa/key-done.ts | 4 ++-- packages/backend/src/server/api/endpoints/i/2fa/password-less.ts | 2 +- packages/backend/src/server/api/endpoints/i/2fa/register-key.ts | 2 +- packages/backend/src/server/api/endpoints/i/2fa/register.ts | 4 ++-- packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts | 2 +- packages/backend/src/server/api/endpoints/i/2fa/unregister.ts | 2 +- packages/backend/src/server/api/endpoints/i/apps.ts | 2 +- packages/backend/src/server/api/endpoints/i/authorized-apps.ts | 2 +- packages/backend/src/server/api/endpoints/i/change-password.ts | 2 +- packages/backend/src/server/api/endpoints/i/delete-account.ts | 2 +- packages/backend/src/server/api/endpoints/i/favorites.ts | 2 +- packages/backend/src/server/api/endpoints/i/gallery/likes.ts | 2 +- packages/backend/src/server/api/endpoints/i/gallery/posts.ts | 2 +- .../backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts | 2 +- packages/backend/src/server/api/endpoints/i/import-blocking.ts | 2 +- packages/backend/src/server/api/endpoints/i/import-following.ts | 2 +- packages/backend/src/server/api/endpoints/i/import-muting.ts | 2 +- packages/backend/src/server/api/endpoints/i/import-user-lists.ts | 2 +- packages/backend/src/server/api/endpoints/i/notifications.ts | 2 +- packages/backend/src/server/api/endpoints/i/page-likes.ts | 2 +- packages/backend/src/server/api/endpoints/i/pages.ts | 2 +- .../src/server/api/endpoints/i/read-all-messaging-messages.ts | 2 +- packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts | 2 +- packages/backend/src/server/api/endpoints/i/read-announcement.ts | 2 +- packages/backend/src/server/api/endpoints/i/regenerate-token.ts | 2 +- packages/backend/src/server/api/endpoints/i/registry/get-all.ts | 2 +- packages/backend/src/server/api/endpoints/i/registry/get-detail.ts | 2 +- packages/backend/src/server/api/endpoints/i/registry/get.ts | 2 +- .../backend/src/server/api/endpoints/i/registry/keys-with-type.ts | 2 +- packages/backend/src/server/api/endpoints/i/registry/keys.ts | 2 +- packages/backend/src/server/api/endpoints/i/registry/remove.ts | 2 +- packages/backend/src/server/api/endpoints/i/registry/scopes.ts | 2 +- packages/backend/src/server/api/endpoints/i/registry/set.ts | 2 +- packages/backend/src/server/api/endpoints/i/revoke-token.ts | 2 +- packages/backend/src/server/api/endpoints/i/signin-history.ts | 2 +- packages/backend/src/server/api/endpoints/i/update-email.ts | 4 ++-- packages/backend/src/server/api/endpoints/i/update.ts | 2 +- packages/backend/src/server/api/endpoints/i/user-group-invites.ts | 2 +- packages/backend/src/server/api/endpoints/i/webhooks/create.ts | 2 +- packages/backend/src/server/api/endpoints/i/webhooks/delete.ts | 2 +- packages/backend/src/server/api/endpoints/i/webhooks/list.ts | 2 +- packages/backend/src/server/api/endpoints/i/webhooks/show.ts | 2 +- packages/backend/src/server/api/endpoints/i/webhooks/update.ts | 2 +- packages/backend/src/server/api/endpoints/messaging/history.ts | 2 +- packages/backend/src/server/api/endpoints/messaging/messages.ts | 2 +- .../backend/src/server/api/endpoints/messaging/messages/create.ts | 2 +- .../backend/src/server/api/endpoints/messaging/messages/delete.ts | 2 +- packages/backend/src/server/api/endpoints/messaging/messages/read.ts | 2 +- packages/backend/src/server/api/endpoints/meta.ts | 4 ++-- packages/backend/src/server/api/endpoints/miauth/gen-token.ts | 2 +- packages/backend/src/server/api/endpoints/mute/create.ts | 2 +- packages/backend/src/server/api/endpoints/mute/delete.ts | 2 +- packages/backend/src/server/api/endpoints/mute/list.ts | 2 +- packages/backend/src/server/api/endpoints/my/apps.ts | 2 +- packages/backend/src/server/api/endpoints/notes.ts | 2 +- packages/backend/src/server/api/endpoints/notes/children.ts | 2 +- packages/backend/src/server/api/endpoints/notes/clips.ts | 2 +- packages/backend/src/server/api/endpoints/notes/conversation.ts | 2 +- packages/backend/src/server/api/endpoints/notes/create.ts | 2 +- packages/backend/src/server/api/endpoints/notes/delete.ts | 2 +- packages/backend/src/server/api/endpoints/notes/favorites/create.ts | 2 +- packages/backend/src/server/api/endpoints/notes/favorites/delete.ts | 2 +- packages/backend/src/server/api/endpoints/notes/featured.ts | 2 +- packages/backend/src/server/api/endpoints/notes/global-timeline.ts | 2 +- packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts | 2 +- packages/backend/src/server/api/endpoints/notes/local-timeline.ts | 2 +- packages/backend/src/server/api/endpoints/notes/mentions.ts | 2 +- .../backend/src/server/api/endpoints/notes/polls/recommendation.ts | 2 +- packages/backend/src/server/api/endpoints/notes/polls/vote.ts | 2 +- packages/backend/src/server/api/endpoints/notes/reactions.ts | 2 +- packages/backend/src/server/api/endpoints/notes/renotes.ts | 2 +- packages/backend/src/server/api/endpoints/notes/replies.ts | 2 +- packages/backend/src/server/api/endpoints/notes/search-by-tag.ts | 2 +- packages/backend/src/server/api/endpoints/notes/search.ts | 4 ++-- packages/backend/src/server/api/endpoints/notes/show.ts | 2 +- packages/backend/src/server/api/endpoints/notes/state.ts | 2 +- .../backend/src/server/api/endpoints/notes/thread-muting/create.ts | 2 +- .../backend/src/server/api/endpoints/notes/thread-muting/delete.ts | 2 +- packages/backend/src/server/api/endpoints/notes/timeline.ts | 2 +- packages/backend/src/server/api/endpoints/notes/translate.ts | 4 ++-- packages/backend/src/server/api/endpoints/notes/unrenote.ts | 2 +- .../backend/src/server/api/endpoints/notes/user-list-timeline.ts | 2 +- .../src/server/api/endpoints/notifications/mark-all-as-read.ts | 2 +- packages/backend/src/server/api/endpoints/page-push.ts | 2 +- packages/backend/src/server/api/endpoints/pages/create.ts | 2 +- packages/backend/src/server/api/endpoints/pages/delete.ts | 2 +- packages/backend/src/server/api/endpoints/pages/featured.ts | 2 +- packages/backend/src/server/api/endpoints/pages/like.ts | 2 +- packages/backend/src/server/api/endpoints/pages/show.ts | 2 +- packages/backend/src/server/api/endpoints/pages/unlike.ts | 2 +- packages/backend/src/server/api/endpoints/pages/update.ts | 2 +- packages/backend/src/server/api/endpoints/pinned-users.ts | 2 +- packages/backend/src/server/api/endpoints/promo/read.ts | 2 +- packages/backend/src/server/api/endpoints/request-reset-password.ts | 4 ++-- packages/backend/src/server/api/endpoints/reset-password.ts | 2 +- packages/backend/src/server/api/endpoints/stats.ts | 2 +- packages/backend/src/server/api/endpoints/sw/register.ts | 2 +- packages/backend/src/server/api/endpoints/sw/unregister.ts | 2 +- packages/backend/src/server/api/endpoints/username/available.ts | 2 +- packages/backend/src/server/api/endpoints/users.ts | 2 +- packages/backend/src/server/api/endpoints/users/clips.ts | 2 +- packages/backend/src/server/api/endpoints/users/followers.ts | 2 +- packages/backend/src/server/api/endpoints/users/following.ts | 2 +- packages/backend/src/server/api/endpoints/users/gallery/posts.ts | 2 +- .../src/server/api/endpoints/users/get-frequently-replied-users.ts | 2 +- packages/backend/src/server/api/endpoints/users/groups/create.ts | 2 +- packages/backend/src/server/api/endpoints/users/groups/delete.ts | 2 +- .../src/server/api/endpoints/users/groups/invitations/accept.ts | 2 +- .../src/server/api/endpoints/users/groups/invitations/reject.ts | 2 +- packages/backend/src/server/api/endpoints/users/groups/invite.ts | 2 +- packages/backend/src/server/api/endpoints/users/groups/joined.ts | 2 +- packages/backend/src/server/api/endpoints/users/groups/leave.ts | 2 +- packages/backend/src/server/api/endpoints/users/groups/owned.ts | 2 +- packages/backend/src/server/api/endpoints/users/groups/pull.ts | 2 +- packages/backend/src/server/api/endpoints/users/groups/show.ts | 2 +- packages/backend/src/server/api/endpoints/users/groups/transfer.ts | 2 +- packages/backend/src/server/api/endpoints/users/groups/update.ts | 2 +- packages/backend/src/server/api/endpoints/users/lists/create.ts | 2 +- packages/backend/src/server/api/endpoints/users/lists/delete.ts | 2 +- packages/backend/src/server/api/endpoints/users/lists/list.ts | 2 +- packages/backend/src/server/api/endpoints/users/lists/pull.ts | 2 +- packages/backend/src/server/api/endpoints/users/lists/push.ts | 2 +- packages/backend/src/server/api/endpoints/users/lists/show.ts | 2 +- packages/backend/src/server/api/endpoints/users/lists/update.ts | 2 +- packages/backend/src/server/api/endpoints/users/notes.ts | 2 +- packages/backend/src/server/api/endpoints/users/pages.ts | 2 +- packages/backend/src/server/api/endpoints/users/reactions.ts | 2 +- packages/backend/src/server/api/endpoints/users/recommendation.ts | 2 +- packages/backend/src/server/api/endpoints/users/relation.ts | 2 +- packages/backend/src/server/api/endpoints/users/report-abuse.ts | 2 +- .../src/server/api/endpoints/users/search-by-username-and-host.ts | 2 +- packages/backend/src/server/api/endpoints/users/search.ts | 2 +- packages/backend/src/server/api/endpoints/users/show.ts | 2 +- packages/backend/src/server/api/integration/DiscordServerService.ts | 4 ++-- packages/backend/src/server/api/integration/GithubServerService.ts | 4 ++-- packages/backend/src/server/api/integration/TwitterServerService.ts | 4 ++-- packages/backend/src/server/api/stream/channels/messaging.ts | 2 +- packages/backend/src/server/api/stream/channels/user-list.ts | 2 +- packages/backend/src/server/web/ClientServerService.ts | 2 +- packages/backend/src/server/web/FeedService.ts | 4 ++-- packages/backend/src/server/web/UrlPreviewService.ts | 4 ++-- packages/shared/.eslintrc.js | 2 +- 408 files changed, 474 insertions(+), 475 deletions(-) (limited to 'packages/backend/src/core/NoteCreateService.ts') diff --git a/packages/backend/src/core/AccountUpdateService.ts b/packages/backend/src/core/AccountUpdateService.ts index 204e1d0170..6fe0e05c6d 100644 --- a/packages/backend/src/core/AccountUpdateService.ts +++ b/packages/backend/src/core/AccountUpdateService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { UsersRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type { User } from '@/models/entities/User.js'; import { ApRendererService } from '@/core/remote/activitypub/ApRendererService.js'; import { RelayService } from '@/core/RelayService.js'; diff --git a/packages/backend/src/core/AiService.ts b/packages/backend/src/core/AiService.ts index e6102a1b91..15084b8ff1 100644 --- a/packages/backend/src/core/AiService.ts +++ b/packages/backend/src/core/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 { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; const _filename = fileURLToPath(import.meta.url); diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts index e0af033952..0146f959e6 100644 --- a/packages/backend/src/core/AntennaService.ts +++ b/packages/backend/src/core/AntennaService.ts @@ -10,7 +10,7 @@ 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 { MutingsRepository, BlockingsRepository, NotesRepository, AntennaNotesRepository, AntennasRepository, UserGroupJoiningsRepository, UserListJoiningsRepository } from '@/models/index.js'; +import type { MutingsRepository, BlockingsRepository, NotesRepository, AntennaNotesRepository, AntennasRepository, UserGroupJoiningsRepository, UserListJoiningsRepository } from '@/models/index.js'; import { UtilityService } from './UtilityService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; diff --git a/packages/backend/src/core/CaptchaService.ts b/packages/backend/src/core/CaptchaService.ts index b1b52fd6a9..67b4b90061 100644 --- a/packages/backend/src/core/CaptchaService.ts +++ b/packages/backend/src/core/CaptchaService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { UsersRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { HttpRequestService } from './HttpRequestService.js'; type CaptchaResponse = { diff --git a/packages/backend/src/core/CreateNotificationService.ts b/packages/backend/src/core/CreateNotificationService.ts index 525fac6d92..feb82dcbf9 100644 --- a/packages/backend/src/core/CreateNotificationService.ts +++ b/packages/backend/src/core/CreateNotificationService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { MutingsRepository, NotificationsRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { MutingsRepository, NotificationsRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import type { Notification } from '@/models/entities/Notification.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 32dad70d1c..e1355fff07 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -2,14 +2,14 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource, In, IsNull } from 'typeorm'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { IdService } from '@/core/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 '@/misc/prelude/url.js'; import type { Note } from '@/models/entities/Note.js'; -import { EmojisRepository } from '@/models/index.js'; +import type { EmojisRepository } from '@/models/index.js'; import { UtilityService } from './UtilityService.js'; import { ReactionService } from './ReactionService.js'; diff --git a/packages/backend/src/core/DeleteAccountService.ts b/packages/backend/src/core/DeleteAccountService.ts index ba67bc499e..53d48c450b 100644 --- a/packages/backend/src/core/DeleteAccountService.ts +++ b/packages/backend/src/core/DeleteAccountService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import { QueueService } from '@/core/QueueService.js'; import { UserSuspendService } from '@/core/UserSuspendService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; diff --git a/packages/backend/src/core/DownloadService.ts b/packages/backend/src/core/DownloadService.ts index 81939d5f51..25965b7ac4 100644 --- a/packages/backend/src/core/DownloadService.ts +++ b/packages/backend/src/core/DownloadService.ts @@ -7,7 +7,7 @@ import PrivateIp from 'private-ip'; import got, * as Got from 'got'; import chalk from 'chalk'; import { DI } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import { createTemp } from '@/misc/create-temp.js'; import { StatusError } from '@/misc/status-error.js'; diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index e356fa0009..643c51c37d 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -4,8 +4,8 @@ import { v4 as uuid } from 'uuid'; import sharp from 'sharp'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { DriveFilesRepository, UsersRepository, DriveFoldersRepository, UserProfilesRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { DriveFilesRepository, UsersRepository, DriveFoldersRepository, UserProfilesRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import Logger from '@/logger.js'; import type { IRemoteUser, User } from '@/models/entities/User.js'; import { MetaService } from '@/core/MetaService.js'; diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts index 521ab7fd81..4c5cf7dfc4 100644 --- a/packages/backend/src/core/EmailService.ts +++ b/packages/backend/src/core/EmailService.ts @@ -3,9 +3,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { validate as validateEmail } from 'deep-email-validator'; import { MetaService } from '@/core/MetaService.js'; import { DI } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; -import { UserProfilesRepository } from '@/models/index.js'; +import type { UserProfilesRepository } from '@/models/index.js'; import { LoggerService } from '@/core/LoggerService.js'; @Injectable() diff --git a/packages/backend/src/core/FederatedInstanceService.ts b/packages/backend/src/core/FederatedInstanceService.ts index a4894a4376..b98a41f757 100644 --- a/packages/backend/src/core/FederatedInstanceService.ts +++ b/packages/backend/src/core/FederatedInstanceService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { InstancesRepository } from '@/models/index.js'; +import type { InstancesRepository } from '@/models/index.js'; import type { Instance } from '@/models/entities/Instance.js'; import { Cache } from '@/misc/cache.js'; import { IdService } from '@/core/IdService.js'; diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts index 376617914e..9a51ed0c0a 100644 --- a/packages/backend/src/core/FetchInstanceMetadataService.ts +++ b/packages/backend/src/core/FetchInstanceMetadataService.ts @@ -4,7 +4,7 @@ import { JSDOM } from 'jsdom'; import fetch from 'node-fetch'; import tinycolor from 'tinycolor2'; import type { Instance } from '@/models/entities/Instance.js'; -import { InstancesRepository } from '@/models/index.js'; +import type { InstancesRepository } from '@/models/index.js'; import { AppLockService } from '@/core/AppLockService.js'; import type Logger from '@/logger.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index c36de63fde..df0c9b5ccb 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -24,7 +24,7 @@ import type { } from '@/server/api/stream/types.js'; import type { Packed } from '@/misc/schema.js'; import { DI } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; @Injectable() export class GlobalEventService { diff --git a/packages/backend/src/core/HashtagService.ts b/packages/backend/src/core/HashtagService.ts index f6c06d48f4..83950aa890 100644 --- a/packages/backend/src/core/HashtagService.ts +++ b/packages/backend/src/core/HashtagService.ts @@ -5,7 +5,7 @@ import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { IdService } from '@/core/IdService.js'; import type { Hashtag } from '@/models/entities/Hashtag.js'; import HashtagChart from '@/core/chart/charts/hashtag.js'; -import { HashtagsRepository, UsersRepository } from '@/models/index.js'; +import type { HashtagsRepository, UsersRepository } from '@/models/index.js'; import { UserEntityService } from './entities/UserEntityService.js'; @Injectable() diff --git a/packages/backend/src/core/HttpRequestService.ts b/packages/backend/src/core/HttpRequestService.ts index f4c00cd259..396fefad1c 100644 --- a/packages/backend/src/core/HttpRequestService.ts +++ b/packages/backend/src/core/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.js'; +import type { 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/core/IdService.ts b/packages/backend/src/core/IdService.ts index 345b72bac5..997be17937 100644 --- a/packages/backend/src/core/IdService.ts +++ b/packages/backend/src/core/IdService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { ulid } from 'ulid'; import { DI } from '@/di-symbols.js'; -import { Config } from '@/config.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/core/ImageProcessingService.ts b/packages/backend/src/core/ImageProcessingService.ts index d215be2131..3a50361a42 100644 --- a/packages/backend/src/core/ImageProcessingService.ts +++ b/packages/backend/src/core/ImageProcessingService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import sharp from 'sharp'; import { DI } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; export type IImage = { data: Buffer; diff --git a/packages/backend/src/core/InstanceActorService.ts b/packages/backend/src/core/InstanceActorService.ts index 57d55870b1..fa906df4a2 100644 --- a/packages/backend/src/core/InstanceActorService.ts +++ b/packages/backend/src/core/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 { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; import { DI } from '@/di-symbols.js'; import { CreateSystemUserService } from './CreateSystemUserService.js'; diff --git a/packages/backend/src/core/InternalStorageService.ts b/packages/backend/src/core/InternalStorageService.ts index 9bc3597baf..6d2a9b2db6 100644 --- a/packages/backend/src/core/InternalStorageService.ts +++ b/packages/backend/src/core/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 { Config } from '@/config.js'; +import type { Config } from '@/config.js'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); diff --git a/packages/backend/src/core/LoggerService.ts b/packages/backend/src/core/LoggerService.ts index 558e3016dc..a3192c0262 100644 --- a/packages/backend/src/core/LoggerService.ts +++ b/packages/backend/src/core/LoggerService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import * as SyslogPro from 'syslog-pro'; import { DI } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import Logger from '@/logger.js'; @Injectable() diff --git a/packages/backend/src/core/MessagingService.ts b/packages/backend/src/core/MessagingService.ts index 1819b32a45..0603da0651 100644 --- a/packages/backend/src/core/MessagingService.ts +++ b/packages/backend/src/core/MessagingService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, Not } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { 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'; @@ -10,7 +10,7 @@ import type { UserGroup } from '@/models/entities/UserGroup.js'; import { QueueService } from '@/core/QueueService.js'; import { toArray } from '@/misc/prelude/array.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; -import { MessagingMessagesRepository, MutingsRepository, UserGroupJoiningsRepository, UsersRepository } from '@/models/index.js'; +import type { MessagingMessagesRepository, MutingsRepository, UserGroupJoiningsRepository, UsersRepository } from '@/models/index.js'; import { IdService } from './IdService.js'; import { GlobalEventService } from './GlobalEventService.js'; import { UserEntityService } from './entities/UserEntityService.js'; diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts index 236be4bbf8..2e03bf3cc0 100644 --- a/packages/backend/src/core/MfmService.ts +++ b/packages/backend/src/core/MfmService.ts @@ -4,7 +4,7 @@ import * as parse5 from 'parse5'; import { JSDOM } from 'jsdom'; import { DI } from '@/di-symbols.js'; import type { UsersRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { intersperse } from '@/misc/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/core/ModerationLogService.ts b/packages/backend/src/core/ModerationLogService.ts index 191148ac25..81ae322b95 100644 --- a/packages/backend/src/core/ModerationLogService.ts +++ b/packages/backend/src/core/ModerationLogService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { ModerationLogsRepository } from '@/models/index.js'; +import type { ModerationLogsRepository } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import { IdService } from '@/core/IdService.js'; diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 5acc07fba6..6e5cce8f62 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/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 { ChannelFollowingsRepository, ChannelsRepository, InstancesRepository, MutedNotesRepository, MutingsRepository, NotesRepository, NoteThreadMutingsRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { ChannelFollowingsRepository, ChannelsRepository, InstancesRepository, MutedNotesRepository, MutingsRepository, NotesRepository, NoteThreadMutingsRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { App } from '@/models/entities/App.js'; import { concat } from '@/misc/prelude/array.js'; @@ -23,7 +23,7 @@ import type { UserProfile } from '@/models/entities/UserProfile.js'; import { RelayService } from '@/core/RelayService.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import { DI } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import NotesChart from '@/core/chart/charts/notes.js'; import PerUserNotesChart from '@/core/chart/charts/per-user-notes.js'; import InstanceChart from '@/core/chart/charts/instance.js'; diff --git a/packages/backend/src/core/NoteDeleteService.ts b/packages/backend/src/core/NoteDeleteService.ts index 48d0d31e88..ccc583c5b6 100644 --- a/packages/backend/src/core/NoteDeleteService.ts +++ b/packages/backend/src/core/NoteDeleteService.ts @@ -2,11 +2,11 @@ import { Brackets, In } from 'typeorm'; import { Injectable, Inject } from '@nestjs/common'; import type { User, ILocalUser, IRemoteUser } from '@/models/entities/User.js'; import type { Note, IMentionedRemoteUsers } from '@/models/entities/Note.js'; -import { InstancesRepository, NotesRepository, UsersRepository } from '@/models/index.js'; +import type { InstancesRepository, NotesRepository, UsersRepository } from '@/models/index.js'; import { RelayService } from '@/core/RelayService.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import { DI } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import NotesChart from '@/core/chart/charts/notes.js'; import PerUserNotesChart from '@/core/chart/charts/per-user-notes.js'; import InstanceChart from '@/core/chart/charts/instance.js'; diff --git a/packages/backend/src/core/NotePiningService.ts b/packages/backend/src/core/NotePiningService.ts index 576e90bd43..b70c051efd 100644 --- a/packages/backend/src/core/NotePiningService.ts +++ b/packages/backend/src/core/NotePiningService.ts @@ -1,13 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } 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 { IdService } from '@/core/IdService.js'; import type { UserNotePining } from '@/models/entities/UserNotePining.js'; import { RelayService } from '@/core/RelayService.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { UserEntityService } from './entities/UserEntityService.js'; import { ApDeliverManagerService } from './remote/activitypub/ApDeliverManagerService.js'; import { ApRendererService } from './remote/activitypub/ApRendererService.js'; diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts index ca9e60889d..2606ca4de0 100644 --- a/packages/backend/src/core/NotificationService.ts +++ b/packages/backend/src/core/NotificationService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { NotificationsRepository } from '@/models/index.js'; +import type { NotificationsRepository } from '@/models/index.js'; import type { UsersRepository } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import type { Notification } from '@/models/entities/Notification.js'; diff --git a/packages/backend/src/core/PollService.ts b/packages/backend/src/core/PollService.ts index 8bc94c8a82..1bb68f7804 100644 --- a/packages/backend/src/core/PollService.ts +++ b/packages/backend/src/core/PollService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Not } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { NotesRepository, UsersRepository, BlockingsRepository } from '@/models/index.js'; +import type { NotesRepository, UsersRepository, BlockingsRepository } from '@/models/index.js'; import type { Note } from '@/models/entities/Note.js'; import { RelayService } from '@/core/RelayService.js'; import type { CacheableUser } from '@/models/entities/User.js'; diff --git a/packages/backend/src/core/ProxyAccountService.ts b/packages/backend/src/core/ProxyAccountService.ts index 40ccc8226a..07d8d0dbd5 100644 --- a/packages/backend/src/core/ProxyAccountService.ts +++ b/packages/backend/src/core/ProxyAccountService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import type { ILocalUser, User } from '@/models/entities/User.js'; import { DI } from '@/di-symbols.js'; import { MetaService } from './MetaService.js'; diff --git a/packages/backend/src/core/PushNotificationService.ts b/packages/backend/src/core/PushNotificationService.ts index 31d29bed97..5eaaed00eb 100644 --- a/packages/backend/src/core/PushNotificationService.ts +++ b/packages/backend/src/core/PushNotificationService.ts @@ -1,10 +1,10 @@ import { Inject, Injectable } from '@nestjs/common'; import push from 'web-push'; import { DI } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import type { Packed } from '@/misc/schema'; import { getNoteSummary } from '@/misc/get-note-summary.js'; -import { SwSubscriptionsRepository } from '@/models/index.js'; +import type { SwSubscriptionsRepository } from '@/models/index.js'; import { MetaService } from './MetaService.js'; // Defined also packages/sw/types.ts#L14-L21 diff --git a/packages/backend/src/core/QueueService.ts b/packages/backend/src/core/QueueService.ts index 7e771c100f..12be57c7fb 100644 --- a/packages/backend/src/core/QueueService.ts +++ b/packages/backend/src/core/QueueService.ts @@ -3,9 +3,9 @@ import { v4 as uuid } from 'uuid'; import type { IActivity } from '@/core/remote/activitypub/type.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { Webhook, webhookEventTypes } from '@/models/entities/Webhook.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; -import { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from './queue/QueueModule.js'; +import type { 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'; diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index 3006456577..d5b3c0e799 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { EmojisRepository, BlockingsRepository, NoteReactionsRepository, UsersRepository, NotesRepository } from '@/models/index.js'; +import type { EmojisRepository, BlockingsRepository, NoteReactionsRepository, UsersRepository, NotesRepository } 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'; diff --git a/packages/backend/src/core/RelayService.ts b/packages/backend/src/core/RelayService.ts index 688ea03d34..5324826ec1 100644 --- a/packages/backend/src/core/RelayService.ts +++ b/packages/backend/src/core/RelayService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import type { ILocalUser, User } from '@/models/entities/User.js'; -import { RelaysRepository, UsersRepository } from '@/models/index.js'; +import type { RelaysRepository, UsersRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import { Cache } from '@/misc/cache.js'; import type { Relay } from '@/models/entities/Relay.js'; diff --git a/packages/backend/src/core/S3Service.ts b/packages/backend/src/core/S3Service.ts index 9549e19990..723a79dc59 100644 --- a/packages/backend/src/core/S3Service.ts +++ b/packages/backend/src/core/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 } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import type { Meta } from '@/models/entities/Meta.js'; import { HttpRequestService } from './HttpRequestService.js'; diff --git a/packages/backend/src/core/SignupService.ts b/packages/backend/src/core/SignupService.ts index a876668b94..8b72d73af6 100644 --- a/packages/backend/src/core/SignupService.ts +++ b/packages/backend/src/core/SignupService.ts @@ -3,8 +3,8 @@ import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; import { DataSource, IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { UsedUsernamesRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { UsedUsernamesRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import { User } from '@/models/entities/User.js'; import { UserProfile } from '@/models/entities/UserProfile.js'; import { IdService } from '@/core/IdService.js'; diff --git a/packages/backend/src/core/TwoFactorAuthenticationService.ts b/packages/backend/src/core/TwoFactorAuthenticationService.ts index be31534c02..0962f88a7c 100644 --- a/packages/backend/src/core/TwoFactorAuthenticationService.ts +++ b/packages/backend/src/core/TwoFactorAuthenticationService.ts @@ -2,8 +2,8 @@ import * as crypto from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import * as jsrsasign from 'jsrsasign'; import { DI } from '@/di-symbols.js'; -import { UsersRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; const ECC_PRELUDE = Buffer.from([0x04]); const NULL_BYTE = Buffer.from([0]); diff --git a/packages/backend/src/core/UserCacheService.ts b/packages/backend/src/core/UserCacheService.ts index 8212abf7bb..666bef3f49 100644 --- a/packages/backend/src/core/UserCacheService.ts +++ b/packages/backend/src/core/UserCacheService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import Redis from 'ioredis'; -import { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } 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'; diff --git a/packages/backend/src/core/UserKeypairStoreService.ts b/packages/backend/src/core/UserKeypairStoreService.ts index e53f37b714..8eca03a10b 100644 --- a/packages/backend/src/core/UserKeypairStoreService.ts +++ b/packages/backend/src/core/UserKeypairStoreService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import type { User } from '@/models/entities/User.js'; -import { UserKeypairsRepository } from '@/models/index.js'; +import type { UserKeypairsRepository } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; import type { UserKeypair } from '@/models/entities/UserKeypair.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/core/UserListService.ts b/packages/backend/src/core/UserListService.ts index 03113f042a..b1d01a1565 100644 --- a/packages/backend/src/core/UserListService.ts +++ b/packages/backend/src/core/UserListService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserListJoiningsRepository, UsersRepository } from '@/models/index.js'; +import type { UserListJoiningsRepository, UsersRepository } 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'; diff --git a/packages/backend/src/core/UserMutingService.ts b/packages/backend/src/core/UserMutingService.ts index 9146360df1..4c09e450c8 100644 --- a/packages/backend/src/core/UserMutingService.ts +++ b/packages/backend/src/core/UserMutingService.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UsersRepository, MutingsRepository } from '@/models/index.js'; +import type { UsersRepository, MutingsRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import { QueueService } from '@/core/QueueService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts index 068341cb2c..82c2e98236 100644 --- a/packages/backend/src/core/UserSuspendService.ts +++ b/packages/backend/src/core/UserSuspendService.ts @@ -1,11 +1,11 @@ import { Inject, Injectable } from '@nestjs/common'; import { Not, IsNull } from 'typeorm'; -import { FollowingsRepository, UsersRepository } from '@/models/index.js'; +import type { FollowingsRepository, UsersRepository } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import { QueueService } from '@/core/QueueService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { ApRendererService } from './remote/activitypub/ApRendererService.js'; import { UserEntityService } from './entities/UserEntityService.js'; diff --git a/packages/backend/src/core/UtilityService.ts b/packages/backend/src/core/UtilityService.ts index ba03dfc069..15dd684286 100644 --- a/packages/backend/src/core/UtilityService.ts +++ b/packages/backend/src/core/UtilityService.ts @@ -2,7 +2,7 @@ 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'; +import type { Config } from '@/config.js'; @Injectable() export class UtilityService { diff --git a/packages/backend/src/core/VideoProcessingService.ts b/packages/backend/src/core/VideoProcessingService.ts index 70b9664c76..af4036a291 100644 --- a/packages/backend/src/core/VideoProcessingService.ts +++ b/packages/backend/src/core/VideoProcessingService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import FFmpeg from 'fluent-ffmpeg'; import { DI } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { ImageProcessingService } from '@/core/ImageProcessingService.js'; import type { IImage } from '@/core/ImageProcessingService.js'; import { createTempDir } from '@/misc/create-temp.js'; diff --git a/packages/backend/src/core/WebhookService.ts b/packages/backend/src/core/WebhookService.ts index 1d74290dd9..347b2b16c4 100644 --- a/packages/backend/src/core/WebhookService.ts +++ b/packages/backend/src/core/WebhookService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import Redis from 'ioredis'; -import { WebhooksRepository } from '@/models/index.js'; +import type { WebhooksRepository } from '@/models/index.js'; import type { Webhook } from '@/models/entities/Webhook.js'; import { DI } from '@/di-symbols.js'; import type { OnApplicationShutdown } from '@nestjs/common'; diff --git a/packages/backend/src/core/chart/charts/federation.ts b/packages/backend/src/core/chart/charts/federation.ts index 4366d4cce1..21e4cedea3 100644 --- a/packages/backend/src/core/chart/charts/federation.ts +++ b/packages/backend/src/core/chart/charts/federation.ts @@ -1,6 +1,6 @@ import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; -import { FollowingsRepository, InstancesRepository } from '@/models/index.js'; +import type { FollowingsRepository, InstancesRepository } from '@/models/index.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { MetaService } from '@/core/MetaService.js'; diff --git a/packages/backend/src/core/chart/charts/instance.ts b/packages/backend/src/core/chart/charts/instance.ts index be70bc79c0..2e0f4c7126 100644 --- a/packages/backend/src/core/chart/charts/instance.ts +++ b/packages/backend/src/core/chart/charts/instance.ts @@ -1,6 +1,6 @@ import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; -import { DriveFilesRepository, FollowingsRepository, UsersRepository, NotesRepository } from '@/models/index.js'; +import type { DriveFilesRepository, FollowingsRepository, UsersRepository, NotesRepository } from '@/models/index.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { Note } from '@/models/entities/Note.js'; import { AppLockService } from '@/core/AppLockService.js'; diff --git a/packages/backend/src/core/chart/charts/notes.ts b/packages/backend/src/core/chart/charts/notes.ts index e1bfeabf99..2153cfe4b4 100644 --- a/packages/backend/src/core/chart/charts/notes.ts +++ b/packages/backend/src/core/chart/charts/notes.ts @@ -1,6 +1,6 @@ import { Injectable, Inject } from '@nestjs/common'; import { Not, IsNull, DataSource } from 'typeorm'; -import { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/index.js'; import type { Note } from '@/models/entities/Note.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/core/chart/charts/per-user-drive.ts b/packages/backend/src/core/chart/charts/per-user-drive.ts index 752203daaf..a44460bb4e 100644 --- a/packages/backend/src/core/chart/charts/per-user-drive.ts +++ b/packages/backend/src/core/chart/charts/per-user-drive.ts @@ -1,6 +1,6 @@ import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; -import { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/index.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/core/chart/charts/per-user-following.ts b/packages/backend/src/core/chart/charts/per-user-following.ts index 48bf3d7c62..5ea08a0872 100644 --- a/packages/backend/src/core/chart/charts/per-user-following.ts +++ b/packages/backend/src/core/chart/charts/per-user-following.ts @@ -4,7 +4,7 @@ import type { User } from '@/models/entities/User.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { FollowingsRepository } from '@/models/index.js'; +import type { FollowingsRepository } from '@/models/index.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/per-user-following.js'; diff --git a/packages/backend/src/core/chart/charts/per-user-notes.ts b/packages/backend/src/core/chart/charts/per-user-notes.ts index ffe52dcd56..5c14309d89 100644 --- a/packages/backend/src/core/chart/charts/per-user-notes.ts +++ b/packages/backend/src/core/chart/charts/per-user-notes.ts @@ -4,7 +4,7 @@ import type { User } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; -import { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/index.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/per-user-notes.js'; diff --git a/packages/backend/src/core/chart/charts/users.ts b/packages/backend/src/core/chart/charts/users.ts index b3187997cf..f0359968eb 100644 --- a/packages/backend/src/core/chart/charts/users.ts +++ b/packages/backend/src/core/chart/charts/users.ts @@ -4,7 +4,7 @@ import type { User } from '@/models/entities/User.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/users.js'; diff --git a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts index 6cc511fb48..1660894571 100644 --- a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts +++ b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { AbuseUserReportsRepository } from '@/models/index.js'; +import type { AbuseUserReportsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { AbuseUserReport } from '@/models/entities/AbuseUserReport.js'; import { UserEntityService } from './UserEntityService.js'; diff --git a/packages/backend/src/core/entities/AntennaEntityService.ts b/packages/backend/src/core/entities/AntennaEntityService.ts index 9193cb81d7..44110e7364 100644 --- a/packages/backend/src/core/entities/AntennaEntityService.ts +++ b/packages/backend/src/core/entities/AntennaEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { AntennaNotesRepository, AntennasRepository, UserGroupJoiningsRepository } from '@/models/index.js'; +import type { AntennaNotesRepository, AntennasRepository, UserGroupJoiningsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { Antenna } from '@/models/entities/Antenna.js'; diff --git a/packages/backend/src/core/entities/AppEntityService.ts b/packages/backend/src/core/entities/AppEntityService.ts index 6491b0b2d9..1cc7ca11dc 100644 --- a/packages/backend/src/core/entities/AppEntityService.ts +++ b/packages/backend/src/core/entities/AppEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { AccessTokensRepository, AppsRepository } from '@/models/index.js'; +import type { AccessTokensRepository, AppsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { App } from '@/models/entities/App.js'; diff --git a/packages/backend/src/core/entities/AuthSessionEntityService.ts b/packages/backend/src/core/entities/AuthSessionEntityService.ts index a4dab3d9cf..bf8efa5f78 100644 --- a/packages/backend/src/core/entities/AuthSessionEntityService.ts +++ b/packages/backend/src/core/entities/AuthSessionEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { AuthSessionsRepository } from '@/models/index.js'; +import type { AuthSessionsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { AuthSession } from '@/models/entities/AuthSession.js'; diff --git a/packages/backend/src/core/entities/BlockingEntityService.ts b/packages/backend/src/core/entities/BlockingEntityService.ts index 74ce6830b6..49a96037ca 100644 --- a/packages/backend/src/core/entities/BlockingEntityService.ts +++ b/packages/backend/src/core/entities/BlockingEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { BlockingsRepository } from '@/models/index.js'; +import type { BlockingsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { Blocking } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/entities/ChannelEntityService.ts b/packages/backend/src/core/entities/ChannelEntityService.ts index fec76e4e63..860967443e 100644 --- a/packages/backend/src/core/entities/ChannelEntityService.ts +++ b/packages/backend/src/core/entities/ChannelEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { ChannelFollowingsRepository, ChannelsRepository, DriveFilesRepository, NoteUnreadsRepository } from '@/models/index.js'; +import type { ChannelFollowingsRepository, ChannelsRepository, DriveFilesRepository, NoteUnreadsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/entities/ClipEntityService.ts b/packages/backend/src/core/entities/ClipEntityService.ts index 27637e42e8..7a5d2f7f0a 100644 --- a/packages/backend/src/core/entities/ClipEntityService.ts +++ b/packages/backend/src/core/entities/ClipEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { ClipsRepository } from '@/models/index.js'; +import type { ClipsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts index 521bf51da0..f0ac6518d0 100644 --- a/packages/backend/src/core/entities/DriveFileEntityService.ts +++ b/packages/backend/src/core/entities/DriveFileEntityService.ts @@ -2,8 +2,8 @@ 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 { NotesRepository, DriveFilesRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { NotesRepository, DriveFilesRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type { Packed } from '@/misc/schema.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { User } from '@/models/entities/User.js'; diff --git a/packages/backend/src/core/entities/DriveFolderEntityService.ts b/packages/backend/src/core/entities/DriveFolderEntityService.ts index beebbc3206..5761fa37bc 100644 --- a/packages/backend/src/core/entities/DriveFolderEntityService.ts +++ b/packages/backend/src/core/entities/DriveFolderEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { DriveFilesRepository, DriveFoldersRepository } from '@/models/index.js'; +import type { DriveFilesRepository, DriveFoldersRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/entities/EmojiEntityService.ts b/packages/backend/src/core/entities/EmojiEntityService.ts index 10ed0f19ee..fc09b5a2c7 100644 --- a/packages/backend/src/core/entities/EmojiEntityService.ts +++ b/packages/backend/src/core/entities/EmojiEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { EmojisRepository } from '@/models/index.js'; +import type { EmojisRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/entities/FollowRequestEntityService.ts b/packages/backend/src/core/entities/FollowRequestEntityService.ts index f7e7fd42e4..4a60c1263f 100644 --- a/packages/backend/src/core/entities/FollowRequestEntityService.ts +++ b/packages/backend/src/core/entities/FollowRequestEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { FollowRequestsRepository } from '@/models/index.js'; +import type { FollowRequestsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/entities/FollowingEntityService.ts b/packages/backend/src/core/entities/FollowingEntityService.ts index 93fed85f72..c7e040a57b 100644 --- a/packages/backend/src/core/entities/FollowingEntityService.ts +++ b/packages/backend/src/core/entities/FollowingEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { FollowingsRepository } from '@/models/index.js'; +import type { FollowingsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/entities/GalleryLikeEntityService.ts b/packages/backend/src/core/entities/GalleryLikeEntityService.ts index 9473ed90b7..bf7c2b7f8d 100644 --- a/packages/backend/src/core/entities/GalleryLikeEntityService.ts +++ b/packages/backend/src/core/entities/GalleryLikeEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { GalleryPosts, GalleryLikesRepository } from '@/models/index.js'; +import type { GalleryPosts, GalleryLikesRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/entities/GalleryPostEntityService.ts b/packages/backend/src/core/entities/GalleryPostEntityService.ts index 82b41697c9..ca98687d7b 100644 --- a/packages/backend/src/core/entities/GalleryPostEntityService.ts +++ b/packages/backend/src/core/entities/GalleryPostEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { GalleryLikesRepository, GalleryPostsRepository } from '@/models/index.js'; +import type { GalleryLikesRepository, GalleryPostsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/entities/HashtagEntityService.ts b/packages/backend/src/core/entities/HashtagEntityService.ts index 6dcbf49030..511992c44f 100644 --- a/packages/backend/src/core/entities/HashtagEntityService.ts +++ b/packages/backend/src/core/entities/HashtagEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { HashtagsRepository } from '@/models/index.js'; +import type { HashtagsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts index c58c2f8f34..c54285d9df 100644 --- a/packages/backend/src/core/entities/InstanceEntityService.ts +++ b/packages/backend/src/core/entities/InstanceEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { InstancesRepository } from '@/models/index.js'; +import type { InstancesRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/entities/MessagingMessageEntityService.ts b/packages/backend/src/core/entities/MessagingMessageEntityService.ts index 04467b94e4..b7c42a5760 100644 --- a/packages/backend/src/core/entities/MessagingMessageEntityService.ts +++ b/packages/backend/src/core/entities/MessagingMessageEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { MessagingMessagesRepository } from '@/models/index.js'; +import type { MessagingMessagesRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/entities/ModerationLogEntityService.ts b/packages/backend/src/core/entities/ModerationLogEntityService.ts index 45d15088fc..2f508710b8 100644 --- a/packages/backend/src/core/entities/ModerationLogEntityService.ts +++ b/packages/backend/src/core/entities/ModerationLogEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { ModerationLogsRepository } from '@/models/index.js'; +import type { ModerationLogsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/entities/MutingEntityService.ts b/packages/backend/src/core/entities/MutingEntityService.ts index c5245bf203..862be009da 100644 --- a/packages/backend/src/core/entities/MutingEntityService.ts +++ b/packages/backend/src/core/entities/MutingEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { MutingsRepository } from '@/models/index.js'; +import type { MutingsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 669680758d..a9c63566b4 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -4,7 +4,7 @@ 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'; +import type { Config } from '@/config.js'; import type { Packed } from '@/misc/schema.js'; import { nyaize } from '@/misc/nyaize.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; diff --git a/packages/backend/src/core/entities/NoteFavoriteEntityService.ts b/packages/backend/src/core/entities/NoteFavoriteEntityService.ts index f0bbf27b6a..1a68a5c628 100644 --- a/packages/backend/src/core/entities/NoteFavoriteEntityService.ts +++ b/packages/backend/src/core/entities/NoteFavoriteEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { NoteFavoritesRepository } from '@/models/index.js'; +import type { NoteFavoritesRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/entities/NoteReactionEntityService.ts b/packages/backend/src/core/entities/NoteReactionEntityService.ts index e64f2af681..47008ee08e 100644 --- a/packages/backend/src/core/entities/NoteReactionEntityService.ts +++ b/packages/backend/src/core/entities/NoteReactionEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { NoteReactionsRepository } from '@/models/index.js'; +import type { NoteReactionsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { OnModuleInit } from '@nestjs/common'; diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index 6a0683d543..c415599fea 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/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 { AccessTokensRepository, NoteReactionsRepository, NotificationsRepository } from '@/models/index.js'; +import type { AccessTokensRepository, NoteReactionsRepository, NotificationsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Notification } from '@/models/entities/Notification.js'; import type { NoteReaction } from '@/models/entities/NoteReaction.js'; diff --git a/packages/backend/src/core/entities/PageEntityService.ts b/packages/backend/src/core/entities/PageEntityService.ts index cbd193fe0a..004443759b 100644 --- a/packages/backend/src/core/entities/PageEntityService.ts +++ b/packages/backend/src/core/entities/PageEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { DriveFilesRepository, PagesRepository, PageLikesRepository } from '@/models/index.js'; +import type { DriveFilesRepository, PagesRepository, PageLikesRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/entities/PageLikeEntityService.ts b/packages/backend/src/core/entities/PageLikeEntityService.ts index dccaf2edec..62d9c82ca6 100644 --- a/packages/backend/src/core/entities/PageLikeEntityService.ts +++ b/packages/backend/src/core/entities/PageLikeEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { PageLikesRepository } from '@/models/index.js'; +import type { PageLikesRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/entities/SigninEntityService.ts b/packages/backend/src/core/entities/SigninEntityService.ts index 521c7669e7..fd89662f7d 100644 --- a/packages/backend/src/core/entities/SigninEntityService.ts +++ b/packages/backend/src/core/entities/SigninEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { SigninsRepository } from '@/models/index.js'; +import type { SigninsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 343e42df07..a35703e80e 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -1,9 +1,9 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; -import { EntityRepository, Repository, In, Not } from 'typeorm'; +import type { EntityRepository, Repository, In, Not } from 'typeorm'; import Ajv from 'ajv'; import { ModuleRef } from '@nestjs/core'; import { DI } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import type { Packed } from '@/misc/schema.js'; import type { Promiseable } from '@/misc/prelude/await-all.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; diff --git a/packages/backend/src/core/entities/UserGroupEntityService.ts b/packages/backend/src/core/entities/UserGroupEntityService.ts index acd26ea1eb..e399197618 100644 --- a/packages/backend/src/core/entities/UserGroupEntityService.ts +++ b/packages/backend/src/core/entities/UserGroupEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { UserGroupJoiningsRepository, UserGroupsRepository } from '@/models/index.js'; +import type { UserGroupJoiningsRepository, UserGroupsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/entities/UserGroupInvitationEntityService.ts b/packages/backend/src/core/entities/UserGroupInvitationEntityService.ts index 50ff2231ab..f5c9be3475 100644 --- a/packages/backend/src/core/entities/UserGroupInvitationEntityService.ts +++ b/packages/backend/src/core/entities/UserGroupInvitationEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { UserGroupInvitationsRepository } from '@/models/index.js'; +import type { UserGroupInvitationsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/entities/UserListEntityService.ts b/packages/backend/src/core/entities/UserListEntityService.ts index 05434d9c8a..e2b0814914 100644 --- a/packages/backend/src/core/entities/UserListEntityService.ts +++ b/packages/backend/src/core/entities/UserListEntityService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { UserListJoiningsRepository, UserListsRepository } from '@/models/index.js'; +import type { UserListJoiningsRepository, UserListsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { } from '@/models/entities/Blocking.js'; diff --git a/packages/backend/src/core/remote/ResolveUserService.ts b/packages/backend/src/core/remote/ResolveUserService.ts index b45168fb02..2fd9e7c378 100644 --- a/packages/backend/src/core/remote/ResolveUserService.ts +++ b/packages/backend/src/core/remote/ResolveUserService.ts @@ -3,9 +3,9 @@ import { Inject, Injectable } from '@nestjs/common'; import chalk from 'chalk'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import type { IRemoteUser, User } from '@/models/entities/User.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { UtilityService } from '../UtilityService.js'; import { WebfingerService } from './WebfingerService.js'; diff --git a/packages/backend/src/core/remote/WebfingerService.ts b/packages/backend/src/core/remote/WebfingerService.ts index ab46314792..d2a88be583 100644 --- a/packages/backend/src/core/remote/WebfingerService.ts +++ b/packages/backend/src/core/remote/WebfingerService.ts @@ -1,7 +1,7 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { query as urlQuery } from '@/misc/prelude/url.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; diff --git a/packages/backend/src/core/remote/activitypub/ApDbResolverService.ts b/packages/backend/src/core/remote/activitypub/ApDbResolverService.ts index 6f197985da..77d200c3c8 100644 --- a/packages/backend/src/core/remote/activitypub/ApDbResolverService.ts +++ b/packages/backend/src/core/remote/activitypub/ApDbResolverService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import escapeRegexp from 'escape-regexp'; import { DI } from '@/di-symbols.js'; -import { MessagingMessagesRepository, NotesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { MessagingMessagesRepository, NotesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.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/UserPublickey.js'; diff --git a/packages/backend/src/core/remote/activitypub/ApDeliverManagerService.ts b/packages/backend/src/core/remote/activitypub/ApDeliverManagerService.ts index a6ee857526..6fc75a0397 100644 --- a/packages/backend/src/core/remote/activitypub/ApDeliverManagerService.ts +++ b/packages/backend/src/core/remote/activitypub/ApDeliverManagerService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, Not } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { FollowingsRepository, UsersRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { FollowingsRepository, UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; import { QueueService } from '@/core/QueueService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; diff --git a/packages/backend/src/core/remote/activitypub/ApInboxService.ts b/packages/backend/src/core/remote/activitypub/ApInboxService.ts index 0482e029d2..a3cb08063d 100644 --- a/packages/backend/src/core/remote/activitypub/ApInboxService.ts +++ b/packages/backend/src/core/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 { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import type { CacheableRemoteUser } from '@/models/entities/User.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; import { ReactionService } from '@/core/ReactionService.js'; diff --git a/packages/backend/src/core/remote/activitypub/ApMfmService.ts b/packages/backend/src/core/remote/activitypub/ApMfmService.ts index 3c3b98b139..8804fde64a 100644 --- a/packages/backend/src/core/remote/activitypub/ApMfmService.ts +++ b/packages/backend/src/core/remote/activitypub/ApMfmService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import * as mfm from 'mfm-js'; import { DI } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { MfmService } from '@/core/MfmService.js'; import type { Note } from '@/models/entities/Note.js'; import { extractApHashtagObjects } from './models/tag.js'; diff --git a/packages/backend/src/core/remote/activitypub/ApRendererService.ts b/packages/backend/src/core/remote/activitypub/ApRendererService.ts index 5a4cef63e0..6058929d35 100644 --- a/packages/backend/src/core/remote/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/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 { Config } from '@/config.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/core/remote/activitypub/ApRequestService.ts b/packages/backend/src/core/remote/activitypub/ApRequestService.ts index 2abaca06af..baad46d668 100644 --- a/packages/backend/src/core/remote/activitypub/ApRequestService.ts +++ b/packages/backend/src/core/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 } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import type { User } from '@/models/entities/User.js'; import { UserKeypairStoreService } from '@/core/UserKeypairStoreService.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; diff --git a/packages/backend/src/core/remote/activitypub/ApResolverService.ts b/packages/backend/src/core/remote/activitypub/ApResolverService.ts index 9d8e177758..e2d6a77370 100644 --- a/packages/backend/src/core/remote/activitypub/ApResolverService.ts +++ b/packages/backend/src/core/remote/activitypub/ApResolverService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import type { ILocalUser } from '@/models/entities/User.js'; import { InstanceActorService } from '@/core/InstanceActorService.js'; -import { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import { MetaService } from '@/core/MetaService.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/core/remote/activitypub/models/ApImageService.ts b/packages/backend/src/core/remote/activitypub/models/ApImageService.ts index da6ed61c56..9bf87f19d4 100644 --- a/packages/backend/src/core/remote/activitypub/models/ApImageService.ts +++ b/packages/backend/src/core/remote/activitypub/models/ApImageService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { DriveFilesRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { DriveFilesRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type { CacheableRemoteUser } from '@/models/entities/User.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import { MetaService } from '@/core/MetaService.js'; diff --git a/packages/backend/src/core/remote/activitypub/models/ApMentionService.ts b/packages/backend/src/core/remote/activitypub/models/ApMentionService.ts index 898da07a26..710e1acfaf 100644 --- a/packages/backend/src/core/remote/activitypub/models/ApMentionService.ts +++ b/packages/backend/src/core/remote/activitypub/models/ApMentionService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { DI } from '@/di-symbols.js'; import type { UsersRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { toArray, unique } from '@/misc/prelude/array.js'; import type { CacheableUser } from '@/models/entities/User.js'; import { isMention } from '../type.js'; diff --git a/packages/backend/src/core/remote/activitypub/models/ApNoteService.ts b/packages/backend/src/core/remote/activitypub/models/ApNoteService.ts index 1efe62333b..a34a1d1eb8 100644 --- a/packages/backend/src/core/remote/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/remote/activitypub/models/ApNoteService.ts @@ -1,9 +1,9 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { DI } from '@/di-symbols.js'; -import { MessagingMessagesRepository, PollsRepository, EmojisRepository } from '@/models/index.js'; +import type { MessagingMessagesRepository, PollsRepository, EmojisRepository } from '@/models/index.js'; import type { UsersRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import type { CacheableRemoteUser } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; import { toArray, toSingle, unique } from '@/misc/prelude/array.js'; diff --git a/packages/backend/src/core/remote/activitypub/models/ApPersonService.ts b/packages/backend/src/core/remote/activitypub/models/ApPersonService.ts index d088fa5554..5135473862 100644 --- a/packages/backend/src/core/remote/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/remote/activitypub/models/ApPersonService.ts @@ -3,8 +3,8 @@ import promiseLimit from 'promise-limit'; import { DataSource } from 'typeorm'; import { ModuleRef } from '@nestjs/core'; import { DI } from '@/di-symbols.js'; -import { FollowingsRepository, InstancesRepository, UserProfilesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { FollowingsRepository, InstancesRepository, UserProfilesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js'; +import type { 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'; diff --git a/packages/backend/src/core/remote/activitypub/models/ApQuestionService.ts b/packages/backend/src/core/remote/activitypub/models/ApQuestionService.ts index 2b89cb030b..acd5cdae83 100644 --- a/packages/backend/src/core/remote/activitypub/models/ApQuestionService.ts +++ b/packages/backend/src/core/remote/activitypub/models/ApQuestionService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { NotesRepository, PollsRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { NotesRepository, PollsRepository } from '@/models/index.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'; diff --git a/packages/backend/src/daemons/JanitorService.ts b/packages/backend/src/daemons/JanitorService.ts index a51f570725..dbad576abe 100644 --- a/packages/backend/src/daemons/JanitorService.ts +++ b/packages/backend/src/daemons/JanitorService.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { LessThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { AttestationChallengesRepository } from '@/models/index.js'; +import type { AttestationChallengesRepository } from '@/models/index.js'; import type { OnApplicationShutdown } from '@nestjs/common'; const interval = 30 * 60 * 1000; diff --git a/packages/backend/src/queue/DbQueueProcessorsService.ts b/packages/backend/src/queue/DbQueueProcessorsService.ts index fcc9873a6f..7622ab8800 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 } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { DeleteDriveFilesProcessorService } from './processors/DeleteDriveFilesProcessorService.js'; import { ExportCustomEmojisProcessorService } from './processors/ExportCustomEmojisProcessorService.js'; import { ExportNotesProcessorService } from './processors/ExportNotesProcessorService.js'; diff --git a/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts b/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts index 402c038be0..659e9b8e42 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 } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { CleanRemoteFilesProcessorService } from './processors/CleanRemoteFilesProcessorService.js'; import { DeleteFileProcessorService } from './processors/DeleteFileProcessorService.js'; import type Bull from 'bull'; diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts index 753df8cad6..a4879cef3d 100644 --- a/packages/backend/src/queue/QueueProcessorService.ts +++ b/packages/backend/src/queue/QueueProcessorService.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { ModuleRef } from '@nestjs/core'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import type Logger from '@/logger.js'; import { QueueService } from '@/core/QueueService.js'; diff --git a/packages/backend/src/queue/SystemQueueProcessorsService.ts b/packages/backend/src/queue/SystemQueueProcessorsService.ts index 7c227296e7..ccb040fae5 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 } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { TickChartsProcessorService } from './processors/TickChartsProcessorService.js'; import { ResyncChartsProcessorService } from './processors/ResyncChartsProcessorService.js'; import { CleanChartsProcessorService } from './processors/CleanChartsProcessorService.js'; diff --git a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts index 17337837a3..e91cba9d10 100644 --- a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts +++ b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { MutingsRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { MutingsRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; diff --git a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts index 6f2fb8dea0..e8e90f1422 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 } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import FederationChart from '@/core/chart/charts/federation.js'; import NotesChart from '@/core/chart/charts/notes.js'; diff --git a/packages/backend/src/queue/processors/CleanProcessorService.ts b/packages/backend/src/queue/processors/CleanProcessorService.ts index 830f0c56b6..6eb457ce9f 100644 --- a/packages/backend/src/queue/processors/CleanProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanProcessorService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, LessThan, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { UserIpsRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { UserIpsRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; diff --git a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts index c3c68be1bc..4d46cdeaf9 100644 --- a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan, Not } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { DriveFilesRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { DriveFilesRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; diff --git a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts index ab82f87d5e..4d6ed2008a 100644 --- a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { DriveFilesRepository, UserProfilesRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { DriveFilesRepository, UserProfilesRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; diff --git a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts index 430fbf19e9..682382b2db 100644 --- a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { UsersRepository, DriveFilesRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { UsersRepository, DriveFilesRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; diff --git a/packages/backend/src/queue/processors/DeleteFileProcessorService.ts b/packages/backend/src/queue/processors/DeleteFileProcessorService.ts index 72923b80a9..6740643fe2 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 } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts index 1bf51c1bc6..9042a21d2c 100644 --- a/packages/backend/src/queue/processors/DeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { DriveFilesRepository, InstancesRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { DriveFilesRepository, InstancesRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { MetaService } from '@/core/MetaService.js'; import { ApRequestService } from '@/core/remote/activitypub/ApRequestService.js'; diff --git a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts index 3e55a351a1..2fc7fe219e 100644 --- a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts +++ b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { PollVotesRepository, NotesRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { PollVotesRepository, NotesRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { CreateNotificationService } from '@/core/CreateNotificationService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; diff --git a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts index cbc483698f..db149b68cf 100644 --- a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts @@ -3,9 +3,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI } from '@/di-symbols.js'; -import { UsersRepository, BlockingsRepository } from '@/models/index.js'; -import type { DriveFilesRepository, UserProfilesRepository, NotesRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { UsersRepository, BlockingsRepository, DriveFilesRepository, UserProfilesRepository, NotesRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; import { createTemp } from '@/misc/create-temp.js'; diff --git a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts index c49a47561b..aa7a0ef929 100644 --- a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts @@ -6,8 +6,8 @@ import { ulid } from 'ulid'; import mime from 'mime-types'; import archiver from 'archiver'; import { DI } from '@/di-symbols.js'; -import { EmojisRepository, UsersRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { EmojisRepository, UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/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 4c6162432a..eac9c6925e 100644 --- a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts @@ -3,8 +3,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, MoreThan, Not } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI } from '@/di-symbols.js'; -import { FollowingsRepository, MutingsRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { FollowingsRepository, MutingsRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; import { createTemp } from '@/misc/create-temp.js'; diff --git a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts index 7781d2787f..e263c245fd 100644 --- a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts @@ -3,8 +3,8 @@ 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 { MutingsRepository, UsersRepository, BlockingsRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { MutingsRepository, UsersRepository, BlockingsRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; import { createTemp } from '@/misc/create-temp.js'; diff --git a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts index 62b3a53c49..533d4bd7c6 100644 --- a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts @@ -3,8 +3,8 @@ 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 { NotesRepository, PollsRepository, UsersRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { NotesRepository, PollsRepository, UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/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 097835ac81..8c3e3dbe17 100644 --- a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts @@ -3,8 +3,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, IsNull, MoreThan } from 'typeorm'; import { format as dateFormat } from 'date-fns'; import { DI } from '@/di-symbols.js'; -import { UserListJoiningsRepository, UserListsRepository, UsersRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { UserListJoiningsRepository, UserListsRepository, UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { DriveService } from '@/core/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 44c8800a68..12f77638bb 100644 --- a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { BlockingsRepository, DriveFilesRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { BlockingsRepository, DriveFilesRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import * as Acct from '@/misc/acct.js'; import { ResolveUserService } from '@/core/remote/ResolveUserService.js'; diff --git a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts index 4919fb2f7b..492f17f9ff 100644 --- a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts @@ -3,8 +3,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan, DataSource } from 'typeorm'; import unzipper from 'unzipper'; import { DI } from '@/di-symbols.js'; -import { EmojisRepository, DriveFilesRepository, UsersRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { EmojisRepository, DriveFilesRepository, UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { CustomEmojiService } from '@/core/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 5e49678d05..f649014399 100644 --- a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { DriveFilesRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { DriveFilesRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import * as Acct from '@/misc/acct.js'; import { ResolveUserService } from '@/core/remote/ResolveUserService.js'; diff --git a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts index c613c7e74e..f004f2d64b 100644 --- a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { DriveFilesRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { DriveFilesRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import * as Acct from '@/misc/acct.js'; import { ResolveUserService } from '@/core/remote/ResolveUserService.js'; diff --git a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts index 96c862e5c9..168f850718 100644 --- a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { DriveFilesRepository, UserListJoiningsRepository, UserListsRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { DriveFilesRepository, UserListJoiningsRepository, UserListsRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import * as Acct from '@/misc/acct.js'; import { ResolveUserService } from '@/core/remote/ResolveUserService.js'; diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index 4593b4fb61..ad72263a8d 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -3,8 +3,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { MoreThan } from 'typeorm'; import httpSignature from '@peertube/http-signature'; import { DI } from '@/di-symbols.js'; -import { DriveFilesRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { DriveFilesRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { MetaService } from '@/core/MetaService.js'; import { ApRequestService } from '@/core/remote/activitypub/ApRequestService.js'; diff --git a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts index 75d02d527a..bf2fdeb7a0 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 } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import FederationChart from '@/core/chart/charts/federation.js'; import NotesChart from '@/core/chart/charts/notes.js'; diff --git a/packages/backend/src/queue/processors/TickChartsProcessorService.ts b/packages/backend/src/queue/processors/TickChartsProcessorService.ts index e16956df0c..96607e1d68 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 } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import FederationChart from '@/core/chart/charts/federation.js'; import NotesChart from '@/core/chart/charts/notes.js'; diff --git a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts index 27243be51b..43e3f37201 100644 --- a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { WebhooksRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { WebhooksRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import { StatusError } from '@/misc/status-error.js'; diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 21ecc7177a..b2fcf9f81a 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -4,9 +4,9 @@ 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 { EmojisRepository, NoteReactionsRepository, UserProfilesRepository, UserNotePiningsRepository, UsersRepository } from '@/models/index.js'; +import type { EmojisRepository, NoteReactionsRepository, UserProfilesRepository, UserNotePiningsRepository, UsersRepository } from '@/models/index.js'; import * as url from '@/misc/prelude/url.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { ApRendererService } from '@/core/remote/activitypub/ApRendererService.js'; import { QueueService } from '@/core/QueueService.js'; import type { ILocalUser, User } from '@/models/entities/User.js'; diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index becf0592d7..dc073e34ac 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -7,8 +7,8 @@ import cors from '@koa/cors'; import Router from '@koa/router'; import send from 'koa-send'; import rename from 'rename'; -import { Config } from '@/config.js'; -import { DriveFilesRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; +import type { DriveFilesRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { createTemp } from '@/misc/create-temp.js'; import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; diff --git a/packages/backend/src/server/MediaProxyServerService.ts b/packages/backend/src/server/MediaProxyServerService.ts index 1e0385602c..31841d39df 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 } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { isMimeImage } from '@/misc/is-mime-image.js'; import { createTemp } from '@/misc/create-temp.js'; import { DownloadService } from '@/core/DownloadService.js'; diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index 04a5f1484b..b07dc4cf8f 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -2,8 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import Router from '@koa/router'; import { IsNull, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { NotesRepository, UsersRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { NotesRepository, UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import { MetaService } from '@/core/MetaService.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { Cache } from '@/misc/cache.js'; diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 14d5ed45ab..d42972614f 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -9,8 +9,8 @@ import koaLogger from 'koa-logger'; import * as slow from 'koa-slow'; import { IsNull } from 'typeorm'; import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { Config } from '@/config.js'; -import { UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; +import type { UserProfilesRepository, UsersRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import type Logger from '@/logger.js'; import { envOption } from '@/env.js'; diff --git a/packages/backend/src/server/WellKnownServerService.ts b/packages/backend/src/server/WellKnownServerService.ts index 7f827d439b..f2eee88e09 100644 --- a/packages/backend/src/server/WellKnownServerService.ts +++ b/packages/backend/src/server/WellKnownServerService.ts @@ -2,8 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import Router from '@koa/router'; import { IsNull, MoreThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { UsersRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import { escapeAttribute, escapeValue } from '@/misc/prelude/xml.js'; import type { User } from '@/models/entities/User.js'; import * as Acct from '@/misc/acct.js'; diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index d13b8d5ced..c3ce12e0c3 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -5,7 +5,7 @@ import { getIpHash } from '@/misc/get-ip-hash.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 { UserIpsRepository } from '@/models/index.js'; +import type { UserIpsRepository } from '@/models/index.js'; import { MetaService } from '@/core/MetaService.js'; import { ApiError } from './error.js'; import { RateLimiterService } from './RateLimiterService.js'; diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts index cfe39238dd..52654dbaee 100644 --- a/packages/backend/src/server/api/ApiServerService.ts +++ b/packages/backend/src/server/api/ApiServerService.ts @@ -5,8 +5,8 @@ import multer from '@koa/multer'; import bodyParser from 'koa-bodyparser'; import cors from '@koa/cors'; import { ModuleRef } from '@nestjs/core'; -import { Config } from '@/config.js'; -import { UsersRepository, InstancesRepository, AccessTokensRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; +import type { UsersRepository, InstancesRepository, AccessTokensRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import endpoints from './endpoints.js'; diff --git a/packages/backend/src/server/api/AuthenticateService.ts b/packages/backend/src/server/api/AuthenticateService.ts index 29d6ba78f0..4ce9b91f42 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 { AccessTokensRepository, AppsRepository, UsersRepository } from '@/models/index.js'; +import type { AccessTokensRepository, AppsRepository, UsersRepository } 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'; diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index 5cda3c6205..a5e2b09012 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -4,8 +4,8 @@ import bcrypt from 'bcryptjs'; import * as speakeasy from 'speakeasy'; import { IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import { UserSecurityKeysRepository, SigninsRepository, UserProfilesRepository, AttestationChallengesRepository, UsersRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { UserSecurityKeysRepository, SigninsRepository, UserProfilesRepository, AttestationChallengesRepository, UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import { getIpHash } from '@/misc/get-ip-hash.js'; import type { ILocalUser } from '@/models/entities/User.js'; import { IdService } from '@/core/IdService.js'; diff --git a/packages/backend/src/server/api/SigninService.ts b/packages/backend/src/server/api/SigninService.ts index 19d14bad67..3b96dfee6f 100644 --- a/packages/backend/src/server/api/SigninService.ts +++ b/packages/backend/src/server/api/SigninService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { SigninsRepository } from '@/models/index.js'; +import type { SigninsRepository } from '@/models/index.js'; import type { UsersRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { IdService } from '@/core/IdService.js'; import type { ILocalUser } from '@/models/entities/User.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts index df040ddcf8..06b8b29134 100644 --- a/packages/backend/src/server/api/SignupApiService.ts +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -2,8 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import rndstr from 'rndstr'; import bcrypt from 'bcryptjs'; import { DI } from '@/di-symbols.js'; -import { RegistrationTicketsRepository, UserPendingsRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { RegistrationTicketsRepository, UserPendingsRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import { MetaService } from '@/core/MetaService.js'; import { CaptchaService } from '@/core/CaptchaService.js'; import { IdService } from '@/core/IdService.js'; diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts index b08b01aef9..8a6906e5fe 100644 --- a/packages/backend/src/server/api/StreamingApiServerService.ts +++ b/packages/backend/src/server/api/StreamingApiServerService.ts @@ -3,8 +3,8 @@ import { Inject, Injectable } from '@nestjs/common'; import Redis from 'ioredis'; import * as websocket from 'websocket'; import { DI } from '@/di-symbols.js'; -import { UsersRepository, BlockingsRepository, ChannelFollowingsRepository, FollowingsRepository, MutingsRepository, UserProfilesRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { UsersRepository, BlockingsRepository, ChannelFollowingsRepository, FollowingsRepository, MutingsRepository, UserProfilesRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import { NoteReadService } from '@/core/NoteReadService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { NotificationService } from '@/core/NotificationService.js'; diff --git a/packages/backend/src/server/api/common/GetterService.ts b/packages/backend/src/server/api/common/GetterService.ts index 5523539b91..a6b60d1f5a 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 { NotesRepository, UsersRepository } from '@/models/index.js'; +import type { NotesRepository, UsersRepository } 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'; 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 480ae7166e..30183ed88b 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 { AbuseUserReportsRepository } from '@/models/index.js'; +import type { AbuseUserReportsRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; 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 1b173379a0..c76ece9e05 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import { SignupService } from '@/core/SignupService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { localUsernameSchema, passwordSchema } from '@/models/entities/User.js'; 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 2e0222f0c6..dc2d499191 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 { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import { QueueService } from '@/core/QueueService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { UserSuspendService } from '@/core/UserSuspendService.js'; 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 6b32391e8d..8fcbde591b 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 { AdsRepository } from '@/models/index.js'; +import type { AdsRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; 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 7abefe156b..f4c9885408 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 { AdsRepository } from '@/models/index.js'; +import type { AdsRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 efece31bbf..29e245ab95 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/list.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { AdsRepository } from '@/models/index.js'; +import type { AdsRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; 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 098a593379..195300666e 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 { AdsRepository } from '@/models/index.js'; +import type { AdsRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 ee07170d62..751b6be7f4 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { AnnouncementsRepository } from '@/models/index.js'; +import type { AnnouncementsRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; 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 9a67bdb1aa..18d50b8b2a 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 { AnnouncementsRepository } from '@/models/index.js'; +import type { AnnouncementsRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 35c14abda2..9b20494129 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 { AnnouncementsRepository, AnnouncementReadsRepository } from '@/models/index.js'; +import type { AnnouncementsRepository, AnnouncementReadsRepository } from '@/models/index.js'; import type { Announcement } from '@/models/entities/Announcement.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; 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 38358dff10..2393c2441c 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 { AnnouncementsRepository } from '@/models/index.js'; +import type { AnnouncementsRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 c8b67fe1c0..d0485fddd8 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 { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DeleteAccountService } from '@/core/DeleteAccountService.js'; import { DI } from '@/di-symbols.js'; 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 051e4c60fb..22b78bf19d 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,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/index.js'; import { DriveService } from '@/core/DriveService.js'; import { DI } from '@/di-symbols.js'; 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 770bade06d..f4d39cd872 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,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import { DI } from '@/di-symbols.js'; 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 3927a89f90..4f7e02fe92 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts @@ -1,7 +1,7 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/index.js'; import { DriveService } from '@/core/DriveService.js'; import { DI } from '@/di-symbols.js'; 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 88529ab0aa..2459a479ab 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 { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; 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 45ea9cdb50..c17f67cf2b 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 { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.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 0b6e744ef8..7c24e8baa8 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,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource, In } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { EmojisRepository } from '@/models/index.js'; +import type { EmojisRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; export const meta = { 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 daa57e8eb2..c4e1987d73 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 { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFilesRepository, EmojisRepository } from '@/models/index.js'; +import type { DriveFilesRepository, EmojisRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; import { GlobalEventService } from '@/core/GlobalEventService.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 08d40834c1..2cdd9c36bd 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 { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { EmojisRepository } from '@/models/index.js'; +import type { EmojisRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import { DI } from '@/di-symbols.js'; 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 81b095cb57..8b2031e6dd 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,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource, In } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { EmojisRepository } from '@/models/index.js'; +import type { EmojisRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; 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 e4278dc33a..dd7cd4cede 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { EmojisRepository } from '@/models/index.js'; +import type { EmojisRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import { ApiError } from '../../../error.js'; 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 9d6fa53417..c03d27878c 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 { EmojisRepository } from '@/models/index.js'; +import type { EmojisRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { UtilityService } from '@/core/UtilityService.js'; import { EmojiEntityService } from '@/core/entities/EmojiEntityService.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 736d664cc3..e50f924044 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 { EmojisRepository } from '@/models/index.js'; +import type { EmojisRepository } from '@/models/index.js'; import type { Emoji } from '@/models/entities/Emoji.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; 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 d6c70eaae7..99512a26b3 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,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource, In } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { EmojisRepository } from '@/models/index.js'; +import type { EmojisRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; export const meta = { 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 c438b7f9b7..697999cc7c 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,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource, In } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { EmojisRepository } from '@/models/index.js'; +import type { EmojisRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; export const meta = { 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 4a9b31fd28..00a5b162bf 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,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource, In } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { EmojisRepository } from '@/models/index.js'; +import type { EmojisRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; export const meta = { 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 e6eb9eb9a6..c576950ac7 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,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { EmojisRepository } from '@/models/index.js'; +import type { EmojisRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 789838661c..38fe99b222 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,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/index.js'; import { DriveService } from '@/core/DriveService.js'; import { DI } from '@/di-symbols.js'; 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 476b821523..b7f2858a77 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,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { InstancesRepository } from '@/models/index.js'; +import type { InstancesRepository } from '@/models/index.js'; import { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataService.js'; import { UtilityService } from '@/core/UtilityService.js'; import { DI } from '@/di-symbols.js'; 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 67165dc47e..b073209a5b 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,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { FollowingsRepository, UsersRepository } from '@/models/index.js'; +import type { FollowingsRepository, UsersRepository } from '@/models/index.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; import { DI } from '@/di-symbols.js'; 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 b9eade5b40..0a529ecb08 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 { InstancesRepository } from '@/models/index.js'; +import type { InstancesRepository } from '@/models/index.js'; import { UtilityService } from '@/core/UtilityService.js'; import { DI } from '@/di-symbols.js'; 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 eddaade919..947a673def 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 { UserIpsRepository } from '@/models/index.js'; +import type { UserIpsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/invite.ts b/packages/backend/src/server/api/endpoints/admin/invite.ts index 5fe341e5ca..bc42bf792a 100644 --- a/packages/backend/src/server/api/endpoints/admin/invite.ts +++ b/packages/backend/src/server/api/endpoints/admin/invite.ts @@ -1,7 +1,7 @@ import rndstr from 'rndstr'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { RegistrationTicketsRepository } from '@/models/index.js'; +import type { RegistrationTicketsRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 615c0a0e70..5b43c180d8 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 '@/core/MetaService.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; export const meta = { 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 fe200da6ad..2fc5a35e8e 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 { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; 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 3dc7158ba9..f0d7a3f12d 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 { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; 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 a179f163df..0cff6bae6a 100644 --- a/packages/backend/src/server/api/endpoints/admin/promo/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/promo/create.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { PromoNotesRepository } from '@/models/index.js'; +import type { PromoNotesRepository } from '@/models/index.js'; import { GetterService } from '@/server/api/common/GetterService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 7446746b45..f7d27be9cb 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,7 @@ import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; import rndstr from 'rndstr'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UsersRepository, UserProfilesRepository } from '@/models/index.js'; +import type { UsersRepository, UserProfilesRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; export const meta = { 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 b5828ae9be..a6e59276fb 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,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UsersRepository, AbuseUserReportsRepository } from '@/models/index.js'; +import type { UsersRepository, AbuseUserReportsRepository } from '@/models/index.js'; import { InstanceActorService } from '@/core/InstanceActorService.js'; import { QueueService } from '@/core/QueueService.js'; import { ApRendererService } from '@/core/remote/activitypub/ApRendererService.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 2424cac425..ac0a84128c 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 { ModerationLogsRepository } from '@/models/index.js'; +import type { ModerationLogsRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; 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 b50564210b..e4031cf960 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 { UsersRepository, SigninsRepository, UserProfilesRepository } from '@/models/index.js'; +import type { UsersRepository, SigninsRepository, UserProfilesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; 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 8d11e3ea7a..d28f9c71b7 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 { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; 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 bec8f7719e..b9dbd211e0 100644 --- a/packages/backend/src/server/api/endpoints/admin/silence-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/silence-user.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; -import { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; 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 d9266aac6c..d30facd125 100644 --- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UsersRepository, FollowingsRepository, NotificationsRepository } from '@/models/index.js'; +import type { UsersRepository, FollowingsRepository, NotificationsRepository } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; 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 b4671a2f41..3a9d410de0 100644 --- a/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import { DI } from '@/di-symbols.js'; 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 96283d251f..2805c21a74 100644 --- a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import { UserSuspendService } from '@/core/UserSuspendService.js'; import { DI } from '@/di-symbols.js'; 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 1ea0e6aac4..33808ee70f 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 { UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { UserProfilesRepository, UsersRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/announcements.ts b/packages/backend/src/server/api/endpoints/announcements.ts index aa44dfd5dc..74168481f6 100644 --- a/packages/backend/src/server/api/endpoints/announcements.ts +++ b/packages/backend/src/server/api/endpoints/announcements.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; -import { AnnouncementReadsRepository, AnnouncementsRepository } from '@/models'; +import type { AnnouncementReadsRepository, AnnouncementsRepository } from '@/models'; export const meta = { tags: ['meta'], diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index 56bd343d55..fe24a10300 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/core/IdService.js'; -import { UserListsRepository, UserGroupJoiningsRepository, AntennasRepository } from '@/models/index.js'; +import type { UserListsRepository, UserGroupJoiningsRepository, AntennasRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/antennas/delete.ts b/packages/backend/src/server/api/endpoints/antennas/delete.ts index 127aca0c33..5da7a2cb66 100644 --- a/packages/backend/src/server/api/endpoints/antennas/delete.ts +++ b/packages/backend/src/server/api/endpoints/antennas/delete.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { AntennasRepository } from '@/models/index.js'; +import type { AntennasRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/antennas/list.ts b/packages/backend/src/server/api/endpoints/antennas/list.ts index bdc44895cc..a0f8979574 100644 --- a/packages/backend/src/server/api/endpoints/antennas/list.ts +++ b/packages/backend/src/server/api/endpoints/antennas/list.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { AntennasRepository } from '@/models/index.js'; +import type { AntennasRepository } from '@/models/index.js'; import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index eba42afe56..fb3c713154 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { NotesRepository, AntennaNotesRepository } from '@/models/index.js'; +import type { NotesRepository, AntennaNotesRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteReadService } from '@/core/NoteReadService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/antennas/show.ts b/packages/backend/src/server/api/endpoints/antennas/show.ts index 8bd8ad124d..ef7ed5b72c 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 { AntennasRepository } from '@/models/index.js'; +import type { AntennasRepository } from '@/models/index.js'; import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts index 59bba04ee1..1955eac949 100644 --- a/packages/backend/src/server/api/endpoints/antennas/update.ts +++ b/packages/backend/src/server/api/endpoints/antennas/update.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { AntennasRepository, UserListsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; +import type { AntennasRepository, UserListsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index e291b5908a..0a5fc31751 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 { UsersRepository, NotesRepository } from '@/models/index.js'; +import type { UsersRepository, NotesRepository } 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 '@/core/remote/activitypub/type.js'; diff --git a/packages/backend/src/server/api/endpoints/app/create.ts b/packages/backend/src/server/api/endpoints/app/create.ts index f52d18f7fe..c1d0a9dd74 100644 --- a/packages/backend/src/server/api/endpoints/app/create.ts +++ b/packages/backend/src/server/api/endpoints/app/create.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { AppsRepository } from '@/models/index.js'; +import type { AppsRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import { unique } from '@/misc/prelude/array.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; diff --git a/packages/backend/src/server/api/endpoints/app/show.ts b/packages/backend/src/server/api/endpoints/app/show.ts index f94fed5344..eaafa8dc1b 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 { AppsRepository } from '@/models/index.js'; +import type { AppsRepository } from '@/models/index.js'; import { AppEntityService } from '@/core/entities/AppEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/auth/accept.ts b/packages/backend/src/server/api/endpoints/auth/accept.ts index 6032b59bef..cb2e661bfb 100644 --- a/packages/backend/src/server/api/endpoints/auth/accept.ts +++ b/packages/backend/src/server/api/endpoints/auth/accept.ts @@ -1,7 +1,7 @@ import * as crypto from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { AuthSessionsRepository, AppsRepository, AccessTokensRepository } from '@/models/index.js'; +import type { AuthSessionsRepository, AppsRepository, AccessTokensRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; import { DI } from '@/di-symbols.js'; 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 7f8325dbbd..6108d8202d 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,9 @@ import { v4 as uuid } from 'uuid'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { AppsRepository, AuthSessionsRepository } from '@/models/index.js'; +import type { AppsRepository, AuthSessionsRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 dff4c74340..db3bf7aa63 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 { AuthSessionsRepository } from '@/models/index.js'; +import type { AuthSessionsRepository } from '@/models/index.js'; import { AuthSessionEntityService } from '@/core/entities/AuthSessionEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 9c9f13f502..b1e7bbfded 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,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UsersRepository, AppsRepository, AccessTokensRepository, AuthSessionsRepository } from '@/models/index.js'; +import type { UsersRepository, AppsRepository, AccessTokensRepository, AuthSessionsRepository } from '@/models/index.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts index 33614a1554..aa6d2ecf20 100644 --- a/packages/backend/src/server/api/endpoints/blocking/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking/create.ts @@ -1,7 +1,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UsersRepository, BlockingsRepository } from '@/models/index.js'; +import type { UsersRepository, BlockingsRepository } from '@/models/index.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts index f2cc28e922..46a499943c 100644 --- a/packages/backend/src/server/api/endpoints/blocking/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts @@ -1,7 +1,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UsersRepository, BlockingsRepository } from '@/models/index.js'; +import type { UsersRepository, BlockingsRepository } from '@/models/index.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/blocking/list.ts b/packages/backend/src/server/api/endpoints/blocking/list.ts index 4f5e11cd68..969aae06f9 100644 --- a/packages/backend/src/server/api/endpoints/blocking/list.ts +++ b/packages/backend/src/server/api/endpoints/blocking/list.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { BlockingsRepository } from '@/models/index.js'; +import type { BlockingsRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { BlockingEntityService } from '@/core/entities/BlockingEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/channels/create.ts b/packages/backend/src/server/api/endpoints/channels/create.ts index 21979884f9..10f8b24629 100644 --- a/packages/backend/src/server/api/endpoints/channels/create.ts +++ b/packages/backend/src/server/api/endpoints/channels/create.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ChannelsRepository, DriveFilesRepository } from '@/models/index.js'; +import type { ChannelsRepository, DriveFilesRepository } from '@/models/index.js'; import type { Channel } from '@/models/entities/Channel.js'; import { IdService } from '@/core/IdService.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/channels/featured.ts b/packages/backend/src/server/api/endpoints/channels/featured.ts index 0c3f9509d1..d25faae38d 100644 --- a/packages/backend/src/server/api/endpoints/channels/featured.ts +++ b/packages/backend/src/server/api/endpoints/channels/featured.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ChannelsRepository } from '@/models/index.js'; +import type { ChannelsRepository } from '@/models/index.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/channels/follow.ts b/packages/backend/src/server/api/endpoints/channels/follow.ts index 6c6b498a94..871d3927bc 100644 --- a/packages/backend/src/server/api/endpoints/channels/follow.ts +++ b/packages/backend/src/server/api/endpoints/channels/follow.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ChannelFollowingsRepository, ChannelsRepository } from '@/models/index.js'; +import type { ChannelFollowingsRepository, ChannelsRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/channels/followed.ts b/packages/backend/src/server/api/endpoints/channels/followed.ts index 5a8ab26af9..f49f3105d5 100644 --- a/packages/backend/src/server/api/endpoints/channels/followed.ts +++ b/packages/backend/src/server/api/endpoints/channels/followed.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ChannelFollowingsRepository } from '@/models/index.js'; +import type { ChannelFollowingsRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/channels/owned.ts b/packages/backend/src/server/api/endpoints/channels/owned.ts index 8b8b5819e6..59df0616be 100644 --- a/packages/backend/src/server/api/endpoints/channels/owned.ts +++ b/packages/backend/src/server/api/endpoints/channels/owned.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ChannelsRepository } from '@/models/index.js'; +import type { ChannelsRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/channels/show.ts b/packages/backend/src/server/api/endpoints/channels/show.ts index 54ae31790b..8718615db2 100644 --- a/packages/backend/src/server/api/endpoints/channels/show.ts +++ b/packages/backend/src/server/api/endpoints/channels/show.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ChannelsRepository } from '@/models/index.js'; +import type { ChannelsRepository } from '@/models/index.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index 1c7f1360b9..58f8835279 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ChannelsRepository, NotesRepository } from '@/models/index.js'; +import type { ChannelsRepository, NotesRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import ActiveUsersChart from '@/core/chart/charts/active-users.js'; diff --git a/packages/backend/src/server/api/endpoints/channels/unfollow.ts b/packages/backend/src/server/api/endpoints/channels/unfollow.ts index b464c55097..ac2ef825be 100644 --- a/packages/backend/src/server/api/endpoints/channels/unfollow.ts +++ b/packages/backend/src/server/api/endpoints/channels/unfollow.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ChannelFollowingsRepository, ChannelsRepository } from '@/models/index.js'; +import type { ChannelFollowingsRepository, ChannelsRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/channels/update.ts b/packages/backend/src/server/api/endpoints/channels/update.ts index ba62e9d371..d006e89bd2 100644 --- a/packages/backend/src/server/api/endpoints/channels/update.ts +++ b/packages/backend/src/server/api/endpoints/channels/update.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFilesRepository, ChannelsRepository } from '@/models/index.js'; +import type { DriveFilesRepository, ChannelsRepository } from '@/models/index.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; 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 c733d28657..77d02815e0 100644 --- a/packages/backend/src/server/api/endpoints/clips/add-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/add-note.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; -import { ClipNotesRepository, ClipsRepository } from '@/models/index.js'; +import type { ClipNotesRepository, ClipsRepository } from '@/models/index.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; diff --git a/packages/backend/src/server/api/endpoints/clips/create.ts b/packages/backend/src/server/api/endpoints/clips/create.ts index 8eca3d66d1..d300203a21 100644 --- a/packages/backend/src/server/api/endpoints/clips/create.ts +++ b/packages/backend/src/server/api/endpoints/clips/create.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/core/IdService.js'; -import { ClipsRepository } from '@/models/index.js'; +import type { ClipsRepository } from '@/models/index.js'; import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/clips/delete.ts b/packages/backend/src/server/api/endpoints/clips/delete.ts index ea361ae9c0..077a9ec40f 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 { ClipsRepository } from '@/models/index.js'; +import type { ClipsRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/clips/list.ts b/packages/backend/src/server/api/endpoints/clips/list.ts index b57affd1c4..63ca069364 100644 --- a/packages/backend/src/server/api/endpoints/clips/list.ts +++ b/packages/backend/src/server/api/endpoints/clips/list.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ClipsRepository } from '@/models/index.js'; +import type { ClipsRepository } from '@/models/index.js'; import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts index 4282498931..6818d31cc4 100644 --- a/packages/backend/src/server/api/endpoints/clips/notes.ts +++ b/packages/backend/src/server/api/endpoints/clips/notes.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { NotesRepository, ClipsRepository, ClipNotesRepository } from '@/models/index.js'; +import type { NotesRepository, ClipsRepository, ClipNotesRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; 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 3fc60e3639..93805af089 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,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ClipNotesRepository, ClipsRepository } from '@/models/index.js'; +import type { ClipNotesRepository, ClipsRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { GetterService } from '../../common/GetterService.js'; diff --git a/packages/backend/src/server/api/endpoints/clips/show.ts b/packages/backend/src/server/api/endpoints/clips/show.ts index 4e93540054..e6d3f4f1f8 100644 --- a/packages/backend/src/server/api/endpoints/clips/show.ts +++ b/packages/backend/src/server/api/endpoints/clips/show.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ClipsRepository } from '@/models/index.js'; +import type { ClipsRepository } from '@/models/index.js'; import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/clips/update.ts b/packages/backend/src/server/api/endpoints/clips/update.ts index 9880505d06..597b67c442 100644 --- a/packages/backend/src/server/api/endpoints/clips/update.ts +++ b/packages/backend/src/server/api/endpoints/clips/update.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ClipsRepository } from '@/models/index.js'; +import type { ClipsRepository } from '@/models/index.js'; import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/drive/files.ts b/packages/backend/src/server/api/endpoints/drive/files.ts index 56055d1340..f6fad50fd9 100644 --- a/packages/backend/src/server/api/endpoints/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/drive/files.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { DI } from '@/di-symbols.js'; 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 9f11eb8b53..328d0e4643 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,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { NotesRepository, DriveFilesRepository } from '@/models/index.js'; +import type { NotesRepository, DriveFilesRepository } from '@/models/index.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 176031d808..290cd4d2ce 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 { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; export const meta = { 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 bff83876d7..d394f5c3da 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/create.ts @@ -1,6 +1,6 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/index.js'; import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { Endpoint } from '@/server/api/endpoint-base.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 9d2ea6011a..be7b050907 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/delete.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/delete.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/index.js'; import { DriveService } from '@/core/DriveService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; 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 6299ca8f6b..d6d85f4e77 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,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { DI } from '@/di-symbols.js'; 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 e4cd5213dd..858063eb4b 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 { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/index.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { DI } from '@/di-symbols.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 bae4d7d66f..474d599cb6 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,6 @@ import { Inject, Injectable } from '@nestjs/common'; import type { DriveFile } from '@/models/entities/DriveFile.js'; -import { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { DI } from '@/di-symbols.js'; 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 03e3663f08..703f92d8c6 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { DriveFilesRepository, DriveFoldersRepository } from '@/models/index.js'; +import type { DriveFilesRepository, DriveFoldersRepository } 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 '@/core/entities/DriveFileEntityService.js'; 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 f4f8df3c2b..19ab03a337 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,6 +1,6 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } 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 '@/core/GlobalEventService.js'; diff --git a/packages/backend/src/server/api/endpoints/drive/folders.ts b/packages/backend/src/server/api/endpoints/drive/folders.ts index 703dc83ecd..b41eaf4463 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFoldersRepository } from '@/models/index.js'; +import type { DriveFoldersRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { DriveFolderEntityService } from '@/core/entities/DriveFolderEntityService.js'; import { DI } from '@/di-symbols.js'; 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 7604eaf489..e7c11a8c13 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/create.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFoldersRepository } from '@/models/index.js'; +import type { DriveFoldersRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import { DriveFolderEntityService } from '@/core/entities/DriveFolderEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; 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 dcbaecf8af..d921bc1b17 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/delete.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/delete.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFoldersRepository, DriveFilesRepository } from '@/models/index.js'; +import type { DriveFoldersRepository, DriveFilesRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 96a87344a9..ee24db11f2 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,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFoldersRepository } from '@/models/index.js'; +import type { DriveFoldersRepository } from '@/models/index.js'; import { DriveFolderEntityService } from '@/core/entities/DriveFolderEntityService.js'; import { DI } from '@/di-symbols.js'; 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 4c25bc705c..c06263b902 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,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFoldersRepository } from '@/models/index.js'; +import type { DriveFoldersRepository } from '@/models/index.js'; import { DriveFolderEntityService } from '@/core/entities/DriveFolderEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 4fcd37bbbf..ee63d291b2 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/update.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFoldersRepository } from '@/models/index.js'; +import type { DriveFoldersRepository } from '@/models/index.js'; import { DriveFolderEntityService } from '@/core/entities/DriveFolderEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/drive/stream.ts b/packages/backend/src/server/api/endpoints/drive/stream.ts index aba73c2092..61bcfea0c3 100644 --- a/packages/backend/src/server/api/endpoints/drive/stream.ts +++ b/packages/backend/src/server/api/endpoints/drive/stream.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/federation/followers.ts b/packages/backend/src/server/api/endpoints/federation/followers.ts index e5222fcbfd..be1d6c8e58 100644 --- a/packages/backend/src/server/api/endpoints/federation/followers.ts +++ b/packages/backend/src/server/api/endpoints/federation/followers.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { FollowingsRepository } from '@/models/index.js'; +import type { FollowingsRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { FollowingEntityService } from '@/core/entities/FollowingEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/federation/following.ts b/packages/backend/src/server/api/endpoints/federation/following.ts index a20c5a31b3..74656ce863 100644 --- a/packages/backend/src/server/api/endpoints/federation/following.ts +++ b/packages/backend/src/server/api/endpoints/federation/following.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { FollowingsRepository } from '@/models/index.js'; +import type { FollowingsRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { FollowingEntityService } from '@/core/entities/FollowingEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts index e7f8cefff5..81276a7ab0 100644 --- a/packages/backend/src/server/api/endpoints/federation/instances.ts +++ b/packages/backend/src/server/api/endpoints/federation/instances.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { InstancesRepository } from '@/models/index.js'; +import type { InstancesRepository } from '@/models/index.js'; import { InstanceEntityService } from '@/core/entities/InstanceEntityService.js'; import { MetaService } from '@/core/MetaService.js'; import { DI } from '@/di-symbols.js'; 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 f855b54537..66502748b3 100644 --- a/packages/backend/src/server/api/endpoints/federation/show-instance.ts +++ b/packages/backend/src/server/api/endpoints/federation/show-instance.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { InstancesRepository } from '@/models/index.js'; +import type { InstancesRepository } from '@/models/index.js'; import { InstanceEntityService } from '@/core/entities/InstanceEntityService.js'; import { UtilityService } from '@/core/UtilityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/federation/stats.ts b/packages/backend/src/server/api/endpoints/federation/stats.ts index d07a08637f..19418e698c 100644 --- a/packages/backend/src/server/api/endpoints/federation/stats.ts +++ b/packages/backend/src/server/api/endpoints/federation/stats.ts @@ -1,6 +1,6 @@ import { IsNull, MoreThan, Not } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { FollowingsRepository, InstancesRepository } from '@/models/index.js'; +import type { FollowingsRepository, InstancesRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { InstanceEntityService } from '@/core/entities/InstanceEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/federation/users.ts b/packages/backend/src/server/api/endpoints/federation/users.ts index 0400cacd02..a028930f21 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 { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/fetch-rss.ts b/packages/backend/src/server/api/endpoints/fetch-rss.ts index 9e6a3cc717..58fa01ac48 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 { Endpoint } from '@/server/api/endpoint-base.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts index 3a06c63d52..be322e2896 100644 --- a/packages/backend/src/server/api/endpoints/following/create.ts +++ b/packages/backend/src/server/api/endpoints/following/create.ts @@ -1,7 +1,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UsersRepository, FollowingsRepository } from '@/models/index.js'; +import type { UsersRepository, FollowingsRepository } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; diff --git a/packages/backend/src/server/api/endpoints/following/delete.ts b/packages/backend/src/server/api/endpoints/following/delete.ts index 07366bc821..afb59dd2c2 100644 --- a/packages/backend/src/server/api/endpoints/following/delete.ts +++ b/packages/backend/src/server/api/endpoints/following/delete.ts @@ -1,7 +1,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UsersRepository, FollowingsRepository } from '@/models/index.js'; +import type { UsersRepository, FollowingsRepository } from '@/models/index.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/following/invalidate.ts b/packages/backend/src/server/api/endpoints/following/invalidate.ts index 8285189d66..e67e136ead 100644 --- a/packages/backend/src/server/api/endpoints/following/invalidate.ts +++ b/packages/backend/src/server/api/endpoints/following/invalidate.ts @@ -1,7 +1,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UsersRepository, FollowingsRepository } from '@/models/index.js'; +import type { UsersRepository, FollowingsRepository } from '@/models/index.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; import { DI } from '@/di-symbols.js'; 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 0b79a80649..213b5ce32e 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { FollowingsRepository } from '@/models/index.js'; +import type { FollowingsRepository } from '@/models/index.js'; import type { UsersRepository } from '@/models/index.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; 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 c36d4a077f..5b11633e6f 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,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { FollowRequestsRepository } from '@/models/index.js'; +import type { FollowRequestsRepository } from '@/models/index.js'; import { FollowRequestEntityService } from '@/core/entities/FollowRequestEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/gallery/featured.ts b/packages/backend/src/server/api/endpoints/gallery/featured.ts index 3b892ef522..9994ce90d7 100644 --- a/packages/backend/src/server/api/endpoints/gallery/featured.ts +++ b/packages/backend/src/server/api/endpoints/gallery/featured.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GalleryPostsRepository } from '@/models/index.js'; +import type { GalleryPostsRepository } from '@/models/index.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/gallery/popular.ts b/packages/backend/src/server/api/endpoints/gallery/popular.ts index 551ea64835..55d3dabfb0 100644 --- a/packages/backend/src/server/api/endpoints/gallery/popular.ts +++ b/packages/backend/src/server/api/endpoints/gallery/popular.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GalleryPostsRepository } from '@/models/index.js'; +import type { GalleryPostsRepository } from '@/models/index.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/gallery/posts.ts b/packages/backend/src/server/api/endpoints/gallery/posts.ts index 4afcbce816..e94003eb79 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GalleryPostsRepository } from '@/models/index.js'; +import type { GalleryPostsRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { DI } from '@/di-symbols.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 9e8bcac131..2842308510 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts @@ -1,7 +1,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFilesRepository, GalleryPostsRepository } from '@/models/index.js'; +import type { DriveFilesRepository, GalleryPostsRepository } from '@/models/index.js'; import { GalleryPost } from '@/models/entities/GalleryPost.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import { IdService } from '@/core/IdService.js'; 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 ad5f95c853..6cdcc17b39 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 { GalleryPostsRepository } from '@/models/index.js'; +import type { GalleryPostsRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 8aca98119b..519e56ed6a 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GalleryLikesRepository, GalleryPostsRepository } from '@/models/index.js'; +import type { GalleryLikesRepository, GalleryPostsRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 723906d60f..f7e828142b 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,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GalleryPostsRepository } from '@/models/index.js'; +import type { GalleryPostsRepository } from '@/models/index.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 d878582998..cfbedcc4d9 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 { GalleryPostsRepository, GalleryLikesRepository } from '@/models/index.js'; +import type { GalleryPostsRepository, GalleryLikesRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.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 1900afaeb6..d261aaa966 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts @@ -1,7 +1,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { DriveFilesRepository, GalleryPostsRepository } from '@/models/index.js'; +import type { DriveFilesRepository, GalleryPostsRepository } from '@/models/index.js'; import { GalleryPost } from '@/models/entities/GalleryPost.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; 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 2d9bf29dd9..dea0f4799c 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 { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/hashtags/list.ts b/packages/backend/src/server/api/endpoints/hashtags/list.ts index a7e7e6ba35..226a11de0b 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/list.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/list.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { HashtagsRepository } from '@/models/index.js'; +import type { HashtagsRepository } from '@/models/index.js'; import { HashtagEntityService } from '@/core/entities/HashtagEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/hashtags/search.ts b/packages/backend/src/server/api/endpoints/hashtags/search.ts index 3fb77bef9b..7f787ea38f 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 { HashtagsRepository } from '@/models/index.js'; +import type { HashtagsRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/hashtags/show.ts b/packages/backend/src/server/api/endpoints/hashtags/show.ts index 59170f6d0e..06b0d6e9b2 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/show.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/show.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { HashtagsRepository } from '@/models/index.js'; +import type { HashtagsRepository } from '@/models/index.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { HashtagEntityService } from '@/core/entities/HashtagEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/hashtags/trend.ts b/packages/backend/src/server/api/endpoints/hashtags/trend.ts index 7e483ea214..cf45cc6c24 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/trend.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/trend.ts @@ -1,7 +1,7 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } 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'; diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts index 10a88fbefa..c3f2ea9ea7 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/users.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index 815b3168b4..3bcd6ff8fb 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; 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 bcf3931b04..ec9ac1ef90 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 { UserProfilesRepository } from '@/models/index.js'; +import type { UserProfilesRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; export const meta = { 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 f2f4c2044e..6e0849f2b2 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,11 +4,11 @@ import * as cbor from 'cbor'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { TwoFactorAuthenticationService } from '@/core/TwoFactorAuthenticationService.js'; -import { AttestationChallengesRepository, UserProfilesRepository, UserSecurityKeysRepository } from '@/models/index.js'; +import type { AttestationChallengesRepository, UserProfilesRepository, UserSecurityKeysRepository } from '@/models/index.js'; const cborDecodeFirst = promisify(cbor.decodeFirst) as any; 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 3eb9f43c2b..0655a86350 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 { UserProfilesRepository } from '@/models/index.js'; +import type { UserProfilesRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; export const meta = { 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 df37db4c6a..19c77365c6 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,7 +3,7 @@ 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 { UserProfilesRepository, AttestationChallengesRepository } from '@/models/index.js'; +import type { UserProfilesRepository, AttestationChallengesRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import { TwoFactorAuthenticationService } from '@/core/TwoFactorAuthenticationService.js'; import { DI } from '@/di-symbols.js'; 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 e20911f35e..a539c5c221 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts @@ -2,10 +2,10 @@ import bcrypt from 'bcryptjs'; import * as speakeasy from 'speakeasy'; import * as QRCode from 'qrcode'; import { Inject, Injectable } from '@nestjs/common'; -import { UserProfilesRepository } from '@/models/index.js'; +import type { UserProfilesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; export const meta = { requireCredential: 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 1889dd7893..f40ec9797d 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,7 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserProfilesRepository, UserSecurityKeysRepository } from '@/models/index.js'; +import type { UserProfilesRepository, UserSecurityKeysRepository } from '@/models/index.js'; import type { UsersRepository } from '@/models/index.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; 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 4607e5d981..4c5b151f78 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 { UserProfilesRepository } from '@/models/index.js'; +import type { UserProfilesRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/i/apps.ts b/packages/backend/src/server/api/endpoints/i/apps.ts index 8d5851659b..3361e5a4d3 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 { AccessTokensRepository } from '@/models/index.js'; +import type { AccessTokensRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; export const meta = { 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 a5592d20de..d0bdb5695b 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,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { AccessTokensRepository } from '@/models/index.js'; +import type { AccessTokensRepository } from '@/models/index.js'; import { AppEntityService } from '@/core/entities/AppEntityService.js'; import { DI } from '@/di-symbols.js'; 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 cc5b712ecf..873835a36c 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 { UserProfilesRepository } from '@/models/index.js'; +import type { UserProfilesRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; export const meta = { 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 a1804599df..77a03d9811 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,6 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; -import { UsersRepository, UserProfilesRepository } from '@/models/index.js'; +import type { UsersRepository, UserProfilesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DeleteAccountService } from '@/core/DeleteAccountService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/i/favorites.ts b/packages/backend/src/server/api/endpoints/i/favorites.ts index 350abd9f7b..ce8ab4962a 100644 --- a/packages/backend/src/server/api/endpoints/i/favorites.ts +++ b/packages/backend/src/server/api/endpoints/i/favorites.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { NoteFavoritesRepository } from '@/models/index.js'; +import type { NoteFavoritesRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteFavoriteEntityService } from '@/core/entities/NoteFavoriteEntityService.js'; import { DI } from '@/di-symbols.js'; 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 ff6bcc01ab..d1b04cb655 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GalleryLikesRepository } from '@/models/index.js'; +import type { GalleryLikesRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { GalleryLikeEntityService } from '@/core/entities/GalleryLikeEntityService.js'; import { DI } from '@/di-symbols.js'; 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 927be51f79..32d14293f7 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GalleryPostsRepository } from '@/models/index.js'; +import type { GalleryPostsRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { DI } from '@/di-symbols.js'; 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 0695abdd85..3179457817 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 { MutedNotesRepository } from '@/models/index.js'; +import type { MutedNotesRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; export const meta = { 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 bfba1fc36f..8c1c158ab1 100644 --- a/packages/backend/src/server/api/endpoints/i/import-blocking.ts +++ b/packages/backend/src/server/api/endpoints/i/import-blocking.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueueService } from '@/core/QueueService.js'; -import { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.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 c7cb2e0330..383bdc02b5 100644 --- a/packages/backend/src/server/api/endpoints/i/import-following.ts +++ b/packages/backend/src/server/api/endpoints/i/import-following.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueueService } from '@/core/QueueService.js'; -import { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.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 060c37c13f..345ad916cb 100644 --- a/packages/backend/src/server/api/endpoints/i/import-muting.ts +++ b/packages/backend/src/server/api/endpoints/i/import-muting.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueueService } from '@/core/QueueService.js'; -import { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.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 a5e17283e5..875af7ec23 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 @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueueService } from '@/core/QueueService.js'; -import { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index 96927dad49..13de3382dd 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 { UsersRepository, FollowingsRepository, MutingsRepository, UserProfilesRepository, NotificationsRepository } from '@/models/index.js'; +import type { UsersRepository, FollowingsRepository, MutingsRepository, UserProfilesRepository, NotificationsRepository } from '@/models/index.js'; import { notificationTypes } from '@/types.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; 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 9a909eedf4..70e6e0a6a8 100644 --- a/packages/backend/src/server/api/endpoints/i/page-likes.ts +++ b/packages/backend/src/server/api/endpoints/i/page-likes.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { PageLikesRepository } from '@/models/index.js'; +import type { PageLikesRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { PageLikeEntityService } from '@/core/entities/PageLikeEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/i/pages.ts b/packages/backend/src/server/api/endpoints/i/pages.ts index 7c4e4a6c7d..285aa34e91 100644 --- a/packages/backend/src/server/api/endpoints/i/pages.ts +++ b/packages/backend/src/server/api/endpoints/i/pages.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { PagesRepository } from '@/models/index.js'; +import type { PagesRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { PageEntityService } from '@/core/entities/PageEntityService.js'; import { DI } from '@/di-symbols.js'; 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 36c3566f55..109d6d1068 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,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { MessagingMessagesRepository, UserGroupJoiningsRepository } from '@/models/index.js'; +import type { MessagingMessagesRepository, UserGroupJoiningsRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; 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 b4bb83c6eb..b92de4b739 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,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { NoteUnreadsRepository } from '@/models/index.js'; +import type { NoteUnreadsRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; 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 5a7909674f..cb5b4b0a60 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,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/core/IdService.js'; -import { AnnouncementReadsRepository, AnnouncementsRepository } from '@/models/index.js'; +import type { AnnouncementReadsRepository, AnnouncementsRepository } from '@/models/index.js'; import type { UsersRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; 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 7796fd97cb..f942f43cc8 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,7 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UsersRepository, UserProfilesRepository } from '@/models/index.js'; +import type { UsersRepository, UserProfilesRepository } from '@/models/index.js'; import generateUserToken from '@/misc/generate-native-user-token.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; 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 3b4db5fae3..17154c1f76 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 { RegistryItemsRepository } from '@/models/index.js'; +import type { RegistryItemsRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; export const meta = { 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 d24dff95b0..233686dbe1 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 { RegistryItemsRepository } from '@/models/index.js'; +import type { RegistryItemsRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 98d94a4c02..99cdf95bad 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 { RegistryItemsRepository } from '@/models/index.js'; +import type { RegistryItemsRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 d1a05d9d06..362a5e89f4 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 { RegistryItemsRepository } from '@/models/index.js'; +import type { RegistryItemsRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; export const meta = { 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 6df5f4ecc3..99f69d8bed 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 { RegistryItemsRepository } from '@/models/index.js'; +import type { RegistryItemsRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; export const meta = { 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 b5870f099d..78a641f5e2 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 { RegistryItemsRepository } from '@/models/index.js'; +import type { RegistryItemsRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 58085ddbc5..0a4ecb9c51 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 { RegistryItemsRepository } from '@/models/index.js'; +import type { RegistryItemsRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; export const meta = { 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 585aac2e01..c8e72203c4 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/set.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/set.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { RegistryItemsRepository } from '@/models/index.js'; +import type { RegistryItemsRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; 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 86a82e6a6c..5e1dddb6b7 100644 --- a/packages/backend/src/server/api/endpoints/i/revoke-token.ts +++ b/packages/backend/src/server/api/endpoints/i/revoke-token.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { AccessTokensRepository } from '@/models/index.js'; +import type { AccessTokensRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; 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 410cd72065..9b30a24336 100644 --- a/packages/backend/src/server/api/endpoints/i/signin-history.ts +++ b/packages/backend/src/server/api/endpoints/i/signin-history.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { SigninsRepository } from '@/models/index.js'; +import type { SigninsRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { SigninEntityService } from '@/core/entities/SigninEntityService.js'; import { DI } from '@/di-symbols.js'; 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 719cc14f09..b656c5c51d 100644 --- a/packages/backend/src/server/api/endpoints/i/update-email.ts +++ b/packages/backend/src/server/api/endpoints/i/update-email.ts @@ -3,10 +3,10 @@ import rndstr from 'rndstr'; import ms from 'ms'; import bcrypt from 'bcryptjs'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UsersRepository, UserProfilesRepository } from '@/models/index.js'; +import type { UsersRepository, UserProfilesRepository } from '@/models/index.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { EmailService } from '@/core/EmailService.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 4b904d4696..9bf0616e3a 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 { UsersRepository, DriveFilesRepository, UserProfilesRepository, PagesRepository } from '@/models/index.js'; +import type { UsersRepository, DriveFilesRepository, UserProfilesRepository, PagesRepository } 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/UserProfile.js'; 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 6dd1626bb8..1ad2f7d68f 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,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserGroupInvitationsRepository } from '@/models/index.js'; +import type { UserGroupInvitationsRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { UserGroupInvitationEntityService } from '@/core/entities/UserGroupInvitationEntityService.js'; import { DI } from '@/di-symbols.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 016b1b5d6a..584c2ba6a4 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/core/IdService.js'; -import { WebhooksRepository } from '@/models/index.js'; +import type { WebhooksRepository } from '@/models/index.js'; import { webhookEventTypes } from '@/models/entities/Webhook.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; 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 53b553b43e..7bdad136aa 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/delete.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { WebhooksRepository } from '@/models/index.js'; +import type { WebhooksRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 8e4aff45dd..58c84938cc 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 { WebhooksRepository } from '@/models/index.js'; +import type { WebhooksRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; export const meta = { 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 622c2ade98..d15ca0050d 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 { WebhooksRepository } from '@/models/index.js'; +import type { WebhooksRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 3a0ef1a526..50098f96e7 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/update.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/update.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { WebhooksRepository } from '@/models/index.js'; +import type { WebhooksRepository } from '@/models/index.js'; import { webhookEventTypes } from '@/models/entities/Webhook.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/messaging/history.ts b/packages/backend/src/server/api/endpoints/messaging/history.ts index da3ba59df9..0b6099d4ac 100644 --- a/packages/backend/src/server/api/endpoints/messaging/history.ts +++ b/packages/backend/src/server/api/endpoints/messaging/history.ts @@ -2,7 +2,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/MessagingMessage.js'; -import { MutingsRepository, UserGroupJoiningsRepository, MessagingMessagesRepository } from '@/models/index.js'; +import type { MutingsRepository, UserGroupJoiningsRepository, MessagingMessagesRepository } from '@/models/index.js'; import { MessagingMessageEntityService } from '@/core/entities/MessagingMessageEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/messaging/messages.ts b/packages/backend/src/server/api/endpoints/messaging/messages.ts index 6579b03987..f563da3278 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Brackets } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UsersRepository } from '@/models/index.js'; -import { UserGroupsRepository, MessagingMessagesRepository, UserGroupJoiningsRepository } from '@/models/index.js'; +import type { UserGroupsRepository, MessagingMessagesRepository, UserGroupJoiningsRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { MessagingMessageEntityService } from '@/core/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 e02afcbcfd..f61662af75 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { BlockingsRepository, UserGroupJoiningsRepository, DriveFilesRepository, UserGroupsRepository } from '@/models/index.js'; +import type { BlockingsRepository, UserGroupJoiningsRepository, DriveFilesRepository, UserGroupsRepository } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import type { UserGroup } from '@/models/entities/UserGroup.js'; import { GetterService } from '@/server/api/common/GetterService.js'; 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 5baecb9114..cd74f5f197 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import ms from 'ms'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { MessagingMessagesRepository } from '@/models/index.js'; +import type { MessagingMessagesRepository } from '@/models/index.js'; import { MessagingService } from '@/core/MessagingService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 6e66cafe1e..bddb6d932d 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/read.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/read.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { MessagingMessagesRepository } from '@/models/index.js'; +import type { MessagingMessagesRepository } from '@/models/index.js'; import { MessagingService } from '@/core/MessagingService.js'; import { DI } from '@/di-symbols.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 9a6258d7dd..5c09c33941 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -1,13 +1,13 @@ import { IsNull, MoreThan } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { AdsRepository, EmojisRepository, UsersRepository } from '@/models/index.js'; +import type { AdsRepository, EmojisRepository, UsersRepository } 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 '@/core/entities/UserEntityService.js'; import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; import { MetaService } from '@/core/MetaService.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; export const meta = { 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 d8eb89c0e6..97def86262 100644 --- a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts +++ b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { AccessTokensRepository } from '@/models/index.js'; +import type { AccessTokensRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import { secureRndstr } from '@/misc/secure-rndstr.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/mute/create.ts b/packages/backend/src/server/api/endpoints/mute/create.ts index cbdd001185..3b4507281f 100644 --- a/packages/backend/src/server/api/endpoints/mute/create.ts +++ b/packages/backend/src/server/api/endpoints/mute/create.ts @@ -1,7 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/core/IdService.js'; -import { MutingsRepository } from '@/models/index.js'; +import type { MutingsRepository } from '@/models/index.js'; import type { Muting } from '@/models/entities/Muting.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/mute/delete.ts b/packages/backend/src/server/api/endpoints/mute/delete.ts index c7098059d5..2fc5cee95e 100644 --- a/packages/backend/src/server/api/endpoints/mute/delete.ts +++ b/packages/backend/src/server/api/endpoints/mute/delete.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { MutingsRepository } from '@/models/index.js'; +import type { MutingsRepository } from '@/models/index.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/mute/list.ts b/packages/backend/src/server/api/endpoints/mute/list.ts index 11c05eb795..9ec6d17273 100644 --- a/packages/backend/src/server/api/endpoints/mute/list.ts +++ b/packages/backend/src/server/api/endpoints/mute/list.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { MutingsRepository } from '@/models/index.js'; +import type { MutingsRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { MutingEntityService } from '@/core/entities/MutingEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/my/apps.ts b/packages/backend/src/server/api/endpoints/my/apps.ts index 90cd53a133..4b7ed80123 100644 --- a/packages/backend/src/server/api/endpoints/my/apps.ts +++ b/packages/backend/src/server/api/endpoints/my/apps.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { AppsRepository } from '@/models/index.js'; +import type { AppsRepository } from '@/models/index.js'; import { AppEntityService } from '@/core/entities/AppEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/notes.ts b/packages/backend/src/server/api/endpoints/notes.ts index 288e195316..0a8f2292ac 100644 --- a/packages/backend/src/server/api/endpoints/notes.ts +++ b/packages/backend/src/server/api/endpoints/notes.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index 86f90e049f..ea7a825f9d 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 { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/clips.ts b/packages/backend/src/server/api/endpoints/notes/clips.ts index 7d893f32a1..579466d4fd 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 { Inject, Injectable } from '@nestjs/common'; -import { ClipNotesRepository, ClipsRepository } from '@/models/index.js'; +import type { ClipNotesRepository, ClipsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/conversation.ts b/packages/backend/src/server/api/endpoints/notes/conversation.ts index 2f8324ed62..ddfee31525 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 { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 30b7a889fc..92bc8a7595 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 { UsersRepository, NotesRepository, BlockingsRepository, DriveFilesRepository, ChannelsRepository } from '@/models/index.js'; +import type { UsersRepository, NotesRepository, BlockingsRepository, DriveFilesRepository, ChannelsRepository } from '@/models/index.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'; diff --git a/packages/backend/src/server/api/endpoints/notes/delete.ts b/packages/backend/src/server/api/endpoints/notes/delete.ts index 4769c8bdf1..5765dfe66f 100644 --- a/packages/backend/src/server/api/endpoints/notes/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/delete.ts @@ -1,6 +1,6 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteDeleteService } from '@/core/NoteDeleteService.js'; import { DI } from '@/di-symbols.js'; 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 bfdd1acd22..edbd300e08 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { NoteFavoritesRepository } from '@/models/index.js'; +import type { NoteFavoritesRepository } from '@/models/index.js'; import { IdService } from '@/core/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/favorites/delete.ts b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts index 6b3a02b101..8f4f2b2b9e 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/common/GetterService.js'; import { DI } from '@/di-symbols.js'; -import { NoteFavoritesRepository } from '@/models/index.js'; +import type { NoteFavoritesRepository } from '@/models/index.js'; import { ApiError } from '../../../error.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index 9985f9d257..76834cfde9 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 { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; 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 73b5afa40a..b6eaccb5ac 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; 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 c6458223eb..6573e9454a 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -1,6 +1,6 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { NotesRepository, FollowingsRepository } from '@/models/index.js'; +import type { NotesRepository, FollowingsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import ActiveUsersChart from '@/core/chart/charts/active-users.js'; 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 7b8859639d..fac14fa225 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -1,6 +1,6 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/index.js'; import type { UsersRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index 9b2dabc88b..92b82eb5de 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -1,6 +1,6 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { NotesRepository, FollowingsRepository } from '@/models/index.js'; +import type { NotesRepository, FollowingsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; 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 11bfdbba0f..6cdc9b902c 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 { Inject, Injectable } from '@nestjs/common'; -import { NotesRepository, MutingsRepository, PollsRepository, PollVotesRepository } from '@/models/index.js'; +import type { NotesRepository, MutingsRepository, PollsRepository, PollVotesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.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 76f07528d7..515f03dcce 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 { UsersRepository, BlockingsRepository, PollsRepository, PollVotesRepository } from '@/models/index.js'; +import type { UsersRepository, BlockingsRepository, PollsRepository, PollVotesRepository } from '@/models/index.js'; import type { IRemoteUser } from '@/models/entities/User.js'; import { IdService } from '@/core/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts index d57950f012..02ae212a30 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts @@ -1,6 +1,6 @@ import { DeepPartial } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { NoteReactionsRepository } from '@/models/index.js'; +import type { NoteReactionsRepository } from '@/models/index.js'; import type { NoteReaction } from '@/models/entities/NoteReaction.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteReactionEntityService } from '@/core/entities/NoteReactionEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index 57b7aeae0d..97ef1a17ec 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 { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index 7020d0c681..4df95962c8 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 { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; 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 0727c9af6c..061e371d65 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 { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } 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'; diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index 484cfc1128..27b477e141 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -1,10 +1,10 @@ import { In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts index c3f5b9dfb0..7849cfa401 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 { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/state.ts b/packages/backend/src/server/api/endpoints/notes/state.ts index 7756d39f7c..a02b8d2559 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 { Inject, Injectable } from '@nestjs/common'; -import { NotesRepository, NoteThreadMutingsRepository, NoteFavoritesRepository } from '@/models/index.js'; +import type { NotesRepository, NoteThreadMutingsRepository, NoteFavoritesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; 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 060581d74b..b8ddf83f3c 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,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { NotesRepository, NoteThreadMutingsRepository } from '@/models/index.js'; +import type { NotesRepository, NoteThreadMutingsRepository } from '@/models/index.js'; import { IdService } from '@/core/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/thread-muting/delete.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts index aed15852d4..54e9b4939f 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 { Inject, Injectable } from '@nestjs/common'; -import { NoteThreadMutingsRepository } from '@/models/index.js'; +import type { NoteThreadMutingsRepository } 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'; diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 53a1ae1348..8542af17db 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -1,6 +1,6 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { NotesRepository, FollowingsRepository } from '@/models/index.js'; +import type { NotesRepository, FollowingsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import ActiveUsersChart from '@/core/chart/charts/active-users.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts index c24f1e401e..7a3daf741e 100644 --- a/packages/backend/src/server/api/endpoints/notes/translate.ts +++ b/packages/backend/src/server/api/endpoints/notes/translate.ts @@ -1,9 +1,9 @@ import { URLSearchParams } from 'node:url'; import fetch from 'node-fetch'; import { Inject, Injectable } from '@nestjs/common'; -import { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { MetaService } from '@/core/MetaService.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/unrenote.ts b/packages/backend/src/server/api/endpoints/notes/unrenote.ts index c0048888b4..7378c4b600 100644 --- a/packages/backend/src/server/api/endpoints/notes/unrenote.ts +++ b/packages/backend/src/server/api/endpoints/notes/unrenote.ts @@ -1,6 +1,6 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import { UsersRepository, NotesRepository } from '@/models/index.js'; +import type { UsersRepository, NotesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteDeleteService } from '@/core/NoteDeleteService.js'; import { DI } from '@/di-symbols.js'; 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 87a464578c..9b23103fd4 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,6 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { NotesRepository, UserListsRepository, UserListJoiningsRepository } from '@/models/index.js'; +import type { NotesRepository, UserListsRepository, UserListJoiningsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; 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 3d1eb2b39c..09134cf48f 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,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { NotificationsRepository } from '@/models/index.js'; +import type { NotificationsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { PushNotificationService } from '@/core/PushNotificationService.js'; diff --git a/packages/backend/src/server/api/endpoints/page-push.ts b/packages/backend/src/server/api/endpoints/page-push.ts index 1b0299c3c6..1841a84539 100644 --- a/packages/backend/src/server/api/endpoints/page-push.ts +++ b/packages/backend/src/server/api/endpoints/page-push.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { PagesRepository } from '@/models/index.js'; +import type { PagesRepository } from '@/models/index.js'; import type { UsersRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/pages/create.ts b/packages/backend/src/server/api/endpoints/pages/create.ts index ac80849aa0..eae8f18403 100644 --- a/packages/backend/src/server/api/endpoints/pages/create.ts +++ b/packages/backend/src/server/api/endpoints/pages/create.ts @@ -1,6 +1,6 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import { DriveFilesRepository, PagesRepository } from '@/models/index.js'; +import type { DriveFilesRepository, PagesRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import { Page } from '@/models/entities/Page.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; diff --git a/packages/backend/src/server/api/endpoints/pages/delete.ts b/packages/backend/src/server/api/endpoints/pages/delete.ts index 4e97755761..e64733131c 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 { PagesRepository } from '@/models/index.js'; +import type { PagesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/pages/featured.ts b/packages/backend/src/server/api/endpoints/pages/featured.ts index 3e3dbb0832..31844165e2 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 { Inject, Injectable } from '@nestjs/common'; -import { PagesRepository } from '@/models/index.js'; +import type { PagesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { PageEntityService } from '@/core/entities/PageEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/pages/like.ts b/packages/backend/src/server/api/endpoints/pages/like.ts index f3c55fed8b..41a11d1a31 100644 --- a/packages/backend/src/server/api/endpoints/pages/like.ts +++ b/packages/backend/src/server/api/endpoints/pages/like.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { PagesRepository, PageLikesRepository } from '@/models/index.js'; +import type { PagesRepository, PageLikesRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/pages/show.ts b/packages/backend/src/server/api/endpoints/pages/show.ts index 6d73889d39..651252afbb 100644 --- a/packages/backend/src/server/api/endpoints/pages/show.ts +++ b/packages/backend/src/server/api/endpoints/pages/show.ts @@ -1,6 +1,6 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { UsersRepository, PagesRepository } from '@/models/index.js'; +import type { UsersRepository, PagesRepository } from '@/models/index.js'; import type { Page } from '@/models/entities/Page.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { PageEntityService } from '@/core/entities/PageEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/pages/unlike.ts b/packages/backend/src/server/api/endpoints/pages/unlike.ts index 88386739be..e397e2a23b 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 { PagesRepository, PageLikesRepository } from '@/models/index.js'; +import type { PagesRepository, PageLikesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/pages/update.ts b/packages/backend/src/server/api/endpoints/pages/update.ts index 8980ac4906..4db0f80b26 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 { PagesRepository, DriveFilesRepository } from '@/models/index.js'; +import type { PagesRepository, DriveFilesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.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 573331e0d8..6c941314e2 100644 --- a/packages/backend/src/server/api/endpoints/pinned-users.ts +++ b/packages/backend/src/server/api/endpoints/pinned-users.ts @@ -1,6 +1,6 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } 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'; diff --git a/packages/backend/src/server/api/endpoints/promo/read.ts b/packages/backend/src/server/api/endpoints/promo/read.ts index 7c8188ce3c..a28229add3 100644 --- a/packages/backend/src/server/api/endpoints/promo/read.ts +++ b/packages/backend/src/server/api/endpoints/promo/read.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { PromoReadsRepository } from '@/models/index.js'; +import type { PromoReadsRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; 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 4766239533..42b10a4fb3 100644 --- a/packages/backend/src/server/api/endpoints/request-reset-password.ts +++ b/packages/backend/src/server/api/endpoints/request-reset-password.ts @@ -2,10 +2,10 @@ import rndstr from 'rndstr'; import ms from 'ms'; import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { PasswordResetRequestsRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { PasswordResetRequestsRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/core/IdService.js'; -import { Config } from '@/config.js'; +import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import { EmailService } from '@/core/EmailService.js'; import { ApiError } from '../error.js'; diff --git a/packages/backend/src/server/api/endpoints/reset-password.ts b/packages/backend/src/server/api/endpoints/reset-password.ts index 48edde5196..cf7fcb7afd 100644 --- a/packages/backend/src/server/api/endpoints/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/reset-password.ts @@ -1,6 +1,6 @@ import bcrypt from 'bcryptjs'; import { Inject, Injectable } from '@nestjs/common'; -import { UserProfilesRepository, PasswordResetRequestsRepository } from '@/models/index.js'; +import type { UserProfilesRepository, PasswordResetRequestsRepository } from '@/models/index.js'; import type { UsersRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/stats.ts b/packages/backend/src/server/api/endpoints/stats.ts index 17af75578b..3adf0a4bb8 100644 --- a/packages/backend/src/server/api/endpoints/stats.ts +++ b/packages/backend/src/server/api/endpoints/stats.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { IsNull } from 'typeorm'; -import { InstancesRepository, NotesRepository, UsersRepository } from '@/models/index.js'; +import type { InstancesRepository, NotesRepository, UsersRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/sw/register.ts b/packages/backend/src/server/api/endpoints/sw/register.ts index 73a084c2ad..ddec877dd4 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 { IdService } from '@/core/IdService.js'; -import { SwSubscriptionsRepository } from '@/models/index.js'; +import type { SwSubscriptionsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { MetaService } from '@/core/MetaService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/sw/unregister.ts b/packages/backend/src/server/api/endpoints/sw/unregister.ts index feb6730154..5772eeee26 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 { SwSubscriptionsRepository } from '@/models/index.js'; +import type { SwSubscriptionsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/username/available.ts b/packages/backend/src/server/api/endpoints/username/available.ts index 56474d6988..c80b6efdcd 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 { UsedUsernamesRepository, UsersRepository } from '@/models/index.js'; +import type { UsedUsernamesRepository, UsersRepository } 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'; diff --git a/packages/backend/src/server/api/endpoints/users.ts b/packages/backend/src/server/api/endpoints/users.ts index 3d05ec2e1d..b015129a7a 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 { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/users/clips.ts b/packages/backend/src/server/api/endpoints/users/clips.ts index 2d5545cbab..e3fd0920c9 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 { Inject, Injectable } from '@nestjs/common'; -import { ClipsRepository } from '@/models/index.js'; +import type { ClipsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index 08bcdd9f88..17ce920011 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -1,6 +1,6 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { UsersRepository, FollowingsRepository, UserProfilesRepository } from '@/models/index.js'; +import type { UsersRepository, FollowingsRepository, UserProfilesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { FollowingEntityService } from '@/core/entities/FollowingEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index 225ab5210a..6dbda0d72f 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -1,6 +1,6 @@ import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { UsersRepository, FollowingsRepository, UserProfilesRepository } from '@/models/index.js'; +import type { UsersRepository, FollowingsRepository, UserProfilesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { FollowingEntityService } from '@/core/entities/FollowingEntityService.js'; 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 2d28d6ca07..6e57eee5fb 100644 --- a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { GalleryPostsRepository } from '@/models/index.js'; +import type { GalleryPostsRepository } from '@/models/index.js'; import { QueryService } from '@/core/QueryService.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { DI } from '@/di-symbols.js'; 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 3eeca7562f..22c49860bd 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 '@/misc/prelude/array.js'; -import { NotesRepository, UsersRepository } from '@/models/index.js'; +import type { NotesRepository, UsersRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; 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 5d7ad84ae0..c1f4f48445 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/create.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/create.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; +import type { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import type { UserGroup } from '@/models/entities/UserGroup.js'; import type { UserGroupJoining } from '@/models/entities/UserGroupJoining.js'; 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 50156b049e..d238ae9f16 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 { UserGroupsRepository } from '@/models/index.js'; +import type { UserGroupsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.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 0490fd41a0..f154a57f61 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,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserGroupInvitationsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; +import type { UserGroupInvitationsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import type { UserGroupJoining } from '@/models/entities/UserGroupJoining.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; 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 26efc1ecf3..1fd3b2f4b3 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 { UserGroupInvitationsRepository } from '@/models/index.js'; +import type { UserGroupInvitationsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.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 4ae32a6bda..127f4ca65b 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invite.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invite.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserGroupsRepository, UserGroupJoiningsRepository, UserGroupInvitationsRepository } from '@/models/index.js'; +import type { UserGroupsRepository, UserGroupJoiningsRepository, UserGroupInvitationsRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import type { UserGroupInvitation } from '@/models/entities/UserGroupInvitation.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; 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 e7e69f257d..8daee3a6f5 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 { Inject, Injectable } from '@nestjs/common'; -import { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; +import type { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserGroupEntityService } from '@/core/entities/UserGroupEntityService.js'; import { DI } from '@/di-symbols.js'; 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 0a63dbb7f1..846f80e64d 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 { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; +import type { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 c9ae39561f..0bc6e8b3fc 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 { Inject, Injectable } from '@nestjs/common'; -import { UserGroupsRepository } from '@/models/index.js'; +import type { UserGroupsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserGroupEntityService } from '@/core/entities/UserGroupEntityService.js'; import { DI } from '@/di-symbols.js'; 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 e6f60eef0a..3474f22c6e 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 { Inject, Injectable } from '@nestjs/common'; -import { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; +import type { UserGroupsRepository, UserGroupJoiningsRepository } 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'; 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 1cebfcd204..2b0f403f33 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 { Inject, Injectable } from '@nestjs/common'; -import { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; +import type { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserGroupEntityService } from '@/core/entities/UserGroupEntityService.js'; import { DI } from '@/di-symbols.js'; 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 a8b2533b73..e0b2b13c7f 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 { Inject, Injectable } from '@nestjs/common'; -import { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; +import type { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserGroupEntityService } from '@/core/entities/UserGroupEntityService.js'; import { GetterService } from '@/server/api/common/GetterService.js'; 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 b679625c85..5af849de14 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 { Inject, Injectable } from '@nestjs/common'; -import { UserGroupsRepository } from '@/models/index.js'; +import type { UserGroupsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserGroupEntityService } from '@/core/entities/UserGroupEntityService.js'; import { DI } from '@/di-symbols.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 aa64ca1229..99f0751ea8 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserListsRepository } from '@/models/index.js'; +import type { UserListsRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import type { UserList } from '@/models/entities/UserList.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; 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 0f4125a39f..237cb075ab 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 { UserListsRepository } from '@/models/index.js'; +import type { UserListsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; 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 919de22377..2104c4377d 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 { Inject, Injectable } from '@nestjs/common'; -import { UserListsRepository } from '@/models/index.js'; +import type { UserListsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserListEntityService } from '@/core/entities/UserListEntityService.js'; import { DI } from '@/di-symbols.js'; 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 89d97be93e..7e54d33376 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/pull.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserListsRepository, UserListJoiningsRepository } from '@/models/index.js'; +import type { UserListsRepository, UserListJoiningsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { GetterService } from '@/server/api/common/GetterService.js'; 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 77ad772b13..06ea43d654 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/push.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/push.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserListsRepository, UserListJoiningsRepository, BlockingsRepository } from '@/models/index.js'; +import type { UserListsRepository, UserListJoiningsRepository, BlockingsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/common/GetterService.js'; import { UserListService } from '@/core/UserListService.js'; 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 62e730b2f7..77f9cba808 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 { Inject, Injectable } from '@nestjs/common'; -import { UserListsRepository } from '@/models/index.js'; +import type { UserListsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserListEntityService } from '@/core/entities/UserListEntityService.js'; import { DI } from '@/di-symbols.js'; 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 c6669d24d1..6453d7d980 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 { Inject, Injectable } from '@nestjs/common'; -import { UserListsRepository } from '@/models/index.js'; +import type { UserListsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserListEntityService } from '@/core/entities/UserListEntityService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index bb8104584c..d2c9616f68 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 { NotesRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/users/pages.ts b/packages/backend/src/server/api/endpoints/users/pages.ts index 96c7ef1e70..e007aa57b2 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 { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { PageEntityService } from '@/core/entities/PageEntityService.js'; -import { PagesRepository } from '@/models'; +import type { PagesRepository } from '@/models'; import { DI } from '@/di-symbols.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts index 6b4d882b7c..9ec911f322 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 { Inject, Injectable } from '@nestjs/common'; -import { UserProfilesRepository, NoteReactionsRepository } from '@/models/index.js'; +import type { UserProfilesRepository, NoteReactionsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { NoteReactionEntityService } from '@/core/entities/NoteReactionEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/users/recommendation.ts b/packages/backend/src/server/api/endpoints/users/recommendation.ts index e50a5706d9..5498b8c854 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 { Inject, Injectable } from '@nestjs/common'; -import { UsersRepository, FollowingsRepository } from '@/models/index.js'; +import type { UsersRepository, FollowingsRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; diff --git a/packages/backend/src/server/api/endpoints/users/relation.ts b/packages/backend/src/server/api/endpoints/users/relation.ts index aea75ae799..ac9104bf92 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 { Inject, Injectable } from '@nestjs/common'; -import { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; 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 5c211a9017..bb37dd2715 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -1,6 +1,6 @@ import * as sanitizeHtml from 'sanitize-html'; import { Inject, Injectable } from '@nestjs/common'; -import { UsersRepository, AbuseUserReportsRepository } from '@/models/index.js'; +import type { UsersRepository, AbuseUserReportsRepository } from '@/models/index.js'; import { IdService } from '@/core/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GlobalEventService } from '@/core/GlobalEventService.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 1747dc93f6..f13df3ee9d 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,6 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { UsersRepository, FollowingsRepository } from '@/models/index.js'; +import type { UsersRepository, FollowingsRepository } 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'; diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index 9879b1b68b..ba07714972 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -1,6 +1,6 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { UsersRepository, UserProfilesRepository } from '@/models/index.js'; +import type { UsersRepository, UserProfilesRepository } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/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 98f5f03063..1e15025bf4 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 { UsersRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; diff --git a/packages/backend/src/server/api/integration/DiscordServerService.ts b/packages/backend/src/server/api/integration/DiscordServerService.ts index ea044c27d5..cc3f4e0633 100644 --- a/packages/backend/src/server/api/integration/DiscordServerService.ts +++ b/packages/backend/src/server/api/integration/DiscordServerService.ts @@ -4,8 +4,8 @@ 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 { UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; +import type { UserProfilesRepository, UsersRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import type { ILocalUser } from '@/models/entities/User.js'; diff --git a/packages/backend/src/server/api/integration/GithubServerService.ts b/packages/backend/src/server/api/integration/GithubServerService.ts index 58b170d0e4..2195e82813 100644 --- a/packages/backend/src/server/api/integration/GithubServerService.ts +++ b/packages/backend/src/server/api/integration/GithubServerService.ts @@ -4,8 +4,8 @@ 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 { UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; +import type { UserProfilesRepository, UsersRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import type { ILocalUser } from '@/models/entities/User.js'; diff --git a/packages/backend/src/server/api/integration/TwitterServerService.ts b/packages/backend/src/server/api/integration/TwitterServerService.ts index a4a67f6c8c..7f209ea285 100644 --- a/packages/backend/src/server/api/integration/TwitterServerService.ts +++ b/packages/backend/src/server/api/integration/TwitterServerService.ts @@ -4,8 +4,8 @@ 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 { UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; +import type { UserProfilesRepository, UsersRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import type { ILocalUser } from '@/models/entities/User.js'; diff --git a/packages/backend/src/server/api/stream/channels/messaging.ts b/packages/backend/src/server/api/stream/channels/messaging.ts index 5bf20c4101..b6ce6c217e 100644 --- a/packages/backend/src/server/api/stream/channels/messaging.ts +++ b/packages/backend/src/server/api/stream/channels/messaging.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserGroupJoiningsRepository, UsersRepository, MessagingMessagesRepository } from '@/models/index.js'; +import type { UserGroupJoiningsRepository, UsersRepository, MessagingMessagesRepository } 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 '@/core/MessagingService.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 a45c7d9468..f9f0d02558 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -1,5 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import { UserListJoiningsRepository, UserListsRepository } from '@/models/index.js'; +import type { UserListJoiningsRepository, UserListsRepository } from '@/models/index.js'; import type { NotesRepository } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import { isUserRelated } from '@/misc/is-user-related.js'; diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 85b31312b3..44acd12796 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.js'; +import type { Config } from '@/config.js'; import { getNoteSummary } from '@/misc/get-note-summary.js'; import { DI } from '@/di-symbols.js'; import * as Acct from '@/misc/acct.js'; diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts index 8b676aebe5..1d7d49961d 100644 --- a/packages/backend/src/server/web/FeedService.ts +++ b/packages/backend/src/server/web/FeedService.ts @@ -2,8 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import { In, IsNull } from 'typeorm'; import { Feed } from 'feed'; import { DI } from '@/di-symbols.js'; -import { DriveFilesRepository, NotesRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { DriveFilesRepository, NotesRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import type { User } from '@/models/entities/User.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts index 1cbb3f36c2..f5dddd2db7 100644 --- a/packages/backend/src/server/web/UrlPreviewService.ts +++ b/packages/backend/src/server/web/UrlPreviewService.ts @@ -1,8 +1,8 @@ import { Inject, Injectable } from '@nestjs/common'; import summaly from 'summaly'; import { DI } from '@/di-symbols.js'; -import { UsersRepository } from '@/models/index.js'; -import { Config } from '@/config.js'; +import type { UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; import { MetaService } from '@/core/MetaService.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import type Logger from '@/logger.js'; diff --git a/packages/shared/.eslintrc.js b/packages/shared/.eslintrc.js index 2952b8ba16..6d38a9fb9f 100644 --- a/packages/shared/.eslintrc.js +++ b/packages/shared/.eslintrc.js @@ -73,7 +73,7 @@ module.exports = { '@typescript-eslint/no-misused-promises': ['error', { 'checksVoidReturn': false, }], - '@typescript-eslint/consistent-type-imports': 'error', + '@typescript-eslint/consistent-type-imports': 'off', '@typescript-eslint/prefer-nullish-coalescing': [ 'error', ], -- cgit v1.2.3-freya From c8f6bc0dabcbe119533ef8f0edfb35d9405818c8 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sat, 24 Sep 2022 07:12:11 +0900 Subject: fixes --- packages/backend/src/core/DriveService.ts | 2 +- .../src/core/FetchInstanceMetadataService.ts | 33 +++++++++++++--------- packages/backend/src/core/MetaService.ts | 2 +- packages/backend/src/core/NoteCreateService.ts | 5 +--- packages/backend/src/core/SignupService.ts | 2 +- .../src/core/TwoFactorAuthenticationService.ts | 2 ++ packages/backend/src/core/UserBlockingService.ts | 8 +++++- packages/backend/src/core/UserCacheService.ts | 2 +- packages/backend/src/core/WebhookService.ts | 2 +- .../src/server/api/endpoints/i/authorized-apps.ts | 4 ++- 10 files changed, 37 insertions(+), 25 deletions(-) (limited to 'packages/backend/src/core/NoteCreateService.ts') diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index 643c51c37d..e0bdd29c0f 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -61,7 +61,7 @@ type AddFileArgs = { type UploadFromUrlArgs = { url: string; - user: { id: User['id']; host: User['host'] } | null; + user: { id: User['id']; host: User['host']; driveCapacityOverrideMb: User['driveCapacityOverrideMb'] } | null; folderId?: DriveFolder['id'] | null; uri?: string | null; sensitive?: boolean; diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts index 9a51ed0c0a..184404123c 100644 --- a/packages/backend/src/core/FetchInstanceMetadataService.ts +++ b/packages/backend/src/core/FetchInstanceMetadataService.ts @@ -13,20 +13,21 @@ import { HttpRequestService } from './HttpRequestService.js'; import type { DOMWindow } from 'jsdom'; type NodeInfo = { - openRegistrations?: any; + openRegistrations?: unknown; software?: { - name?: any; - version?: any; + name?: unknown; + version?: unknown; }; metadata?: { - name?: any; - nodeName?: any; - nodeDescription?: any; - description?: any; + name?: unknown; + nodeName?: unknown; + nodeDescription?: unknown; + description?: unknown; maintainer?: { - name?: any; - email?: any; + name?: unknown; + email?: unknown; }; + themeColor?: unknown; }; }; @@ -81,7 +82,7 @@ export class FetchInstanceMetadataService { } as Record; if (info) { - updates.softwareName = info.software?.name.toLowerCase(); + updates.softwareName = typeof info.software?.name === 'string' ? 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; @@ -238,8 +239,10 @@ export class FetchInstanceMetadataService { private 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 (typeof info.metadata.nodeName === 'string') { + return info.metadata.nodeName; + } else if (typeof info.metadata.name === 'string') { + return info.metadata.name; } } @@ -260,8 +263,10 @@ export class FetchInstanceMetadataService { private 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 (typeof info.metadata.nodeDescription === 'string') { + return info.metadata.nodeDescription; + } else if (typeof info.metadata.description === 'string') { + return info.metadata.description; } } diff --git a/packages/backend/src/core/MetaService.ts b/packages/backend/src/core/MetaService.ts index 1744f64217..c3d41bfccb 100644 --- a/packages/backend/src/core/MetaService.ts +++ b/packages/backend/src/core/MetaService.ts @@ -34,7 +34,7 @@ export class MetaService implements OnApplicationShutdown { this.redisSubscriber.on('message', this.onMessage); } - private async onMessage(_, data): Promise { + private async onMessage(_: string, data: string): Promise { const obj = JSON.parse(data); if (obj.channel === 'internal') { diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 6e5cce8f62..a23e105674 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -534,7 +534,6 @@ export class NoteCreateService { }); const nm = new NotificationManager(this.mutingsRepository, this.createNotificationService, user, note); - const nmRelatedPromises = []; await this.createMentionedEvents(mentionedUsers, note, nm); @@ -583,9 +582,7 @@ export class NoteCreateService { } } - Promise.all(nmRelatedPromises).then(() => { - nm.deliver(); - }); + nm.deliver(); //#region AP deliver if (this.userEntityService.isLocalUser(user)) { diff --git a/packages/backend/src/core/SignupService.ts b/packages/backend/src/core/SignupService.ts index 8b72d73af6..2239d5fd83 100644 --- a/packages/backend/src/core/SignupService.ts +++ b/packages/backend/src/core/SignupService.ts @@ -3,7 +3,7 @@ import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; import { DataSource, IsNull } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { UsedUsernamesRepository } from '@/models/index.js'; +import type { UsedUsernamesRepository, UsersRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; import { User } from '@/models/entities/User.js'; import { UserProfile } from '@/models/entities/UserProfile.js'; diff --git a/packages/backend/src/core/TwoFactorAuthenticationService.ts b/packages/backend/src/core/TwoFactorAuthenticationService.ts index 0962f88a7c..54e87ec36f 100644 --- a/packages/backend/src/core/TwoFactorAuthenticationService.ts +++ b/packages/backend/src/core/TwoFactorAuthenticationService.ts @@ -67,6 +67,8 @@ function verifyCertificateChain(certificates: string[]) { const CACert = i + 1 >= certificates.length ? Cert : certificates[i + 1]; const certStruct = jsrsasign.ASN1HEX.getTLVbyList(certificate.hex!, 0, [0]); + if (certStruct == null) throw new Error('certStruct is null'); + const algorithm = certificate.getSignatureAlgorithmField(); const signatureHex = certificate.getSignatureValueHex(); diff --git a/packages/backend/src/core/UserBlockingService.ts b/packages/backend/src/core/UserBlockingService.ts index 5ff76b48a9..b7a434684e 100644 --- a/packages/backend/src/core/UserBlockingService.ts +++ b/packages/backend/src/core/UserBlockingService.ts @@ -9,12 +9,16 @@ import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js'; import { DI } from '@/di-symbols.js'; import logger from '@/logger.js'; import type { UsersRepository, FollowingsRepository, FollowRequestsRepository, BlockingsRepository, UserListsRepository, UserListJoiningsRepository } from '@/models/index.js'; +import Logger from '@/logger.js'; import { UserEntityService } from './entities/UserEntityService.js'; import { WebhookService } from './WebhookService.js'; import { ApRendererService } from './remote/activitypub/ApRendererService.js'; +import { LoggerService } from './LoggerService.js'; @Injectable() export class UserBlockingService { + private logger: Logger; + constructor( @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -41,7 +45,9 @@ export class UserBlockingService { private webhookService: WebhookService, private apRendererService: ApRendererService, private perUserFollowingChart: PerUserFollowingChart, + private loggerService: LoggerService, ) { + this.logger = this.loggerService.getLogger('user-block'); } public async block(blocker: User, blockee: User) { @@ -181,7 +187,7 @@ export class UserBlockingService { }); if (blocking == null) { - logger.warn('ブロック解除がリクエストされましたがブロックしていませんでした'); + this.logger.warn('ブロック解除がリクエストされましたがブロックしていませんでした'); return; } diff --git a/packages/backend/src/core/UserCacheService.ts b/packages/backend/src/core/UserCacheService.ts index 666bef3f49..b7166010ee 100644 --- a/packages/backend/src/core/UserCacheService.ts +++ b/packages/backend/src/core/UserCacheService.ts @@ -33,7 +33,7 @@ export class UserCacheService implements OnApplicationShutdown { this.redisSubscriber.on('message', this.onMessage); } - private async onMessage(_, data) { + private async onMessage(_: string, data: string): Promise { const obj = JSON.parse(data); if (obj.channel === 'internal') { diff --git a/packages/backend/src/core/WebhookService.ts b/packages/backend/src/core/WebhookService.ts index 347b2b16c4..1a690314f8 100644 --- a/packages/backend/src/core/WebhookService.ts +++ b/packages/backend/src/core/WebhookService.ts @@ -32,7 +32,7 @@ export class WebhookService implements OnApplicationShutdown { return this.webhooks; } - private async onMessage(_, data) { + private async onMessage(_: string, data: string): Promise { const obj = JSON.parse(data); if (obj.channel === 'internal') { 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 d0bdb5695b..f5a946eb91 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 { Inject, Injectable } from '@nestjs/common'; +import { IsNull, Not } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { AccessTokensRepository } from '@/models/index.js'; import { AppEntityService } from '@/core/entities/AppEntityService.js'; @@ -34,6 +35,7 @@ export default class extends Endpoint { const tokens = await this.accessTokensRepository.find({ where: { userId: me.id, + appId: Not(IsNull()), }, take: ps.limit, skip: ps.offset, @@ -42,7 +44,7 @@ export default class extends Endpoint { }, }); - return await Promise.all(tokens.map(token => this.appEntityService.pack(token.appId, me, { + return await Promise.all(tokens.map(token => this.appEntityService.pack(token.appId!, me, { detail: true, }))); }); -- cgit v1.2.3-freya From 22ccb0fa716a84560c8599781647baaaeb8e80bd Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 4 Dec 2022 10:16:03 +0900 Subject: refactor --- packages/backend/src/core/AccountUpdateService.ts | 6 +- packages/backend/src/core/AntennaService.ts | 2 +- packages/backend/src/core/CaptchaService.ts | 2 +- packages/backend/src/core/CoreModule.ts | 48 +- .../backend/src/core/CreateNotificationService.ts | 4 +- packages/backend/src/core/CustomEmojiService.ts | 4 +- packages/backend/src/core/DriveService.ts | 6 +- .../backend/src/core/FederatedInstanceService.ts | 2 +- .../src/core/FetchInstanceMetadataService.ts | 2 +- packages/backend/src/core/HashtagService.ts | 2 +- packages/backend/src/core/InstanceActorService.ts | 2 +- packages/backend/src/core/MessagingService.ts | 12 +- packages/backend/src/core/NoteCreateService.ts | 16 +- packages/backend/src/core/NoteDeleteService.ts | 8 +- packages/backend/src/core/NotePiningService.ts | 6 +- packages/backend/src/core/NoteReadService.ts | 2 +- packages/backend/src/core/NotificationService.ts | 2 +- packages/backend/src/core/PollService.ts | 6 +- packages/backend/src/core/ProxyAccountService.ts | 2 +- .../backend/src/core/PushNotificationService.ts | 2 +- packages/backend/src/core/QueueModule.ts | 112 ++++ packages/backend/src/core/QueueService.ts | 4 +- packages/backend/src/core/ReactionService.ts | 10 +- packages/backend/src/core/RelayService.ts | 2 +- packages/backend/src/core/RemoteLoggerService.ts | 14 + .../backend/src/core/RemoteUserResolveService.ts | 132 ++++ packages/backend/src/core/S3Service.ts | 2 +- packages/backend/src/core/SignupService.ts | 2 +- packages/backend/src/core/UserBlockingService.ts | 8 +- packages/backend/src/core/UserCacheService.ts | 2 +- packages/backend/src/core/UserFollowingService.ts | 4 +- packages/backend/src/core/UserListService.ts | 4 +- packages/backend/src/core/UserSuspendService.ts | 4 +- packages/backend/src/core/WebfingerService.ts | 48 ++ .../src/core/activitypub/ApAudienceService.ts | 104 +++ .../src/core/activitypub/ApDbResolverService.ts | 179 +++++ .../core/activitypub/ApDeliverManagerService.ts | 199 ++++++ .../backend/src/core/activitypub/ApInboxService.ts | 740 +++++++++++++++++++++ .../src/core/activitypub/ApLoggerService.ts | 14 + .../backend/src/core/activitypub/ApMfmService.ts | 30 + .../src/core/activitypub/ApRendererService.ts | 703 ++++++++++++++++++++ .../src/core/activitypub/ApRequestService.ts | 182 +++++ .../src/core/activitypub/ApResolverService.ts | 195 ++++++ .../src/core/activitypub/LdSignatureService.ts | 147 ++++ .../backend/src/core/activitypub/misc/contexts.ts | 526 +++++++++++++++ .../src/core/activitypub/models/ApImageService.ts | 90 +++ .../core/activitypub/models/ApMentionService.ts | 39 ++ .../src/core/activitypub/models/ApNoteService.ts | 403 +++++++++++ .../src/core/activitypub/models/ApPersonService.ts | 594 +++++++++++++++++ .../core/activitypub/models/ApQuestionService.ts | 109 +++ .../backend/src/core/activitypub/models/icon.ts | 5 + .../src/core/activitypub/models/identifier.ts | 5 + .../backend/src/core/activitypub/models/tag.ts | 19 + packages/backend/src/core/activitypub/type.ts | 296 +++++++++ .../backend/src/core/chart/charts/active-users.ts | 2 +- .../backend/src/core/chart/charts/ap-request.ts | 2 +- packages/backend/src/core/chart/charts/drive.ts | 2 +- .../backend/src/core/chart/charts/federation.ts | 2 +- packages/backend/src/core/chart/charts/hashtag.ts | 2 +- packages/backend/src/core/chart/charts/instance.ts | 2 +- packages/backend/src/core/chart/charts/notes.ts | 2 +- .../src/core/chart/charts/per-user-drive.ts | 2 +- .../src/core/chart/charts/per-user-following.ts | 2 +- .../src/core/chart/charts/per-user-notes.ts | 2 +- .../src/core/chart/charts/per-user-reactions.ts | 2 +- .../backend/src/core/chart/charts/test-grouped.ts | 2 +- .../src/core/chart/charts/test-intersection.ts | 2 +- .../backend/src/core/chart/charts/test-unique.ts | 2 +- packages/backend/src/core/chart/charts/test.ts | 2 +- packages/backend/src/core/chart/charts/users.ts | 2 +- .../src/core/entities/InstanceEntityService.ts | 2 +- packages/backend/src/core/queue/QueueModule.ts | 112 ---- .../backend/src/core/remote/RemoteLoggerService.ts | 14 - .../backend/src/core/remote/ResolveUserService.ts | 132 ---- .../backend/src/core/remote/WebfingerService.ts | 48 -- .../core/remote/activitypub/ApAudienceService.ts | 104 --- .../core/remote/activitypub/ApDbResolverService.ts | 179 ----- .../remote/activitypub/ApDeliverManagerService.ts | 199 ------ .../src/core/remote/activitypub/ApInboxService.ts | 740 --------------------- .../src/core/remote/activitypub/ApLoggerService.ts | 14 - .../src/core/remote/activitypub/ApMfmService.ts | 30 - .../core/remote/activitypub/ApRendererService.ts | 703 -------------------- .../core/remote/activitypub/ApRequestService.ts | 182 ----- .../core/remote/activitypub/ApResolverService.ts | 195 ------ .../core/remote/activitypub/LdSignatureService.ts | 147 ---- .../src/core/remote/activitypub/misc/contexts.ts | 526 --------------- .../remote/activitypub/models/ApImageService.ts | 90 --- .../remote/activitypub/models/ApMentionService.ts | 39 -- .../remote/activitypub/models/ApNoteService.ts | 403 ----------- .../remote/activitypub/models/ApPersonService.ts | 594 ----------------- .../remote/activitypub/models/ApQuestionService.ts | 109 --- .../src/core/remote/activitypub/models/icon.ts | 5 - .../core/remote/activitypub/models/identifier.ts | 5 - .../src/core/remote/activitypub/models/tag.ts | 19 - .../backend/src/core/remote/activitypub/type.ts | 296 --------- .../queue/processors/DeliverProcessorService.ts | 2 +- .../processors/ImportBlockingProcessorService.ts | 6 +- .../processors/ImportFollowingProcessorService.ts | 6 +- .../processors/ImportMutingProcessorService.ts | 6 +- .../processors/ImportUserListsProcessorService.ts | 6 +- .../src/queue/processors/InboxProcessorService.ts | 12 +- packages/backend/src/queue/types.ts | 2 +- .../backend/src/server/ActivityPubServerService.ts | 2 +- .../api/endpoints/admin/queue/deliver-delayed.ts | 2 +- .../api/endpoints/admin/queue/inbox-delayed.ts | 2 +- .../src/server/api/endpoints/admin/queue/stats.ts | 2 +- .../endpoints/admin/resolve-abuse-user-report.ts | 2 +- .../backend/src/server/api/endpoints/ap/get.ts | 2 +- .../backend/src/server/api/endpoints/ap/show.ts | 10 +- .../api/endpoints/federation/update-remote-user.ts | 2 +- .../src/server/api/endpoints/notes/polls/vote.ts | 2 +- .../backend/src/server/api/endpoints/users/show.ts | 6 +- .../backend/src/server/web/ClientServerService.ts | 2 +- packages/backend/test/misc/mock-resolver.ts | 4 +- packages/backend/test/tests/activitypub.ts | 6 +- packages/backend/test/tests/ap-request.ts | 2 +- packages/backend/test/unit/RelayService.ts | 2 +- 117 files changed, 5035 insertions(+), 5035 deletions(-) create mode 100644 packages/backend/src/core/QueueModule.ts create mode 100644 packages/backend/src/core/RemoteLoggerService.ts create mode 100644 packages/backend/src/core/RemoteUserResolveService.ts create mode 100644 packages/backend/src/core/WebfingerService.ts create mode 100644 packages/backend/src/core/activitypub/ApAudienceService.ts create mode 100644 packages/backend/src/core/activitypub/ApDbResolverService.ts create mode 100644 packages/backend/src/core/activitypub/ApDeliverManagerService.ts create mode 100644 packages/backend/src/core/activitypub/ApInboxService.ts create mode 100644 packages/backend/src/core/activitypub/ApLoggerService.ts create mode 100644 packages/backend/src/core/activitypub/ApMfmService.ts create mode 100644 packages/backend/src/core/activitypub/ApRendererService.ts create mode 100644 packages/backend/src/core/activitypub/ApRequestService.ts create mode 100644 packages/backend/src/core/activitypub/ApResolverService.ts create mode 100644 packages/backend/src/core/activitypub/LdSignatureService.ts create mode 100644 packages/backend/src/core/activitypub/misc/contexts.ts create mode 100644 packages/backend/src/core/activitypub/models/ApImageService.ts create mode 100644 packages/backend/src/core/activitypub/models/ApMentionService.ts create mode 100644 packages/backend/src/core/activitypub/models/ApNoteService.ts create mode 100644 packages/backend/src/core/activitypub/models/ApPersonService.ts create mode 100644 packages/backend/src/core/activitypub/models/ApQuestionService.ts create mode 100644 packages/backend/src/core/activitypub/models/icon.ts create mode 100644 packages/backend/src/core/activitypub/models/identifier.ts create mode 100644 packages/backend/src/core/activitypub/models/tag.ts create mode 100644 packages/backend/src/core/activitypub/type.ts delete mode 100644 packages/backend/src/core/queue/QueueModule.ts delete mode 100644 packages/backend/src/core/remote/RemoteLoggerService.ts delete mode 100644 packages/backend/src/core/remote/ResolveUserService.ts delete mode 100644 packages/backend/src/core/remote/WebfingerService.ts delete mode 100644 packages/backend/src/core/remote/activitypub/ApAudienceService.ts delete mode 100644 packages/backend/src/core/remote/activitypub/ApDbResolverService.ts delete mode 100644 packages/backend/src/core/remote/activitypub/ApDeliverManagerService.ts delete mode 100644 packages/backend/src/core/remote/activitypub/ApInboxService.ts delete mode 100644 packages/backend/src/core/remote/activitypub/ApLoggerService.ts delete mode 100644 packages/backend/src/core/remote/activitypub/ApMfmService.ts delete mode 100644 packages/backend/src/core/remote/activitypub/ApRendererService.ts delete mode 100644 packages/backend/src/core/remote/activitypub/ApRequestService.ts delete mode 100644 packages/backend/src/core/remote/activitypub/ApResolverService.ts delete mode 100644 packages/backend/src/core/remote/activitypub/LdSignatureService.ts delete mode 100644 packages/backend/src/core/remote/activitypub/misc/contexts.ts delete mode 100644 packages/backend/src/core/remote/activitypub/models/ApImageService.ts delete mode 100644 packages/backend/src/core/remote/activitypub/models/ApMentionService.ts delete mode 100644 packages/backend/src/core/remote/activitypub/models/ApNoteService.ts delete mode 100644 packages/backend/src/core/remote/activitypub/models/ApPersonService.ts delete mode 100644 packages/backend/src/core/remote/activitypub/models/ApQuestionService.ts delete mode 100644 packages/backend/src/core/remote/activitypub/models/icon.ts delete mode 100644 packages/backend/src/core/remote/activitypub/models/identifier.ts delete mode 100644 packages/backend/src/core/remote/activitypub/models/tag.ts delete mode 100644 packages/backend/src/core/remote/activitypub/type.ts (limited to 'packages/backend/src/core/NoteCreateService.ts') diff --git a/packages/backend/src/core/AccountUpdateService.ts b/packages/backend/src/core/AccountUpdateService.ts index 6fe0e05c6d..a5ab4fdfce 100644 --- a/packages/backend/src/core/AccountUpdateService.ts +++ b/packages/backend/src/core/AccountUpdateService.ts @@ -3,10 +3,10 @@ import { DI } from '@/di-symbols.js'; import type { UsersRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; import type { User } from '@/models/entities/User.js'; -import { ApRendererService } from '@/core/remote/activitypub/ApRendererService.js'; +import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { RelayService } from '@/core/RelayService.js'; -import { ApDeliverManagerService } from '@/core/remote/activitypub/ApDeliverManagerService.js'; -import { UserEntityService } from './entities/UserEntityService.js'; +import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; @Injectable() export class AccountUpdateService { diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts index af76767f31..8046ba5311 100644 --- a/packages/backend/src/core/AntennaService.ts +++ b/packages/backend/src/core/AntennaService.ts @@ -11,7 +11,7 @@ import { Cache } from '@/misc/cache.js'; import type { Packed } from '@/misc/schema.js'; import { DI } from '@/di-symbols.js'; import type { MutingsRepository, BlockingsRepository, NotesRepository, AntennaNotesRepository, AntennasRepository, UserGroupJoiningsRepository, UserListJoiningsRepository } from '@/models/index.js'; -import { UtilityService } from './UtilityService.js'; +import { UtilityService } from '@/core/UtilityService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() diff --git a/packages/backend/src/core/CaptchaService.ts b/packages/backend/src/core/CaptchaService.ts index b2bc24ac2c..b60271812c 100644 --- a/packages/backend/src/core/CaptchaService.ts +++ b/packages/backend/src/core/CaptchaService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { UsersRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; -import { HttpRequestService } from './HttpRequestService.js'; +import { HttpRequestService } from '@/core/HttpRequestService.js'; type CaptchaResponse = { success: boolean; diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index da07728d22..085addaa05 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -94,25 +94,25 @@ 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 { ApAudienceService } from './activitypub/ApAudienceService.js'; +import { ApDbResolverService } from './activitypub/ApDbResolverService.js'; +import { ApDeliverManagerService } from './activitypub/ApDeliverManagerService.js'; +import { ApInboxService } from './activitypub/ApInboxService.js'; +import { ApLoggerService } from './activitypub/ApLoggerService.js'; +import { ApMfmService } from './activitypub/ApMfmService.js'; +import { ApRendererService } from './activitypub/ApRendererService.js'; +import { ApRequestService } from './activitypub/ApRequestService.js'; +import { ApResolverService } from './activitypub/ApResolverService.js'; +import { LdSignatureService } from './activitypub/LdSignatureService.js'; +import { RemoteLoggerService } from './RemoteLoggerService.js'; +import { RemoteUserResolveService } from './RemoteUserResolveService.js'; +import { WebfingerService } from './WebfingerService.js'; +import { ApImageService } from './activitypub/models/ApImageService.js'; +import { ApMentionService } from './activitypub/models/ApMentionService.js'; +import { ApNoteService } from './activitypub/models/ApNoteService.js'; +import { ApPersonService } from './activitypub/models/ApPersonService.js'; +import { ApQuestionService } from './activitypub/models/ApQuestionService.js'; +import { QueueModule } from './QueueModule.js'; import { QueueService } from './QueueService.js'; import { LoggerService } from './LoggerService.js'; import type { Provider } from '@nestjs/common'; @@ -226,7 +226,7 @@ const $ApRequestService: Provider = { provide: 'ApRequestService', useExisting: const $ApResolverService: Provider = { provide: 'ApResolverService', useExisting: ApResolverService }; const $LdSignatureService: Provider = { provide: 'LdSignatureService', useExisting: LdSignatureService }; const $RemoteLoggerService: Provider = { provide: 'RemoteLoggerService', useExisting: RemoteLoggerService }; -const $ResolveUserService: Provider = { provide: 'ResolveUserService', useExisting: ResolveUserService }; +const $RemoteUserResolveService: Provider = { provide: 'RemoteUserResolveService', useExisting: RemoteUserResolveService }; const $WebfingerService: Provider = { provide: 'WebfingerService', useExisting: WebfingerService }; const $ApImageService: Provider = { provide: 'ApImageService', useExisting: ApImageService }; const $ApMentionService: Provider = { provide: 'ApMentionService', useExisting: ApMentionService }; @@ -346,7 +346,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting ApResolverService, LdSignatureService, RemoteLoggerService, - ResolveUserService, + RemoteUserResolveService, WebfingerService, ApImageService, ApMentionService, @@ -462,7 +462,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $ApResolverService, $LdSignatureService, $RemoteLoggerService, - $ResolveUserService, + $RemoteUserResolveService, $WebfingerService, $ApImageService, $ApMentionService, @@ -578,7 +578,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting ApResolverService, LdSignatureService, RemoteLoggerService, - ResolveUserService, + RemoteUserResolveService, WebfingerService, ApImageService, ApMentionService, @@ -693,7 +693,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $ApResolverService, $LdSignatureService, $RemoteLoggerService, - $ResolveUserService, + $RemoteUserResolveService, $WebfingerService, $ApImageService, $ApMentionService, diff --git a/packages/backend/src/core/CreateNotificationService.ts b/packages/backend/src/core/CreateNotificationService.ts index feb82dcbf9..504661c3bd 100644 --- a/packages/backend/src/core/CreateNotificationService.ts +++ b/packages/backend/src/core/CreateNotificationService.ts @@ -5,8 +5,8 @@ import type { Notification } from '@/models/entities/Notification.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; -import { NotificationEntityService } from './entities/NotificationEntityService.js'; -import { PushNotificationService } from './PushNotificationService.js'; +import { NotificationEntityService } from '@/core/entities/NotificationEntityService.js'; +import { PushNotificationService } from '@/core/PushNotificationService.js'; @Injectable() export class CreateNotificationService { diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index e1355fff07..3319f3efa8 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -10,8 +10,8 @@ import { Cache } from '@/misc/cache.js'; import { query } from '@/misc/prelude/url.js'; import type { Note } from '@/models/entities/Note.js'; import type { EmojisRepository } from '@/models/index.js'; -import { UtilityService } from './UtilityService.js'; -import { ReactionService } from './ReactionService.js'; +import { UtilityService } from '@/core/UtilityService.js'; +import { ReactionService } from '@/core/ReactionService.js'; /** * 添付用絵文字情報 diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index e0bdd29c0f..1d2ba5df8c 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -28,9 +28,9 @@ import InstanceChart from '@/core/chart/charts/instance.js'; import { DownloadService } from '@/core/DownloadService.js'; import { S3Service } from '@/core/S3Service.js'; import { InternalStorageService } from '@/core/InternalStorageService.js'; -import { DriveFileEntityService } from './entities/DriveFileEntityService.js'; -import { UserEntityService } from './entities/UserEntityService.js'; -import { FileInfoService } from './FileInfoService.js'; +import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { FileInfoService } from '@/core/FileInfoService.js'; import type S3 from 'aws-sdk/clients/s3.js'; type AddFileArgs = { diff --git a/packages/backend/src/core/FederatedInstanceService.ts b/packages/backend/src/core/FederatedInstanceService.ts index b98a41f757..a05c95a2ae 100644 --- a/packages/backend/src/core/FederatedInstanceService.ts +++ b/packages/backend/src/core/FederatedInstanceService.ts @@ -4,7 +4,7 @@ import type { Instance } from '@/models/entities/Instance.js'; import { Cache } from '@/misc/cache.js'; import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; -import { UtilityService } from './UtilityService.js'; +import { UtilityService } from '@/core/UtilityService.js'; @Injectable() export class FederatedInstanceService { diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts index 184404123c..b92ebe6059 100644 --- a/packages/backend/src/core/FetchInstanceMetadataService.ts +++ b/packages/backend/src/core/FetchInstanceMetadataService.ts @@ -9,7 +9,7 @@ import { AppLockService } from '@/core/AppLockService.js'; import type Logger from '@/logger.js'; import { DI } from '@/di-symbols.js'; import { LoggerService } from '@/core/LoggerService.js'; -import { HttpRequestService } from './HttpRequestService.js'; +import { HttpRequestService } from '@/core/HttpRequestService.js'; import type { DOMWindow } from 'jsdom'; type NodeInfo = { diff --git a/packages/backend/src/core/HashtagService.ts b/packages/backend/src/core/HashtagService.ts index 83950aa890..5ca058e9a4 100644 --- a/packages/backend/src/core/HashtagService.ts +++ b/packages/backend/src/core/HashtagService.ts @@ -6,7 +6,7 @@ import { IdService } from '@/core/IdService.js'; import type { Hashtag } from '@/models/entities/Hashtag.js'; import HashtagChart from '@/core/chart/charts/hashtag.js'; import type { HashtagsRepository, UsersRepository } from '@/models/index.js'; -import { UserEntityService } from './entities/UserEntityService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; @Injectable() export class HashtagService { diff --git a/packages/backend/src/core/InstanceActorService.ts b/packages/backend/src/core/InstanceActorService.ts index fa906df4a2..f35a28147d 100644 --- a/packages/backend/src/core/InstanceActorService.ts +++ b/packages/backend/src/core/InstanceActorService.ts @@ -4,7 +4,7 @@ import type { ILocalUser } from '@/models/entities/User.js'; import type { UsersRepository } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; import { DI } from '@/di-symbols.js'; -import { CreateSystemUserService } from './CreateSystemUserService.js'; +import { CreateSystemUserService } from '@/core/CreateSystemUserService.js'; const ACTOR_USERNAME = 'instance.actor' as const; diff --git a/packages/backend/src/core/MessagingService.ts b/packages/backend/src/core/MessagingService.ts index 0603da0651..9de28ad8db 100644 --- a/packages/backend/src/core/MessagingService.ts +++ b/packages/backend/src/core/MessagingService.ts @@ -11,12 +11,12 @@ import { QueueService } from '@/core/QueueService.js'; import { toArray } from '@/misc/prelude/array.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; import type { MessagingMessagesRepository, MutingsRepository, UserGroupJoiningsRepository, UsersRepository } from '@/models/index.js'; -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'; +import { IdService } from '@/core/IdService.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; +import { MessagingMessageEntityService } from '@/core/entities/MessagingMessageEntityService.js'; +import { PushNotificationService } from '@/core/PushNotificationService.js'; @Injectable() export class MessagingService { diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index a23e105674..cf1566a5e8 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -34,12 +34,12 @@ import { WebhookService } from '@/core/WebhookService.js'; import { HashtagService } from '@/core/HashtagService.js'; import { AntennaService } from '@/core/AntennaService.js'; import { QueueService } from '@/core/QueueService.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'; -import { ApDeliverManagerService } from './remote/activitypub/ApDeliverManagerService.js'; +import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; +import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; +import { NoteReadService } from '@/core/NoteReadService.js'; +import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js'; const mutedWordsCache = new Cache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5); @@ -179,7 +179,7 @@ export class NoteCreateService { private hashtagService: HashtagService, private antennaService: AntennaService, private webhookService: WebhookService, - private resolveUserService: ResolveUserService, + private remoteUserResolveService: RemoteUserResolveService, private apDeliverManagerService: ApDeliverManagerService, private apRendererService: ApRendererService, private notesChart: NotesChart, @@ -726,7 +726,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.remoteUserResolveService.resolveUser(m.username, m.host ?? user.host).catch(() => null), ))).filter(x => x != null) as User[]; // Drop duplicate users diff --git a/packages/backend/src/core/NoteDeleteService.ts b/packages/backend/src/core/NoteDeleteService.ts index ccc583c5b6..ce6e755a7e 100644 --- a/packages/backend/src/core/NoteDeleteService.ts +++ b/packages/backend/src/core/NoteDeleteService.ts @@ -11,10 +11,10 @@ import NotesChart from '@/core/chart/charts/notes.js'; import PerUserNotesChart from '@/core/chart/charts/per-user-notes.js'; import InstanceChart from '@/core/chart/charts/instance.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { ApRendererService } from './remote/activitypub/ApRendererService.js'; -import { ApDeliverManagerService } from './remote/activitypub/ApDeliverManagerService.js'; -import { UserEntityService } from './entities/UserEntityService.js'; -import { NoteEntityService } from './entities/NoteEntityService.js'; +import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; +import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; @Injectable() export class NoteDeleteService { diff --git a/packages/backend/src/core/NotePiningService.ts b/packages/backend/src/core/NotePiningService.ts index 8c4a761ba6..a04b52fe4c 100644 --- a/packages/backend/src/core/NotePiningService.ts +++ b/packages/backend/src/core/NotePiningService.ts @@ -8,9 +8,9 @@ import { IdService } from '@/core/IdService.js'; import type { UserNotePining } from '@/models/entities/UserNotePining.js'; import { RelayService } from '@/core/RelayService.js'; import type { Config } from '@/config.js'; -import { UserEntityService } from './entities/UserEntityService.js'; -import { ApDeliverManagerService } from './remote/activitypub/ApDeliverManagerService.js'; -import { ApRendererService } from './remote/activitypub/ApRendererService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; +import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; @Injectable() export class NotePiningService { diff --git a/packages/backend/src/core/NoteReadService.ts b/packages/backend/src/core/NoteReadService.ts index 2c84e1d4d5..e0feaa957d 100644 --- a/packages/backend/src/core/NoteReadService.ts +++ b/packages/backend/src/core/NoteReadService.ts @@ -8,7 +8,7 @@ import type { Note } from '@/models/entities/Note.js'; import { IdService } from '@/core/IdService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import type { UsersRepository, NoteUnreadsRepository, MutingsRepository, NoteThreadMutingsRepository, FollowingsRepository, ChannelFollowingsRepository, AntennaNotesRepository } from '@/models/index.js'; -import { UserEntityService } from './entities/UserEntityService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { NotificationService } from './NotificationService.js'; import { AntennaService } from './AntennaService.js'; diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts index 2606ca4de0..8bbc95b02d 100644 --- a/packages/backend/src/core/NotificationService.ts +++ b/packages/backend/src/core/NotificationService.ts @@ -5,7 +5,7 @@ import type { NotificationsRepository } from '@/models/index.js'; import type { UsersRepository } 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 { UserEntityService } from '@/core/entities/UserEntityService.js'; import { GlobalEventService } from './GlobalEventService.js'; import { PushNotificationService } from './PushNotificationService.js'; diff --git a/packages/backend/src/core/PollService.ts b/packages/backend/src/core/PollService.ts index e3e12b5320..287ce8ada4 100644 --- a/packages/backend/src/core/PollService.ts +++ b/packages/backend/src/core/PollService.ts @@ -8,9 +8,9 @@ import type { CacheableUser } from '@/models/entities/User.js'; import { IdService } from '@/core/IdService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { CreateNotificationService } from '@/core/CreateNotificationService.js'; -import { ApRendererService } from './remote/activitypub/ApRendererService.js'; -import { UserEntityService } from './entities/UserEntityService.js'; -import { ApDeliverManagerService } from './remote/activitypub/ApDeliverManagerService.js'; +import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; @Injectable() export class PollService { diff --git a/packages/backend/src/core/ProxyAccountService.ts b/packages/backend/src/core/ProxyAccountService.ts index 07d8d0dbd5..4cbdadd029 100644 --- a/packages/backend/src/core/ProxyAccountService.ts +++ b/packages/backend/src/core/ProxyAccountService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UsersRepository } from '@/models/index.js'; import type { ILocalUser, User } from '@/models/entities/User.js'; import { DI } from '@/di-symbols.js'; -import { MetaService } from './MetaService.js'; +import { MetaService } from '@/core/MetaService.js'; @Injectable() export class ProxyAccountService { diff --git a/packages/backend/src/core/PushNotificationService.ts b/packages/backend/src/core/PushNotificationService.ts index 5eaaed00eb..98e0841799 100644 --- a/packages/backend/src/core/PushNotificationService.ts +++ b/packages/backend/src/core/PushNotificationService.ts @@ -5,7 +5,7 @@ import type { Config } from '@/config.js'; import type { Packed } from '@/misc/schema'; import { getNoteSummary } from '@/misc/get-note-summary.js'; import type { SwSubscriptionsRepository } from '@/models/index.js'; -import { MetaService } from './MetaService.js'; +import { MetaService } from '@/core/MetaService.js'; // Defined also packages/sw/types.ts#L14-L21 type pushNotificationsTypes = { diff --git a/packages/backend/src/core/QueueModule.ts b/packages/backend/src/core/QueueModule.ts new file mode 100644 index 0000000000..edd843977b --- /dev/null +++ b/packages/backend/src/core/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/core/QueueService.ts b/packages/backend/src/core/QueueService.ts index d9ad26747f..a27d68ee19 100644 --- a/packages/backend/src/core/QueueService.ts +++ b/packages/backend/src/core/QueueService.ts @@ -1,11 +1,11 @@ import { Inject, Injectable } from '@nestjs/common'; import { v4 as uuid } from 'uuid'; -import type { IActivity } from '@/core/remote/activitypub/type.js'; +import type { IActivity } from '@/core/activitypub/type.js'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { Webhook, webhookEventTypes } from '@/models/entities/Webhook.js'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; -import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from './queue/QueueModule.js'; +import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from './QueueModule.js'; import type { ThinUser } from '../queue/types.js'; import type httpSignature from '@peertube/http-signature'; diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index d5b3c0e799..7a9724e7dd 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -12,11 +12,11 @@ import { GlobalEventService } from '@/core/GlobalEventService.js'; import { CreateNotificationService } from '@/core/CreateNotificationService.js'; import PerUserReactionsChart from '@/core/chart/charts/per-user-reactions.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 { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; +import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; +import { MetaService } from '@/core/MetaService.js'; import { UtilityService } from './UtilityService.js'; const legacies: Record = { diff --git a/packages/backend/src/core/RelayService.ts b/packages/backend/src/core/RelayService.ts index 3c67e0573f..7951edddcb 100644 --- a/packages/backend/src/core/RelayService.ts +++ b/packages/backend/src/core/RelayService.ts @@ -7,7 +7,7 @@ import { Cache } from '@/misc/cache.js'; import type { Relay } from '@/models/entities/Relay.js'; import { QueueService } from '@/core/QueueService.js'; import { CreateSystemUserService } from '@/core/CreateSystemUserService.js'; -import { ApRendererService } from '@/core/remote/activitypub/ApRendererService.js'; +import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { DI } from '@/di-symbols.js'; import { deepClone } from '@/misc/clone.js'; diff --git a/packages/backend/src/core/RemoteLoggerService.ts b/packages/backend/src/core/RemoteLoggerService.ts new file mode 100644 index 0000000000..68246466c8 --- /dev/null +++ b/packages/backend/src/core/RemoteLoggerService.ts @@ -0,0 +1,14 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type Logger from '@/logger.js'; +import { LoggerService } from '@/core/LoggerService.js'; + +@Injectable() +export class RemoteLoggerService { + public logger: Logger; + + constructor( + private loggerService: LoggerService, + ) { + this.logger = this.loggerService.getLogger('remote', 'cyan'); + } +} diff --git a/packages/backend/src/core/RemoteUserResolveService.ts b/packages/backend/src/core/RemoteUserResolveService.ts new file mode 100644 index 0000000000..809b50f6e9 --- /dev/null +++ b/packages/backend/src/core/RemoteUserResolveService.ts @@ -0,0 +1,132 @@ +import { URL } from 'node:url'; +import { Inject, Injectable } from '@nestjs/common'; +import chalk from 'chalk'; +import { IsNull } from 'typeorm'; +import { DI } from '@/di-symbols.js'; +import type { UsersRepository } from '@/models/index.js'; +import type { IRemoteUser, User } from '@/models/entities/User.js'; +import type { Config } from '@/config.js'; +import type Logger from '@/logger.js'; +import { UtilityService } from '@/core/UtilityService.js'; +import { WebfingerService } from '@/core/WebfingerService.js'; +import { RemoteLoggerService } from '@/core/RemoteLoggerService.js'; +import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; + +@Injectable() +export class RemoteUserResolveService { + private logger: Logger; + + constructor( + @Inject(DI.config) + private config: Config, + + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + private utilityService: UtilityService, + private webfingerService: WebfingerService, + private remoteLoggerService: RemoteLoggerService, + private apPersonService: ApPersonService, + ) { + this.logger = this.remoteLoggerService.logger.createSubLogger('resolve-user'); + } + + public async resolveUser(username: string, host: string | null): Promise { + const usernameLower = username.toLowerCase(); + + if (host == null) { + 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'); + } else { + return u; + } + }); + } + + host = this.utilityService.toPuny(host); + + if (this.config.host === host) { + 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'); + } 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); + + this.logger.succ(`return new remote user: ${chalk.magenta(acctLower)}`); + return await this.apPersonService.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(), + }); + + 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. + 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); + if (uri.hostname !== host) { + throw new Error('Invalid uri'); + } + + await this.usersRepository.update({ + usernameLower, + host: host, + }, { + uri: self.href, + }); + } else { + this.logger.info(`uri is fine: ${acctLower}`); + } + + await this.apPersonService.updatePerson(self.href); + + 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'); + } else { + return u; + } + }); + } + + this.logger.info(`return existing remote user: ${acctLower}`); + return user; + } + + private async resolveSelf(acctLower: string) { + this.logger.info(`WebFinger for ${chalk.yellow(acctLower)}`); + 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) { + 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/core/S3Service.ts b/packages/backend/src/core/S3Service.ts index 723a79dc59..1374ee06c8 100644 --- a/packages/backend/src/core/S3Service.ts +++ b/packages/backend/src/core/S3Service.ts @@ -4,7 +4,7 @@ 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.js'; -import { HttpRequestService } from './HttpRequestService.js'; +import { HttpRequestService } from '@/core/HttpRequestService.js'; @Injectable() export class S3Service { diff --git a/packages/backend/src/core/SignupService.ts b/packages/backend/src/core/SignupService.ts index 2239d5fd83..1e34d9e4f8 100644 --- a/packages/backend/src/core/SignupService.ts +++ b/packages/backend/src/core/SignupService.ts @@ -12,7 +12,7 @@ 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 { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UtilityService } from './UtilityService.js'; @Injectable() diff --git a/packages/backend/src/core/UserBlockingService.ts b/packages/backend/src/core/UserBlockingService.ts index b7a434684e..3399bb510f 100644 --- a/packages/backend/src/core/UserBlockingService.ts +++ b/packages/backend/src/core/UserBlockingService.ts @@ -10,10 +10,10 @@ import { DI } from '@/di-symbols.js'; import logger from '@/logger.js'; import type { UsersRepository, FollowingsRepository, FollowRequestsRepository, BlockingsRepository, UserListsRepository, UserListJoiningsRepository } from '@/models/index.js'; import Logger from '@/logger.js'; -import { UserEntityService } from './entities/UserEntityService.js'; -import { WebhookService } from './WebhookService.js'; -import { ApRendererService } from './remote/activitypub/ApRendererService.js'; -import { LoggerService } from './LoggerService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; +import { LoggerService } from '@/core/LoggerService.js'; +import { WebhookService } from '@/core/WebhookService.js'; @Injectable() export class UserBlockingService { diff --git a/packages/backend/src/core/UserCacheService.ts b/packages/backend/src/core/UserCacheService.ts index b7166010ee..25a600a8da 100644 --- a/packages/backend/src/core/UserCacheService.ts +++ b/packages/backend/src/core/UserCacheService.ts @@ -4,7 +4,7 @@ import type { UsersRepository } 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 { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index 31e08c1366..2f51e2a9df 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -14,8 +14,8 @@ import { CreateNotificationService } from '@/core/CreateNotificationService.js'; import { DI } from '@/di-symbols.js'; import type { BlockingsRepository, FollowingsRepository, FollowRequestsRepository, InstancesRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; import Logger from '../logger.js'; -import { UserEntityService } from './entities/UserEntityService.js'; -import { ApRendererService } from './remote/activitypub/ApRendererService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; const logger = new Logger('following/create'); diff --git a/packages/backend/src/core/UserListService.ts b/packages/backend/src/core/UserListService.ts index b1d01a1565..1d1ead5a1f 100644 --- a/packages/backend/src/core/UserListService.ts +++ b/packages/backend/src/core/UserListService.ts @@ -7,8 +7,8 @@ import { IdService } from '@/core/IdService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; -import { UserEntityService } from './entities/UserEntityService.js'; -import { ProxyAccountService } from './ProxyAccountService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { ProxyAccountService } from '@/core/ProxyAccountService.js'; @Injectable() export class UserListService { diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts index 82c2e98236..02f686bab6 100644 --- a/packages/backend/src/core/UserSuspendService.ts +++ b/packages/backend/src/core/UserSuspendService.ts @@ -6,8 +6,8 @@ import { QueueService } from '@/core/QueueService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; -import { ApRendererService } from './remote/activitypub/ApRendererService.js'; -import { UserEntityService } from './entities/UserEntityService.js'; +import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; @Injectable() export class UserSuspendService { diff --git a/packages/backend/src/core/WebfingerService.ts b/packages/backend/src/core/WebfingerService.ts new file mode 100644 index 0000000000..d2a88be583 --- /dev/null +++ b/packages/backend/src/core/WebfingerService.ts @@ -0,0 +1,48 @@ +import { URL } from 'node:url'; +import { Inject, Injectable } from '@nestjs/common'; +import { DI } from '@/di-symbols.js'; +import type { Config } from '@/config.js'; +import { query as urlQuery } from '@/misc/prelude/url.js'; +import { HttpRequestService } from '@/core/HttpRequestService.js'; + +type ILink = { + href: string; + rel?: string; +}; + +type IWebFinger = { + links: ILink[]; + subject: string; +}; + +@Injectable() +export class WebfingerService { + constructor( + @Inject(DI.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; + } + + private 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/core/activitypub/ApAudienceService.ts b/packages/backend/src/core/activitypub/ApAudienceService.ts new file mode 100644 index 0000000000..744017aa3a --- /dev/null +++ b/packages/backend/src/core/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 '@/misc/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, + }; + } + + private 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; + } + + private isPublic(id: string) { + return [ + 'https://www.w3.org/ns/activitystreams#Public', + 'as#Public', + 'Public', + ].includes(id); + } + + private isFollowers(id: string, actor: CacheableRemoteUser) { + return ( + id === (actor.followersUri ?? `${actor.uri}/followers`) + ); + } +} diff --git a/packages/backend/src/core/activitypub/ApDbResolverService.ts b/packages/backend/src/core/activitypub/ApDbResolverService.ts new file mode 100644 index 0000000000..77d200c3c8 --- /dev/null +++ b/packages/backend/src/core/activitypub/ApDbResolverService.ts @@ -0,0 +1,179 @@ +import { Inject, Injectable } from '@nestjs/common'; +import escapeRegexp from 'escape-regexp'; +import { DI } from '@/di-symbols.js'; +import type { MessagingMessagesRepository, NotesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.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/UserPublickey.js'; +import { UserCacheService } from '@/core/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 = { + /** 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 { + private publicKeyCache: Cache; + private publicKeyByUserIdCache: Cache; + + constructor( + @Inject(DI.config) + private config: Config, + + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.messagingMessagesRepository) + private messagingMessagesRepository: MessagingMessagesRepository, + + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + + @Inject(DI.userPublickeysRepository) + private userPublickeysRepository: UserPublickeysRepository, + + private userCacheService: UserCacheService, + private apPersonService: ApPersonService, + ) { + 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, () => this.usersRepository.findOneBy({ + id: parsed.id, + }).then(x => x ?? undefined)) ?? null; + } else { + return await this.userCacheService.uriPersonCache.fetch(parsed.uri, () => this.usersRepository.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, () => this.usersRepository.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 this.apPersonService.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/core/activitypub/ApDeliverManagerService.ts b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts new file mode 100644 index 0000000000..6fc75a0397 --- /dev/null +++ b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts @@ -0,0 +1,199 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { IsNull, Not } from 'typeorm'; +import { DI } from '@/di-symbols.js'; +import type { FollowingsRepository, UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; +import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; +import { QueueService } from '@/core/QueueService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; + +interface IRecipe { + type: string; +} + +interface IFollowersRecipe extends IRecipe { + type: 'Followers'; +} + +interface IDirectRecipe extends IRecipe { + type: 'Direct'; + to: IRemoteUser; +} + +const isFollowers = (recipe: any): recipe is IFollowersRecipe => + recipe.type === 'Followers'; + +const isDirect = (recipe: any): recipe is IDirectRecipe => + recipe.type === 'Direct'; + +@Injectable() +export class ApDeliverManagerService { + constructor( + @Inject(DI.config) + private config: Config, + + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.followingsRepository) + private followingsRepository: FollowingsRepository, + + private userEntityService: UserEntityService, + 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.userEntityService, + 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.userEntityService, + this.followingsRepository, + this.queueService, + actor, + activity, + ); + 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 { + private actor: { id: User['id']; host: null; }; + private activity: any; + private recipes: IRecipe[] = []; + + /** + * Constructor + * @param actor Actor + * @param activity Activity to deliver + */ + constructor( + private userEntityService: UserEntityService, + private followingsRepository: FollowingsRepository, + private queueService: QueueService, + + actor: { id: User['id']; host: null; }, + activity: any, + ) { + this.actor = actor; + this.activity = activity; + } + + /** + * Add recipe for followers deliver + */ + public addFollowersRecipe() { + const deliver = { + type: 'Followers', + } as IFollowersRecipe; + + this.addRecipe(deliver); + } + + /** + * Add recipe for direct deliver + * @param to To + */ + public addDirectRecipe(to: IRemoteUser) { + const recipe = { + type: 'Direct', + to, + } as IDirectRecipe; + + this.addRecipe(recipe); + } + + /** + * Add recipe + * @param recipe Recipe + */ + public addRecipe(recipe: IRecipe) { + this.recipes.push(recipe); + } + + /** + * Execute delivers + */ + public async execute() { + if (!this.userEntityService.isLocalUser(this.actor)) return; + + const inboxes = new Set(); + + /* + build inbox list + + Process follower recipes first to avoid duplication when processing + direct recipes later. + */ + if (this.recipes.some(r => isFollowers(r))) { + // followers deliver + // TODO: SELECT DISTINCT ON ("followerSharedInbox") "followerSharedInbox" みたいな問い合わせにすればよりパフォーマンス向上できそう + // ただ、sharedInboxがnullなリモートユーザーも稀におり、その対応ができなさそう? + const followers = await this.followingsRepository.find({ + where: { + followeeId: this.actor.id, + followerHost: Not(IsNull()), + }, + select: { + followerSharedInbox: true, + followerInbox: true, + }, + }) as { + followerSharedInbox: string | null; + followerInbox: string; + }[]; + + for (const following of followers) { + const inbox = following.followerSharedInbox ?? following.followerInbox; + inboxes.add(inbox); + } + } + + this.recipes.filter((recipe): recipe is IDirectRecipe => + // followers recipes have already been processed + isDirect(recipe) + // check that shared inbox has not been added yet + && !(recipe.to.sharedInbox && inboxes.has(recipe.to.sharedInbox)) + // check that they actually have an inbox + && recipe.to.inbox != null, + ) + .forEach(recipe => inboxes.add(recipe.to.inbox!)); + + // deliver + for (const inbox of inboxes) { + this.queueService.deliver(this.actor, this.activity, inbox); + } + } +} diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts new file mode 100644 index 0000000000..3da384ec2d --- /dev/null +++ b/packages/backend/src/core/activitypub/ApInboxService.ts @@ -0,0 +1,740 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { In } from 'typeorm'; +import { DI } from '@/di-symbols.js'; +import type { Config } from '@/config.js'; +import type { CacheableRemoteUser } from '@/models/entities/User.js'; +import { UserFollowingService } from '@/core/UserFollowingService.js'; +import { ReactionService } from '@/core/ReactionService.js'; +import { RelayService } from '@/core/RelayService.js'; +import { NotePiningService } from '@/core/NotePiningService.js'; +import { UserBlockingService } from '@/core/UserBlockingService.js'; +import { NoteDeleteService } from '@/core/NoteDeleteService.js'; +import { NoteCreateService } from '@/core/NoteCreateService.js'; +import { concat, toArray, toSingle, unique } from '@/misc/prelude/array.js'; +import { AppLockService } from '@/core/AppLockService.js'; +import type Logger from '@/logger.js'; +import { MetaService } from '@/core/MetaService.js'; +import { IdService } from '@/core/IdService.js'; +import { StatusError } from '@/misc/status-error.js'; +import { UtilityService } from '@/core/UtilityService.js'; +import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { QueueService } from '@/core/QueueService.js'; +import { MessagingService } from '@/core/MessagingService.js'; +import type { UsersRepository, NotesRepository, FollowingsRepository, MessagingMessagesRepository, AbuseUserReportsRepository, FollowRequestsRepository } from '@/models/index.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'; + +@Injectable() +export class ApInboxService { + private logger: Logger; + + constructor( + @Inject(DI.config) + private config: Config, + + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + + @Inject(DI.followingsRepository) + private followingsRepository: FollowingsRepository, + + @Inject(DI.messagingMessagesRepository) + private messagingMessagesRepository: MessagingMessagesRepository, + + @Inject(DI.abuseUserReportsRepository) + private abuseUserReportsRepository: AbuseUserReportsRepository, + + @Inject(DI.followRequestsRepository) + private followRequestsRepository: FollowRequestsRepository, + + 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, + private userBlockingService: UserBlockingService, + private noteCreateService: NoteCreateService, + private noteDeleteService: NoteDeleteService, + private appLockService: AppLockService, + private apResolverService: ApResolverService, + 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; + } + + public async performActivity(actor: CacheableRemoteUser, activity: IObject) { + if (isCollectionOrOrderedCollection(activity)) { + const resolver = this.apResolverService.createResolver(); + for (const item of toArray(isCollection(activity) ? activity.items : activity.orderedItems)) { + const act = await resolver.resolve(item); + try { + await this.performOneActivity(actor, act); + } catch (err) { + if (err instanceof Error || typeof err === 'string') { + this.logger.error(err); + } + } + } + } 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 { + if (actor.isSuspended) return; + + if (isCreate(activity)) { + await this.create(actor, activity); + } else if (isDelete(activity)) { + await this.delete(actor, activity); + } else if (isUpdate(activity)) { + await this.update(actor, activity); + } else if (isRead(activity)) { + await this.read(actor, activity); + } else if (isFollow(activity)) { + await this.follow(actor, activity); + } else if (isAccept(activity)) { + await this.accept(actor, activity); + } else if (isReject(activity)) { + await this.reject(actor, activity); + } else if (isAdd(activity)) { + await this.add(actor, activity).catch(err => this.logger.error(err)); + } else if (isRemove(activity)) { + await this.remove(actor, activity).catch(err => this.logger.error(err)); + } else if (isAnnounce(activity)) { + await this.announce(actor, activity); + } else if (isLike(activity)) { + await this.like(actor, activity); + } else if (isUndo(activity)) { + await this.undo(actor, activity); + } else if (isBlock(activity)) { + await this.block(actor, activity); + } else if (isFlag(activity)) { + await this.flag(actor, activity); + } else { + this.logger.warn(`unrecognized activity type: ${(activity as any).type}`); + } + } + + private async follow(actor: CacheableRemoteUser, activity: IFollow): Promise { + const followee = await this.apDbResolverService.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'; + } + + private async like(actor: CacheableRemoteUser, activity: ILike): Promise { + const targetUri = getApId(activity.object); + + 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); + + return await this.reactionService.create(actor, note, activity._misskey_reaction ?? activity.content ?? activity.name).catch(err => { + if (err.id === '51c42bb4-931a-456b-bff7-e5a8a70dd298') { + return 'skip: already reacted'; + } else { + throw err; + } + }).then(() => 'ok'); + } + + private async read(actor: CacheableRemoteUser, activity: IRead): Promise { + const id = await getApId(activity.object); + + if (!this.utilityService.isSelfHost(this.utilityService.extractDbHost(id))) { + return `skip: Read to foreign host (${id})`; + } + + const messageId = id.split('/').pop(); + + const message = await this.messagingMessagesRepository.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 this.messagingService.readUserMessagingMessage(message.recipientId!, message.userId, [message.id]); + return `ok: mark as read (${message.userId} => ${message.recipientId} ${message.id})`; + } + + private async accept(actor: CacheableRemoteUser, activity: IAccept): Promise { + const uri = activity.id ?? activity; + + this.logger.info(`Accept: ${uri}`); + + const resolver = this.apResolverService.createResolver(); + + 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); + + return `skip: Unknown Accept type: ${getApType(object)}`; + } + + private async acceptFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { + // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある + + const follower = await this.apDbResolverService.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'; + } + + private 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 this.apNoteService.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}`); + } + + private async announce(actor: CacheableRemoteUser, activity: IAnnounce): Promise { + const uri = getApId(activity); + + this.logger.info(`Announce: ${uri}`); + + const targetUri = getApId(activity.object); + + this.announceNote(actor, activity, targetUri); + } + + private async announceNote(actor: CacheableRemoteUser, activity: IAnnounce, targetUri: string): Promise { + const uri = getApId(activity); + + if (actor.isSuspended) { + return; + } + + // アナウンス先をブロックしてたら中断 + const meta = await this.metaService.fetch(); + if (meta.blockedHosts.includes(this.utilityService.extractDbHost(uri))) return; + + const unlock = await this.appLockService.getApLock(uri); + + try { + // 既に同じURIを持つものが登録されていないかチェック + const exist = await this.apNoteService.fetchNote(uri); + if (exist) { + return; + } + + // Announce対象をresolve + let renote; + try { + renote = await this.apNoteService.resolveNote(targetUri); + if (renote == null) throw new Error('announce target is null'); + } catch (err) { + // 対象が4xxならスキップ + 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} - ${err.statusCode ?? err}`); + } + throw err; + } + + if (!await this.noteEntityService.isVisibleForMe(renote, actor.id)) { + this.logger.warn('skip: invalid actor for this activity'); + return; + } + + this.logger.info(`Creating the (Re)Note: ${uri}`); + + const activityAudience = await this.apAudienceService.parseAudience(actor, activity.to, activity.cc); + + await this.noteCreateService.create(actor, { + createdAt: activity.published ? new Date(activity.published) : null, + renote, + visibility: activityAudience.visibility, + visibleUsers: activityAudience.visibleUsers, + uri, + }); + } finally { + unlock(); + } + } + + private async block(actor: CacheableRemoteUser, activity: IBlock): Promise { + // ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず + + const blockee = await this.apDbResolverService.getUserFromApId(activity.object); + + if (blockee == null) { + return 'skip: blockee not found'; + } + + if (blockee.host != null) { + return 'skip: ブロックしようとしているユーザーはローカルユーザーではありません'; + } + + await this.userBlockingService.block(await this.usersRepository.findOneByOrFail({ id: actor.id }), await this.usersRepository.findOneByOrFail({ id: blockee.id })); + return 'ok'; + } + + private async create(actor: CacheableRemoteUser, activity: ICreate): Promise { + const uri = getApId(activity); + + this.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 = this.apResolverService.createResolver(); + + const object = await resolver.resolve(activity.object).catch(e => { + this.logger.error(`Resolution failed: ${e}`); + throw e; + }); + + if (isPost(object)) { + this.createNote(resolver, actor, object, false, activity); + } else { + this.logger.warn(`Unknown type: ${getApType(object)}`); + } + } + + private 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 (this.utilityService.extractDbHost(actor.uri) !== this.utilityService.extractDbHost(note.id)) { + return 'skip: host in actor.uri !== note.id'; + } + } + } + + const unlock = await this.appLockService.getApLock(uri); + + try { + const exist = await this.apNoteService.fetchNote(note); + if (exist) return 'skip: note exists'; + + await this.apNoteService.createNote(note, resolver, silent); + return 'ok'; + } catch (err) { + if (err instanceof StatusError && err.isClientError) { + return `skip ${err.statusCode}`; + } else { + throw err; + } + } finally { + unlock(); + } + } + + private 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}`; + } + } + + private async deleteActor(actor: CacheableRemoteUser, uri: string): Promise { + this.logger.info(`Deleting the Actor: ${uri}`); + + if (actor.uri !== uri) { + return `skip: delete actor ${actor.uri} !== ${uri}`; + } + + const user = await this.usersRepository.findOneByOrFail({ id: actor.id }); + if (user.isDeleted) { + this.logger.info('skip: already deleted'); + } + + const job = await this.queueService.createDeleteAccountJob(actor); + + await this.usersRepository.update(actor.id, { + isDeleted: true, + }); + + return `ok: queued ${job.name} ${job.id}`; + } + + private async deleteNote(actor: CacheableRemoteUser, uri: string): Promise { + this.logger.info(`Deleting the Note: ${uri}`); + + const unlock = await this.appLockService.getApLock(uri); + + try { + const note = await this.apDbResolverService.getNoteFromApId(uri); + + if (note == null) { + const message = await this.apDbResolverService.getMessageFromApId(uri); + if (message == null) return 'message not found'; + + if (message.userId !== actor.id) { + return '投稿を削除しようとしているユーザーは投稿の作成者ではありません'; + } + + await this.messagingService.deleteMessage(message); + + return 'ok: message deleted'; + } + + if (note.userId !== actor.id) { + return '投稿を削除しようとしているユーザーは投稿の作成者ではありません'; + } + + await this.noteDeleteService.delete(actor, note); + return 'ok: note deleted'; + } finally { + unlock(); + } + } + + private 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(this.config.url + '/users/')).map(uri => uri.split('/').pop()!); + const users = await this.usersRepository.findBy({ + id: In(userIds), + }); + if (users.length < 1) return 'skip'; + + await this.abuseUserReportsRepository.insert({ + id: this.idService.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'; + } + + private async reject(actor: CacheableRemoteUser, activity: IReject): Promise { + const uri = activity.id ?? activity; + + this.logger.info(`Reject: ${uri}`); + + const resolver = this.apResolverService.createResolver(); + + const object = await resolver.resolve(activity.object).catch(e => { + this.logger.error(`Resolution failed: ${e}`); + throw e; + }); + + if (isFollow(object)) return await this.rejectFollow(actor, object); + + return `skip: Unknown Reject type: ${getApType(object)}`; + } + + private async rejectFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { + // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある + + const follower = await this.apDbResolverService.getUserFromApId(activity.actor); + + if (follower == null) { + return 'skip: follower not found'; + } + + if (!this.userEntityService.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'; + } + + private 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 this.apNoteService.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}`); + } + + private 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; + + this.logger.info(`Undo: ${uri}`); + + const resolver = this.apResolverService.createResolver(); + + const object = await resolver.resolve(activity.object).catch(e => { + this.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)}`; + } + + private async undoAccept(actor: CacheableRemoteUser, activity: IAccept): Promise { + const follower = await this.apDbResolverService.getUserFromApId(activity.object); + if (follower == null) { + return 'skip: follower not found'; + } + + const following = await this.followingsRepository.findOneBy({ + followerId: follower.id, + followeeId: actor.id, + }); + + if (following) { + await this.userFollowingService.unfollow(follower, actor); + return 'ok: unfollowed'; + } + + return 'skip: フォローされていない'; + } + + private async undoAnnounce(actor: CacheableRemoteUser, activity: IAnnounce): Promise { + const uri = getApId(activity); + + const note = await this.notesRepository.findOneBy({ + uri, + userId: actor.id, + }); + + if (!note) return 'skip: no such Announce'; + + await this.noteDeleteService.delete(actor, note); + return 'ok: deleted'; + } + + private async undoBlock(actor: CacheableRemoteUser, activity: IBlock): Promise { + const blockee = await this.apDbResolverService.getUserFromApId(activity.object); + + if (blockee == null) { + return 'skip: blockee not found'; + } + + if (blockee.host != null) { + return 'skip: ブロック解除しようとしているユーザーはローカルユーザーではありません'; + } + + await this.userBlockingService.unblock(await this.usersRepository.findOneByOrFail({ id: actor.id }), blockee); + return 'ok'; + } + + private async undoFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { + const followee = await this.apDbResolverService.getUserFromApId(activity.object); + if (followee == null) { + return 'skip: followee not found'; + } + + if (followee.host != null) { + return 'skip: フォロー解除しようとしているユーザーはローカルユーザーではありません'; + } + + const req = await this.followRequestsRepository.findOneBy({ + followerId: actor.id, + followeeId: followee.id, + }); + + const following = await this.followingsRepository.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: リクエストもフォローもされていない'; + } + + private async undoLike(actor: CacheableRemoteUser, activity: ILike): Promise { + const targetUri = getApId(activity.object); + + const note = await this.apNoteService.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'; + } + + private async update(actor: CacheableRemoteUser, activity: IUpdate): Promise { + if ('actor' in activity && actor.uri !== activity.actor) { + return 'skip: invalid actor'; + } + + this.logger.debug('Update'); + + const resolver = this.apResolverService.createResolver(); + + const object = await resolver.resolve(activity.object).catch(e => { + this.logger.error(`Resolution failed: ${e}`); + throw e; + }); + + if (isActor(object)) { + await this.apPersonService.updatePerson(actor.uri!, resolver, object); + return 'ok: Person updated'; + } else if (getApType(object) === 'Question') { + await this.apQuestionService.updateQuestion(object, resolver).catch(err => console.error(err)); + return 'ok: Question updated'; + } else { + return `skip: Unknown type: ${getApType(object)}`; + } + } +} diff --git a/packages/backend/src/core/activitypub/ApLoggerService.ts b/packages/backend/src/core/activitypub/ApLoggerService.ts new file mode 100644 index 0000000000..a742cc42da --- /dev/null +++ b/packages/backend/src/core/activitypub/ApLoggerService.ts @@ -0,0 +1,14 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type Logger from '@/logger.js'; +import { RemoteLoggerService } from '@/core/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/core/activitypub/ApMfmService.ts b/packages/backend/src/core/activitypub/ApMfmService.ts new file mode 100644 index 0000000000..8804fde64a --- /dev/null +++ b/packages/backend/src/core/activitypub/ApMfmService.ts @@ -0,0 +1,30 @@ +import { Inject, Injectable } from '@nestjs/common'; +import * as mfm from 'mfm-js'; +import { DI } from '@/di-symbols.js'; +import type { Config } from '@/config.js'; +import { MfmService } from '@/core/MfmService.js'; +import type { Note } from '@/models/entities/Note.js'; +import { extractApHashtagObjects } from './models/tag.js'; +import type { IObject } from './type.js'; + +@Injectable() +export class ApMfmService { + constructor( + @Inject(DI.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); + } + + 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/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts new file mode 100644 index 0000000000..38a92567c3 --- /dev/null +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -0,0 +1,703 @@ +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 { 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 '@/core/UserKeypairStoreService.js'; +import { MfmService } from '@/core/MfmService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; +import type { UserKeypair } from '@/models/entities/UserKeypair.js'; +import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFilesRepository, EmojisRepository, PollsRepository } from '@/models/index.js'; +import { LdSignatureService } from './LdSignatureService.js'; +import { ApMfmService } from './ApMfmService.js'; +import type { IActivity, IObject } from './type.js'; +import type { IIdentifier } from './models/identifier.js'; + +@Injectable() +export class ApRendererService { + constructor( + @Inject(DI.config) + private config: Config, + + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.userProfilesRepository) + private userProfilesRepository: UserProfilesRepository, + + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + + @Inject(DI.driveFilesRepository) + private driveFilesRepository: DriveFilesRepository, + + @Inject(DI.emojisRepository) + private emojisRepository: EmojisRepository, + + @Inject(DI.pollsRepository) + private pollsRepository: PollsRepository, + + private userEntityService: UserEntityService, + private driveFileEntityService: DriveFileEntityService, + private ldSignatureService: LdSignatureService, + private userKeypairStoreService: UserKeypairStoreService, + private apMfmService: ApMfmService, + private mfmService: MfmService, + ) { + } + + 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.driveFileEntityService.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.userEntityService.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.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; + } + + 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.driveFileEntityService.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: { uri: string | null }) { + 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.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}`, + }; + } + + 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 = this.apMfmService.getNoteHtml(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: this.apMfmService.getNoteHtml(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: summary ?? undefined, + content: content ?? undefined, + _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), + this.userProfilesRepository.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 ? this.mfmService.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 { + const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); + + const ldSignature = this.ldSignatureService.use(); + 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?: IObject[]) { + const page: any = { + id, + type: 'OrderedCollection', + totalItems, + }; + + if (first) page.first = first; + if (last) page.last = last; + if (orderedItems) page.orderedItems = orderedItems; + + return page; + } + + private 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/core/activitypub/ApRequestService.ts b/packages/backend/src/core/activitypub/ApRequestService.ts new file mode 100644 index 0000000000..baad46d668 --- /dev/null +++ b/packages/backend/src/core/activitypub/ApRequestService.ts @@ -0,0 +1,182 @@ +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 type { User } from '@/models/entities/User.js'; +import { UserKeypairStoreService } from '@/core/UserKeypairStoreService.js'; +import { HttpRequestService } from '@/core/HttpRequestService.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.config) + private config: Config, + + private userKeypairStoreService: UserKeypairStoreService, + private httpRequestService: HttpRequestService, + ) { + } + + private 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, + }; + } + + private 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, + }; + } + + private 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, + }; + } + + private 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'); + } + + private 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; + } + + private 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 this.httpRequestService.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 this.httpRequestService.getResponse({ + url, + method: req.request.method, + headers: req.request.headers, + }); + + return await res.json(); + } +} diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts new file mode 100644 index 0000000000..bcdb9383d1 --- /dev/null +++ b/packages/backend/src/core/activitypub/ApResolverService.ts @@ -0,0 +1,195 @@ +import { Inject, Injectable } from '@nestjs/common'; +import type { ILocalUser } from '@/models/entities/User.js'; +import { InstanceActorService } from '@/core/InstanceActorService.js'; +import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; +import { MetaService } from '@/core/MetaService.js'; +import { HttpRequestService } from '@/core/HttpRequestService.js'; +import { DI } from '@/di-symbols.js'; +import { UtilityService } from '@/core/UtilityService.js'; +import { isCollectionOrOrderedCollection } from './type.js'; +import { ApDbResolverService } from './ApDbResolverService.js'; +import { ApRendererService } from './ApRendererService.js'; +import { ApRequestService } from './ApRequestService.js'; +import type { IObject, ICollection, IOrderedCollection } from './type.js'; + +@Injectable() +export class ApResolverService { + constructor( + @Inject(DI.config) + private config: Config, + + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + + @Inject(DI.pollsRepository) + private pollsRepository: PollsRepository, + + @Inject(DI.noteReactionsRepository) + private noteReactionsRepository: NoteReactionsRepository, + + private utilityService: UtilityService, + private instanceActorService: InstanceActorService, + private metaService: MetaService, + private apRequestService: ApRequestService, + private httpRequestService: HttpRequestService, + private apRendererService: ApRendererService, + private apDbResolverService: ApDbResolverService, + ) { + } + + public createResolver(): Resolver { + return new Resolver( + this.config, + this.usersRepository, + this.notesRepository, + this.pollsRepository, + this.noteReactionsRepository, + this.utilityService, + this.instanceActorService, + this.metaService, + this.apRequestService, + this.httpRequestService, + this.apRendererService, + this.apDbResolverService, + ); + } +} + +export class Resolver { + private history: Set; + private user?: ILocalUser; + + constructor( + private config: Config, + private usersRepository: UsersRepository, + private notesRepository: NotesRepository, + private pollsRepository: PollsRepository, + private noteReactionsRepository: NoteReactionsRepository, + private utilityService: UtilityService, + private instanceActorService: InstanceActorService, + private metaService: MetaService, + private apRequestService: ApRequestService, + private httpRequestService: HttpRequestService, + private apRendererService: ApRendererService, + private apDbResolverService: ApDbResolverService, + private recursionLimit = 100 + ) { + this.history = new Set(); + } + + public getHistory(): string[] { + return Array.from(this.history); + } + + public async resolveCollection(value: string | IObject): Promise { + const collection = typeof value === 'string' + ? await this.resolve(value) + : value; + + if (isCollectionOrOrderedCollection(collection)) { + return collection; + } else { + throw new Error(`unrecognized collection type: ${collection.type}`); + } + } + + public async resolve(value: string | IObject): Promise { + if (value == null) { + throw new Error('resolvee is null (or undefined)'); + } + + if (typeof value !== 'string') { + return value; + } + + if (value.includes('#')) { + // URLs with fragment parts cannot be resolved correctly because + // the fragment part does not get transmitted over HTTP(S). + // Avoid strange behaviour by not trying to resolve these at all. + throw new Error(`cannot resolve URL with fragment: ${value}`); + } + + if (this.history.has(value)) { + throw new Error('cannot resolve already resolved one'); + } + + if (this.history.size > this.recursionLimit) { + throw new Error(`hit recursion limit: ${this.utilityService.extractDbHost(value)}`); + } + + this.history.add(value); + + const host = this.utilityService.extractDbHost(value); + if (this.utilityService.isSelfHost(host)) { + return await this.resolveLocal(value); + } + + const meta = await this.metaService.fetch(); + if (meta.blockedHosts.includes(host)) { + throw new Error('Instance is blocked'); + } + + if (this.config.signToActivityPubGet && !this.user) { + this.user = await this.instanceActorService.getInstanceActor(); + } + + const object = (this.user + ? 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']) ? + !(object['@context'] as unknown[]).includes('https://www.w3.org/ns/activitystreams') : + object['@context'] !== 'https://www.w3.org/ns/activitystreams' + )) { + throw new Error('invalid response'); + } + + return object; + } + + private resolveLocal(url: string): Promise { + const parsed = this.apDbResolverService.parseUri(url); + if (!parsed.local) throw new Error('resolveLocal: not local'); + + switch (parsed.type) { + case 'notes': + 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 this.apRendererService.renderActivity(this.apRendererService.renderCreate(this.apRendererService.renderNote(note), note)); + } else { + return this.apRendererService.renderNote(note); + } + }); + case 'users': + 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([ + this.notesRepository.findOneByOrFail({ id: parsed.id }), + this.pollsRepository.findOneByOrFail({ noteId: parsed.id }), + ]) + .then(([note, poll]) => this.apRendererService.renderQuestion({ id: note.userId }, note, poll)); + case 'likes': + 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 => this.usersRepository.findOneByOrFail({ id })), + ) + .then(([follower, followee]) => this.apRendererService.renderActivity(this.apRendererService.renderFollow(follower, followee, url))); + default: + throw new Error(`resolveLocal: type ${parsed.type} unhandled`); + } + } +} diff --git a/packages/backend/src/core/activitypub/LdSignatureService.ts b/packages/backend/src/core/activitypub/LdSignatureService.ts new file mode 100644 index 0000000000..ea39f15b2b --- /dev/null +++ b/packages/backend/src/core/activitypub/LdSignatureService.ts @@ -0,0 +1,147 @@ +import * as crypto from 'node:crypto'; +import { Inject, Injectable } from '@nestjs/common'; +import fetch from 'node-fetch'; +import { HttpRequestService } from '@/core/HttpRequestService.js'; +import { CONTEXTS } from './misc/contexts.js'; + +// RsaSignature2017 based from https://github.com/transmute-industries/RsaSignature2017 + +@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( + private httpRequestService: HttpRequestService, + ) { + } + + public async signRsaSignature2017(data: any, privateKey: string, creator: string, domain?: string, created?: Date): Promise { + const options = { + type: 'RsaSignature2017', + creator, + domain, + nonce: crypto.randomBytes(16).toString('hex'), + created: (created ?? new Date()).toISOString(), + } as { + type: string; + creator: string; + domain?: string; + nonce: string; + created: string; + }; + + if (!domain) { + delete options.domain; + } + + const toBeSigned = await this.createVerifyData(data, options); + + const signer = crypto.createSign('sha256'); + signer.update(toBeSigned); + signer.end(); + + const signature = signer.sign(privateKey); + + return { + ...data, + signature: { + ...options, + signatureValue: signature.toString('base64'), + }, + }; + } + + public async verifyRsaSignature2017(data: any, publicKey: string): Promise { + const toBeSigned = await this.createVerifyData(data, data.signature); + const verifier = crypto.createVerify('sha256'); + verifier.update(toBeSigned); + return verifier.verify(publicKey, data.signature.signatureValue, 'base64'); + } + + public async createVerifyData(data: any, options: any) { + const transformedOptions = { + ...options, + '@context': 'https://w3id.org/identity/v1', + }; + delete transformedOptions['type']; + delete transformedOptions['id']; + delete transformedOptions['signatureValue']; + const canonizedOptions = await this.normalize(transformedOptions); + const optionsHash = this.sha256(canonizedOptions.toString()); + const transformedData = { ...data }; + delete transformedData['signature']; + const cannonidedData = await this.normalize(transformedData); + if (this.debug) console.debug(`cannonidedData: ${cannonidedData}`); + const documentHash = this.sha256(cannonidedData.toString()); + const verifyData = `${optionsHash}${documentHash}`; + return verifyData; + } + + public async normalize(data: any) { + const customLoader = this.getLoader(); + return 42; + } + + private getLoader() { + return async (url: string): Promise => { + if (!url.match('^https?\:\/\/')) throw `Invalid URL ${url}`; + + if (this.preLoad) { + if (url in CONTEXTS) { + if (this.debug) console.debug(`HIT: ${url}`); + return { + contextUrl: null, + document: CONTEXTS[url], + documentUrl: url, + }; + } + } + + if (this.debug) console.debug(`MISS: ${url}`); + const document = await this.fetchDocument(url); + return { + contextUrl: null, + document: document, + documentUrl: url, + }; + }; + } + + private async fetchDocument(url: string) { + const json = await fetch(url, { + headers: { + Accept: 'application/ld+json, application/json', + }, + // TODO + //timeout: this.loderTimeout, + agent: u => u.protocol === 'http:' ? this.httpRequestService.httpAgent : this.httpRequestService.httpsAgent, + }).then(res => { + if (!res.ok) { + throw `${res.status} ${res.statusText}`; + } else { + return res.json(); + } + }); + + return json; + } + + public sha256(data: string): string { + const hash = crypto.createHash('sha256'); + hash.update(data); + return hash.digest('hex'); + } +} diff --git a/packages/backend/src/core/activitypub/misc/contexts.ts b/packages/backend/src/core/activitypub/misc/contexts.ts new file mode 100644 index 0000000000..aee0d3629c --- /dev/null +++ b/packages/backend/src/core/activitypub/misc/contexts.ts @@ -0,0 +1,526 @@ +/* eslint:disable:quotemark indent */ +const id_v1 = { + '@context': { + 'id': '@id', + 'type': '@type', + + 'cred': 'https://w3id.org/credentials#', + 'dc': 'http://purl.org/dc/terms/', + 'identity': 'https://w3id.org/identity#', + 'perm': 'https://w3id.org/permissions#', + 'ps': 'https://w3id.org/payswarm#', + 'rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', + 'rdfs': 'http://www.w3.org/2000/01/rdf-schema#', + 'sec': 'https://w3id.org/security#', + 'schema': 'http://schema.org/', + 'xsd': 'http://www.w3.org/2001/XMLSchema#', + + 'Group': 'https://www.w3.org/ns/activitystreams#Group', + + 'claim': { '@id': 'cred:claim', '@type': '@id' }, + 'credential': { '@id': 'cred:credential', '@type': '@id' }, + 'issued': { '@id': 'cred:issued', '@type': 'xsd:dateTime' }, + 'issuer': { '@id': 'cred:issuer', '@type': '@id' }, + 'recipient': { '@id': 'cred:recipient', '@type': '@id' }, + 'Credential': 'cred:Credential', + 'CryptographicKeyCredential': 'cred:CryptographicKeyCredential', + + 'about': { '@id': 'schema:about', '@type': '@id' }, + 'address': { '@id': 'schema:address', '@type': '@id' }, + 'addressCountry': 'schema:addressCountry', + 'addressLocality': 'schema:addressLocality', + 'addressRegion': 'schema:addressRegion', + 'comment': 'rdfs:comment', + 'created': { '@id': 'dc:created', '@type': 'xsd:dateTime' }, + 'creator': { '@id': 'dc:creator', '@type': '@id' }, + 'description': 'schema:description', + 'email': 'schema:email', + 'familyName': 'schema:familyName', + 'givenName': 'schema:givenName', + 'image': { '@id': 'schema:image', '@type': '@id' }, + 'label': 'rdfs:label', + 'name': 'schema:name', + 'postalCode': 'schema:postalCode', + 'streetAddress': 'schema:streetAddress', + 'title': 'dc:title', + 'url': { '@id': 'schema:url', '@type': '@id' }, + 'Person': 'schema:Person', + 'PostalAddress': 'schema:PostalAddress', + 'Organization': 'schema:Organization', + + 'identityService': { '@id': 'identity:identityService', '@type': '@id' }, + 'idp': { '@id': 'identity:idp', '@type': '@id' }, + 'Identity': 'identity:Identity', + + 'paymentProcessor': 'ps:processor', + 'preferences': { '@id': 'ps:preferences', '@type': '@vocab' }, + + 'cipherAlgorithm': 'sec:cipherAlgorithm', + 'cipherData': 'sec:cipherData', + 'cipherKey': 'sec:cipherKey', + 'digestAlgorithm': 'sec:digestAlgorithm', + 'digestValue': 'sec:digestValue', + 'domain': 'sec:domain', + 'expires': { '@id': 'sec:expiration', '@type': 'xsd:dateTime' }, + 'initializationVector': 'sec:initializationVector', + 'member': { '@id': 'schema:member', '@type': '@id' }, + 'memberOf': { '@id': 'schema:memberOf', '@type': '@id' }, + 'nonce': 'sec:nonce', + 'normalizationAlgorithm': 'sec:normalizationAlgorithm', + 'owner': { '@id': 'sec:owner', '@type': '@id' }, + 'password': 'sec:password', + 'privateKey': { '@id': 'sec:privateKey', '@type': '@id' }, + 'privateKeyPem': 'sec:privateKeyPem', + 'publicKey': { '@id': 'sec:publicKey', '@type': '@id' }, + 'publicKeyPem': 'sec:publicKeyPem', + 'publicKeyService': { '@id': 'sec:publicKeyService', '@type': '@id' }, + 'revoked': { '@id': 'sec:revoked', '@type': 'xsd:dateTime' }, + 'signature': 'sec:signature', + 'signatureAlgorithm': 'sec:signatureAlgorithm', + 'signatureValue': 'sec:signatureValue', + 'CryptographicKey': 'sec:Key', + 'EncryptedMessage': 'sec:EncryptedMessage', + 'GraphSignature2012': 'sec:GraphSignature2012', + 'LinkedDataSignature2015': 'sec:LinkedDataSignature2015', + + 'accessControl': { '@id': 'perm:accessControl', '@type': '@id' }, + 'writePermission': { '@id': 'perm:writePermission', '@type': '@id' }, + }, +}; + +const security_v1 = { + '@context': { + 'id': '@id', + 'type': '@type', + + 'dc': 'http://purl.org/dc/terms/', + 'sec': 'https://w3id.org/security#', + 'xsd': 'http://www.w3.org/2001/XMLSchema#', + + 'EcdsaKoblitzSignature2016': 'sec:EcdsaKoblitzSignature2016', + 'Ed25519Signature2018': 'sec:Ed25519Signature2018', + 'EncryptedMessage': 'sec:EncryptedMessage', + 'GraphSignature2012': 'sec:GraphSignature2012', + 'LinkedDataSignature2015': 'sec:LinkedDataSignature2015', + 'LinkedDataSignature2016': 'sec:LinkedDataSignature2016', + 'CryptographicKey': 'sec:Key', + + 'authenticationTag': 'sec:authenticationTag', + 'canonicalizationAlgorithm': 'sec:canonicalizationAlgorithm', + 'cipherAlgorithm': 'sec:cipherAlgorithm', + 'cipherData': 'sec:cipherData', + 'cipherKey': 'sec:cipherKey', + 'created': { '@id': 'dc:created', '@type': 'xsd:dateTime' }, + 'creator': { '@id': 'dc:creator', '@type': '@id' }, + 'digestAlgorithm': 'sec:digestAlgorithm', + 'digestValue': 'sec:digestValue', + 'domain': 'sec:domain', + 'encryptionKey': 'sec:encryptionKey', + 'expiration': { '@id': 'sec:expiration', '@type': 'xsd:dateTime' }, + 'expires': { '@id': 'sec:expiration', '@type': 'xsd:dateTime' }, + 'initializationVector': 'sec:initializationVector', + 'iterationCount': 'sec:iterationCount', + 'nonce': 'sec:nonce', + 'normalizationAlgorithm': 'sec:normalizationAlgorithm', + 'owner': { '@id': 'sec:owner', '@type': '@id' }, + 'password': 'sec:password', + 'privateKey': { '@id': 'sec:privateKey', '@type': '@id' }, + 'privateKeyPem': 'sec:privateKeyPem', + 'publicKey': { '@id': 'sec:publicKey', '@type': '@id' }, + 'publicKeyBase58': 'sec:publicKeyBase58', + 'publicKeyPem': 'sec:publicKeyPem', + 'publicKeyWif': 'sec:publicKeyWif', + 'publicKeyService': { '@id': 'sec:publicKeyService', '@type': '@id' }, + 'revoked': { '@id': 'sec:revoked', '@type': 'xsd:dateTime' }, + 'salt': 'sec:salt', + 'signature': 'sec:signature', + 'signatureAlgorithm': 'sec:signingAlgorithm', + 'signatureValue': 'sec:signatureValue', + }, +}; + +const activitystreams = { + '@context': { + '@vocab': '_:', + 'xsd': 'http://www.w3.org/2001/XMLSchema#', + 'as': 'https://www.w3.org/ns/activitystreams#', + 'ldp': 'http://www.w3.org/ns/ldp#', + 'vcard': 'http://www.w3.org/2006/vcard/ns#', + 'id': '@id', + 'type': '@type', + 'Accept': 'as:Accept', + 'Activity': 'as:Activity', + 'IntransitiveActivity': 'as:IntransitiveActivity', + 'Add': 'as:Add', + 'Announce': 'as:Announce', + 'Application': 'as:Application', + 'Arrive': 'as:Arrive', + 'Article': 'as:Article', + 'Audio': 'as:Audio', + 'Block': 'as:Block', + 'Collection': 'as:Collection', + 'CollectionPage': 'as:CollectionPage', + 'Relationship': 'as:Relationship', + 'Create': 'as:Create', + 'Delete': 'as:Delete', + 'Dislike': 'as:Dislike', + 'Document': 'as:Document', + 'Event': 'as:Event', + 'Follow': 'as:Follow', + 'Flag': 'as:Flag', + 'Group': 'as:Group', + 'Ignore': 'as:Ignore', + 'Image': 'as:Image', + 'Invite': 'as:Invite', + 'Join': 'as:Join', + 'Leave': 'as:Leave', + 'Like': 'as:Like', + 'Link': 'as:Link', + 'Mention': 'as:Mention', + 'Note': 'as:Note', + 'Object': 'as:Object', + 'Offer': 'as:Offer', + 'OrderedCollection': 'as:OrderedCollection', + 'OrderedCollectionPage': 'as:OrderedCollectionPage', + 'Organization': 'as:Organization', + 'Page': 'as:Page', + 'Person': 'as:Person', + 'Place': 'as:Place', + 'Profile': 'as:Profile', + 'Question': 'as:Question', + 'Reject': 'as:Reject', + 'Remove': 'as:Remove', + 'Service': 'as:Service', + 'TentativeAccept': 'as:TentativeAccept', + 'TentativeReject': 'as:TentativeReject', + 'Tombstone': 'as:Tombstone', + 'Undo': 'as:Undo', + 'Update': 'as:Update', + 'Video': 'as:Video', + 'View': 'as:View', + 'Listen': 'as:Listen', + 'Read': 'as:Read', + 'Move': 'as:Move', + 'Travel': 'as:Travel', + 'IsFollowing': 'as:IsFollowing', + 'IsFollowedBy': 'as:IsFollowedBy', + 'IsContact': 'as:IsContact', + 'IsMember': 'as:IsMember', + 'subject': { + '@id': 'as:subject', + '@type': '@id', + }, + 'relationship': { + '@id': 'as:relationship', + '@type': '@id', + }, + 'actor': { + '@id': 'as:actor', + '@type': '@id', + }, + 'attributedTo': { + '@id': 'as:attributedTo', + '@type': '@id', + }, + 'attachment': { + '@id': 'as:attachment', + '@type': '@id', + }, + 'bcc': { + '@id': 'as:bcc', + '@type': '@id', + }, + 'bto': { + '@id': 'as:bto', + '@type': '@id', + }, + 'cc': { + '@id': 'as:cc', + '@type': '@id', + }, + 'context': { + '@id': 'as:context', + '@type': '@id', + }, + 'current': { + '@id': 'as:current', + '@type': '@id', + }, + 'first': { + '@id': 'as:first', + '@type': '@id', + }, + 'generator': { + '@id': 'as:generator', + '@type': '@id', + }, + 'icon': { + '@id': 'as:icon', + '@type': '@id', + }, + 'image': { + '@id': 'as:image', + '@type': '@id', + }, + 'inReplyTo': { + '@id': 'as:inReplyTo', + '@type': '@id', + }, + 'items': { + '@id': 'as:items', + '@type': '@id', + }, + 'instrument': { + '@id': 'as:instrument', + '@type': '@id', + }, + 'orderedItems': { + '@id': 'as:items', + '@type': '@id', + '@container': '@list', + }, + 'last': { + '@id': 'as:last', + '@type': '@id', + }, + 'location': { + '@id': 'as:location', + '@type': '@id', + }, + 'next': { + '@id': 'as:next', + '@type': '@id', + }, + 'object': { + '@id': 'as:object', + '@type': '@id', + }, + 'oneOf': { + '@id': 'as:oneOf', + '@type': '@id', + }, + 'anyOf': { + '@id': 'as:anyOf', + '@type': '@id', + }, + 'closed': { + '@id': 'as:closed', + '@type': 'xsd:dateTime', + }, + 'origin': { + '@id': 'as:origin', + '@type': '@id', + }, + 'accuracy': { + '@id': 'as:accuracy', + '@type': 'xsd:float', + }, + 'prev': { + '@id': 'as:prev', + '@type': '@id', + }, + 'preview': { + '@id': 'as:preview', + '@type': '@id', + }, + 'replies': { + '@id': 'as:replies', + '@type': '@id', + }, + 'result': { + '@id': 'as:result', + '@type': '@id', + }, + 'audience': { + '@id': 'as:audience', + '@type': '@id', + }, + 'partOf': { + '@id': 'as:partOf', + '@type': '@id', + }, + 'tag': { + '@id': 'as:tag', + '@type': '@id', + }, + 'target': { + '@id': 'as:target', + '@type': '@id', + }, + 'to': { + '@id': 'as:to', + '@type': '@id', + }, + 'url': { + '@id': 'as:url', + '@type': '@id', + }, + 'altitude': { + '@id': 'as:altitude', + '@type': 'xsd:float', + }, + 'content': 'as:content', + 'contentMap': { + '@id': 'as:content', + '@container': '@language', + }, + 'name': 'as:name', + 'nameMap': { + '@id': 'as:name', + '@container': '@language', + }, + 'duration': { + '@id': 'as:duration', + '@type': 'xsd:duration', + }, + 'endTime': { + '@id': 'as:endTime', + '@type': 'xsd:dateTime', + }, + 'height': { + '@id': 'as:height', + '@type': 'xsd:nonNegativeInteger', + }, + 'href': { + '@id': 'as:href', + '@type': '@id', + }, + 'hreflang': 'as:hreflang', + 'latitude': { + '@id': 'as:latitude', + '@type': 'xsd:float', + }, + 'longitude': { + '@id': 'as:longitude', + '@type': 'xsd:float', + }, + 'mediaType': 'as:mediaType', + 'published': { + '@id': 'as:published', + '@type': 'xsd:dateTime', + }, + 'radius': { + '@id': 'as:radius', + '@type': 'xsd:float', + }, + 'rel': 'as:rel', + 'startIndex': { + '@id': 'as:startIndex', + '@type': 'xsd:nonNegativeInteger', + }, + 'startTime': { + '@id': 'as:startTime', + '@type': 'xsd:dateTime', + }, + 'summary': 'as:summary', + 'summaryMap': { + '@id': 'as:summary', + '@container': '@language', + }, + 'totalItems': { + '@id': 'as:totalItems', + '@type': 'xsd:nonNegativeInteger', + }, + 'units': 'as:units', + 'updated': { + '@id': 'as:updated', + '@type': 'xsd:dateTime', + }, + 'width': { + '@id': 'as:width', + '@type': 'xsd:nonNegativeInteger', + }, + 'describes': { + '@id': 'as:describes', + '@type': '@id', + }, + 'formerType': { + '@id': 'as:formerType', + '@type': '@id', + }, + 'deleted': { + '@id': 'as:deleted', + '@type': 'xsd:dateTime', + }, + 'inbox': { + '@id': 'ldp:inbox', + '@type': '@id', + }, + 'outbox': { + '@id': 'as:outbox', + '@type': '@id', + }, + 'following': { + '@id': 'as:following', + '@type': '@id', + }, + 'followers': { + '@id': 'as:followers', + '@type': '@id', + }, + 'streams': { + '@id': 'as:streams', + '@type': '@id', + }, + 'preferredUsername': 'as:preferredUsername', + 'endpoints': { + '@id': 'as:endpoints', + '@type': '@id', + }, + 'uploadMedia': { + '@id': 'as:uploadMedia', + '@type': '@id', + }, + 'proxyUrl': { + '@id': 'as:proxyUrl', + '@type': '@id', + }, + 'liked': { + '@id': 'as:liked', + '@type': '@id', + }, + 'oauthAuthorizationEndpoint': { + '@id': 'as:oauthAuthorizationEndpoint', + '@type': '@id', + }, + 'oauthTokenEndpoint': { + '@id': 'as:oauthTokenEndpoint', + '@type': '@id', + }, + 'provideClientKey': { + '@id': 'as:provideClientKey', + '@type': '@id', + }, + 'signClientKey': { + '@id': 'as:signClientKey', + '@type': '@id', + }, + 'sharedInbox': { + '@id': 'as:sharedInbox', + '@type': '@id', + }, + 'Public': { + '@id': 'as:Public', + '@type': '@id', + }, + 'source': 'as:source', + 'likes': { + '@id': 'as:likes', + '@type': '@id', + }, + 'shares': { + '@id': 'as:shares', + '@type': '@id', + }, + 'alsoKnownAs': { + '@id': 'as:alsoKnownAs', + '@type': '@id', + }, + }, +}; + +export const CONTEXTS: Record = { + 'https://w3id.org/identity/v1': id_v1, + 'https://w3id.org/security/v1': security_v1, + 'https://www.w3.org/ns/activitystreams': activitystreams, +}; diff --git a/packages/backend/src/core/activitypub/models/ApImageService.ts b/packages/backend/src/core/activitypub/models/ApImageService.ts new file mode 100644 index 0000000000..9bf87f19d4 --- /dev/null +++ b/packages/backend/src/core/activitypub/models/ApImageService.ts @@ -0,0 +1,90 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI } from '@/di-symbols.js'; +import type { DriveFilesRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; +import type { CacheableRemoteUser } from '@/models/entities/User.js'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; +import { MetaService } from '@/core/MetaService.js'; +import { truncate } from '@/misc/truncate.js'; +import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js'; +import { DriveService } from '@/core/DriveService.js'; +import type Logger from '@/logger.js'; +import { ApResolverService } from '../ApResolverService.js'; +import { ApLoggerService } from '../ApLoggerService.js'; + +@Injectable() +export class ApImageService { + private logger: Logger; + + constructor( + @Inject(DI.config) + private config: Config, + + @Inject(DI.driveFilesRepository) + private driveFilesRepository: DriveFilesRepository, + + 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/core/activitypub/models/ApMentionService.ts b/packages/backend/src/core/activitypub/models/ApMentionService.ts new file mode 100644 index 0000000000..1275e24c62 --- /dev/null +++ b/packages/backend/src/core/activitypub/models/ApMentionService.ts @@ -0,0 +1,39 @@ +import { Inject, Injectable } from '@nestjs/common'; +import promiseLimit from 'promise-limit'; +import { DI } from '@/di-symbols.js'; +import type { UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; +import { toArray, unique } from '@/misc/prelude/array.js'; +import type { CacheableUser } from '@/models/entities/User.js'; +import { isMention } from '../type.js'; +import { ApResolverService, Resolver } from '../ApResolverService.js'; +import { ApPersonService } from './ApPersonService.js'; +import type { IObject, IApMention } from '../type.js'; + +@Injectable() +export class ApMentionService { + constructor( + @Inject(DI.config) + private config: Config, + + private apResolverService: ApResolverService, + private apPersonService: ApPersonService, + ) { + } + + public async extractApMentions(tags: IObject | IObject[] | null | undefined, resolver: Resolver) { + const hrefs = unique(this.extractApMentionObjects(tags).map(x => x.href as string)); + + const limit = promiseLimit(2); + const mentionedUsers = (await Promise.all( + hrefs.map(x => limit(() => this.apPersonService.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/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts new file mode 100644 index 0000000000..7cf6725a38 --- /dev/null +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -0,0 +1,403 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import promiseLimit from 'promise-limit'; +import { DI } from '@/di-symbols.js'; +import type { MessagingMessagesRepository, PollsRepository, EmojisRepository } from '@/models/index.js'; +import type { UsersRepository } from '@/models/index.js'; +import type { Config } from '@/config.js'; +import type { CacheableRemoteUser } from '@/models/entities/User.js'; +import type { Note } from '@/models/entities/Note.js'; +import { toArray, toSingle, unique } from '@/misc/prelude/array.js'; +import type { Emoji } from '@/models/entities/Emoji.js'; +import { MetaService } from '@/core/MetaService.js'; +import { AppLockService } from '@/core/AppLockService.js'; +import type { DriveFile } from '@/models/entities/DriveFile.js'; +import { NoteCreateService } from '@/core/NoteCreateService.js'; +import type Logger from '@/logger.js'; +import { IdService } from '@/core/IdService.js'; +import { PollService } from '@/core/PollService.js'; +import { StatusError } from '@/misc/status-error.js'; +import { UtilityService } from '@/core/UtilityService.js'; +import { MessagingService } from '@/core/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'; +import { ApQuestionService } from './ApQuestionService.js'; +import { ApImageService } from './ApImageService.js'; +import type { Resolver } from '../ApResolverService.js'; +import type { IObject, IPost } from '../type.js'; + +@Injectable() +export class ApNoteService { + private logger: Logger; + + constructor( + @Inject(DI.config) + private config: Config, + + @Inject(DI.pollsRepository) + private pollsRepository: PollsRepository, + + @Inject(DI.emojisRepository) + private emojisRepository: EmojisRepository, + + @Inject(DI.messagingMessagesRepository) + private messagingMessagesRepository: MessagingMessagesRepository, + + private idService: IdService, + private apMfmService: ApMfmService, + private apResolverService: ApResolverService, + + // 循環参照のため / for circular dependency + @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, + private apDbResolverService: ApDbResolverService, + private apLoggerService: ApLoggerService, + ) { + this.logger = this.apLoggerService.logger; + } + + public validateNote(object: any, uri: string) { + const expectHost = this.utilityService.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 && 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 && 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; + } + + /** + * Noteをフェッチします。 + * + * Misskeyに対象のNoteが登録されていればそれを返します。 + */ + public async fetchNote(object: string | IObject): Promise { + return await this.apDbResolverService.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) { + this.logger.error(`${err.message}`, { + resolver: { + history: resolver.getHistory(), + }, + value: value, + object: object, + }); + throw new Error('invalid note'); + } + + const note: IPost = object; + + this.logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`); + + this.logger.info(`Creating the Note: ${note.id}`); + + // 投稿者をフェッチ + const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as CacheableRemoteUser; + + // 投稿者が凍結されていたらスキップ + if (actor.isSuspended) { + throw new Error('actor has been suspended'); + } + + const noteAudience = await this.apAudienceService.parseAudience(actor, note.to, note.cc, resolver); + 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 isMessaging = note._misskey_talk && visibility === 'specified'; + + const apMentions = await this.apMentionService.extractApMentions(note.tag, resolver); + 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(() => this.apImageService.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) { + this.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 this.messagingMessagesRepository.findOneBy({ id }); + if (talk) { + isMessaging = true; + return null; + } + } + + this.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 this.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.apMfmService.htmlToMfm(note.content, note.tag); + } + + // vote + if (reply && reply.hasPoll) { + 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()) { + 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 this.pollService.vote(actor, reply, index); + + // リモートフォロワーにUpdate配信 + this.pollService.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 => { + this.logger.info(`extractEmojis: ${e}`); + return [] as Emoji[]; + }); + + const apEmojis = emojis.map(emoji => emoji.name); + + const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined); + + 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); + 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(this.utilityService.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 = this.utilityService.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 this.emojisRepository.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 this.emojisRepository.update({ + host, + name, + }, { + uri: tag.id, + originalUrl: tag.icon!.url, + publicUrl: tag.icon!.url, + updatedAt: new Date(), + }); + + return await this.emojisRepository.findOneBy({ + host, + name, + }) as Emoji; + } + + return exists; + } + + this.logger.info(`register emoji host=${host}, name=${name}`); + + return await this.emojisRepository.insert({ + id: this.idService.genId(), + host, + name, + uri: tag.id, + originalUrl: tag.icon!.url, + publicUrl: tag.icon!.url, + updatedAt: new Date(), + aliases: [], + } as Partial).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0])); + })); + } +} diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts new file mode 100644 index 0000000000..f9d6f42ef6 --- /dev/null +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -0,0 +1,594 @@ +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 { FollowingsRepository, InstancesRepository, UserProfilesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js'; +import type { 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 type { UserCacheService } from '@/core/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 '@/core/IdService.js'; +import type { MfmService } from '@/core/MfmService.js'; +import type { Emoji } from '@/models/entities/Emoji.js'; +import { toArray } from '@/misc/prelude/array.js'; +import type { GlobalEventService } from '@/core/GlobalEventService.js'; +import type { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; +import type { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataService.js'; +import { UserProfile } from '@/models/entities/UserProfile.js'; +import { UserPublickey } from '@/models/entities/UserPublickey.js'; +import type UsersChart from '@/core/chart/charts/users.js'; +import type InstanceChart from '@/core/chart/charts/instance.js'; +import type { HashtagService } from '@/core/HashtagService.js'; +import { UserNotePining } from '@/models/entities/UserNotePining.js'; +import { StatusError } from '@/misc/status-error.js'; +import type { UtilityService } from '@/core/UtilityService.js'; +import type { UserEntityService } from '@/core/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'; +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'; + +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); + } +} + +@Injectable() +export class ApPersonService implements OnModuleInit { + 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; + private logger: Logger; + + constructor( + private moduleRef: ModuleRef, + + @Inject(DI.config) + private config: Config, + + @Inject(DI.db) + private db: DataSource, + + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.userProfilesRepository) + private userProfilesRepository: UserProfilesRepository, + + @Inject(DI.userPublickeysRepository) + private userPublickeysRepository: UserPublickeysRepository, + + @Inject(DI.instancesRepository) + private instancesRepository: InstancesRepository, + + @Inject(DI.followingsRepository) + private followingsRepository: FollowingsRepository, + + //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, + ) { + } + + onModuleInit() { + 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; + } + + /** + * Validate and convert to actor object + * @param x Fetched object + * @param uri Fetch target URI + */ + private 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をフェッチします。 + * + * 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 this.usersRepository.findOneBy({ id }); + if (u) this.userCacheService.uriPersonCache.set(uri, u); + return u; + } + + //#region このサーバーに既に登録されていたらそれを返す + const exist = await this.usersRepository.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 = this.validateActor(object, uri); + + this.logger.info(`Creating the Person: ${person.id}`); + + const host = this.utilityService.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 this.usersRepository.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 => { + this.instancesRepository.increment({ id: i.id }, 'usersCount', 1); + this.instanceChart.newUser(i.host); + this.fetchInstanceMetadataService.fetchInstanceMetadata(i); + }); + + this.usersChart.update(user!, true); + + // ハッシュタグ更新 + this.hashtagService.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 this.usersRepository.update(user!.id, { + avatarId, + bannerId, + }); + + user!.avatarId = avatarId; + user!.bannerId = bannerId; + //#endregion + + //#region カスタム絵文字取得 + const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host).catch(err => { + this.logger.info(`extractEmojis: ${err}`); + return [] as Emoji[]; + }); + + const emojiNames = emojis.map(emoji => emoji.name); + + await this.usersRepository.update(user!.id, { + emojis: emojiNames, + }); + //#endregion + + await this.updateFeatured(user!.id, resolver).catch(err => this.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 this.usersRepository.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 = this.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 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 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 this.usersRepository.update(exist.id, updates); + + if (person.publicKey) { + await this.userPublickeysRepository.update({ userId: exist.id }, { + keyId: person.publicKey.id, + keyPem: person.publicKey.publicKeyPem, + }); + } + + 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, + birthday: bday ? bday[0] : null, + location: person['vcard:Address'] ?? null, + }); + + this.globalEventService.publishInternalEvent('remoteUserUpdated', { id: exist.id }); + + // ハッシュタグ更新 + this.hashtagService.updateUsertags(exist, tags); + + // 該当ユーザーが既にフォロワーになっていた場合はFollowingもアップデートする + await this.followingsRepository.update({ + followerId: exist.id, + }, { + followerSharedInbox: person.sharedInbox ?? (person.endpoints ? person.endpoints.sharedInbox : undefined), + }); + + await this.updateFeatured(exist.id, resolver).catch(err => this.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'], resolver?: Resolver) { + const user = await this.usersRepository.findOneByOrFail({ id: userId }); + if (!this.userEntityService.isRemoteUser(user)) return; + if (!user.featured) return; + + this.logger.info(`Updating the featured: ${user.uri}`); + + if (resolver == null) 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(() => this.apNoteService.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/core/activitypub/models/ApQuestionService.ts b/packages/backend/src/core/activitypub/models/ApQuestionService.ts new file mode 100644 index 0000000000..5793b98353 --- /dev/null +++ b/packages/backend/src/core/activitypub/models/ApQuestionService.ts @@ -0,0 +1,109 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI } from '@/di-symbols.js'; +import type { NotesRepository, PollsRepository } from '@/models/index.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'; +import { ApLoggerService } from '../ApLoggerService.js'; +import { ApResolverService } from '../ApResolverService.js'; +import type { Resolver } from '../ApResolverService.js'; +import type { IObject, IQuestion } from '../type.js'; + +@Injectable() +export class ApQuestionService { + private logger: Logger; + + constructor( + @Inject(DI.config) + private config: Config, + + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + + @Inject(DI.pollsRepository) + private pollsRepository: PollsRepository, + + 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, resolver?: Resolver) { + 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 + if (resolver == null) 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/core/activitypub/models/icon.ts b/packages/backend/src/core/activitypub/models/icon.ts new file mode 100644 index 0000000000..50794a937d --- /dev/null +++ b/packages/backend/src/core/activitypub/models/icon.ts @@ -0,0 +1,5 @@ +export type IIcon = { + type: string; + mediaType?: string; + url?: string; +}; diff --git a/packages/backend/src/core/activitypub/models/identifier.ts b/packages/backend/src/core/activitypub/models/identifier.ts new file mode 100644 index 0000000000..f6c3bb8c88 --- /dev/null +++ b/packages/backend/src/core/activitypub/models/identifier.ts @@ -0,0 +1,5 @@ +export type IIdentifier = { + type: string; + name: string; + value: string; +}; diff --git a/packages/backend/src/core/activitypub/models/tag.ts b/packages/backend/src/core/activitypub/models/tag.ts new file mode 100644 index 0000000000..803846a0b0 --- /dev/null +++ b/packages/backend/src/core/activitypub/models/tag.ts @@ -0,0 +1,19 @@ +import { toArray } from '@/misc/prelude/array.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 []; + + const hashtags = extractApHashtagObjects(tags); + + return hashtags.map(tag => { + const m = tag.name.match(/^#(.+)/); + return m ? m[1] : null; + }).filter((x): x is string => x != null); +} + +export function extractApHashtagObjects(tags: IObject | IObject[] | null | undefined): IApHashtag[] { + if (tags == null) return []; + return toArray(tags).filter(isHashtag); +} diff --git a/packages/backend/src/core/activitypub/type.ts b/packages/backend/src/core/activitypub/type.ts new file mode 100644 index 0000000000..dcc5110aa5 --- /dev/null +++ b/packages/backend/src/core/activitypub/type.ts @@ -0,0 +1,296 @@ +export type obj = { [x: string]: any }; +export type ApObject = IObject | string | (IObject | string)[]; + +export interface IObject { + '@context': string | string[] | obj | obj[]; + type: string | string[]; + id?: string; + summary?: string; + published?: string; + cc?: ApObject; + to?: ApObject; + attributedTo: ApObject; + attachment?: any[]; + inReplyTo?: any; + replies?: ICollection; + content?: string; + name?: string; + startTime?: Date; + endTime?: Date; + icon?: any; + image?: any; + url?: ApObject; + href?: string; + tag?: IObject | IObject[]; + sensitive?: boolean; +} + +/** + * Get array of ActivityStreams Objects id + */ +export function getApIds(value: ApObject | undefined): string[] { + if (value == null) return []; + const array = Array.isArray(value) ? value : [value]; + return array.map(x => getApId(x)); +} + +/** + * Get first ActivityStreams Object id + */ +export function getOneApId(value: ApObject): string { + const firstOne = Array.isArray(value) ? value[0] : value; + return getApId(firstOne); +} + +/** + * Get ActivityStreams Object id + */ +export function getApId(value: string | IObject): string { + if (typeof value === 'string') return value; + if (typeof value.id === 'string') return value.id; + throw new Error('cannot detemine id'); +} + +/** + * Get ActivityStreams Object type + */ +export function getApType(value: IObject): string { + if (typeof value.type === 'string') return value.type; + if (Array.isArray(value.type) && typeof value.type[0] === 'string') return value.type[0]; + throw new Error('cannot detect type'); +} + +export function getOneApHrefNullable(value: ApObject | undefined): string | undefined { + const firstOne = Array.isArray(value) ? value[0] : value; + return getApHrefNullable(firstOne); +} + +export function getApHrefNullable(value: string | IObject | undefined): string | undefined { + if (typeof value === 'string') return value; + if (typeof value?.href === 'string') return value.href; + return undefined; +} + +export interface IActivity extends IObject { + //type: 'Activity'; + actor: IObject | string; + object: IObject | string; + target?: IObject | string; + /** LD-Signature */ + signature?: { + type: string; + created: Date; + creator: string; + domain?: string; + nonce?: string; + signatureValue: string; + }; +} + +export interface ICollection extends IObject { + type: 'Collection'; + totalItems: number; + items: ApObject; +} + +export interface IOrderedCollection extends IObject { + type: 'OrderedCollection'; + totalItems: number; + orderedItems: ApObject; +} + +export const validPost = ['Note', 'Question', 'Article', 'Audio', 'Document', 'Image', 'Page', 'Video', 'Event']; + +export const isPost = (object: IObject): object is IPost => + validPost.includes(getApType(object)); + +export interface IPost extends IObject { + type: 'Note' | 'Question' | 'Article' | 'Audio' | 'Document' | 'Image' | 'Page' | 'Video' | 'Event'; + source?: { + content: string; + mediaType: string; + }; + _misskey_quote?: string; + _misskey_content?: string; + quoteUrl?: string; + _misskey_talk?: boolean; +} + +export interface IQuestion extends IObject { + type: 'Note' | 'Question'; + source?: { + content: string; + mediaType: string; + }; + _misskey_quote?: string; + quoteUrl?: string; + oneOf?: IQuestionChoice[]; + anyOf?: IQuestionChoice[]; + endTime?: Date; + closed?: Date; +} + +export const isQuestion = (object: IObject): object is IQuestion => + getApType(object) === 'Note' || getApType(object) === 'Question'; + +interface IQuestionChoice { + name?: string; + replies?: ICollection; + _misskey_votes?: number; +} +export interface ITombstone extends IObject { + type: 'Tombstone'; + formerType?: string; + deleted?: Date; +} + +export const isTombstone = (object: IObject): object is ITombstone => + getApType(object) === 'Tombstone'; + +export const validActor = ['Person', 'Service', 'Group', 'Organization', 'Application']; + +export const isActor = (object: IObject): object is IActor => + validActor.includes(getApType(object)); + +export interface IActor extends IObject { + type: 'Person' | 'Service' | 'Organization' | 'Group' | 'Application'; + name?: string; + preferredUsername?: string; + manuallyApprovesFollowers?: boolean; + discoverable?: boolean; + inbox: string; + sharedInbox?: string; // 後方互換性のため + publicKey?: { + id: string; + publicKeyPem: string; + }; + followers?: string | ICollection | IOrderedCollection; + following?: string | ICollection | IOrderedCollection; + featured?: string | IOrderedCollection; + outbox: string | IOrderedCollection; + endpoints?: { + sharedInbox?: string; + }; + 'vcard:bday'?: string; + 'vcard:Address'?: string; +} + +export const isCollection = (object: IObject): object is ICollection => + getApType(object) === 'Collection'; + +export const isOrderedCollection = (object: IObject): object is IOrderedCollection => + getApType(object) === 'OrderedCollection'; + +export const isCollectionOrOrderedCollection = (object: IObject): object is ICollection | IOrderedCollection => + isCollection(object) || isOrderedCollection(object); + +export interface IApPropertyValue extends IObject { + type: 'PropertyValue'; + identifier: IApPropertyValue; + name: string; + value: string; +} + +export const isPropertyValue = (object: IObject): object is IApPropertyValue => + object && + getApType(object) === 'PropertyValue' && + typeof object.name === 'string' && + typeof (object as any).value === 'string'; + +export interface IApMention extends IObject { + type: 'Mention'; + href: string; +} + +export const isMention = (object: IObject): object is IApMention => + getApType(object) === 'Mention' && + typeof object.href === 'string'; + +export interface IApHashtag extends IObject { + type: 'Hashtag'; + name: string; +} + +export const isHashtag = (object: IObject): object is IApHashtag => + getApType(object) === 'Hashtag' && + typeof object.name === 'string'; + +export interface IApEmoji extends IObject { + type: 'Emoji'; + updated: Date; +} + +export const isEmoji = (object: IObject): object is IApEmoji => + getApType(object) === 'Emoji' && !Array.isArray(object.icon) && object.icon.url != null; + +export interface ICreate extends IActivity { + type: 'Create'; +} + +export interface IDelete extends IActivity { + type: 'Delete'; +} + +export interface IUpdate extends IActivity { + type: 'Update'; +} + +export interface IRead extends IActivity { + type: 'Read'; +} + +export interface IUndo extends IActivity { + type: 'Undo'; +} + +export interface IFollow extends IActivity { + type: 'Follow'; +} + +export interface IAccept extends IActivity { + type: 'Accept'; +} + +export interface IReject extends IActivity { + type: 'Reject'; +} + +export interface IAdd extends IActivity { + type: 'Add'; +} + +export interface IRemove extends IActivity { + type: 'Remove'; +} + +export interface ILike extends IActivity { + type: 'Like' | 'EmojiReaction' | 'EmojiReact'; + _misskey_reaction?: string; +} + +export interface IAnnounce extends IActivity { + type: 'Announce'; +} + +export interface IBlock extends IActivity { + type: 'Block'; +} + +export interface IFlag extends IActivity { + type: 'Flag'; +} + +export const isCreate = (object: IObject): object is ICreate => getApType(object) === 'Create'; +export const isDelete = (object: IObject): object is IDelete => getApType(object) === 'Delete'; +export const isUpdate = (object: IObject): object is IUpdate => getApType(object) === 'Update'; +export const isRead = (object: IObject): object is IRead => getApType(object) === 'Read'; +export const isUndo = (object: IObject): object is IUndo => getApType(object) === 'Undo'; +export const isFollow = (object: IObject): object is IFollow => getApType(object) === 'Follow'; +export const isAccept = (object: IObject): object is IAccept => getApType(object) === 'Accept'; +export const isReject = (object: IObject): object is IReject => getApType(object) === 'Reject'; +export const isAdd = (object: IObject): object is IAdd => getApType(object) === 'Add'; +export const isRemove = (object: IObject): object is IRemove => getApType(object) === 'Remove'; +export const isLike = (object: IObject): object is ILike => getApType(object) === 'Like' || getApType(object) === 'EmojiReaction' || getApType(object) === 'EmojiReact'; +export const isAnnounce = (object: IObject): object is IAnnounce => getApType(object) === 'Announce'; +export const isBlock = (object: IObject): object is IBlock => getApType(object) === 'Block'; +export const isFlag = (object: IObject): object is IFlag => getApType(object) === 'Flag'; diff --git a/packages/backend/src/core/chart/charts/active-users.ts b/packages/backend/src/core/chart/charts/active-users.ts index 40c60910ea..6683d76587 100644 --- a/packages/backend/src/core/chart/charts/active-users.ts +++ b/packages/backend/src/core/chart/charts/active-users.ts @@ -5,7 +5,7 @@ import type { User } from '@/models/entities/User.js'; import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; -import { name, schema } from './entities/active-users.js'; +import { name, schema } from '@/core/entities/active-users.js'; import type { KVs } from '../core.js'; const week = 1000 * 60 * 60 * 24 * 7; diff --git a/packages/backend/src/core/chart/charts/ap-request.ts b/packages/backend/src/core/chart/charts/ap-request.ts index 4b91fbbf18..1de21a6a16 100644 --- a/packages/backend/src/core/chart/charts/ap-request.ts +++ b/packages/backend/src/core/chart/charts/ap-request.ts @@ -4,7 +4,7 @@ import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; -import { name, schema } from './entities/ap-request.js'; +import { name, schema } from '@/core/entities/ap-request.js'; import type { KVs } from '../core.js'; /** diff --git a/packages/backend/src/core/chart/charts/drive.ts b/packages/backend/src/core/chart/charts/drive.ts index 494dfbbe57..638e31ac8d 100644 --- a/packages/backend/src/core/chart/charts/drive.ts +++ b/packages/backend/src/core/chart/charts/drive.ts @@ -5,7 +5,7 @@ import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; -import { name, schema } from './entities/drive.js'; +import { name, schema } from '@/core/entities/drive.js'; import type { KVs } from '../core.js'; /** diff --git a/packages/backend/src/core/chart/charts/federation.ts b/packages/backend/src/core/chart/charts/federation.ts index 21e4cedea3..75a565cebc 100644 --- a/packages/backend/src/core/chart/charts/federation.ts +++ b/packages/backend/src/core/chart/charts/federation.ts @@ -6,7 +6,7 @@ import { DI } from '@/di-symbols.js'; import { MetaService } from '@/core/MetaService.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; -import { name, schema } from './entities/federation.js'; +import { name, schema } from '@/core/entities/federation.js'; import type { KVs } from '../core.js'; /** diff --git a/packages/backend/src/core/chart/charts/hashtag.ts b/packages/backend/src/core/chart/charts/hashtag.ts index 8b8c795cfd..ff83b8aa5d 100644 --- a/packages/backend/src/core/chart/charts/hashtag.ts +++ b/packages/backend/src/core/chart/charts/hashtag.ts @@ -6,7 +6,7 @@ import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; -import { name, schema } from './entities/hashtag.js'; +import { name, schema } from '@/core/entities/hashtag.js'; import type { KVs } from '../core.js'; /** diff --git a/packages/backend/src/core/chart/charts/instance.ts b/packages/backend/src/core/chart/charts/instance.ts index 2e0f4c7126..41a35a2123 100644 --- a/packages/backend/src/core/chart/charts/instance.ts +++ b/packages/backend/src/core/chart/charts/instance.ts @@ -8,7 +8,7 @@ import { DI } from '@/di-symbols.js'; import { UtilityService } from '@/core/UtilityService.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; -import { name, schema } from './entities/instance.js'; +import { name, schema } from '@/core/entities/instance.js'; import type { KVs } from '../core.js'; /** diff --git a/packages/backend/src/core/chart/charts/notes.ts b/packages/backend/src/core/chart/charts/notes.ts index 2153cfe4b4..083b0d5519 100644 --- a/packages/backend/src/core/chart/charts/notes.ts +++ b/packages/backend/src/core/chart/charts/notes.ts @@ -6,7 +6,7 @@ import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; -import { name, schema } from './entities/notes.js'; +import { name, schema } from '@/core/entities/notes.js'; import type { KVs } from '../core.js'; /** diff --git a/packages/backend/src/core/chart/charts/per-user-drive.ts b/packages/backend/src/core/chart/charts/per-user-drive.ts index a44460bb4e..9b2e2d3b5a 100644 --- a/packages/backend/src/core/chart/charts/per-user-drive.ts +++ b/packages/backend/src/core/chart/charts/per-user-drive.ts @@ -7,7 +7,7 @@ import { DI } from '@/di-symbols.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; -import { name, schema } from './entities/per-user-drive.js'; +import { name, schema } from '@/core/entities/per-user-drive.js'; import type { KVs } from '../core.js'; /** diff --git a/packages/backend/src/core/chart/charts/per-user-following.ts b/packages/backend/src/core/chart/charts/per-user-following.ts index 5ea08a0872..6bd6f1a7dc 100644 --- a/packages/backend/src/core/chart/charts/per-user-following.ts +++ b/packages/backend/src/core/chart/charts/per-user-following.ts @@ -7,7 +7,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { FollowingsRepository } from '@/models/index.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; -import { name, schema } from './entities/per-user-following.js'; +import { name, schema } from '@/core/entities/per-user-following.js'; import type { KVs } from '../core.js'; /** diff --git a/packages/backend/src/core/chart/charts/per-user-notes.ts b/packages/backend/src/core/chart/charts/per-user-notes.ts index 5c14309d89..53bacd434a 100644 --- a/packages/backend/src/core/chart/charts/per-user-notes.ts +++ b/packages/backend/src/core/chart/charts/per-user-notes.ts @@ -7,7 +7,7 @@ import { DI } from '@/di-symbols.js'; import type { NotesRepository } from '@/models/index.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; -import { name, schema } from './entities/per-user-notes.js'; +import { name, schema } from '@/core/entities/per-user-notes.js'; import type { KVs } from '../core.js'; /** diff --git a/packages/backend/src/core/chart/charts/per-user-reactions.ts b/packages/backend/src/core/chart/charts/per-user-reactions.ts index 4160219720..78a7be0383 100644 --- a/packages/backend/src/core/chart/charts/per-user-reactions.ts +++ b/packages/backend/src/core/chart/charts/per-user-reactions.ts @@ -7,7 +7,7 @@ import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; -import { name, schema } from './entities/per-user-reactions.js'; +import { name, schema } from '@/core/entities/per-user-reactions.js'; import type { KVs } from '../core.js'; /** diff --git a/packages/backend/src/core/chart/charts/test-grouped.ts b/packages/backend/src/core/chart/charts/test-grouped.ts index bc215f3942..95585ec93e 100644 --- a/packages/backend/src/core/chart/charts/test-grouped.ts +++ b/packages/backend/src/core/chart/charts/test-grouped.ts @@ -4,7 +4,7 @@ import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Logger from '@/logger.js'; import Chart from '../core.js'; -import { name, schema } from './entities/test-grouped.js'; +import { name, schema } from '@/core/entities/test-grouped.js'; import type { KVs } from '../core.js'; /** diff --git a/packages/backend/src/core/chart/charts/test-intersection.ts b/packages/backend/src/core/chart/charts/test-intersection.ts index a074a7dded..c404a211a5 100644 --- a/packages/backend/src/core/chart/charts/test-intersection.ts +++ b/packages/backend/src/core/chart/charts/test-intersection.ts @@ -4,7 +4,7 @@ import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Logger from '@/logger.js'; import Chart from '../core.js'; -import { name, schema } from './entities/test-intersection.js'; +import { name, schema } from '@/core/entities/test-intersection.js'; import type { KVs } from '../core.js'; /** diff --git a/packages/backend/src/core/chart/charts/test-unique.ts b/packages/backend/src/core/chart/charts/test-unique.ts index 4d3e2f2403..5430db852b 100644 --- a/packages/backend/src/core/chart/charts/test-unique.ts +++ b/packages/backend/src/core/chart/charts/test-unique.ts @@ -4,7 +4,7 @@ import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Logger from '@/logger.js'; import Chart from '../core.js'; -import { name, schema } from './entities/test-unique.js'; +import { name, schema } from '@/core/entities/test-unique.js'; import type { KVs } from '../core.js'; /** diff --git a/packages/backend/src/core/chart/charts/test.ts b/packages/backend/src/core/chart/charts/test.ts index 72caf79e0f..7510b533c2 100644 --- a/packages/backend/src/core/chart/charts/test.ts +++ b/packages/backend/src/core/chart/charts/test.ts @@ -4,7 +4,7 @@ import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Logger from '@/logger.js'; import Chart from '../core.js'; -import { name, schema } from './entities/test.js'; +import { name, schema } from '@/core/entities/test.js'; import type { KVs } from '../core.js'; /** diff --git a/packages/backend/src/core/chart/charts/users.ts b/packages/backend/src/core/chart/charts/users.ts index f0359968eb..0731617354 100644 --- a/packages/backend/src/core/chart/charts/users.ts +++ b/packages/backend/src/core/chart/charts/users.ts @@ -7,7 +7,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { UsersRepository } from '@/models/index.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; -import { name, schema } from './entities/users.js'; +import { name, schema } from '@/core/entities/users.js'; import type { KVs } from '../core.js'; /** diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts index c54285d9df..ebf6116f27 100644 --- a/packages/backend/src/core/entities/InstanceEntityService.ts +++ b/packages/backend/src/core/entities/InstanceEntityService.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 { Instance } from '@/models/entities/Instance.js'; -import { MetaService } from '../MetaService.js'; +import { MetaService } from '.@/core/MetaService.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() diff --git a/packages/backend/src/core/queue/QueueModule.ts b/packages/backend/src/core/queue/QueueModule.ts deleted file mode 100644 index 3a271ea37f..0000000000 --- a/packages/backend/src/core/queue/QueueModule.ts +++ /dev/null @@ -1,112 +0,0 @@ -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/core/remote/RemoteLoggerService.ts b/packages/backend/src/core/remote/RemoteLoggerService.ts deleted file mode 100644 index 68246466c8..0000000000 --- a/packages/backend/src/core/remote/RemoteLoggerService.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type Logger from '@/logger.js'; -import { LoggerService } from '@/core/LoggerService.js'; - -@Injectable() -export class RemoteLoggerService { - public logger: Logger; - - constructor( - private loggerService: LoggerService, - ) { - this.logger = this.loggerService.getLogger('remote', 'cyan'); - } -} diff --git a/packages/backend/src/core/remote/ResolveUserService.ts b/packages/backend/src/core/remote/ResolveUserService.ts deleted file mode 100644 index 2fd9e7c378..0000000000 --- a/packages/backend/src/core/remote/ResolveUserService.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { URL } from 'node:url'; -import { Inject, Injectable } from '@nestjs/common'; -import chalk from 'chalk'; -import { IsNull } from 'typeorm'; -import { DI } from '@/di-symbols.js'; -import type { UsersRepository } from '@/models/index.js'; -import type { IRemoteUser, User } from '@/models/entities/User.js'; -import type { Config } from '@/config.js'; -import type Logger from '@/logger.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 { - private logger: Logger; - - constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - private utilityService: UtilityService, - private webfingerService: WebfingerService, - private remoteLoggerService: RemoteLoggerService, - private apPersonService: ApPersonService, - ) { - this.logger = this.remoteLoggerService.logger.createSubLogger('resolve-user'); - } - - public async resolveUser(username: string, host: string | null): Promise { - const usernameLower = username.toLowerCase(); - - if (host == null) { - 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'); - } else { - return u; - } - }); - } - - host = this.utilityService.toPuny(host); - - if (this.config.host === host) { - 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'); - } 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); - - this.logger.succ(`return new remote user: ${chalk.magenta(acctLower)}`); - return await this.apPersonService.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(), - }); - - 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. - 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); - if (uri.hostname !== host) { - throw new Error('Invalid uri'); - } - - await this.usersRepository.update({ - usernameLower, - host: host, - }, { - uri: self.href, - }); - } else { - this.logger.info(`uri is fine: ${acctLower}`); - } - - await this.apPersonService.updatePerson(self.href); - - 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'); - } else { - return u; - } - }); - } - - this.logger.info(`return existing remote user: ${acctLower}`); - return user; - } - - private async resolveSelf(acctLower: string) { - this.logger.info(`WebFinger for ${chalk.yellow(acctLower)}`); - 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) { - 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/core/remote/WebfingerService.ts b/packages/backend/src/core/remote/WebfingerService.ts deleted file mode 100644 index d2a88be583..0000000000 --- a/packages/backend/src/core/remote/WebfingerService.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { URL } from 'node:url'; -import { Inject, Injectable } from '@nestjs/common'; -import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; -import { query as urlQuery } from '@/misc/prelude/url.js'; -import { HttpRequestService } from '@/core/HttpRequestService.js'; - -type ILink = { - href: string; - rel?: string; -}; - -type IWebFinger = { - links: ILink[]; - subject: string; -}; - -@Injectable() -export class WebfingerService { - constructor( - @Inject(DI.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; - } - - private 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/core/remote/activitypub/ApAudienceService.ts b/packages/backend/src/core/remote/activitypub/ApAudienceService.ts deleted file mode 100644 index 744017aa3a..0000000000 --- a/packages/backend/src/core/remote/activitypub/ApAudienceService.ts +++ /dev/null @@ -1,104 +0,0 @@ -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 '@/misc/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, - }; - } - - private 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; - } - - private isPublic(id: string) { - return [ - 'https://www.w3.org/ns/activitystreams#Public', - 'as#Public', - 'Public', - ].includes(id); - } - - private isFollowers(id: string, actor: CacheableRemoteUser) { - return ( - id === (actor.followersUri ?? `${actor.uri}/followers`) - ); - } -} diff --git a/packages/backend/src/core/remote/activitypub/ApDbResolverService.ts b/packages/backend/src/core/remote/activitypub/ApDbResolverService.ts deleted file mode 100644 index 77d200c3c8..0000000000 --- a/packages/backend/src/core/remote/activitypub/ApDbResolverService.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import escapeRegexp from 'escape-regexp'; -import { DI } from '@/di-symbols.js'; -import type { MessagingMessagesRepository, NotesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.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/UserPublickey.js'; -import { UserCacheService } from '@/core/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 = { - /** 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 { - private publicKeyCache: Cache; - private publicKeyByUserIdCache: Cache; - - constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.messagingMessagesRepository) - private messagingMessagesRepository: MessagingMessagesRepository, - - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - - @Inject(DI.userPublickeysRepository) - private userPublickeysRepository: UserPublickeysRepository, - - private userCacheService: UserCacheService, - private apPersonService: ApPersonService, - ) { - 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, () => this.usersRepository.findOneBy({ - id: parsed.id, - }).then(x => x ?? undefined)) ?? null; - } else { - return await this.userCacheService.uriPersonCache.fetch(parsed.uri, () => this.usersRepository.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, () => this.usersRepository.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 this.apPersonService.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/core/remote/activitypub/ApDeliverManagerService.ts b/packages/backend/src/core/remote/activitypub/ApDeliverManagerService.ts deleted file mode 100644 index 6fc75a0397..0000000000 --- a/packages/backend/src/core/remote/activitypub/ApDeliverManagerService.ts +++ /dev/null @@ -1,199 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { IsNull, Not } from 'typeorm'; -import { DI } from '@/di-symbols.js'; -import type { FollowingsRepository, UsersRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; -import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; -import { QueueService } from '@/core/QueueService.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; - -interface IRecipe { - type: string; -} - -interface IFollowersRecipe extends IRecipe { - type: 'Followers'; -} - -interface IDirectRecipe extends IRecipe { - type: 'Direct'; - to: IRemoteUser; -} - -const isFollowers = (recipe: any): recipe is IFollowersRecipe => - recipe.type === 'Followers'; - -const isDirect = (recipe: any): recipe is IDirectRecipe => - recipe.type === 'Direct'; - -@Injectable() -export class ApDeliverManagerService { - constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.followingsRepository) - private followingsRepository: FollowingsRepository, - - private userEntityService: UserEntityService, - 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.userEntityService, - 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.userEntityService, - this.followingsRepository, - this.queueService, - actor, - activity, - ); - 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 { - private actor: { id: User['id']; host: null; }; - private activity: any; - private recipes: IRecipe[] = []; - - /** - * Constructor - * @param actor Actor - * @param activity Activity to deliver - */ - constructor( - private userEntityService: UserEntityService, - private followingsRepository: FollowingsRepository, - private queueService: QueueService, - - actor: { id: User['id']; host: null; }, - activity: any, - ) { - this.actor = actor; - this.activity = activity; - } - - /** - * Add recipe for followers deliver - */ - public addFollowersRecipe() { - const deliver = { - type: 'Followers', - } as IFollowersRecipe; - - this.addRecipe(deliver); - } - - /** - * Add recipe for direct deliver - * @param to To - */ - public addDirectRecipe(to: IRemoteUser) { - const recipe = { - type: 'Direct', - to, - } as IDirectRecipe; - - this.addRecipe(recipe); - } - - /** - * Add recipe - * @param recipe Recipe - */ - public addRecipe(recipe: IRecipe) { - this.recipes.push(recipe); - } - - /** - * Execute delivers - */ - public async execute() { - if (!this.userEntityService.isLocalUser(this.actor)) return; - - const inboxes = new Set(); - - /* - build inbox list - - Process follower recipes first to avoid duplication when processing - direct recipes later. - */ - if (this.recipes.some(r => isFollowers(r))) { - // followers deliver - // TODO: SELECT DISTINCT ON ("followerSharedInbox") "followerSharedInbox" みたいな問い合わせにすればよりパフォーマンス向上できそう - // ただ、sharedInboxがnullなリモートユーザーも稀におり、その対応ができなさそう? - const followers = await this.followingsRepository.find({ - where: { - followeeId: this.actor.id, - followerHost: Not(IsNull()), - }, - select: { - followerSharedInbox: true, - followerInbox: true, - }, - }) as { - followerSharedInbox: string | null; - followerInbox: string; - }[]; - - for (const following of followers) { - const inbox = following.followerSharedInbox ?? following.followerInbox; - inboxes.add(inbox); - } - } - - this.recipes.filter((recipe): recipe is IDirectRecipe => - // followers recipes have already been processed - isDirect(recipe) - // check that shared inbox has not been added yet - && !(recipe.to.sharedInbox && inboxes.has(recipe.to.sharedInbox)) - // check that they actually have an inbox - && recipe.to.inbox != null, - ) - .forEach(recipe => inboxes.add(recipe.to.inbox!)); - - // deliver - for (const inbox of inboxes) { - this.queueService.deliver(this.actor, this.activity, inbox); - } - } -} diff --git a/packages/backend/src/core/remote/activitypub/ApInboxService.ts b/packages/backend/src/core/remote/activitypub/ApInboxService.ts deleted file mode 100644 index 3da384ec2d..0000000000 --- a/packages/backend/src/core/remote/activitypub/ApInboxService.ts +++ /dev/null @@ -1,740 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { In } from 'typeorm'; -import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; -import type { CacheableRemoteUser } from '@/models/entities/User.js'; -import { UserFollowingService } from '@/core/UserFollowingService.js'; -import { ReactionService } from '@/core/ReactionService.js'; -import { RelayService } from '@/core/RelayService.js'; -import { NotePiningService } from '@/core/NotePiningService.js'; -import { UserBlockingService } from '@/core/UserBlockingService.js'; -import { NoteDeleteService } from '@/core/NoteDeleteService.js'; -import { NoteCreateService } from '@/core/NoteCreateService.js'; -import { concat, toArray, toSingle, unique } from '@/misc/prelude/array.js'; -import { AppLockService } from '@/core/AppLockService.js'; -import type Logger from '@/logger.js'; -import { MetaService } from '@/core/MetaService.js'; -import { IdService } from '@/core/IdService.js'; -import { StatusError } from '@/misc/status-error.js'; -import { UtilityService } from '@/core/UtilityService.js'; -import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { QueueService } from '@/core/QueueService.js'; -import { MessagingService } from '@/core/MessagingService.js'; -import type { UsersRepository, NotesRepository, FollowingsRepository, MessagingMessagesRepository, AbuseUserReportsRepository, FollowRequestsRepository } from '@/models/index.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'; - -@Injectable() -export class ApInboxService { - private logger: Logger; - - constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - - @Inject(DI.followingsRepository) - private followingsRepository: FollowingsRepository, - - @Inject(DI.messagingMessagesRepository) - private messagingMessagesRepository: MessagingMessagesRepository, - - @Inject(DI.abuseUserReportsRepository) - private abuseUserReportsRepository: AbuseUserReportsRepository, - - @Inject(DI.followRequestsRepository) - private followRequestsRepository: FollowRequestsRepository, - - 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, - private userBlockingService: UserBlockingService, - private noteCreateService: NoteCreateService, - private noteDeleteService: NoteDeleteService, - private appLockService: AppLockService, - private apResolverService: ApResolverService, - 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; - } - - public async performActivity(actor: CacheableRemoteUser, activity: IObject) { - if (isCollectionOrOrderedCollection(activity)) { - const resolver = this.apResolverService.createResolver(); - for (const item of toArray(isCollection(activity) ? activity.items : activity.orderedItems)) { - const act = await resolver.resolve(item); - try { - await this.performOneActivity(actor, act); - } catch (err) { - if (err instanceof Error || typeof err === 'string') { - this.logger.error(err); - } - } - } - } 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 { - if (actor.isSuspended) return; - - if (isCreate(activity)) { - await this.create(actor, activity); - } else if (isDelete(activity)) { - await this.delete(actor, activity); - } else if (isUpdate(activity)) { - await this.update(actor, activity); - } else if (isRead(activity)) { - await this.read(actor, activity); - } else if (isFollow(activity)) { - await this.follow(actor, activity); - } else if (isAccept(activity)) { - await this.accept(actor, activity); - } else if (isReject(activity)) { - await this.reject(actor, activity); - } else if (isAdd(activity)) { - await this.add(actor, activity).catch(err => this.logger.error(err)); - } else if (isRemove(activity)) { - await this.remove(actor, activity).catch(err => this.logger.error(err)); - } else if (isAnnounce(activity)) { - await this.announce(actor, activity); - } else if (isLike(activity)) { - await this.like(actor, activity); - } else if (isUndo(activity)) { - await this.undo(actor, activity); - } else if (isBlock(activity)) { - await this.block(actor, activity); - } else if (isFlag(activity)) { - await this.flag(actor, activity); - } else { - this.logger.warn(`unrecognized activity type: ${(activity as any).type}`); - } - } - - private async follow(actor: CacheableRemoteUser, activity: IFollow): Promise { - const followee = await this.apDbResolverService.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'; - } - - private async like(actor: CacheableRemoteUser, activity: ILike): Promise { - const targetUri = getApId(activity.object); - - 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); - - return await this.reactionService.create(actor, note, activity._misskey_reaction ?? activity.content ?? activity.name).catch(err => { - if (err.id === '51c42bb4-931a-456b-bff7-e5a8a70dd298') { - return 'skip: already reacted'; - } else { - throw err; - } - }).then(() => 'ok'); - } - - private async read(actor: CacheableRemoteUser, activity: IRead): Promise { - const id = await getApId(activity.object); - - if (!this.utilityService.isSelfHost(this.utilityService.extractDbHost(id))) { - return `skip: Read to foreign host (${id})`; - } - - const messageId = id.split('/').pop(); - - const message = await this.messagingMessagesRepository.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 this.messagingService.readUserMessagingMessage(message.recipientId!, message.userId, [message.id]); - return `ok: mark as read (${message.userId} => ${message.recipientId} ${message.id})`; - } - - private async accept(actor: CacheableRemoteUser, activity: IAccept): Promise { - const uri = activity.id ?? activity; - - this.logger.info(`Accept: ${uri}`); - - const resolver = this.apResolverService.createResolver(); - - 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); - - return `skip: Unknown Accept type: ${getApType(object)}`; - } - - private async acceptFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { - // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある - - const follower = await this.apDbResolverService.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'; - } - - private 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 this.apNoteService.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}`); - } - - private async announce(actor: CacheableRemoteUser, activity: IAnnounce): Promise { - const uri = getApId(activity); - - this.logger.info(`Announce: ${uri}`); - - const targetUri = getApId(activity.object); - - this.announceNote(actor, activity, targetUri); - } - - private async announceNote(actor: CacheableRemoteUser, activity: IAnnounce, targetUri: string): Promise { - const uri = getApId(activity); - - if (actor.isSuspended) { - return; - } - - // アナウンス先をブロックしてたら中断 - const meta = await this.metaService.fetch(); - if (meta.blockedHosts.includes(this.utilityService.extractDbHost(uri))) return; - - const unlock = await this.appLockService.getApLock(uri); - - try { - // 既に同じURIを持つものが登録されていないかチェック - const exist = await this.apNoteService.fetchNote(uri); - if (exist) { - return; - } - - // Announce対象をresolve - let renote; - try { - renote = await this.apNoteService.resolveNote(targetUri); - if (renote == null) throw new Error('announce target is null'); - } catch (err) { - // 対象が4xxならスキップ - 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} - ${err.statusCode ?? err}`); - } - throw err; - } - - if (!await this.noteEntityService.isVisibleForMe(renote, actor.id)) { - this.logger.warn('skip: invalid actor for this activity'); - return; - } - - this.logger.info(`Creating the (Re)Note: ${uri}`); - - const activityAudience = await this.apAudienceService.parseAudience(actor, activity.to, activity.cc); - - await this.noteCreateService.create(actor, { - createdAt: activity.published ? new Date(activity.published) : null, - renote, - visibility: activityAudience.visibility, - visibleUsers: activityAudience.visibleUsers, - uri, - }); - } finally { - unlock(); - } - } - - private async block(actor: CacheableRemoteUser, activity: IBlock): Promise { - // ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず - - const blockee = await this.apDbResolverService.getUserFromApId(activity.object); - - if (blockee == null) { - return 'skip: blockee not found'; - } - - if (blockee.host != null) { - return 'skip: ブロックしようとしているユーザーはローカルユーザーではありません'; - } - - await this.userBlockingService.block(await this.usersRepository.findOneByOrFail({ id: actor.id }), await this.usersRepository.findOneByOrFail({ id: blockee.id })); - return 'ok'; - } - - private async create(actor: CacheableRemoteUser, activity: ICreate): Promise { - const uri = getApId(activity); - - this.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 = this.apResolverService.createResolver(); - - const object = await resolver.resolve(activity.object).catch(e => { - this.logger.error(`Resolution failed: ${e}`); - throw e; - }); - - if (isPost(object)) { - this.createNote(resolver, actor, object, false, activity); - } else { - this.logger.warn(`Unknown type: ${getApType(object)}`); - } - } - - private 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 (this.utilityService.extractDbHost(actor.uri) !== this.utilityService.extractDbHost(note.id)) { - return 'skip: host in actor.uri !== note.id'; - } - } - } - - const unlock = await this.appLockService.getApLock(uri); - - try { - const exist = await this.apNoteService.fetchNote(note); - if (exist) return 'skip: note exists'; - - await this.apNoteService.createNote(note, resolver, silent); - return 'ok'; - } catch (err) { - if (err instanceof StatusError && err.isClientError) { - return `skip ${err.statusCode}`; - } else { - throw err; - } - } finally { - unlock(); - } - } - - private 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}`; - } - } - - private async deleteActor(actor: CacheableRemoteUser, uri: string): Promise { - this.logger.info(`Deleting the Actor: ${uri}`); - - if (actor.uri !== uri) { - return `skip: delete actor ${actor.uri} !== ${uri}`; - } - - const user = await this.usersRepository.findOneByOrFail({ id: actor.id }); - if (user.isDeleted) { - this.logger.info('skip: already deleted'); - } - - const job = await this.queueService.createDeleteAccountJob(actor); - - await this.usersRepository.update(actor.id, { - isDeleted: true, - }); - - return `ok: queued ${job.name} ${job.id}`; - } - - private async deleteNote(actor: CacheableRemoteUser, uri: string): Promise { - this.logger.info(`Deleting the Note: ${uri}`); - - const unlock = await this.appLockService.getApLock(uri); - - try { - const note = await this.apDbResolverService.getNoteFromApId(uri); - - if (note == null) { - const message = await this.apDbResolverService.getMessageFromApId(uri); - if (message == null) return 'message not found'; - - if (message.userId !== actor.id) { - return '投稿を削除しようとしているユーザーは投稿の作成者ではありません'; - } - - await this.messagingService.deleteMessage(message); - - return 'ok: message deleted'; - } - - if (note.userId !== actor.id) { - return '投稿を削除しようとしているユーザーは投稿の作成者ではありません'; - } - - await this.noteDeleteService.delete(actor, note); - return 'ok: note deleted'; - } finally { - unlock(); - } - } - - private 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(this.config.url + '/users/')).map(uri => uri.split('/').pop()!); - const users = await this.usersRepository.findBy({ - id: In(userIds), - }); - if (users.length < 1) return 'skip'; - - await this.abuseUserReportsRepository.insert({ - id: this.idService.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'; - } - - private async reject(actor: CacheableRemoteUser, activity: IReject): Promise { - const uri = activity.id ?? activity; - - this.logger.info(`Reject: ${uri}`); - - const resolver = this.apResolverService.createResolver(); - - const object = await resolver.resolve(activity.object).catch(e => { - this.logger.error(`Resolution failed: ${e}`); - throw e; - }); - - if (isFollow(object)) return await this.rejectFollow(actor, object); - - return `skip: Unknown Reject type: ${getApType(object)}`; - } - - private async rejectFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { - // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある - - const follower = await this.apDbResolverService.getUserFromApId(activity.actor); - - if (follower == null) { - return 'skip: follower not found'; - } - - if (!this.userEntityService.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'; - } - - private 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 this.apNoteService.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}`); - } - - private 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; - - this.logger.info(`Undo: ${uri}`); - - const resolver = this.apResolverService.createResolver(); - - const object = await resolver.resolve(activity.object).catch(e => { - this.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)}`; - } - - private async undoAccept(actor: CacheableRemoteUser, activity: IAccept): Promise { - const follower = await this.apDbResolverService.getUserFromApId(activity.object); - if (follower == null) { - return 'skip: follower not found'; - } - - const following = await this.followingsRepository.findOneBy({ - followerId: follower.id, - followeeId: actor.id, - }); - - if (following) { - await this.userFollowingService.unfollow(follower, actor); - return 'ok: unfollowed'; - } - - return 'skip: フォローされていない'; - } - - private async undoAnnounce(actor: CacheableRemoteUser, activity: IAnnounce): Promise { - const uri = getApId(activity); - - const note = await this.notesRepository.findOneBy({ - uri, - userId: actor.id, - }); - - if (!note) return 'skip: no such Announce'; - - await this.noteDeleteService.delete(actor, note); - return 'ok: deleted'; - } - - private async undoBlock(actor: CacheableRemoteUser, activity: IBlock): Promise { - const blockee = await this.apDbResolverService.getUserFromApId(activity.object); - - if (blockee == null) { - return 'skip: blockee not found'; - } - - if (blockee.host != null) { - return 'skip: ブロック解除しようとしているユーザーはローカルユーザーではありません'; - } - - await this.userBlockingService.unblock(await this.usersRepository.findOneByOrFail({ id: actor.id }), blockee); - return 'ok'; - } - - private async undoFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { - const followee = await this.apDbResolverService.getUserFromApId(activity.object); - if (followee == null) { - return 'skip: followee not found'; - } - - if (followee.host != null) { - return 'skip: フォロー解除しようとしているユーザーはローカルユーザーではありません'; - } - - const req = await this.followRequestsRepository.findOneBy({ - followerId: actor.id, - followeeId: followee.id, - }); - - const following = await this.followingsRepository.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: リクエストもフォローもされていない'; - } - - private async undoLike(actor: CacheableRemoteUser, activity: ILike): Promise { - const targetUri = getApId(activity.object); - - const note = await this.apNoteService.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'; - } - - private async update(actor: CacheableRemoteUser, activity: IUpdate): Promise { - if ('actor' in activity && actor.uri !== activity.actor) { - return 'skip: invalid actor'; - } - - this.logger.debug('Update'); - - const resolver = this.apResolverService.createResolver(); - - const object = await resolver.resolve(activity.object).catch(e => { - this.logger.error(`Resolution failed: ${e}`); - throw e; - }); - - if (isActor(object)) { - await this.apPersonService.updatePerson(actor.uri!, resolver, object); - return 'ok: Person updated'; - } else if (getApType(object) === 'Question') { - await this.apQuestionService.updateQuestion(object, resolver).catch(err => console.error(err)); - return 'ok: Question updated'; - } else { - return `skip: Unknown type: ${getApType(object)}`; - } - } -} diff --git a/packages/backend/src/core/remote/activitypub/ApLoggerService.ts b/packages/backend/src/core/remote/activitypub/ApLoggerService.ts deleted file mode 100644 index 82fd7c5f18..0000000000 --- a/packages/backend/src/core/remote/activitypub/ApLoggerService.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type Logger from '@/logger.js'; -import { RemoteLoggerService } from '@/core/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/core/remote/activitypub/ApMfmService.ts b/packages/backend/src/core/remote/activitypub/ApMfmService.ts deleted file mode 100644 index 8804fde64a..0000000000 --- a/packages/backend/src/core/remote/activitypub/ApMfmService.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import * as mfm from 'mfm-js'; -import { DI } from '@/di-symbols.js'; -import type { Config } from '@/config.js'; -import { MfmService } from '@/core/MfmService.js'; -import type { Note } from '@/models/entities/Note.js'; -import { extractApHashtagObjects } from './models/tag.js'; -import type { IObject } from './type.js'; - -@Injectable() -export class ApMfmService { - constructor( - @Inject(DI.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); - } - - 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/core/remote/activitypub/ApRendererService.ts b/packages/backend/src/core/remote/activitypub/ApRendererService.ts deleted file mode 100644 index 38a92567c3..0000000000 --- a/packages/backend/src/core/remote/activitypub/ApRendererService.ts +++ /dev/null @@ -1,703 +0,0 @@ -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 { 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 '@/core/UserKeypairStoreService.js'; -import { MfmService } from '@/core/MfmService.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; -import type { UserKeypair } from '@/models/entities/UserKeypair.js'; -import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFilesRepository, EmojisRepository, PollsRepository } from '@/models/index.js'; -import { LdSignatureService } from './LdSignatureService.js'; -import { ApMfmService } from './ApMfmService.js'; -import type { IActivity, IObject } from './type.js'; -import type { IIdentifier } from './models/identifier.js'; - -@Injectable() -export class ApRendererService { - constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.userProfilesRepository) - private userProfilesRepository: UserProfilesRepository, - - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - - @Inject(DI.driveFilesRepository) - private driveFilesRepository: DriveFilesRepository, - - @Inject(DI.emojisRepository) - private emojisRepository: EmojisRepository, - - @Inject(DI.pollsRepository) - private pollsRepository: PollsRepository, - - private userEntityService: UserEntityService, - private driveFileEntityService: DriveFileEntityService, - private ldSignatureService: LdSignatureService, - private userKeypairStoreService: UserKeypairStoreService, - private apMfmService: ApMfmService, - private mfmService: MfmService, - ) { - } - - 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.driveFileEntityService.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.userEntityService.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.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; - } - - 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.driveFileEntityService.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: { uri: string | null }) { - 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.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}`, - }; - } - - 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 = this.apMfmService.getNoteHtml(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: this.apMfmService.getNoteHtml(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: summary ?? undefined, - content: content ?? undefined, - _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), - this.userProfilesRepository.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 ? this.mfmService.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 { - const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); - - const ldSignature = this.ldSignatureService.use(); - 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?: IObject[]) { - const page: any = { - id, - type: 'OrderedCollection', - totalItems, - }; - - if (first) page.first = first; - if (last) page.last = last; - if (orderedItems) page.orderedItems = orderedItems; - - return page; - } - - private 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/core/remote/activitypub/ApRequestService.ts b/packages/backend/src/core/remote/activitypub/ApRequestService.ts deleted file mode 100644 index baad46d668..0000000000 --- a/packages/backend/src/core/remote/activitypub/ApRequestService.ts +++ /dev/null @@ -1,182 +0,0 @@ -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 type { User } from '@/models/entities/User.js'; -import { UserKeypairStoreService } from '@/core/UserKeypairStoreService.js'; -import { HttpRequestService } from '@/core/HttpRequestService.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.config) - private config: Config, - - private userKeypairStoreService: UserKeypairStoreService, - private httpRequestService: HttpRequestService, - ) { - } - - private 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, - }; - } - - private 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, - }; - } - - private 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, - }; - } - - private 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'); - } - - private 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; - } - - private 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 this.httpRequestService.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 this.httpRequestService.getResponse({ - url, - method: req.request.method, - headers: req.request.headers, - }); - - return await res.json(); - } -} diff --git a/packages/backend/src/core/remote/activitypub/ApResolverService.ts b/packages/backend/src/core/remote/activitypub/ApResolverService.ts deleted file mode 100644 index bcdb9383d1..0000000000 --- a/packages/backend/src/core/remote/activitypub/ApResolverService.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import type { ILocalUser } from '@/models/entities/User.js'; -import { InstanceActorService } from '@/core/InstanceActorService.js'; -import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; -import { MetaService } from '@/core/MetaService.js'; -import { HttpRequestService } from '@/core/HttpRequestService.js'; -import { DI } from '@/di-symbols.js'; -import { UtilityService } from '@/core/UtilityService.js'; -import { isCollectionOrOrderedCollection } from './type.js'; -import { ApDbResolverService } from './ApDbResolverService.js'; -import { ApRendererService } from './ApRendererService.js'; -import { ApRequestService } from './ApRequestService.js'; -import type { IObject, ICollection, IOrderedCollection } from './type.js'; - -@Injectable() -export class ApResolverService { - constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - - @Inject(DI.pollsRepository) - private pollsRepository: PollsRepository, - - @Inject(DI.noteReactionsRepository) - private noteReactionsRepository: NoteReactionsRepository, - - private utilityService: UtilityService, - private instanceActorService: InstanceActorService, - private metaService: MetaService, - private apRequestService: ApRequestService, - private httpRequestService: HttpRequestService, - private apRendererService: ApRendererService, - private apDbResolverService: ApDbResolverService, - ) { - } - - public createResolver(): Resolver { - return new Resolver( - this.config, - this.usersRepository, - this.notesRepository, - this.pollsRepository, - this.noteReactionsRepository, - this.utilityService, - this.instanceActorService, - this.metaService, - this.apRequestService, - this.httpRequestService, - this.apRendererService, - this.apDbResolverService, - ); - } -} - -export class Resolver { - private history: Set; - private user?: ILocalUser; - - constructor( - private config: Config, - private usersRepository: UsersRepository, - private notesRepository: NotesRepository, - private pollsRepository: PollsRepository, - private noteReactionsRepository: NoteReactionsRepository, - private utilityService: UtilityService, - private instanceActorService: InstanceActorService, - private metaService: MetaService, - private apRequestService: ApRequestService, - private httpRequestService: HttpRequestService, - private apRendererService: ApRendererService, - private apDbResolverService: ApDbResolverService, - private recursionLimit = 100 - ) { - this.history = new Set(); - } - - public getHistory(): string[] { - return Array.from(this.history); - } - - public async resolveCollection(value: string | IObject): Promise { - const collection = typeof value === 'string' - ? await this.resolve(value) - : value; - - if (isCollectionOrOrderedCollection(collection)) { - return collection; - } else { - throw new Error(`unrecognized collection type: ${collection.type}`); - } - } - - public async resolve(value: string | IObject): Promise { - if (value == null) { - throw new Error('resolvee is null (or undefined)'); - } - - if (typeof value !== 'string') { - return value; - } - - if (value.includes('#')) { - // URLs with fragment parts cannot be resolved correctly because - // the fragment part does not get transmitted over HTTP(S). - // Avoid strange behaviour by not trying to resolve these at all. - throw new Error(`cannot resolve URL with fragment: ${value}`); - } - - if (this.history.has(value)) { - throw new Error('cannot resolve already resolved one'); - } - - if (this.history.size > this.recursionLimit) { - throw new Error(`hit recursion limit: ${this.utilityService.extractDbHost(value)}`); - } - - this.history.add(value); - - const host = this.utilityService.extractDbHost(value); - if (this.utilityService.isSelfHost(host)) { - return await this.resolveLocal(value); - } - - const meta = await this.metaService.fetch(); - if (meta.blockedHosts.includes(host)) { - throw new Error('Instance is blocked'); - } - - if (this.config.signToActivityPubGet && !this.user) { - this.user = await this.instanceActorService.getInstanceActor(); - } - - const object = (this.user - ? 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']) ? - !(object['@context'] as unknown[]).includes('https://www.w3.org/ns/activitystreams') : - object['@context'] !== 'https://www.w3.org/ns/activitystreams' - )) { - throw new Error('invalid response'); - } - - return object; - } - - private resolveLocal(url: string): Promise { - const parsed = this.apDbResolverService.parseUri(url); - if (!parsed.local) throw new Error('resolveLocal: not local'); - - switch (parsed.type) { - case 'notes': - 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 this.apRendererService.renderActivity(this.apRendererService.renderCreate(this.apRendererService.renderNote(note), note)); - } else { - return this.apRendererService.renderNote(note); - } - }); - case 'users': - 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([ - this.notesRepository.findOneByOrFail({ id: parsed.id }), - this.pollsRepository.findOneByOrFail({ noteId: parsed.id }), - ]) - .then(([note, poll]) => this.apRendererService.renderQuestion({ id: note.userId }, note, poll)); - case 'likes': - 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 => this.usersRepository.findOneByOrFail({ id })), - ) - .then(([follower, followee]) => this.apRendererService.renderActivity(this.apRendererService.renderFollow(follower, followee, url))); - default: - throw new Error(`resolveLocal: type ${parsed.type} unhandled`); - } - } -} diff --git a/packages/backend/src/core/remote/activitypub/LdSignatureService.ts b/packages/backend/src/core/remote/activitypub/LdSignatureService.ts deleted file mode 100644 index ea39f15b2b..0000000000 --- a/packages/backend/src/core/remote/activitypub/LdSignatureService.ts +++ /dev/null @@ -1,147 +0,0 @@ -import * as crypto from 'node:crypto'; -import { Inject, Injectable } from '@nestjs/common'; -import fetch from 'node-fetch'; -import { HttpRequestService } from '@/core/HttpRequestService.js'; -import { CONTEXTS } from './misc/contexts.js'; - -// RsaSignature2017 based from https://github.com/transmute-industries/RsaSignature2017 - -@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( - private httpRequestService: HttpRequestService, - ) { - } - - public async signRsaSignature2017(data: any, privateKey: string, creator: string, domain?: string, created?: Date): Promise { - const options = { - type: 'RsaSignature2017', - creator, - domain, - nonce: crypto.randomBytes(16).toString('hex'), - created: (created ?? new Date()).toISOString(), - } as { - type: string; - creator: string; - domain?: string; - nonce: string; - created: string; - }; - - if (!domain) { - delete options.domain; - } - - const toBeSigned = await this.createVerifyData(data, options); - - const signer = crypto.createSign('sha256'); - signer.update(toBeSigned); - signer.end(); - - const signature = signer.sign(privateKey); - - return { - ...data, - signature: { - ...options, - signatureValue: signature.toString('base64'), - }, - }; - } - - public async verifyRsaSignature2017(data: any, publicKey: string): Promise { - const toBeSigned = await this.createVerifyData(data, data.signature); - const verifier = crypto.createVerify('sha256'); - verifier.update(toBeSigned); - return verifier.verify(publicKey, data.signature.signatureValue, 'base64'); - } - - public async createVerifyData(data: any, options: any) { - const transformedOptions = { - ...options, - '@context': 'https://w3id.org/identity/v1', - }; - delete transformedOptions['type']; - delete transformedOptions['id']; - delete transformedOptions['signatureValue']; - const canonizedOptions = await this.normalize(transformedOptions); - const optionsHash = this.sha256(canonizedOptions.toString()); - const transformedData = { ...data }; - delete transformedData['signature']; - const cannonidedData = await this.normalize(transformedData); - if (this.debug) console.debug(`cannonidedData: ${cannonidedData}`); - const documentHash = this.sha256(cannonidedData.toString()); - const verifyData = `${optionsHash}${documentHash}`; - return verifyData; - } - - public async normalize(data: any) { - const customLoader = this.getLoader(); - return 42; - } - - private getLoader() { - return async (url: string): Promise => { - if (!url.match('^https?\:\/\/')) throw `Invalid URL ${url}`; - - if (this.preLoad) { - if (url in CONTEXTS) { - if (this.debug) console.debug(`HIT: ${url}`); - return { - contextUrl: null, - document: CONTEXTS[url], - documentUrl: url, - }; - } - } - - if (this.debug) console.debug(`MISS: ${url}`); - const document = await this.fetchDocument(url); - return { - contextUrl: null, - document: document, - documentUrl: url, - }; - }; - } - - private async fetchDocument(url: string) { - const json = await fetch(url, { - headers: { - Accept: 'application/ld+json, application/json', - }, - // TODO - //timeout: this.loderTimeout, - agent: u => u.protocol === 'http:' ? this.httpRequestService.httpAgent : this.httpRequestService.httpsAgent, - }).then(res => { - if (!res.ok) { - throw `${res.status} ${res.statusText}`; - } else { - return res.json(); - } - }); - - return json; - } - - public sha256(data: string): string { - const hash = crypto.createHash('sha256'); - hash.update(data); - return hash.digest('hex'); - } -} diff --git a/packages/backend/src/core/remote/activitypub/misc/contexts.ts b/packages/backend/src/core/remote/activitypub/misc/contexts.ts deleted file mode 100644 index aee0d3629c..0000000000 --- a/packages/backend/src/core/remote/activitypub/misc/contexts.ts +++ /dev/null @@ -1,526 +0,0 @@ -/* eslint:disable:quotemark indent */ -const id_v1 = { - '@context': { - 'id': '@id', - 'type': '@type', - - 'cred': 'https://w3id.org/credentials#', - 'dc': 'http://purl.org/dc/terms/', - 'identity': 'https://w3id.org/identity#', - 'perm': 'https://w3id.org/permissions#', - 'ps': 'https://w3id.org/payswarm#', - 'rdf': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', - 'rdfs': 'http://www.w3.org/2000/01/rdf-schema#', - 'sec': 'https://w3id.org/security#', - 'schema': 'http://schema.org/', - 'xsd': 'http://www.w3.org/2001/XMLSchema#', - - 'Group': 'https://www.w3.org/ns/activitystreams#Group', - - 'claim': { '@id': 'cred:claim', '@type': '@id' }, - 'credential': { '@id': 'cred:credential', '@type': '@id' }, - 'issued': { '@id': 'cred:issued', '@type': 'xsd:dateTime' }, - 'issuer': { '@id': 'cred:issuer', '@type': '@id' }, - 'recipient': { '@id': 'cred:recipient', '@type': '@id' }, - 'Credential': 'cred:Credential', - 'CryptographicKeyCredential': 'cred:CryptographicKeyCredential', - - 'about': { '@id': 'schema:about', '@type': '@id' }, - 'address': { '@id': 'schema:address', '@type': '@id' }, - 'addressCountry': 'schema:addressCountry', - 'addressLocality': 'schema:addressLocality', - 'addressRegion': 'schema:addressRegion', - 'comment': 'rdfs:comment', - 'created': { '@id': 'dc:created', '@type': 'xsd:dateTime' }, - 'creator': { '@id': 'dc:creator', '@type': '@id' }, - 'description': 'schema:description', - 'email': 'schema:email', - 'familyName': 'schema:familyName', - 'givenName': 'schema:givenName', - 'image': { '@id': 'schema:image', '@type': '@id' }, - 'label': 'rdfs:label', - 'name': 'schema:name', - 'postalCode': 'schema:postalCode', - 'streetAddress': 'schema:streetAddress', - 'title': 'dc:title', - 'url': { '@id': 'schema:url', '@type': '@id' }, - 'Person': 'schema:Person', - 'PostalAddress': 'schema:PostalAddress', - 'Organization': 'schema:Organization', - - 'identityService': { '@id': 'identity:identityService', '@type': '@id' }, - 'idp': { '@id': 'identity:idp', '@type': '@id' }, - 'Identity': 'identity:Identity', - - 'paymentProcessor': 'ps:processor', - 'preferences': { '@id': 'ps:preferences', '@type': '@vocab' }, - - 'cipherAlgorithm': 'sec:cipherAlgorithm', - 'cipherData': 'sec:cipherData', - 'cipherKey': 'sec:cipherKey', - 'digestAlgorithm': 'sec:digestAlgorithm', - 'digestValue': 'sec:digestValue', - 'domain': 'sec:domain', - 'expires': { '@id': 'sec:expiration', '@type': 'xsd:dateTime' }, - 'initializationVector': 'sec:initializationVector', - 'member': { '@id': 'schema:member', '@type': '@id' }, - 'memberOf': { '@id': 'schema:memberOf', '@type': '@id' }, - 'nonce': 'sec:nonce', - 'normalizationAlgorithm': 'sec:normalizationAlgorithm', - 'owner': { '@id': 'sec:owner', '@type': '@id' }, - 'password': 'sec:password', - 'privateKey': { '@id': 'sec:privateKey', '@type': '@id' }, - 'privateKeyPem': 'sec:privateKeyPem', - 'publicKey': { '@id': 'sec:publicKey', '@type': '@id' }, - 'publicKeyPem': 'sec:publicKeyPem', - 'publicKeyService': { '@id': 'sec:publicKeyService', '@type': '@id' }, - 'revoked': { '@id': 'sec:revoked', '@type': 'xsd:dateTime' }, - 'signature': 'sec:signature', - 'signatureAlgorithm': 'sec:signatureAlgorithm', - 'signatureValue': 'sec:signatureValue', - 'CryptographicKey': 'sec:Key', - 'EncryptedMessage': 'sec:EncryptedMessage', - 'GraphSignature2012': 'sec:GraphSignature2012', - 'LinkedDataSignature2015': 'sec:LinkedDataSignature2015', - - 'accessControl': { '@id': 'perm:accessControl', '@type': '@id' }, - 'writePermission': { '@id': 'perm:writePermission', '@type': '@id' }, - }, -}; - -const security_v1 = { - '@context': { - 'id': '@id', - 'type': '@type', - - 'dc': 'http://purl.org/dc/terms/', - 'sec': 'https://w3id.org/security#', - 'xsd': 'http://www.w3.org/2001/XMLSchema#', - - 'EcdsaKoblitzSignature2016': 'sec:EcdsaKoblitzSignature2016', - 'Ed25519Signature2018': 'sec:Ed25519Signature2018', - 'EncryptedMessage': 'sec:EncryptedMessage', - 'GraphSignature2012': 'sec:GraphSignature2012', - 'LinkedDataSignature2015': 'sec:LinkedDataSignature2015', - 'LinkedDataSignature2016': 'sec:LinkedDataSignature2016', - 'CryptographicKey': 'sec:Key', - - 'authenticationTag': 'sec:authenticationTag', - 'canonicalizationAlgorithm': 'sec:canonicalizationAlgorithm', - 'cipherAlgorithm': 'sec:cipherAlgorithm', - 'cipherData': 'sec:cipherData', - 'cipherKey': 'sec:cipherKey', - 'created': { '@id': 'dc:created', '@type': 'xsd:dateTime' }, - 'creator': { '@id': 'dc:creator', '@type': '@id' }, - 'digestAlgorithm': 'sec:digestAlgorithm', - 'digestValue': 'sec:digestValue', - 'domain': 'sec:domain', - 'encryptionKey': 'sec:encryptionKey', - 'expiration': { '@id': 'sec:expiration', '@type': 'xsd:dateTime' }, - 'expires': { '@id': 'sec:expiration', '@type': 'xsd:dateTime' }, - 'initializationVector': 'sec:initializationVector', - 'iterationCount': 'sec:iterationCount', - 'nonce': 'sec:nonce', - 'normalizationAlgorithm': 'sec:normalizationAlgorithm', - 'owner': { '@id': 'sec:owner', '@type': '@id' }, - 'password': 'sec:password', - 'privateKey': { '@id': 'sec:privateKey', '@type': '@id' }, - 'privateKeyPem': 'sec:privateKeyPem', - 'publicKey': { '@id': 'sec:publicKey', '@type': '@id' }, - 'publicKeyBase58': 'sec:publicKeyBase58', - 'publicKeyPem': 'sec:publicKeyPem', - 'publicKeyWif': 'sec:publicKeyWif', - 'publicKeyService': { '@id': 'sec:publicKeyService', '@type': '@id' }, - 'revoked': { '@id': 'sec:revoked', '@type': 'xsd:dateTime' }, - 'salt': 'sec:salt', - 'signature': 'sec:signature', - 'signatureAlgorithm': 'sec:signingAlgorithm', - 'signatureValue': 'sec:signatureValue', - }, -}; - -const activitystreams = { - '@context': { - '@vocab': '_:', - 'xsd': 'http://www.w3.org/2001/XMLSchema#', - 'as': 'https://www.w3.org/ns/activitystreams#', - 'ldp': 'http://www.w3.org/ns/ldp#', - 'vcard': 'http://www.w3.org/2006/vcard/ns#', - 'id': '@id', - 'type': '@type', - 'Accept': 'as:Accept', - 'Activity': 'as:Activity', - 'IntransitiveActivity': 'as:IntransitiveActivity', - 'Add': 'as:Add', - 'Announce': 'as:Announce', - 'Application': 'as:Application', - 'Arrive': 'as:Arrive', - 'Article': 'as:Article', - 'Audio': 'as:Audio', - 'Block': 'as:Block', - 'Collection': 'as:Collection', - 'CollectionPage': 'as:CollectionPage', - 'Relationship': 'as:Relationship', - 'Create': 'as:Create', - 'Delete': 'as:Delete', - 'Dislike': 'as:Dislike', - 'Document': 'as:Document', - 'Event': 'as:Event', - 'Follow': 'as:Follow', - 'Flag': 'as:Flag', - 'Group': 'as:Group', - 'Ignore': 'as:Ignore', - 'Image': 'as:Image', - 'Invite': 'as:Invite', - 'Join': 'as:Join', - 'Leave': 'as:Leave', - 'Like': 'as:Like', - 'Link': 'as:Link', - 'Mention': 'as:Mention', - 'Note': 'as:Note', - 'Object': 'as:Object', - 'Offer': 'as:Offer', - 'OrderedCollection': 'as:OrderedCollection', - 'OrderedCollectionPage': 'as:OrderedCollectionPage', - 'Organization': 'as:Organization', - 'Page': 'as:Page', - 'Person': 'as:Person', - 'Place': 'as:Place', - 'Profile': 'as:Profile', - 'Question': 'as:Question', - 'Reject': 'as:Reject', - 'Remove': 'as:Remove', - 'Service': 'as:Service', - 'TentativeAccept': 'as:TentativeAccept', - 'TentativeReject': 'as:TentativeReject', - 'Tombstone': 'as:Tombstone', - 'Undo': 'as:Undo', - 'Update': 'as:Update', - 'Video': 'as:Video', - 'View': 'as:View', - 'Listen': 'as:Listen', - 'Read': 'as:Read', - 'Move': 'as:Move', - 'Travel': 'as:Travel', - 'IsFollowing': 'as:IsFollowing', - 'IsFollowedBy': 'as:IsFollowedBy', - 'IsContact': 'as:IsContact', - 'IsMember': 'as:IsMember', - 'subject': { - '@id': 'as:subject', - '@type': '@id', - }, - 'relationship': { - '@id': 'as:relationship', - '@type': '@id', - }, - 'actor': { - '@id': 'as:actor', - '@type': '@id', - }, - 'attributedTo': { - '@id': 'as:attributedTo', - '@type': '@id', - }, - 'attachment': { - '@id': 'as:attachment', - '@type': '@id', - }, - 'bcc': { - '@id': 'as:bcc', - '@type': '@id', - }, - 'bto': { - '@id': 'as:bto', - '@type': '@id', - }, - 'cc': { - '@id': 'as:cc', - '@type': '@id', - }, - 'context': { - '@id': 'as:context', - '@type': '@id', - }, - 'current': { - '@id': 'as:current', - '@type': '@id', - }, - 'first': { - '@id': 'as:first', - '@type': '@id', - }, - 'generator': { - '@id': 'as:generator', - '@type': '@id', - }, - 'icon': { - '@id': 'as:icon', - '@type': '@id', - }, - 'image': { - '@id': 'as:image', - '@type': '@id', - }, - 'inReplyTo': { - '@id': 'as:inReplyTo', - '@type': '@id', - }, - 'items': { - '@id': 'as:items', - '@type': '@id', - }, - 'instrument': { - '@id': 'as:instrument', - '@type': '@id', - }, - 'orderedItems': { - '@id': 'as:items', - '@type': '@id', - '@container': '@list', - }, - 'last': { - '@id': 'as:last', - '@type': '@id', - }, - 'location': { - '@id': 'as:location', - '@type': '@id', - }, - 'next': { - '@id': 'as:next', - '@type': '@id', - }, - 'object': { - '@id': 'as:object', - '@type': '@id', - }, - 'oneOf': { - '@id': 'as:oneOf', - '@type': '@id', - }, - 'anyOf': { - '@id': 'as:anyOf', - '@type': '@id', - }, - 'closed': { - '@id': 'as:closed', - '@type': 'xsd:dateTime', - }, - 'origin': { - '@id': 'as:origin', - '@type': '@id', - }, - 'accuracy': { - '@id': 'as:accuracy', - '@type': 'xsd:float', - }, - 'prev': { - '@id': 'as:prev', - '@type': '@id', - }, - 'preview': { - '@id': 'as:preview', - '@type': '@id', - }, - 'replies': { - '@id': 'as:replies', - '@type': '@id', - }, - 'result': { - '@id': 'as:result', - '@type': '@id', - }, - 'audience': { - '@id': 'as:audience', - '@type': '@id', - }, - 'partOf': { - '@id': 'as:partOf', - '@type': '@id', - }, - 'tag': { - '@id': 'as:tag', - '@type': '@id', - }, - 'target': { - '@id': 'as:target', - '@type': '@id', - }, - 'to': { - '@id': 'as:to', - '@type': '@id', - }, - 'url': { - '@id': 'as:url', - '@type': '@id', - }, - 'altitude': { - '@id': 'as:altitude', - '@type': 'xsd:float', - }, - 'content': 'as:content', - 'contentMap': { - '@id': 'as:content', - '@container': '@language', - }, - 'name': 'as:name', - 'nameMap': { - '@id': 'as:name', - '@container': '@language', - }, - 'duration': { - '@id': 'as:duration', - '@type': 'xsd:duration', - }, - 'endTime': { - '@id': 'as:endTime', - '@type': 'xsd:dateTime', - }, - 'height': { - '@id': 'as:height', - '@type': 'xsd:nonNegativeInteger', - }, - 'href': { - '@id': 'as:href', - '@type': '@id', - }, - 'hreflang': 'as:hreflang', - 'latitude': { - '@id': 'as:latitude', - '@type': 'xsd:float', - }, - 'longitude': { - '@id': 'as:longitude', - '@type': 'xsd:float', - }, - 'mediaType': 'as:mediaType', - 'published': { - '@id': 'as:published', - '@type': 'xsd:dateTime', - }, - 'radius': { - '@id': 'as:radius', - '@type': 'xsd:float', - }, - 'rel': 'as:rel', - 'startIndex': { - '@id': 'as:startIndex', - '@type': 'xsd:nonNegativeInteger', - }, - 'startTime': { - '@id': 'as:startTime', - '@type': 'xsd:dateTime', - }, - 'summary': 'as:summary', - 'summaryMap': { - '@id': 'as:summary', - '@container': '@language', - }, - 'totalItems': { - '@id': 'as:totalItems', - '@type': 'xsd:nonNegativeInteger', - }, - 'units': 'as:units', - 'updated': { - '@id': 'as:updated', - '@type': 'xsd:dateTime', - }, - 'width': { - '@id': 'as:width', - '@type': 'xsd:nonNegativeInteger', - }, - 'describes': { - '@id': 'as:describes', - '@type': '@id', - }, - 'formerType': { - '@id': 'as:formerType', - '@type': '@id', - }, - 'deleted': { - '@id': 'as:deleted', - '@type': 'xsd:dateTime', - }, - 'inbox': { - '@id': 'ldp:inbox', - '@type': '@id', - }, - 'outbox': { - '@id': 'as:outbox', - '@type': '@id', - }, - 'following': { - '@id': 'as:following', - '@type': '@id', - }, - 'followers': { - '@id': 'as:followers', - '@type': '@id', - }, - 'streams': { - '@id': 'as:streams', - '@type': '@id', - }, - 'preferredUsername': 'as:preferredUsername', - 'endpoints': { - '@id': 'as:endpoints', - '@type': '@id', - }, - 'uploadMedia': { - '@id': 'as:uploadMedia', - '@type': '@id', - }, - 'proxyUrl': { - '@id': 'as:proxyUrl', - '@type': '@id', - }, - 'liked': { - '@id': 'as:liked', - '@type': '@id', - }, - 'oauthAuthorizationEndpoint': { - '@id': 'as:oauthAuthorizationEndpoint', - '@type': '@id', - }, - 'oauthTokenEndpoint': { - '@id': 'as:oauthTokenEndpoint', - '@type': '@id', - }, - 'provideClientKey': { - '@id': 'as:provideClientKey', - '@type': '@id', - }, - 'signClientKey': { - '@id': 'as:signClientKey', - '@type': '@id', - }, - 'sharedInbox': { - '@id': 'as:sharedInbox', - '@type': '@id', - }, - 'Public': { - '@id': 'as:Public', - '@type': '@id', - }, - 'source': 'as:source', - 'likes': { - '@id': 'as:likes', - '@type': '@id', - }, - 'shares': { - '@id': 'as:shares', - '@type': '@id', - }, - 'alsoKnownAs': { - '@id': 'as:alsoKnownAs', - '@type': '@id', - }, - }, -}; - -export const CONTEXTS: Record = { - 'https://w3id.org/identity/v1': id_v1, - 'https://w3id.org/security/v1': security_v1, - 'https://www.w3.org/ns/activitystreams': activitystreams, -}; diff --git a/packages/backend/src/core/remote/activitypub/models/ApImageService.ts b/packages/backend/src/core/remote/activitypub/models/ApImageService.ts deleted file mode 100644 index 9bf87f19d4..0000000000 --- a/packages/backend/src/core/remote/activitypub/models/ApImageService.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { DI } from '@/di-symbols.js'; -import type { DriveFilesRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; -import type { CacheableRemoteUser } from '@/models/entities/User.js'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; -import { MetaService } from '@/core/MetaService.js'; -import { truncate } from '@/misc/truncate.js'; -import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js'; -import { DriveService } from '@/core/DriveService.js'; -import type Logger from '@/logger.js'; -import { ApResolverService } from '../ApResolverService.js'; -import { ApLoggerService } from '../ApLoggerService.js'; - -@Injectable() -export class ApImageService { - private logger: Logger; - - constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.driveFilesRepository) - private driveFilesRepository: DriveFilesRepository, - - 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/core/remote/activitypub/models/ApMentionService.ts b/packages/backend/src/core/remote/activitypub/models/ApMentionService.ts deleted file mode 100644 index 1275e24c62..0000000000 --- a/packages/backend/src/core/remote/activitypub/models/ApMentionService.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import promiseLimit from 'promise-limit'; -import { DI } from '@/di-symbols.js'; -import type { UsersRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; -import { toArray, unique } from '@/misc/prelude/array.js'; -import type { CacheableUser } from '@/models/entities/User.js'; -import { isMention } from '../type.js'; -import { ApResolverService, Resolver } from '../ApResolverService.js'; -import { ApPersonService } from './ApPersonService.js'; -import type { IObject, IApMention } from '../type.js'; - -@Injectable() -export class ApMentionService { - constructor( - @Inject(DI.config) - private config: Config, - - private apResolverService: ApResolverService, - private apPersonService: ApPersonService, - ) { - } - - public async extractApMentions(tags: IObject | IObject[] | null | undefined, resolver: Resolver) { - const hrefs = unique(this.extractApMentionObjects(tags).map(x => x.href as string)); - - const limit = promiseLimit(2); - const mentionedUsers = (await Promise.all( - hrefs.map(x => limit(() => this.apPersonService.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/core/remote/activitypub/models/ApNoteService.ts b/packages/backend/src/core/remote/activitypub/models/ApNoteService.ts deleted file mode 100644 index 7cf6725a38..0000000000 --- a/packages/backend/src/core/remote/activitypub/models/ApNoteService.ts +++ /dev/null @@ -1,403 +0,0 @@ -import { forwardRef, Inject, Injectable } from '@nestjs/common'; -import promiseLimit from 'promise-limit'; -import { DI } from '@/di-symbols.js'; -import type { MessagingMessagesRepository, PollsRepository, EmojisRepository } from '@/models/index.js'; -import type { UsersRepository } from '@/models/index.js'; -import type { Config } from '@/config.js'; -import type { CacheableRemoteUser } from '@/models/entities/User.js'; -import type { Note } from '@/models/entities/Note.js'; -import { toArray, toSingle, unique } from '@/misc/prelude/array.js'; -import type { Emoji } from '@/models/entities/Emoji.js'; -import { MetaService } from '@/core/MetaService.js'; -import { AppLockService } from '@/core/AppLockService.js'; -import type { DriveFile } from '@/models/entities/DriveFile.js'; -import { NoteCreateService } from '@/core/NoteCreateService.js'; -import type Logger from '@/logger.js'; -import { IdService } from '@/core/IdService.js'; -import { PollService } from '@/core/PollService.js'; -import { StatusError } from '@/misc/status-error.js'; -import { UtilityService } from '@/core/UtilityService.js'; -import { MessagingService } from '@/core/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'; -import { ApQuestionService } from './ApQuestionService.js'; -import { ApImageService } from './ApImageService.js'; -import type { Resolver } from '../ApResolverService.js'; -import type { IObject, IPost } from '../type.js'; - -@Injectable() -export class ApNoteService { - private logger: Logger; - - constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.pollsRepository) - private pollsRepository: PollsRepository, - - @Inject(DI.emojisRepository) - private emojisRepository: EmojisRepository, - - @Inject(DI.messagingMessagesRepository) - private messagingMessagesRepository: MessagingMessagesRepository, - - private idService: IdService, - private apMfmService: ApMfmService, - private apResolverService: ApResolverService, - - // 循環参照のため / for circular dependency - @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, - private apDbResolverService: ApDbResolverService, - private apLoggerService: ApLoggerService, - ) { - this.logger = this.apLoggerService.logger; - } - - public validateNote(object: any, uri: string) { - const expectHost = this.utilityService.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 && 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 && 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; - } - - /** - * Noteをフェッチします。 - * - * Misskeyに対象のNoteが登録されていればそれを返します。 - */ - public async fetchNote(object: string | IObject): Promise { - return await this.apDbResolverService.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) { - this.logger.error(`${err.message}`, { - resolver: { - history: resolver.getHistory(), - }, - value: value, - object: object, - }); - throw new Error('invalid note'); - } - - const note: IPost = object; - - this.logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`); - - this.logger.info(`Creating the Note: ${note.id}`); - - // 投稿者をフェッチ - const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as CacheableRemoteUser; - - // 投稿者が凍結されていたらスキップ - if (actor.isSuspended) { - throw new Error('actor has been suspended'); - } - - const noteAudience = await this.apAudienceService.parseAudience(actor, note.to, note.cc, resolver); - 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 isMessaging = note._misskey_talk && visibility === 'specified'; - - const apMentions = await this.apMentionService.extractApMentions(note.tag, resolver); - 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(() => this.apImageService.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) { - this.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 this.messagingMessagesRepository.findOneBy({ id }); - if (talk) { - isMessaging = true; - return null; - } - } - - this.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 this.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.apMfmService.htmlToMfm(note.content, note.tag); - } - - // vote - if (reply && reply.hasPoll) { - 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()) { - 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 this.pollService.vote(actor, reply, index); - - // リモートフォロワーにUpdate配信 - this.pollService.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 => { - this.logger.info(`extractEmojis: ${e}`); - return [] as Emoji[]; - }); - - const apEmojis = emojis.map(emoji => emoji.name); - - const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined); - - 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); - 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(this.utilityService.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 = this.utilityService.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 this.emojisRepository.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 this.emojisRepository.update({ - host, - name, - }, { - uri: tag.id, - originalUrl: tag.icon!.url, - publicUrl: tag.icon!.url, - updatedAt: new Date(), - }); - - return await this.emojisRepository.findOneBy({ - host, - name, - }) as Emoji; - } - - return exists; - } - - this.logger.info(`register emoji host=${host}, name=${name}`); - - return await this.emojisRepository.insert({ - id: this.idService.genId(), - host, - name, - uri: tag.id, - originalUrl: tag.icon!.url, - publicUrl: tag.icon!.url, - updatedAt: new Date(), - aliases: [], - } as Partial).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0])); - })); - } -} diff --git a/packages/backend/src/core/remote/activitypub/models/ApPersonService.ts b/packages/backend/src/core/remote/activitypub/models/ApPersonService.ts deleted file mode 100644 index f9d6f42ef6..0000000000 --- a/packages/backend/src/core/remote/activitypub/models/ApPersonService.ts +++ /dev/null @@ -1,594 +0,0 @@ -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 { FollowingsRepository, InstancesRepository, UserProfilesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js'; -import type { 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 type { UserCacheService } from '@/core/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 '@/core/IdService.js'; -import type { MfmService } from '@/core/MfmService.js'; -import type { Emoji } from '@/models/entities/Emoji.js'; -import { toArray } from '@/misc/prelude/array.js'; -import type { GlobalEventService } from '@/core/GlobalEventService.js'; -import type { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; -import type { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataService.js'; -import { UserProfile } from '@/models/entities/UserProfile.js'; -import { UserPublickey } from '@/models/entities/UserPublickey.js'; -import type UsersChart from '@/core/chart/charts/users.js'; -import type InstanceChart from '@/core/chart/charts/instance.js'; -import type { HashtagService } from '@/core/HashtagService.js'; -import { UserNotePining } from '@/models/entities/UserNotePining.js'; -import { StatusError } from '@/misc/status-error.js'; -import type { UtilityService } from '@/core/UtilityService.js'; -import type { UserEntityService } from '@/core/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'; -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'; - -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); - } -} - -@Injectable() -export class ApPersonService implements OnModuleInit { - 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; - private logger: Logger; - - constructor( - private moduleRef: ModuleRef, - - @Inject(DI.config) - private config: Config, - - @Inject(DI.db) - private db: DataSource, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.userProfilesRepository) - private userProfilesRepository: UserProfilesRepository, - - @Inject(DI.userPublickeysRepository) - private userPublickeysRepository: UserPublickeysRepository, - - @Inject(DI.instancesRepository) - private instancesRepository: InstancesRepository, - - @Inject(DI.followingsRepository) - private followingsRepository: FollowingsRepository, - - //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, - ) { - } - - onModuleInit() { - 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; - } - - /** - * Validate and convert to actor object - * @param x Fetched object - * @param uri Fetch target URI - */ - private 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をフェッチします。 - * - * 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 this.usersRepository.findOneBy({ id }); - if (u) this.userCacheService.uriPersonCache.set(uri, u); - return u; - } - - //#region このサーバーに既に登録されていたらそれを返す - const exist = await this.usersRepository.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 = this.validateActor(object, uri); - - this.logger.info(`Creating the Person: ${person.id}`); - - const host = this.utilityService.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 this.usersRepository.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 => { - this.instancesRepository.increment({ id: i.id }, 'usersCount', 1); - this.instanceChart.newUser(i.host); - this.fetchInstanceMetadataService.fetchInstanceMetadata(i); - }); - - this.usersChart.update(user!, true); - - // ハッシュタグ更新 - this.hashtagService.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 this.usersRepository.update(user!.id, { - avatarId, - bannerId, - }); - - user!.avatarId = avatarId; - user!.bannerId = bannerId; - //#endregion - - //#region カスタム絵文字取得 - const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host).catch(err => { - this.logger.info(`extractEmojis: ${err}`); - return [] as Emoji[]; - }); - - const emojiNames = emojis.map(emoji => emoji.name); - - await this.usersRepository.update(user!.id, { - emojis: emojiNames, - }); - //#endregion - - await this.updateFeatured(user!.id, resolver).catch(err => this.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 this.usersRepository.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 = this.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 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 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 this.usersRepository.update(exist.id, updates); - - if (person.publicKey) { - await this.userPublickeysRepository.update({ userId: exist.id }, { - keyId: person.publicKey.id, - keyPem: person.publicKey.publicKeyPem, - }); - } - - 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, - birthday: bday ? bday[0] : null, - location: person['vcard:Address'] ?? null, - }); - - this.globalEventService.publishInternalEvent('remoteUserUpdated', { id: exist.id }); - - // ハッシュタグ更新 - this.hashtagService.updateUsertags(exist, tags); - - // 該当ユーザーが既にフォロワーになっていた場合はFollowingもアップデートする - await this.followingsRepository.update({ - followerId: exist.id, - }, { - followerSharedInbox: person.sharedInbox ?? (person.endpoints ? person.endpoints.sharedInbox : undefined), - }); - - await this.updateFeatured(exist.id, resolver).catch(err => this.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'], resolver?: Resolver) { - const user = await this.usersRepository.findOneByOrFail({ id: userId }); - if (!this.userEntityService.isRemoteUser(user)) return; - if (!user.featured) return; - - this.logger.info(`Updating the featured: ${user.uri}`); - - if (resolver == null) 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(() => this.apNoteService.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/core/remote/activitypub/models/ApQuestionService.ts b/packages/backend/src/core/remote/activitypub/models/ApQuestionService.ts deleted file mode 100644 index 5793b98353..0000000000 --- a/packages/backend/src/core/remote/activitypub/models/ApQuestionService.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { DI } from '@/di-symbols.js'; -import type { NotesRepository, PollsRepository } from '@/models/index.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'; -import { ApLoggerService } from '../ApLoggerService.js'; -import { ApResolverService } from '../ApResolverService.js'; -import type { Resolver } from '../ApResolverService.js'; -import type { IObject, IQuestion } from '../type.js'; - -@Injectable() -export class ApQuestionService { - private logger: Logger; - - constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - - @Inject(DI.pollsRepository) - private pollsRepository: PollsRepository, - - 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, resolver?: Resolver) { - 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 - if (resolver == null) 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/core/remote/activitypub/models/icon.ts b/packages/backend/src/core/remote/activitypub/models/icon.ts deleted file mode 100644 index 50794a937d..0000000000 --- a/packages/backend/src/core/remote/activitypub/models/icon.ts +++ /dev/null @@ -1,5 +0,0 @@ -export type IIcon = { - type: string; - mediaType?: string; - url?: string; -}; diff --git a/packages/backend/src/core/remote/activitypub/models/identifier.ts b/packages/backend/src/core/remote/activitypub/models/identifier.ts deleted file mode 100644 index f6c3bb8c88..0000000000 --- a/packages/backend/src/core/remote/activitypub/models/identifier.ts +++ /dev/null @@ -1,5 +0,0 @@ -export type IIdentifier = { - type: string; - name: string; - value: string; -}; diff --git a/packages/backend/src/core/remote/activitypub/models/tag.ts b/packages/backend/src/core/remote/activitypub/models/tag.ts deleted file mode 100644 index 803846a0b0..0000000000 --- a/packages/backend/src/core/remote/activitypub/models/tag.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { toArray } from '@/misc/prelude/array.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 []; - - const hashtags = extractApHashtagObjects(tags); - - return hashtags.map(tag => { - const m = tag.name.match(/^#(.+)/); - return m ? m[1] : null; - }).filter((x): x is string => x != null); -} - -export function extractApHashtagObjects(tags: IObject | IObject[] | null | undefined): IApHashtag[] { - if (tags == null) return []; - return toArray(tags).filter(isHashtag); -} diff --git a/packages/backend/src/core/remote/activitypub/type.ts b/packages/backend/src/core/remote/activitypub/type.ts deleted file mode 100644 index dcc5110aa5..0000000000 --- a/packages/backend/src/core/remote/activitypub/type.ts +++ /dev/null @@ -1,296 +0,0 @@ -export type obj = { [x: string]: any }; -export type ApObject = IObject | string | (IObject | string)[]; - -export interface IObject { - '@context': string | string[] | obj | obj[]; - type: string | string[]; - id?: string; - summary?: string; - published?: string; - cc?: ApObject; - to?: ApObject; - attributedTo: ApObject; - attachment?: any[]; - inReplyTo?: any; - replies?: ICollection; - content?: string; - name?: string; - startTime?: Date; - endTime?: Date; - icon?: any; - image?: any; - url?: ApObject; - href?: string; - tag?: IObject | IObject[]; - sensitive?: boolean; -} - -/** - * Get array of ActivityStreams Objects id - */ -export function getApIds(value: ApObject | undefined): string[] { - if (value == null) return []; - const array = Array.isArray(value) ? value : [value]; - return array.map(x => getApId(x)); -} - -/** - * Get first ActivityStreams Object id - */ -export function getOneApId(value: ApObject): string { - const firstOne = Array.isArray(value) ? value[0] : value; - return getApId(firstOne); -} - -/** - * Get ActivityStreams Object id - */ -export function getApId(value: string | IObject): string { - if (typeof value === 'string') return value; - if (typeof value.id === 'string') return value.id; - throw new Error('cannot detemine id'); -} - -/** - * Get ActivityStreams Object type - */ -export function getApType(value: IObject): string { - if (typeof value.type === 'string') return value.type; - if (Array.isArray(value.type) && typeof value.type[0] === 'string') return value.type[0]; - throw new Error('cannot detect type'); -} - -export function getOneApHrefNullable(value: ApObject | undefined): string | undefined { - const firstOne = Array.isArray(value) ? value[0] : value; - return getApHrefNullable(firstOne); -} - -export function getApHrefNullable(value: string | IObject | undefined): string | undefined { - if (typeof value === 'string') return value; - if (typeof value?.href === 'string') return value.href; - return undefined; -} - -export interface IActivity extends IObject { - //type: 'Activity'; - actor: IObject | string; - object: IObject | string; - target?: IObject | string; - /** LD-Signature */ - signature?: { - type: string; - created: Date; - creator: string; - domain?: string; - nonce?: string; - signatureValue: string; - }; -} - -export interface ICollection extends IObject { - type: 'Collection'; - totalItems: number; - items: ApObject; -} - -export interface IOrderedCollection extends IObject { - type: 'OrderedCollection'; - totalItems: number; - orderedItems: ApObject; -} - -export const validPost = ['Note', 'Question', 'Article', 'Audio', 'Document', 'Image', 'Page', 'Video', 'Event']; - -export const isPost = (object: IObject): object is IPost => - validPost.includes(getApType(object)); - -export interface IPost extends IObject { - type: 'Note' | 'Question' | 'Article' | 'Audio' | 'Document' | 'Image' | 'Page' | 'Video' | 'Event'; - source?: { - content: string; - mediaType: string; - }; - _misskey_quote?: string; - _misskey_content?: string; - quoteUrl?: string; - _misskey_talk?: boolean; -} - -export interface IQuestion extends IObject { - type: 'Note' | 'Question'; - source?: { - content: string; - mediaType: string; - }; - _misskey_quote?: string; - quoteUrl?: string; - oneOf?: IQuestionChoice[]; - anyOf?: IQuestionChoice[]; - endTime?: Date; - closed?: Date; -} - -export const isQuestion = (object: IObject): object is IQuestion => - getApType(object) === 'Note' || getApType(object) === 'Question'; - -interface IQuestionChoice { - name?: string; - replies?: ICollection; - _misskey_votes?: number; -} -export interface ITombstone extends IObject { - type: 'Tombstone'; - formerType?: string; - deleted?: Date; -} - -export const isTombstone = (object: IObject): object is ITombstone => - getApType(object) === 'Tombstone'; - -export const validActor = ['Person', 'Service', 'Group', 'Organization', 'Application']; - -export const isActor = (object: IObject): object is IActor => - validActor.includes(getApType(object)); - -export interface IActor extends IObject { - type: 'Person' | 'Service' | 'Organization' | 'Group' | 'Application'; - name?: string; - preferredUsername?: string; - manuallyApprovesFollowers?: boolean; - discoverable?: boolean; - inbox: string; - sharedInbox?: string; // 後方互換性のため - publicKey?: { - id: string; - publicKeyPem: string; - }; - followers?: string | ICollection | IOrderedCollection; - following?: string | ICollection | IOrderedCollection; - featured?: string | IOrderedCollection; - outbox: string | IOrderedCollection; - endpoints?: { - sharedInbox?: string; - }; - 'vcard:bday'?: string; - 'vcard:Address'?: string; -} - -export const isCollection = (object: IObject): object is ICollection => - getApType(object) === 'Collection'; - -export const isOrderedCollection = (object: IObject): object is IOrderedCollection => - getApType(object) === 'OrderedCollection'; - -export const isCollectionOrOrderedCollection = (object: IObject): object is ICollection | IOrderedCollection => - isCollection(object) || isOrderedCollection(object); - -export interface IApPropertyValue extends IObject { - type: 'PropertyValue'; - identifier: IApPropertyValue; - name: string; - value: string; -} - -export const isPropertyValue = (object: IObject): object is IApPropertyValue => - object && - getApType(object) === 'PropertyValue' && - typeof object.name === 'string' && - typeof (object as any).value === 'string'; - -export interface IApMention extends IObject { - type: 'Mention'; - href: string; -} - -export const isMention = (object: IObject): object is IApMention => - getApType(object) === 'Mention' && - typeof object.href === 'string'; - -export interface IApHashtag extends IObject { - type: 'Hashtag'; - name: string; -} - -export const isHashtag = (object: IObject): object is IApHashtag => - getApType(object) === 'Hashtag' && - typeof object.name === 'string'; - -export interface IApEmoji extends IObject { - type: 'Emoji'; - updated: Date; -} - -export const isEmoji = (object: IObject): object is IApEmoji => - getApType(object) === 'Emoji' && !Array.isArray(object.icon) && object.icon.url != null; - -export interface ICreate extends IActivity { - type: 'Create'; -} - -export interface IDelete extends IActivity { - type: 'Delete'; -} - -export interface IUpdate extends IActivity { - type: 'Update'; -} - -export interface IRead extends IActivity { - type: 'Read'; -} - -export interface IUndo extends IActivity { - type: 'Undo'; -} - -export interface IFollow extends IActivity { - type: 'Follow'; -} - -export interface IAccept extends IActivity { - type: 'Accept'; -} - -export interface IReject extends IActivity { - type: 'Reject'; -} - -export interface IAdd extends IActivity { - type: 'Add'; -} - -export interface IRemove extends IActivity { - type: 'Remove'; -} - -export interface ILike extends IActivity { - type: 'Like' | 'EmojiReaction' | 'EmojiReact'; - _misskey_reaction?: string; -} - -export interface IAnnounce extends IActivity { - type: 'Announce'; -} - -export interface IBlock extends IActivity { - type: 'Block'; -} - -export interface IFlag extends IActivity { - type: 'Flag'; -} - -export const isCreate = (object: IObject): object is ICreate => getApType(object) === 'Create'; -export const isDelete = (object: IObject): object is IDelete => getApType(object) === 'Delete'; -export const isUpdate = (object: IObject): object is IUpdate => getApType(object) === 'Update'; -export const isRead = (object: IObject): object is IRead => getApType(object) === 'Read'; -export const isUndo = (object: IObject): object is IUndo => getApType(object) === 'Undo'; -export const isFollow = (object: IObject): object is IFollow => getApType(object) === 'Follow'; -export const isAccept = (object: IObject): object is IAccept => getApType(object) === 'Accept'; -export const isReject = (object: IObject): object is IReject => getApType(object) === 'Reject'; -export const isAdd = (object: IObject): object is IAdd => getApType(object) === 'Add'; -export const isRemove = (object: IObject): object is IRemove => getApType(object) === 'Remove'; -export const isLike = (object: IObject): object is ILike => getApType(object) === 'Like' || getApType(object) === 'EmojiReaction' || getApType(object) === 'EmojiReact'; -export const isAnnounce = (object: IObject): object is IAnnounce => getApType(object) === 'Announce'; -export const isBlock = (object: IObject): object is IBlock => getApType(object) === 'Block'; -export const isFlag = (object: IObject): object is IFlag => getApType(object) === 'Flag'; diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts index 9042a21d2c..2a4b201a7d 100644 --- a/packages/backend/src/queue/processors/DeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts @@ -5,7 +5,7 @@ import type { DriveFilesRepository, InstancesRepository } from '@/models/index.j import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { MetaService } from '@/core/MetaService.js'; -import { ApRequestService } from '@/core/remote/activitypub/ApRequestService.js'; +import { ApRequestService } from '@/core/activitypub/ApRequestService.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataService.js'; import { Cache } from '@/misc/cache.js'; diff --git a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts index 6c6789cff4..9442a60d8d 100644 --- a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts @@ -5,7 +5,7 @@ import type { UsersRepository, BlockingsRepository, DriveFilesRepository } from import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import * as Acct from '@/misc/acct.js'; -import { ResolveUserService } from '@/core/remote/ResolveUserService.js'; +import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; import { DownloadService } from '@/core/DownloadService.js'; import { UtilityService } from '@/core/UtilityService.js'; @@ -32,7 +32,7 @@ export class ImportBlockingProcessorService { private utilityService: UtilityService, private userBlockingService: UserBlockingService, - private resolveUserService: ResolveUserService, + private remoteUserResolveService: RemoteUserResolveService, private downloadService: DownloadService, private queueLoggerService: QueueLoggerService, ) { @@ -78,7 +78,7 @@ export class ImportBlockingProcessorService { if (host == null && target == null) continue; if (target == null) { - target = await this.resolveUserService.resolveUser(username, host); + target = await this.remoteUserResolveService.resolveUser(username, host); } if (target == null) { diff --git a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts index 9f2e5e801a..667f7279fb 100644 --- a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts @@ -5,7 +5,7 @@ import type { UsersRepository, DriveFilesRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import * as Acct from '@/misc/acct.js'; -import { ResolveUserService } from '@/core/remote/ResolveUserService.js'; +import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js'; import { DownloadService } from '@/core/DownloadService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; import { UtilityService } from '@/core/UtilityService.js'; @@ -29,7 +29,7 @@ export class ImportFollowingProcessorService { private utilityService: UtilityService, private userFollowingService: UserFollowingService, - private resolveUserService: ResolveUserService, + private remoteUserResolveService: RemoteUserResolveService, private downloadService: DownloadService, private queueLoggerService: QueueLoggerService, ) { @@ -75,7 +75,7 @@ export class ImportFollowingProcessorService { if (host == null && target == null) continue; if (target == null) { - target = await this.resolveUserService.resolveUser(username, host); + target = await this.remoteUserResolveService.resolveUser(username, host); } if (target == null) { diff --git a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts index 62ad3b5c88..f3c16e73d5 100644 --- a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts @@ -5,7 +5,7 @@ import type { UsersRepository, DriveFilesRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import * as Acct from '@/misc/acct.js'; -import { ResolveUserService } from '@/core/remote/ResolveUserService.js'; +import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js'; import { DownloadService } from '@/core/DownloadService.js'; import { UserMutingService } from '@/core/UserMutingService.js'; import { UtilityService } from '@/core/UtilityService.js'; @@ -29,7 +29,7 @@ export class ImportMutingProcessorService { private utilityService: UtilityService, private userMutingService: UserMutingService, - private resolveUserService: ResolveUserService, + private remoteUserResolveService: RemoteUserResolveService, private downloadService: DownloadService, private queueLoggerService: QueueLoggerService, ) { @@ -75,7 +75,7 @@ export class ImportMutingProcessorService { if (host == null && target == null) continue; if (target == null) { - target = await this.resolveUserService.resolveUser(username, host); + target = await this.remoteUserResolveService.resolveUser(username, host); } if (target == null) { diff --git a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts index f214d59e1c..1519877c5f 100644 --- a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts @@ -5,7 +5,7 @@ import type { UsersRepository, DriveFilesRepository, UserListJoiningsRepository, import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import * as Acct from '@/misc/acct.js'; -import { ResolveUserService } from '@/core/remote/ResolveUserService.js'; +import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js'; import { DownloadService } from '@/core/DownloadService.js'; import { UserListService } from '@/core/UserListService.js'; import { IdService } from '@/core/IdService.js'; @@ -37,7 +37,7 @@ export class ImportUserListsProcessorService { private utilityService: UtilityService, private idService: IdService, private userListService: UserListService, - private resolveUserService: ResolveUserService, + private remoteUserResolveService: RemoteUserResolveService, private downloadService: DownloadService, private queueLoggerService: QueueLoggerService, ) { @@ -95,7 +95,7 @@ export class ImportUserListsProcessorService { }); if (target == null) { - target = await this.resolveUserService.resolveUser(username, host); + target = await this.remoteUserResolveService.resolveUser(username, host); } if (await this.userListJoiningsRepository.findOneBy({ userListId: list!.id, userId: target.id }) != null) continue; diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index e8cd7dcaca..8f1c474020 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -7,7 +7,7 @@ import type { InstancesRepository, DriveFilesRepository } from '@/models/index.j import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { MetaService } from '@/core/MetaService.js'; -import { ApRequestService } from '@/core/remote/activitypub/ApRequestService.js'; +import { ApRequestService } from '@/core/activitypub/ApRequestService.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataService.js'; import { Cache } from '@/misc/cache.js'; @@ -15,15 +15,15 @@ import type { Instance } from '@/models/entities/Instance.js'; import InstanceChart from '@/core/chart/charts/instance.js'; import ApRequestChart from '@/core/chart/charts/ap-request.js'; import FederationChart from '@/core/chart/charts/federation.js'; -import { getApId } from '@/core/remote/activitypub/type.js'; +import { getApId } from '@/core/activitypub/type.js'; import type { CacheableRemoteUser } from '@/models/entities/User.js'; import type { UserPublickey } from '@/models/entities/UserPublickey.js'; -import { ApDbResolverService } from '@/core/remote/activitypub/ApDbResolverService.js'; +import { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js'; import { StatusError } from '@/misc/status-error.js'; import { UtilityService } from '@/core/UtilityService.js'; -import { ApPersonService } from '@/core/remote/activitypub/models/ApPersonService.js'; -import { LdSignatureService } from '@/core/remote/activitypub/LdSignatureService.js'; -import { ApInboxService } from '@/core/remote/activitypub/ApInboxService.js'; +import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; +import { LdSignatureService } from '@/core/activitypub/LdSignatureService.js'; +import { ApInboxService } from '@/core/activitypub/ApInboxService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DeliverJobData, InboxJobData } from '../types.js'; diff --git a/packages/backend/src/queue/types.ts b/packages/backend/src/queue/types.ts index 18ec997a1b..1214c9eb95 100644 --- a/packages/backend/src/queue/types.ts +++ b/packages/backend/src/queue/types.ts @@ -2,7 +2,7 @@ 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 '@/core/remote/activitypub/type.js'; +import type { IActivity } from '@/core/activitypub/type.js'; import type httpSignature from '@peertube/http-signature'; export type DeliverJobData = { diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 9689a623fd..0ce2c9cd97 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -8,7 +8,7 @@ import { DI } from '@/di-symbols.js'; import type { FollowingsRepository, NotesRepository, EmojisRepository, NoteReactionsRepository, UserProfilesRepository, UserNotePiningsRepository, UsersRepository } from '@/models/index.js'; import * as url from '@/misc/prelude/url.js'; import type { Config } from '@/config.js'; -import { ApRendererService } from '@/core/remote/activitypub/ApRendererService.js'; +import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { QueueService } from '@/core/QueueService.js'; import type { ILocalUser, User } from '@/models/entities/User.js'; import { UserKeypairStoreService } from '@/core/UserKeypairStoreService.js'; 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 526efa9f9d..9442bda5eb 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 type { DeliverQueue } from '@/core/queue/QueueModule.js'; +import type { DeliverQueue } from '@/core/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 b8934428c3..55a3410d49 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 type { InboxQueue } from '@/core/queue/QueueModule.js'; +import type { InboxQueue } from '@/core/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 605ea3d042..7f3732c970 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 type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from '@/core/queue/QueueModule.js'; +import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from '@/core/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 a6e59276fb..cdaec13a3f 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 @@ -3,7 +3,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UsersRepository, AbuseUserReportsRepository } from '@/models/index.js'; import { InstanceActorService } from '@/core/InstanceActorService.js'; import { QueueService } from '@/core/QueueService.js'; -import { ApRendererService } from '@/core/remote/activitypub/ApRendererService.js'; +import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { DI } from '@/di-symbols.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/ap/get.ts b/packages/backend/src/server/api/endpoints/ap/get.ts index 3d4c85e50b..8bafb3b122 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 { ApResolverService } from '@/core/remote/activitypub/ApResolverService.js'; +import { ApResolverService } from '@/core/activitypub/ApResolverService.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 0a5fc31751..c218ec4642 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -4,13 +4,13 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UsersRepository, NotesRepository } 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 '@/core/remote/activitypub/type.js'; +import { isActor, isPost, getApId } from '@/core/activitypub/type.js'; import type { SchemaType } from '@/misc/schema.js'; -import { ApResolverService } from '@/core/remote/activitypub/ApResolverService.js'; -import { ApDbResolverService } from '@/core/remote/activitypub/ApDbResolverService.js'; +import { ApResolverService } from '@/core/activitypub/ApResolverService.js'; +import { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js'; import { MetaService } from '@/core/MetaService.js'; -import { ApPersonService } from '@/core/remote/activitypub/models/ApPersonService.js'; -import { ApNoteService } from '@/core/remote/activitypub/models/ApNoteService.js'; +import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; +import { ApNoteService } from '@/core/activitypub/models/ApNoteService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { UtilityService } from '@/core/UtilityService.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 30e77aab45..c19252f198 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 { ApPersonService } from '@/core/remote/activitypub/models/ApPersonService.js'; +import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; import { GetterService } from '@/server/api/GetterService.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 6b3b062c15..793d7c5408 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -7,7 +7,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { GetterService } from '@/server/api/GetterService.js'; import { QueueService } from '@/core/QueueService.js'; import { PollService } from '@/core/PollService.js'; -import { ApRendererService } from '@/core/remote/activitypub/ApRendererService.js'; +import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { CreateNotificationService } from '@/core/CreateNotificationService.js'; import { DI } from '@/di-symbols.js'; diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts index 1e15025bf4..7932d5cd12 100644 --- a/packages/backend/src/server/api/endpoints/users/show.ts +++ b/packages/backend/src/server/api/endpoints/users/show.ts @@ -4,7 +4,7 @@ import type { UsersRepository } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { ResolveUserService } from '@/core/remote/ResolveUserService.js'; +import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; import { ApiLoggerService } from '../../ApiLoggerService.js'; @@ -89,7 +89,7 @@ export default class extends Endpoint { private usersRepository: UsersRepository, private userEntityService: UserEntityService, - private resolveUserService: ResolveUserService, + private remoteUserResolveService: RemoteUserResolveService, private apiLoggerService: ApiLoggerService, ) { super(meta, paramDef, async (ps, me) => { @@ -121,7 +121,7 @@ export default class extends Endpoint { } else { // Lookup user if (typeof ps.host === 'string' && typeof ps.username === 'string') { - user = await this.resolveUserService.resolveUser(ps.username, ps.host).catch(err => { + user = await this.remoteUserResolveService.resolveUser(ps.username, ps.host).catch(err => { this.apiLoggerService.logger.warn(`failed to resolve remote user: ${err}`); throw new ApiError(meta.errors.failedToResolveRemoteUser); }); diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 4c3f2bfd36..3fcf8b7c0d 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -14,7 +14,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 '@/core/MetaService.js'; -import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from '@/core/queue/QueueModule.js'; +import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from '@/core/QueueModule.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { PageEntityService } from '@/core/entities/PageEntityService.js'; diff --git a/packages/backend/test/misc/mock-resolver.ts b/packages/backend/test/misc/mock-resolver.ts index ba89ac329a..cad4d8af6c 100644 --- a/packages/backend/test/misc/mock-resolver.ts +++ b/packages/backend/test/misc/mock-resolver.ts @@ -1,5 +1,5 @@ -import Resolver from '../../src/remote/activitypub/resolver.js'; -import { IObject } from '../../src/remote/activitypub/type.js'; +import Resolver from '../../src/activitypub/resolver.js'; +import { IObject } from '../../src/activitypub/type.js'; type MockResponse = { type: string; diff --git a/packages/backend/test/tests/activitypub.ts b/packages/backend/test/tests/activitypub.ts index 6f549ca9c2..08ec0a59ea 100644 --- a/packages/backend/test/tests/activitypub.ts +++ b/packages/backend/test/tests/activitypub.ts @@ -29,7 +29,7 @@ describe('ActivityPub', () => { it('Minimum Actor', async () => { const { MockResolver } = await import('../misc/mock-resolver.js'); - const { createPerson } = await import('../../src/remote/activitypub/models/person.js'); + const { createPerson } = await import('../../src/activitypub/models/person.js'); const resolver = new MockResolver(); resolver._register(actor.id, actor); @@ -43,7 +43,7 @@ describe('ActivityPub', () => { it('Minimum Note', async () => { const { MockResolver } = await import('../misc/mock-resolver.js'); - const { createNote } = await import('../../src/remote/activitypub/models/note.js'); + const { createNote } = await import('../../src/activitypub/models/note.js'); const resolver = new MockResolver(); resolver._register(actor.id, actor); @@ -76,7 +76,7 @@ describe('ActivityPub', () => { it('Actor', async () => { const { MockResolver } = await import('../misc/mock-resolver.js'); - const { createPerson } = await import('../../src/remote/activitypub/models/person.js'); + const { createPerson } = await import('../../src/activitypub/models/person.js'); const resolver = new MockResolver(); resolver._register(actor.id, actor); diff --git a/packages/backend/test/tests/ap-request.ts b/packages/backend/test/tests/ap-request.ts index 299df9be32..d628f03f44 100644 --- a/packages/backend/test/tests/ap-request.ts +++ b/packages/backend/test/tests/ap-request.ts @@ -1,7 +1,7 @@ import * as assert from 'assert'; import httpSignature from '@peertube/http-signature'; import { genRsaKeyPair } from '../../src/misc/gen-key-pair.js'; -import { createSignedPost, createSignedGet } from '../../src/remote/activitypub/ap-request.js'; +import { createSignedPost, createSignedGet } from '../../src/activitypub/ap-request.js'; export const buildParsedSignature = (signingString: string, signature: string, algorithm: string) => { return { diff --git a/packages/backend/test/unit/RelayService.ts b/packages/backend/test/unit/RelayService.ts index bb555648e9..5f87fea7aa 100644 --- a/packages/backend/test/unit/RelayService.ts +++ b/packages/backend/test/unit/RelayService.ts @@ -5,7 +5,7 @@ import { ModuleMocker } from 'jest-mock'; import { Test } from '@nestjs/testing'; import { GlobalModule } from '@/GlobalModule.js'; import { RelayService } from '@/core/RelayService.js'; -import { ApRendererService } from '@/core/remote/activitypub/ApRendererService.js'; +import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { CreateSystemUserService } from '@/core/CreateSystemUserService.js'; import { QueueService } from '@/core/QueueService.js'; import { IdService } from '@/core/IdService.js'; -- cgit v1.2.3-freya From bbb49457f9fb5d46402e913c92ebf77722cad6ff Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 4 Dec 2022 15:03:09 +0900 Subject: refactor: introduce bindThis decorator to bind this automaticaly --- packages/backend/src/core/AccountUpdateService.ts | 2 + packages/backend/src/core/AiService.ts | 3 + packages/backend/src/core/AntennaService.ts | 6 ++ packages/backend/src/core/AppLockService.ts | 4 + packages/backend/src/core/CaptchaService.ts | 5 ++ .../backend/src/core/CreateNotificationService.ts | 4 + .../backend/src/core/CreateSystemUserService.ts | 2 + packages/backend/src/core/CustomEmojiService.ts | 8 ++ packages/backend/src/core/DeleteAccountService.ts | 2 + packages/backend/src/core/DownloadService.ts | 4 + packages/backend/src/core/DriveService.ts | 11 +++ packages/backend/src/core/EmailService.ts | 3 + .../backend/src/core/FederatedInstanceService.ts | 2 + .../src/core/FetchInstanceMetadataService.ts | 10 +++ packages/backend/src/core/FileInfoService.ts | 11 +++ packages/backend/src/core/GlobalEventService.ts | 3 + packages/backend/src/core/HashtagService.ts | 4 + packages/backend/src/core/HttpRequestService.ts | 5 ++ packages/backend/src/core/IdService.ts | 2 + .../backend/src/core/ImageProcessingService.ts | 7 ++ packages/backend/src/core/InstanceActorService.ts | 2 + .../backend/src/core/InternalStorageService.ts | 6 ++ packages/backend/src/core/LoggerService.ts | 2 + packages/backend/src/core/MessagingService.ts | 7 ++ packages/backend/src/core/MetaService.ts | 7 +- packages/backend/src/core/MfmService.ts | 3 + packages/backend/src/core/ModerationLogService.ts | 2 + packages/backend/src/core/NoteCreateService.ts | 13 +++ packages/backend/src/core/NoteDeleteService.ts | 4 + packages/backend/src/core/NotePiningService.ts | 4 + packages/backend/src/core/NoteReadService.ts | 3 + packages/backend/src/core/NotificationService.ts | 5 ++ packages/backend/src/core/PollService.ts | 3 + packages/backend/src/core/ProxyAccountService.ts | 2 + .../backend/src/core/PushNotificationService.ts | 1 + packages/backend/src/core/QueryService.ts | 10 +++ packages/backend/src/core/QueueService.ts | 20 +++++ packages/backend/src/core/ReactionService.ts | 8 ++ packages/backend/src/core/RelayService.ts | 8 ++ packages/backend/src/core/RemoteLoggerService.ts | 1 + .../backend/src/core/RemoteUserResolveService.ts | 3 + packages/backend/src/core/S3Service.ts | 2 + packages/backend/src/core/SignupService.ts | 2 + .../src/core/TwoFactorAuthenticationService.ts | 4 + packages/backend/src/core/UserBlockingService.ts | 6 ++ packages/backend/src/core/UserCacheService.ts | 5 +- packages/backend/src/core/UserFollowingService.ts | 16 ++++ .../backend/src/core/UserKeypairStoreService.ts | 2 + packages/backend/src/core/UserListService.ts | 2 + packages/backend/src/core/UserMutingService.ts | 2 + packages/backend/src/core/UserSuspendService.ts | 3 + packages/backend/src/core/UtilityService.ts | 6 ++ .../backend/src/core/VideoProcessingService.ts | 2 + packages/backend/src/core/WebfingerService.ts | 3 + packages/backend/src/core/WebhookService.ts | 6 +- .../src/core/activitypub/ApAudienceService.ts | 5 ++ .../src/core/activitypub/ApDbResolverService.ts | 7 ++ .../core/activitypub/ApDeliverManagerService.ts | 8 ++ .../backend/src/core/activitypub/ApInboxService.ts | 28 ++++++ .../src/core/activitypub/ApLoggerService.ts | 1 + .../backend/src/core/activitypub/ApMfmService.ts | 3 + .../src/core/activitypub/ApRendererService.ts | 33 +++++++ .../src/core/activitypub/ApRequestService.ts | 9 ++ .../src/core/activitypub/ApResolverService.ts | 100 +++++++++++---------- .../src/core/activitypub/LdSignatureService.ts | 33 ++++--- .../src/core/activitypub/models/ApImageService.ts | 3 + .../core/activitypub/models/ApMentionService.ts | 3 + .../src/core/activitypub/models/ApNoteService.ts | 6 ++ .../src/core/activitypub/models/ApPersonService.ts | 8 ++ .../core/activitypub/models/ApQuestionService.ts | 3 + .../backend/src/core/chart/ChartLoggerService.ts | 1 + .../src/core/chart/ChartManagementService.ts | 2 + .../backend/src/core/chart/charts/active-users.ts | 3 + .../backend/src/core/chart/charts/ap-request.ts | 4 + packages/backend/src/core/chart/charts/drive.ts | 2 + .../backend/src/core/chart/charts/federation.ts | 3 + packages/backend/src/core/chart/charts/hashtag.ts | 2 + packages/backend/src/core/chart/charts/instance.ts | 8 ++ packages/backend/src/core/chart/charts/notes.ts | 2 + .../src/core/chart/charts/per-user-drive.ts | 2 + .../src/core/chart/charts/per-user-following.ts | 2 + .../src/core/chart/charts/per-user-notes.ts | 2 + .../src/core/chart/charts/per-user-reactions.ts | 2 + .../backend/src/core/chart/charts/test-grouped.ts | 2 + .../src/core/chart/charts/test-intersection.ts | 3 + .../backend/src/core/chart/charts/test-unique.ts | 2 + packages/backend/src/core/chart/charts/test.ts | 3 + packages/backend/src/core/chart/charts/users.ts | 2 + packages/backend/src/core/chart/core.ts | 11 +++ .../core/entities/AbuseUserReportEntityService.ts | 3 + .../src/core/entities/AntennaEntityService.ts | 2 + .../backend/src/core/entities/AppEntityService.ts | 2 + .../src/core/entities/AuthSessionEntityService.ts | 2 + .../src/core/entities/BlockingEntityService.ts | 3 + .../src/core/entities/ChannelEntityService.ts | 2 + .../backend/src/core/entities/ClipEntityService.ts | 3 + .../src/core/entities/DriveFileEntityService.ts | 11 +++ .../src/core/entities/DriveFolderEntityService.ts | 2 + .../src/core/entities/EmojiEntityService.ts | 3 + .../core/entities/FollowRequestEntityService.ts | 2 + .../src/core/entities/FollowingEntityService.ts | 7 ++ .../src/core/entities/GalleryLikeEntityService.ts | 3 + .../src/core/entities/GalleryPostEntityService.ts | 3 + .../src/core/entities/HashtagEntityService.ts | 3 + .../src/core/entities/InstanceEntityService.ts | 3 + .../core/entities/MessagingMessageEntityService.ts | 2 + .../core/entities/ModerationLogEntityService.ts | 3 + .../src/core/entities/MutingEntityService.ts | 3 + .../backend/src/core/entities/NoteEntityService.ts | 8 ++ .../src/core/entities/NoteFavoriteEntityService.ts | 3 + .../src/core/entities/NoteReactionEntityService.ts | 2 + .../src/core/entities/NotificationEntityService.ts | 3 + .../backend/src/core/entities/PageEntityService.ts | 3 + .../src/core/entities/PageLikeEntityService.ts | 3 + .../src/core/entities/SigninEntityService.ts | 2 + .../backend/src/core/entities/UserEntityService.ts | 12 +++ .../src/core/entities/UserGroupEntityService.ts | 2 + .../entities/UserGroupInvitationEntityService.ts | 3 + .../src/core/entities/UserListEntityService.ts | 2 + packages/backend/src/daemons/JanitorService.ts | 3 + packages/backend/src/daemons/QueueStatsService.ts | 3 + packages/backend/src/daemons/ServerStatsService.ts | 3 + packages/backend/src/decorators.ts | 41 +++++++++ packages/backend/src/logger.ts | 8 ++ packages/backend/src/misc/cache.ts | 7 ++ packages/backend/src/misc/i18n.ts | 3 +- packages/backend/src/postgre.ts | 8 ++ .../backend/src/queue/DbQueueProcessorsService.ts | 2 + .../queue/ObjectStorageQueueProcessorsService.ts | 2 + packages/backend/src/queue/QueueLoggerService.ts | 1 + .../backend/src/queue/QueueProcessorService.ts | 2 + .../src/queue/SystemQueueProcessorsService.ts | 2 + .../CheckExpiredMutingsProcessorService.ts | 2 + .../processors/CleanChartsProcessorService.ts | 2 + .../src/queue/processors/CleanProcessorService.ts | 2 + .../processors/CleanRemoteFilesProcessorService.ts | 2 + .../processors/DeleteAccountProcessorService.ts | 2 + .../processors/DeleteDriveFilesProcessorService.ts | 2 + .../queue/processors/DeleteFileProcessorService.ts | 2 + .../queue/processors/DeliverProcessorService.ts | 2 + .../EndedPollNotificationProcessorService.ts | 2 + .../processors/ExportBlockingProcessorService.ts | 2 + .../ExportCustomEmojisProcessorService.ts | 2 + .../processors/ExportFollowingProcessorService.ts | 2 + .../processors/ExportMutingProcessorService.ts | 2 + .../processors/ExportNotesProcessorService.ts | 2 + .../processors/ExportUserListsProcessorService.ts | 2 + .../processors/ImportBlockingProcessorService.ts | 2 + .../ImportCustomEmojisProcessorService.ts | 2 + .../processors/ImportFollowingProcessorService.ts | 2 + .../processors/ImportMutingProcessorService.ts | 2 + .../processors/ImportUserListsProcessorService.ts | 2 + .../src/queue/processors/InboxProcessorService.ts | 2 + .../processors/ResyncChartsProcessorService.ts | 2 + .../queue/processors/TickChartsProcessorService.ts | 2 + .../processors/WebhookDeliverProcessorService.ts | 2 + .../backend/src/server/ActivityPubServerService.ts | 12 ++- packages/backend/src/server/FileServerService.ts | 6 +- .../backend/src/server/MediaProxyServerService.ts | 5 +- .../backend/src/server/NodeinfoServerService.ts | 5 +- packages/backend/src/server/ServerService.ts | 2 + .../backend/src/server/WellKnownServerService.ts | 4 +- packages/backend/src/server/api/ApiCallService.ts | 7 ++ .../backend/src/server/api/ApiLoggerService.ts | 1 + .../backend/src/server/api/ApiServerService.ts | 4 +- .../backend/src/server/api/AuthenticateService.ts | 2 + packages/backend/src/server/api/GetterService.ts | 5 ++ .../backend/src/server/api/RateLimiterService.ts | 2 + .../backend/src/server/api/SigninApiService.ts | 2 + packages/backend/src/server/api/SigninService.ts | 2 + .../backend/src/server/api/SignupApiService.ts | 3 + .../src/server/api/StreamingApiServerService.ts | 2 + .../src/server/api/endpoints/admin/suspend-user.ts | 3 + .../backend/src/server/api/endpoints/ap/show.ts | 3 + .../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 + packages/backend/src/server/api/stream/channel.ts | 2 + .../src/server/api/stream/channels/admin.ts | 3 + .../src/server/api/stream/channels/antenna.ts | 7 +- .../src/server/api/stream/channels/channel.ts | 11 ++- .../src/server/api/stream/channels/drive.ts | 3 + .../server/api/stream/channels/global-timeline.ts | 7 +- .../src/server/api/stream/channels/hashtag.ts | 7 +- .../server/api/stream/channels/home-timeline.ts | 7 +- .../server/api/stream/channels/hybrid-timeline.ts | 7 +- .../server/api/stream/channels/local-timeline.ts | 7 +- .../backend/src/server/api/stream/channels/main.ts | 3 + .../server/api/stream/channels/messaging-index.ts | 3 + .../src/server/api/stream/channels/messaging.ts | 13 ++- .../src/server/api/stream/channels/queue-stats.ts | 10 ++- .../src/server/api/stream/channels/server-stats.ts | 10 ++- .../src/server/api/stream/channels/user-list.ts | 13 ++- packages/backend/src/server/api/stream/index.ts | 32 ++++++- .../backend/src/server/web/ClientServerService.ts | 5 +- packages/backend/src/server/web/FeedService.ts | 2 + .../backend/src/server/web/UrlPreviewService.ts | 3 + packages/backend/test/misc/mock-resolver.ts | 1 + 199 files changed, 969 insertions(+), 96 deletions(-) create mode 100644 packages/backend/src/decorators.ts (limited to 'packages/backend/src/core/NoteCreateService.ts') diff --git a/packages/backend/src/core/AccountUpdateService.ts b/packages/backend/src/core/AccountUpdateService.ts index a5ab4fdfce..5f6dfca0ca 100644 --- a/packages/backend/src/core/AccountUpdateService.ts +++ b/packages/backend/src/core/AccountUpdateService.ts @@ -7,6 +7,7 @@ import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { RelayService } from '@/core/RelayService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class AccountUpdateService { @@ -24,6 +25,7 @@ export class AccountUpdateService { ) { } + @bindThis public async publishToFollowers(userId: User['id']) { const user = await this.usersRepository.findOneBy({ id: userId }); if (user == null) throw new Error('user not found'); diff --git a/packages/backend/src/core/AiService.ts b/packages/backend/src/core/AiService.ts index 15084b8ff1..aee9504c85 100644 --- a/packages/backend/src/core/AiService.ts +++ b/packages/backend/src/core/AiService.ts @@ -12,6 +12,7 @@ const _dirname = dirname(_filename); const REQUIRED_CPU_FLAGS = ['avx2', 'fma']; let isSupportedCpu: undefined | boolean = undefined; +import { bindThis } from '@/decorators.js'; @Injectable() export class AiService { @@ -23,6 +24,7 @@ export class AiService { ) { } + @bindThis public async detectSensitive(path: string): Promise { try { if (isSupportedCpu === undefined) { @@ -53,6 +55,7 @@ export class AiService { } } + @bindThis private async getCpuFlags(): Promise { const str = await si.cpuFlags(); return str.split(/\s+/); diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts index 8046ba5311..9c120c993d 100644 --- a/packages/backend/src/core/AntennaService.ts +++ b/packages/backend/src/core/AntennaService.ts @@ -13,6 +13,7 @@ import { DI } from '@/di-symbols.js'; import type { MutingsRepository, BlockingsRepository, NotesRepository, AntennaNotesRepository, AntennasRepository, UserGroupJoiningsRepository, UserListJoiningsRepository } from '@/models/index.js'; import { UtilityService } from '@/core/UtilityService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; @Injectable() export class AntennaService implements OnApplicationShutdown { @@ -56,10 +57,12 @@ export class AntennaService implements OnApplicationShutdown { this.redisSubscriber.on('message', this.onRedisMessage); } + @bindThis public onApplicationShutdown(signal?: string | undefined) { this.redisSubscriber.off('message', this.onRedisMessage); } + @bindThis private async onRedisMessage(_: string, data: string): Promise { const obj = JSON.parse(data); @@ -81,6 +84,7 @@ export class AntennaService implements OnApplicationShutdown { } } + @bindThis public async addNoteToAntenna(antenna: Antenna, note: Note, noteUser: { id: User['id']; }): Promise { // 通知しない設定になっているか、自分自身の投稿なら既読にする const read = !antenna.notify || (antenna.userId === noteUser.id); @@ -133,6 +137,7 @@ export class AntennaService implements OnApplicationShutdown { /** * noteUserFollowers / antennaUserFollowing はどちらか一方が指定されていればよい */ + @bindThis 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; @@ -217,6 +222,7 @@ export class AntennaService implements OnApplicationShutdown { return true; } + @bindThis public async getAntennas() { if (!this.antennasFetched) { this.antennas = await this.antennasRepository.find(); diff --git a/packages/backend/src/core/AppLockService.ts b/packages/backend/src/core/AppLockService.ts index 04b3d8b112..1f512b5790 100644 --- a/packages/backend/src/core/AppLockService.ts +++ b/packages/backend/src/core/AppLockService.ts @@ -8,6 +8,7 @@ import { DI } from '@/di-symbols.js'; * Retry delay (ms) for lock acquisition */ const retryDelay = 100; +import { bindThis } from '@/decorators.js'; @Injectable() export class AppLockService { @@ -26,14 +27,17 @@ export class AppLockService { * @param timeout Lock timeout (ms), The timeout releases previous lock. * @returns Unlock function */ + @bindThis public getApLock(uri: string, timeout = 30 * 1000): Promise<() => void> { return this.lock(`ap-object:${uri}`, timeout); } + @bindThis public getFetchInstanceMetadataLock(host: string, timeout = 30 * 1000): Promise<() => void> { return this.lock(`instance:${host}`, timeout); } + @bindThis public getChartInsertLock(lockKey: string, timeout = 30 * 1000): Promise<() => void> { return this.lock(`chart-insert:${lockKey}`, timeout); } diff --git a/packages/backend/src/core/CaptchaService.ts b/packages/backend/src/core/CaptchaService.ts index b60271812c..0207cf58a0 100644 --- a/packages/backend/src/core/CaptchaService.ts +++ b/packages/backend/src/core/CaptchaService.ts @@ -3,6 +3,7 @@ import { DI } from '@/di-symbols.js'; import type { UsersRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; +import { bindThis } from '@/decorators.js'; type CaptchaResponse = { success: boolean; @@ -19,6 +20,7 @@ export class CaptchaService { ) { } + @bindThis private async getCaptchaResponse(url: string, secret: string, response: string): Promise { const params = new URLSearchParams({ secret, @@ -45,6 +47,7 @@ export class CaptchaService { return await res.json() as CaptchaResponse; } + @bindThis public async verifyRecaptcha(secret: string, response: string | null | undefined): Promise { if (response == null) { throw 'recaptcha-failed: no response provided'; @@ -60,6 +63,7 @@ export class CaptchaService { } } + @bindThis public async verifyHcaptcha(secret: string, response: string | null | undefined): Promise { if (response == null) { throw 'hcaptcha-failed: no response provided'; @@ -75,6 +79,7 @@ export class CaptchaService { } } + @bindThis public async verifyTurnstile(secret: string, response: string | null | undefined): Promise { if (response == null) { throw 'turnstile-failed: no response provided'; diff --git a/packages/backend/src/core/CreateNotificationService.ts b/packages/backend/src/core/CreateNotificationService.ts index 504661c3bd..f376b7b9cf 100644 --- a/packages/backend/src/core/CreateNotificationService.ts +++ b/packages/backend/src/core/CreateNotificationService.ts @@ -7,6 +7,7 @@ import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; import { NotificationEntityService } from '@/core/entities/NotificationEntityService.js'; import { PushNotificationService } from '@/core/PushNotificationService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class CreateNotificationService { @@ -30,6 +31,7 @@ export class CreateNotificationService { ) { } + @bindThis public async createNotification( notifieeId: User['id'], type: Notification['type'], @@ -90,6 +92,7 @@ export class CreateNotificationService { // TODO: locale ファイルをクライアント用とサーバー用で分けたい + @bindThis private async emailNotificationFollow(userId: User['id'], follower: User) { /* const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); @@ -101,6 +104,7 @@ export class CreateNotificationService { */ } + @bindThis private async emailNotificationReceiveFollowRequest(userId: User['id'], follower: User) { /* const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); diff --git a/packages/backend/src/core/CreateSystemUserService.ts b/packages/backend/src/core/CreateSystemUserService.ts index 71f50d7cb3..1e753f65cc 100644 --- a/packages/backend/src/core/CreateSystemUserService.ts +++ b/packages/backend/src/core/CreateSystemUserService.ts @@ -10,6 +10,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class CreateSystemUserService { @@ -21,6 +22,7 @@ export class CreateSystemUserService { ) { } + @bindThis public async createSystemUser(username: string): Promise { const password = uuid(); diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 3319f3efa8..36f88fd743 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -20,6 +20,7 @@ type PopulatedEmoji = { name: string; url: string; }; +import { bindThis } from '@/decorators.js'; @Injectable() export class CustomEmojiService { @@ -43,6 +44,7 @@ export class CustomEmojiService { this.cache = new Cache(1000 * 60 * 60 * 12); } + @bindThis public async add(data: { driveFile: DriveFile; name: string; @@ -67,6 +69,7 @@ export class CustomEmojiService { return emoji; } + @bindThis private normalizeHost(src: string | undefined, noteUserHost: string | null): string | null { // クエリに使うホスト let host = src === '.' ? null // .はローカルホスト (ここがマッチするのはリアクションのみ) @@ -79,6 +82,7 @@ export class CustomEmojiService { return host; } + @bindThis private parseEmojiStr(emojiName: string, noteUserHost: string | null) { const match = emojiName.match(/^(\w+)(?:@([\w.-]+))?$/); if (!match) return { name: null, host: null }; @@ -97,6 +101,7 @@ export class CustomEmojiService { * @param noteUserHost ノートやユーザープロフィールの所有者のホスト * @returns 絵文字情報, nullは未マッチを意味する */ + @bindThis public async populateEmoji(emojiName: string, noteUserHost: string | null): Promise { const { name, host } = this.parseEmojiStr(emojiName, noteUserHost); if (name == null) return null; @@ -123,11 +128,13 @@ export class CustomEmojiService { /** * 複数の添付用絵文字情報を解決する (キャシュ付き, 存在しないものは結果から除外される) */ + @bindThis 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); } + @bindThis public aggregateNoteEmojis(notes: Note[]) { let emojis: { name: string | null; host: string | null; }[] = []; for (const note of notes) { @@ -154,6 +161,7 @@ export class CustomEmojiService { /** * 与えられた絵文字のリストをデータベースから取得し、キャッシュに追加します */ + @bindThis 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[] = []; diff --git a/packages/backend/src/core/DeleteAccountService.ts b/packages/backend/src/core/DeleteAccountService.ts index 53d48c450b..e42c738707 100644 --- a/packages/backend/src/core/DeleteAccountService.ts +++ b/packages/backend/src/core/DeleteAccountService.ts @@ -4,6 +4,7 @@ import { QueueService } from '@/core/QueueService.js'; import { UserSuspendService } from '@/core/UserSuspendService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class DeleteAccountService { @@ -17,6 +18,7 @@ export class DeleteAccountService { ) { } + @bindThis public async deleteAccount(user: { id: string; host: string | null; diff --git a/packages/backend/src/core/DownloadService.ts b/packages/backend/src/core/DownloadService.ts index 25965b7ac4..9097bb08e0 100644 --- a/packages/backend/src/core/DownloadService.ts +++ b/packages/backend/src/core/DownloadService.ts @@ -15,6 +15,7 @@ import { LoggerService } from '@/core/LoggerService.js'; import type Logger from '@/logger.js'; const pipeline = util.promisify(stream.pipeline); +import { bindThis } from '@/decorators.js'; @Injectable() export class DownloadService { @@ -30,6 +31,7 @@ export class DownloadService { this.logger = this.loggerService.getLogger('download'); } + @bindThis public async downloadUrl(url: string, path: string): Promise { this.logger.info(`Downloading ${chalk.cyan(url)} ...`); @@ -94,6 +96,7 @@ export class DownloadService { this.logger.succ(`Download finished: ${chalk.cyan(url)}`); } + @bindThis public async downloadTextFile(url: string): Promise { // Create temp file const [path, cleanup] = await createTemp(); @@ -112,6 +115,7 @@ export class DownloadService { } } + @bindThis private isPrivateIp(ip: string): boolean { for (const net of this.config.allowedPrivateNetworks ?? []) { const cidr = new IPCIDR(net); diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index 1d2ba5df8c..b83047dbc2 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -71,6 +71,7 @@ type UploadFromUrlArgs = { requestIp?: string | null; requestHeaders?: Record | null; }; +import { bindThis } from '@/decorators.js'; @Injectable() export class DriveService { @@ -122,6 +123,7 @@ export class DriveService { * @param hash Hash for original * @param size Size for original */ + @bindThis private 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); @@ -242,6 +244,7 @@ export class DriveService { * @param type Content-Type for original * @param generateWeb Generate webpublic or not */ + @bindThis public async generateAlts(path: string, type: string, generateWeb: boolean) { if (type.startsWith('video/')) { try { @@ -345,6 +348,7 @@ export class DriveService { /** * Upload to ObjectStorage */ + @bindThis private 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'; @@ -372,6 +376,7 @@ export class DriveService { if (result) this.registerLogger.debug(`Uploaded: ${result.Bucket}/${result.Key} => ${result.Location}`); } + @bindThis private async deleteOldFile(user: IRemoteUser) { const q = this.driveFilesRepository.createQueryBuilder('file') .where('file.userId = :userId', { userId: user.id }) @@ -398,6 +403,7 @@ export class DriveService { * Add file to drive * */ + @bindThis public async addFile({ user, path, @@ -601,6 +607,7 @@ export class DriveService { return file; } + @bindThis public async deleteFile(file: DriveFile, isExpired = false) { if (file.storedInternal) { this.internalStorageService.del(file.accessKey!); @@ -627,6 +634,7 @@ export class DriveService { this.deletePostProcess(file, isExpired); } + @bindThis public async deleteFileSync(file: DriveFile, isExpired = false) { if (file.storedInternal) { this.internalStorageService.del(file.accessKey!); @@ -657,6 +665,7 @@ export class DriveService { this.deletePostProcess(file, isExpired); } + @bindThis private async deletePostProcess(file: DriveFile, isExpired = false) { // リモートファイル期限切れ削除後は直リンクにする if (isExpired && file.userHost !== null && file.uri != null) { @@ -683,6 +692,7 @@ export class DriveService { } } + @bindThis public async deleteObjectStorageFile(key: string) { const meta = await this.metaService.fetch(); @@ -694,6 +704,7 @@ export class DriveService { }).promise(); } + @bindThis public async uploadFromUrl({ url, user, diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts index 019b9087cd..59932a5b88 100644 --- a/packages/backend/src/core/EmailService.ts +++ b/packages/backend/src/core/EmailService.ts @@ -7,6 +7,7 @@ import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import type { UserProfilesRepository } from '@/models/index.js'; import { LoggerService } from '@/core/LoggerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class EmailService { @@ -25,6 +26,7 @@ export class EmailService { this.logger = this.loggerService.getLogger('email'); } + @bindThis public async sendEmail(to: string, subject: string, html: string, text: string) { const meta = await this.metaService.fetch(true); @@ -141,6 +143,7 @@ export class EmailService { } } + @bindThis public async validateEmailForAccount(emailAddress: string): Promise<{ available: boolean; reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp'; diff --git a/packages/backend/src/core/FederatedInstanceService.ts b/packages/backend/src/core/FederatedInstanceService.ts index a05c95a2ae..97745a1168 100644 --- a/packages/backend/src/core/FederatedInstanceService.ts +++ b/packages/backend/src/core/FederatedInstanceService.ts @@ -5,6 +5,7 @@ import { Cache } from '@/misc/cache.js'; import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; import { UtilityService } from '@/core/UtilityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class FederatedInstanceService { @@ -20,6 +21,7 @@ export class FederatedInstanceService { this.cache = new Cache(1000 * 60 * 60); } + @bindThis public async registerOrFetchInstanceDoc(host: string): Promise { host = this.utilityService.toPuny(host); diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts index b92ebe6059..4d4945ca7f 100644 --- a/packages/backend/src/core/FetchInstanceMetadataService.ts +++ b/packages/backend/src/core/FetchInstanceMetadataService.ts @@ -30,6 +30,7 @@ type NodeInfo = { themeColor?: unknown; }; }; +import { bindThis } from '@/decorators.js'; @Injectable() export class FetchInstanceMetadataService { @@ -46,6 +47,7 @@ export class FetchInstanceMetadataService { this.logger = this.loggerService.getLogger('metadata', 'cyan'); } + @bindThis public async fetchInstanceMetadata(instance: Instance, force = false): Promise { const unlock = await this.appLockService.getFetchInstanceMetadataLock(instance.host); @@ -105,6 +107,7 @@ export class FetchInstanceMetadataService { } } + @bindThis private async fetchNodeinfo(instance: Instance): Promise { this.logger.info(`Fetching nodeinfo of ${instance.host} ...`); @@ -148,6 +151,7 @@ export class FetchInstanceMetadataService { } } + @bindThis private async fetchDom(instance: Instance): Promise { this.logger.info(`Fetching HTML of ${instance.host} ...`); @@ -161,6 +165,7 @@ export class FetchInstanceMetadataService { return doc; } + @bindThis private async fetchManifest(instance: Instance): Promise | null> { const url = 'https://' + instance.host; @@ -171,6 +176,7 @@ export class FetchInstanceMetadataService { return manifest; } + @bindThis private async fetchFaviconUrl(instance: Instance, doc: DOMWindow['document'] | null): Promise { const url = 'https://' + instance.host; @@ -198,6 +204,7 @@ export class FetchInstanceMetadataService { return null; } + @bindThis private 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; @@ -226,6 +233,7 @@ export class FetchInstanceMetadataService { return null; } + @bindThis private 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; @@ -237,6 +245,7 @@ export class FetchInstanceMetadataService { return null; } + @bindThis private async getSiteName(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record | null): Promise { if (info && info.metadata) { if (typeof info.metadata.nodeName === 'string') { @@ -261,6 +270,7 @@ export class FetchInstanceMetadataService { return null; } + @bindThis private async getDescription(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record | null): Promise { if (info && info.metadata) { if (typeof info.metadata.nodeDescription === 'string') { diff --git a/packages/backend/src/core/FileInfoService.ts b/packages/backend/src/core/FileInfoService.ts index fd8a4fdd3a..bea1b3402e 100644 --- a/packages/backend/src/core/FileInfoService.ts +++ b/packages/backend/src/core/FileInfoService.ts @@ -14,6 +14,7 @@ import sharp from 'sharp'; import { encode } from 'blurhash'; import { createTempDir } from '@/misc/create-temp.js'; import { AiService } from '@/core/AiService.js'; +import { bindThis } from '@/decorators.js'; const pipeline = util.promisify(stream.pipeline); @@ -42,6 +43,7 @@ const TYPE_SVG = { mime: 'image/svg+xml', ext: 'svg', }; + @Injectable() export class FileInfoService { constructor( @@ -52,6 +54,7 @@ export class FileInfoService { /** * Get file information */ + @bindThis public async getFileInfo(path: string, opts: { skipSensitiveDetection: boolean; sensitiveThreshold?: number; @@ -135,6 +138,7 @@ export class FileInfoService { }; } + @bindThis private async detectSensitivity(source: string, mime: string, sensitiveThreshold: number, sensitiveThresholdForPorn: number, analyzeVideo: boolean): Promise<[sensitive: boolean, porn: boolean]> { let sensitive = false; let porn = false; @@ -269,6 +273,7 @@ export class FileInfoService { } } + @bindThis private exists(path: string): Promise { return fs.promises.access(path).then(() => true, () => false); } @@ -276,6 +281,7 @@ export class FileInfoService { /** * Detect MIME Type and extension */ + @bindThis public async detectType(path: string): Promise<{ mime: string; ext: string | null; @@ -312,6 +318,7 @@ export class FileInfoService { /** * Check the file is SVG or not */ + @bindThis public async checkSvg(path: string) { try { const size = await this.getFileSize(path); @@ -325,6 +332,7 @@ export class FileInfoService { /** * Get file size */ + @bindThis public async getFileSize(path: string): Promise { const getStat = util.promisify(fs.stat); return (await getStat(path)).size; @@ -333,6 +341,7 @@ export class FileInfoService { /** * Calculate MD5 hash */ + @bindThis private async calcHash(path: string): Promise { const hash = crypto.createHash('md5').setEncoding('hex'); await pipeline(fs.createReadStream(path), hash); @@ -342,6 +351,7 @@ export class FileInfoService { /** * Detect dimensions of image */ + @bindThis private async detectImageSize(path: string): Promise<{ width: number; height: number; @@ -358,6 +368,7 @@ export class FileInfoService { /** * Calculate average color of image */ + @bindThis private getBlurhash(path: string): Promise { return new Promise((resolve, reject) => { sharp(path) diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index df0c9b5ccb..90f911027e 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -25,6 +25,7 @@ import type { import type { Packed } from '@/misc/schema.js'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class GlobalEventService { @@ -37,6 +38,7 @@ export class GlobalEventService { ) { } + @bindThis private publish(channel: StreamChannels, type: string | null, value?: any): void { const message = type == null ? value : value == null ? { type: type, body: null } : @@ -99,6 +101,7 @@ export class GlobalEventService { this.publish(`messagingIndexStream:${userId}`, type, typeof value === 'undefined' ? null : value); } + @bindThis public publishNotesStream(note: Packed<'Note'>): void { this.publish('notesStream', null, note); } diff --git a/packages/backend/src/core/HashtagService.ts b/packages/backend/src/core/HashtagService.ts index 5ca058e9a4..309cfe8c3f 100644 --- a/packages/backend/src/core/HashtagService.ts +++ b/packages/backend/src/core/HashtagService.ts @@ -7,6 +7,7 @@ import type { Hashtag } from '@/models/entities/Hashtag.js'; import HashtagChart from '@/core/chart/charts/hashtag.js'; import type { HashtagsRepository, UsersRepository } from '@/models/index.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class HashtagService { @@ -23,12 +24,14 @@ export class HashtagService { ) { } + @bindThis public async updateHashtags(user: { id: User['id']; host: User['host']; }, tags: string[]) { for (const tag of tags) { await this.updateHashtag(user, tag); } } + @bindThis public async updateUsertags(user: User, tags: string[]) { for (const tag of tags) { await this.updateHashtag(user, tag, true, true); @@ -39,6 +42,7 @@ export class HashtagService { } } + @bindThis public async updateHashtag(user: { id: User['id']; host: User['host']; }, tag: string, isUserAttached = false, inc = true) { tag = normalizeForSearch(tag); diff --git a/packages/backend/src/core/HttpRequestService.ts b/packages/backend/src/core/HttpRequestService.ts index 396fefad1c..d998307973 100644 --- a/packages/backend/src/core/HttpRequestService.ts +++ b/packages/backend/src/core/HttpRequestService.ts @@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { StatusError } from '@/misc/status-error.js'; +import { bindThis } from '@/decorators.js'; import type { Response } from 'node-fetch'; import type { URL } from 'node:url'; @@ -84,6 +85,7 @@ export class HttpRequestService { * @param url URL * @param bypassProxy Allways bypass proxy */ + @bindThis 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; @@ -92,6 +94,7 @@ export class HttpRequestService { } } + @bindThis public async getJson(url: string, accept = 'application/json, */*', timeout = 10000, headers?: Record): Promise { const res = await this.getResponse({ url, @@ -106,6 +109,7 @@ export class HttpRequestService { return await res.json(); } + @bindThis public async getHtml(url: string, accept = 'text/html, */*', timeout = 10000, headers?: Record): Promise { const res = await this.getResponse({ url, @@ -120,6 +124,7 @@ export class HttpRequestService { return await res.text(); } + @bindThis public async getResponse(args: { url: string, method: string, diff --git a/packages/backend/src/core/IdService.ts b/packages/backend/src/core/IdService.ts index 997be17937..0e8a7b13ad 100644 --- a/packages/backend/src/core/IdService.ts +++ b/packages/backend/src/core/IdService.ts @@ -6,6 +6,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class IdService { @@ -18,6 +19,7 @@ export class IdService { this.metohd = config.id.toLowerCase(); } + @bindThis public genId(date?: Date): string { if (!date || (date > new Date())) date = new Date(); diff --git a/packages/backend/src/core/ImageProcessingService.ts b/packages/backend/src/core/ImageProcessingService.ts index 3a50361a42..3a61873044 100644 --- a/packages/backend/src/core/ImageProcessingService.ts +++ b/packages/backend/src/core/ImageProcessingService.ts @@ -8,6 +8,7 @@ export type IImage = { ext: string | null; type: string; }; +import { bindThis } from '@/decorators.js'; @Injectable() export class ImageProcessingService { @@ -21,10 +22,12 @@ export class ImageProcessingService { * Convert to JPEG * with resize, remove metadata, resolve orientation, stop animation */ + @bindThis public async convertToJpeg(path: string, width: number, height: number): Promise { return this.convertSharpToJpeg(await sharp(path), width, height); } + @bindThis public async convertSharpToJpeg(sharp: sharp.Sharp, width: number, height: number): Promise { const data = await sharp .resize(width, height, { @@ -49,10 +52,12 @@ export class ImageProcessingService { * Convert to WebP * with resize, remove metadata, resolve orientation, stop animation */ + @bindThis public async convertToWebp(path: string, width: number, height: number, quality = 85): Promise { return this.convertSharpToWebp(await sharp(path), width, height, quality); } + @bindThis public async convertSharpToWebp(sharp: sharp.Sharp, width: number, height: number, quality = 85): Promise { const data = await sharp .resize(width, height, { @@ -76,10 +81,12 @@ export class ImageProcessingService { * Convert to PNG * with resize, remove metadata, resolve orientation, stop animation */ + @bindThis public async convertToPng(path: string, width: number, height: number): Promise { return this.convertSharpToPng(await sharp(path), width, height); } + @bindThis public async convertSharpToPng(sharp: sharp.Sharp, width: number, height: number): Promise { const data = await sharp .resize(width, height, { diff --git a/packages/backend/src/core/InstanceActorService.ts b/packages/backend/src/core/InstanceActorService.ts index f35a28147d..abd6685d61 100644 --- a/packages/backend/src/core/InstanceActorService.ts +++ b/packages/backend/src/core/InstanceActorService.ts @@ -7,6 +7,7 @@ import { DI } from '@/di-symbols.js'; import { CreateSystemUserService } from '@/core/CreateSystemUserService.js'; const ACTOR_USERNAME = 'instance.actor' as const; +import { bindThis } from '@/decorators.js'; @Injectable() export class InstanceActorService { @@ -21,6 +22,7 @@ export class InstanceActorService { this.cache = new Cache(Infinity); } + @bindThis public async getInstanceActor(): Promise { const cached = this.cache.get(null); if (cached) return cached; diff --git a/packages/backend/src/core/InternalStorageService.ts b/packages/backend/src/core/InternalStorageService.ts index 6d2a9b2db6..e32cbf645c 100644 --- a/packages/backend/src/core/InternalStorageService.ts +++ b/packages/backend/src/core/InternalStorageService.ts @@ -10,6 +10,7 @@ const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); const path = Path.resolve(_dirname, '../../../../files'); +import { bindThis } from '@/decorators.js'; @Injectable() export class InternalStorageService { @@ -19,26 +20,31 @@ export class InternalStorageService { ) { } + @bindThis public resolvePath(key: string) { return Path.resolve(path, key); } + @bindThis public read(key: string) { return fs.createReadStream(this.resolvePath(key)); } + @bindThis public saveFromPath(key: string, srcPath: string) { fs.mkdirSync(path, { recursive: true }); fs.copyFileSync(srcPath, this.resolvePath(key)); return `${this.config.url}/files/${key}`; } + @bindThis public saveFromBuffer(key: string, data: Buffer) { fs.mkdirSync(path, { recursive: true }); fs.writeFileSync(this.resolvePath(key), data); return `${this.config.url}/files/${key}`; } + @bindThis public del(key: string) { fs.unlink(this.resolvePath(key), () => {}); } diff --git a/packages/backend/src/core/LoggerService.ts b/packages/backend/src/core/LoggerService.ts index a3192c0262..4303f3ae22 100644 --- a/packages/backend/src/core/LoggerService.ts +++ b/packages/backend/src/core/LoggerService.ts @@ -3,6 +3,7 @@ import * as SyslogPro from 'syslog-pro'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import Logger from '@/logger.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class LoggerService { @@ -27,6 +28,7 @@ export class LoggerService { } } + @bindThis public getLogger(domain: string, color?: string | undefined, store?: boolean) { return new Logger(domain, color, store, this.syslogClient); } diff --git a/packages/backend/src/core/MessagingService.ts b/packages/backend/src/core/MessagingService.ts index 9de28ad8db..f4a1090658 100644 --- a/packages/backend/src/core/MessagingService.ts +++ b/packages/backend/src/core/MessagingService.ts @@ -17,6 +17,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { MessagingMessageEntityService } from '@/core/entities/MessagingMessageEntityService.js'; import { PushNotificationService } from '@/core/PushNotificationService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class MessagingService { @@ -46,6 +47,7 @@ export class MessagingService { ) { } + @bindThis 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(), @@ -140,11 +142,13 @@ export class MessagingService { return messageObj; } + @bindThis public async deleteMessage(message: MessagingMessage) { await this.messagingMessagesRepository.delete(message.id); this.postDeleteMessage(message); } + @bindThis private async postDeleteMessage(message: MessagingMessage) { if (message.recipientId) { const user = await this.usersRepository.findOneByOrFail({ id: message.userId }); @@ -165,6 +169,7 @@ export class MessagingService { /** * Mark messages as read */ + @bindThis public async readUserMessagingMessage( userId: User['id'], otherpartyId: User['id'], @@ -220,6 +225,7 @@ export class MessagingService { /** * Mark messages as read */ + @bindThis public async readGroupMessagingMessage( userId: User['id'], groupId: UserGroup['id'], @@ -284,6 +290,7 @@ export class MessagingService { } } + @bindThis 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)); diff --git a/packages/backend/src/core/MetaService.ts b/packages/backend/src/core/MetaService.ts index c3d41bfccb..ff05779aee 100644 --- a/packages/backend/src/core/MetaService.ts +++ b/packages/backend/src/core/MetaService.ts @@ -5,6 +5,7 @@ import { DI } from '@/di-symbols.js'; import { Meta } from '@/models/entities/Meta.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; @Injectable() export class MetaService implements OnApplicationShutdown { @@ -20,7 +21,7 @@ export class MetaService implements OnApplicationShutdown { private globalEventService: GlobalEventService, ) { - this.onMessage = this.onMessage.bind(this); + //this.onMessage = this.onMessage.bind(this); if (process.env.NODE_ENV !== 'test') { this.intervalId = setInterval(() => { @@ -34,6 +35,7 @@ export class MetaService implements OnApplicationShutdown { this.redisSubscriber.on('message', this.onMessage); } + @bindThis private async onMessage(_: string, data: string): Promise { const obj = JSON.parse(data); @@ -50,6 +52,7 @@ export class MetaService implements OnApplicationShutdown { } } + @bindThis public async fetch(noCache = false): Promise { if (!noCache && this.cache) return this.cache; @@ -84,6 +87,7 @@ export class MetaService implements OnApplicationShutdown { }); } + @bindThis public async update(data: Partial): Promise { const updated = await this.db.transaction(async transactionalEntityManager => { const metas = await transactionalEntityManager.find(Meta, { @@ -114,6 +118,7 @@ export class MetaService implements OnApplicationShutdown { return updated; } + @bindThis public onApplicationShutdown(signal?: string | undefined) { clearInterval(this.intervalId); this.redisSubscriber.off('message', this.onMessage); diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts index 2e03bf3cc0..d53623baaa 100644 --- a/packages/backend/src/core/MfmService.ts +++ b/packages/backend/src/core/MfmService.ts @@ -14,6 +14,7 @@ const treeAdapter = TreeAdapter.defaultTreeAdapter; const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/; const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/; +import { bindThis } from '@/decorators.js'; @Injectable() export class MfmService { @@ -23,6 +24,7 @@ export class MfmService { ) { } + @bindThis 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'); @@ -228,6 +230,7 @@ export class MfmService { } } + @bindThis public toHtml(nodes: mfm.MfmNode[] | null, mentionedRemoteUsers: IMentionedRemoteUsers = []) { if (nodes == null) { return null; diff --git a/packages/backend/src/core/ModerationLogService.ts b/packages/backend/src/core/ModerationLogService.ts index 81ae322b95..80e8cb9e52 100644 --- a/packages/backend/src/core/ModerationLogService.ts +++ b/packages/backend/src/core/ModerationLogService.ts @@ -3,6 +3,7 @@ import { DI } from '@/di-symbols.js'; import type { ModerationLogsRepository } from '@/models/index.js'; import type { User } from '@/models/entities/User.js'; import { IdService } from '@/core/IdService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ModerationLogService { @@ -14,6 +15,7 @@ export class ModerationLogService { ) { } + @bindThis public async insertModerationLog(moderator: { id: User['id'] }, type: string, info?: Record) { await this.moderationLogsRepository.insert({ id: this.idService.genId(), diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index cf1566a5e8..a41df28050 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -64,6 +64,7 @@ class NotificationManager { this.queue = []; } + @bindThis public push(notifiee: ILocalUser['id'], reason: NotificationType) { // 自分自身へは通知しない if (this.notifier.id === notifiee) return; @@ -83,6 +84,7 @@ class NotificationManager { } } + @bindThis public async deliver() { for (const x of this.queue) { // ミュート情報を取得 @@ -130,6 +132,7 @@ type Option = { url?: string | null; app?: App | null; }; +import { bindThis } from '@/decorators.js'; @Injectable() export class NoteCreateService { @@ -188,6 +191,7 @@ export class NoteCreateService { private instanceChart: InstanceChart, ) {} + @bindThis public async create(user: { id: User['id']; username: User['username']; @@ -307,6 +311,7 @@ export class NoteCreateService { return note; } + @bindThis private async insertNote(user: { id: User['id']; host: User['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]) { const insert = new Note({ id: this.idService.genId(data.createdAt!), @@ -403,6 +408,7 @@ export class NoteCreateService { } } + @bindThis private async postNoteCreated(note: Note, user: { id: User['id']; username: User['username']; @@ -644,6 +650,7 @@ export class NoteCreateService { this.index(note); } + @bindThis private incRenoteCount(renote: Note) { this.notesRepository.createQueryBuilder().update() .set({ @@ -654,6 +661,7 @@ export class NoteCreateService { .execute(); } + @bindThis private async createMentionedEvents(mentionedUsers: MinimumUser[], note: Note, nm: NotificationManager) { for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) { const threadMuted = await this.noteThreadMutingsRepository.findOneBy({ @@ -683,10 +691,12 @@ export class NoteCreateService { } } + @bindThis private saveReply(reply: Note, note: Note) { this.notesRepository.increment({ id: reply.id }, 'repliesCount', 1); } + @bindThis private async renderNoteOrRenoteActivity(data: Option, note: Note) { if (data.localOnly) return null; @@ -697,6 +707,7 @@ export class NoteCreateService { return this.apRendererService.renderActivity(content); } + @bindThis private index(note: Note) { if (note.text == null || this.config.elasticsearch == null) return; /* @@ -711,6 +722,7 @@ export class NoteCreateService { });*/ } + @bindThis private incNotesCountOfUser(user: { id: User['id']; }) { this.usersRepository.createQueryBuilder().update() .set({ @@ -721,6 +733,7 @@ export class NoteCreateService { .execute(); } + @bindThis private async extractMentionedUsers(user: { host: User['host']; }, tokens: mfm.MfmNode[]): Promise { if (tokens == null) return []; diff --git a/packages/backend/src/core/NoteDeleteService.ts b/packages/backend/src/core/NoteDeleteService.ts index ce6e755a7e..7331d03552 100644 --- a/packages/backend/src/core/NoteDeleteService.ts +++ b/packages/backend/src/core/NoteDeleteService.ts @@ -15,6 +15,7 @@ import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class NoteDeleteService { @@ -112,6 +113,7 @@ export class NoteDeleteService { }); } + @bindThis private async findCascadingNotes(note: Note) { const cascadingNotes: Note[] = []; @@ -134,6 +136,7 @@ export class NoteDeleteService { return cascadingNotes.filter(note => note.userHost === null); // filter out non-local users } + @bindThis private async getMentionedRemoteUsers(note: Note) { const where = [] as any[]; @@ -159,6 +162,7 @@ export class NoteDeleteService { }) as IRemoteUser[]; } + @bindThis private async deliverToConcerned(user: { id: ILocalUser['id']; host: null; }, note: Note, content: any) { this.apDeliverManagerService.deliverToFollowers(user, content); this.relayService.deliverToRelays(user, content); diff --git a/packages/backend/src/core/NotePiningService.ts b/packages/backend/src/core/NotePiningService.ts index a04b52fe4c..f8997574a7 100644 --- a/packages/backend/src/core/NotePiningService.ts +++ b/packages/backend/src/core/NotePiningService.ts @@ -11,6 +11,7 @@ import type { Config } from '@/config.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class NotePiningService { @@ -40,6 +41,7 @@ export class NotePiningService { * @param user * @param noteId */ + @bindThis public async addPinned(user: { id: User['id']; host: User['host']; }, noteId: Note['id']) { // Fetch pinee const note = await this.notesRepository.findOneBy({ @@ -79,6 +81,7 @@ export class NotePiningService { * @param user * @param noteId */ + @bindThis public async removePinned(user: { id: User['id']; host: User['host']; }, noteId: Note['id']) { // Fetch unpinee const note = await this.notesRepository.findOneBy({ @@ -101,6 +104,7 @@ export class NotePiningService { } } + @bindThis 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'); diff --git a/packages/backend/src/core/NoteReadService.ts b/packages/backend/src/core/NoteReadService.ts index e0feaa957d..f70495ff30 100644 --- a/packages/backend/src/core/NoteReadService.ts +++ b/packages/backend/src/core/NoteReadService.ts @@ -11,6 +11,7 @@ import type { UsersRepository, NoteUnreadsRepository, MutingsRepository, NoteThr import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { NotificationService } from './NotificationService.js'; import { AntennaService } from './AntennaService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class NoteReadService { @@ -44,6 +45,7 @@ export class NoteReadService { ) { } + @bindThis public async insertNoteUnread(userId: User['id'], note: Note, params: { // NOTE: isSpecifiedがtrueならisMentionedは必ずfalse isSpecified: boolean; @@ -94,6 +96,7 @@ export class NoteReadService { }, 2000); } + @bindThis public async read( userId: User['id'], notes: (Note | Packed<'Note'>)[], diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts index 8bbc95b02d..9fef36dd2c 100644 --- a/packages/backend/src/core/NotificationService.ts +++ b/packages/backend/src/core/NotificationService.ts @@ -8,6 +8,7 @@ import type { Notification } from '@/models/entities/Notification.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { GlobalEventService } from './GlobalEventService.js'; import { PushNotificationService } from './PushNotificationService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class NotificationService { @@ -21,6 +22,7 @@ export class NotificationService { ) { } + @bindThis public async readNotification( userId: User['id'], notificationIds: Notification['id'][], @@ -42,6 +44,7 @@ export class NotificationService { else return this.postReadNotifications(userId, notificationIds); } + @bindThis public async readNotificationByQuery( userId: User['id'], query: Record, @@ -55,11 +58,13 @@ export class NotificationService { return this.readNotification(userId, notificationIds); } + @bindThis private postReadAllNotifications(userId: User['id']) { this.globalEventService.publishMainStream(userId, 'readAllNotifications'); return this.pushNotificationService.pushNotification(userId, 'readAllNotifications', undefined); } + @bindThis private postReadNotifications(userId: User['id'], notificationIds: Notification['id'][]) { this.globalEventService.publishMainStream(userId, 'readNotifications', notificationIds); return this.pushNotificationService.pushNotification(userId, 'readNotifications', { notificationIds }); diff --git a/packages/backend/src/core/PollService.ts b/packages/backend/src/core/PollService.ts index 287ce8ada4..3cc9b0cc9b 100644 --- a/packages/backend/src/core/PollService.ts +++ b/packages/backend/src/core/PollService.ts @@ -11,6 +11,7 @@ import { CreateNotificationService } from '@/core/CreateNotificationService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class PollService { @@ -40,6 +41,7 @@ export class PollService { ) { } + @bindThis public async vote(user: CacheableUser, note: Note, choice: number) { const poll = await this.pollsRepository.findOneBy({ noteId: note.id }); @@ -99,6 +101,7 @@ export class PollService { }); } + @bindThis public async deliverQuestionUpdate(noteId: Note['id']) { const note = await this.notesRepository.findOneBy({ id: noteId }); if (note == null) throw new Error('note not found'); diff --git a/packages/backend/src/core/ProxyAccountService.ts b/packages/backend/src/core/ProxyAccountService.ts index 4cbdadd029..55b70bfc94 100644 --- a/packages/backend/src/core/ProxyAccountService.ts +++ b/packages/backend/src/core/ProxyAccountService.ts @@ -3,6 +3,7 @@ import type { UsersRepository } from '@/models/index.js'; import type { ILocalUser, User } from '@/models/entities/User.js'; import { DI } from '@/di-symbols.js'; import { MetaService } from '@/core/MetaService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ProxyAccountService { @@ -14,6 +15,7 @@ export class ProxyAccountService { ) { } + @bindThis public async fetch(): Promise { const meta = await this.metaService.fetch(); if (meta.proxyAccountId == null) return null; diff --git a/packages/backend/src/core/PushNotificationService.ts b/packages/backend/src/core/PushNotificationService.ts index 98e0841799..bffb24cf4c 100644 --- a/packages/backend/src/core/PushNotificationService.ts +++ b/packages/backend/src/core/PushNotificationService.ts @@ -37,6 +37,7 @@ function truncateNotification(notification: Packed<'Notification'>): any { return notification; } +import { bindThis } from '@/decorators.js'; @Injectable() export class PushNotificationService { diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index 771adeaed5..4cc844ccea 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -4,6 +4,7 @@ import { DI } from '@/di-symbols.js'; import type { User } from '@/models/entities/User.js'; import type { UserProfilesRepository, FollowingsRepository, ChannelFollowingsRepository, MutedNotesRepository, BlockingsRepository, NoteThreadMutingsRepository, MutingsRepository } from '@/models/index.js'; import type { SelectQueryBuilder } from 'typeorm'; +import { bindThis } from '@/decorators.js'; @Injectable() export class QueryService { @@ -59,6 +60,7 @@ export class QueryService { } // ここでいうBlockedは被Blockedの意 + @bindThis public generateBlockedUserQuery(q: SelectQueryBuilder, me: { id: User['id'] }): void { const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking') .select('blocking.blockerId') @@ -81,6 +83,7 @@ export class QueryService { q.setParameters(blockingQuery.getParameters()); } + @bindThis public generateBlockQueryForUsers(q: SelectQueryBuilder, me: { id: User['id'] }): void { const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking') .select('blocking.blockeeId') @@ -97,6 +100,7 @@ export class QueryService { q.setParameters(blockedQuery.getParameters()); } + @bindThis public generateChannelQuery(q: SelectQueryBuilder, me?: { id: User['id'] } | null): void { if (me == null) { q.andWhere('note.channelId IS NULL'); @@ -118,6 +122,7 @@ export class QueryService { } } + @bindThis public generateMutedNoteQuery(q: SelectQueryBuilder, me: { id: User['id'] }): void { const mutedQuery = this.mutedNotesRepository.createQueryBuilder('muted') .select('muted.noteId') @@ -128,6 +133,7 @@ export class QueryService { q.setParameters(mutedQuery.getParameters()); } + @bindThis public generateMutedNoteThreadQuery(q: SelectQueryBuilder, me: { id: User['id'] }): void { const mutedQuery = this.noteThreadMutingsRepository.createQueryBuilder('threadMuted') .select('threadMuted.threadId') @@ -142,6 +148,7 @@ export class QueryService { q.setParameters(mutedQuery.getParameters()); } + @bindThis public generateMutedUserQuery(q: SelectQueryBuilder, me: { id: User['id'] }, exclude?: User): void { const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') .select('muting.muteeId') @@ -186,6 +193,7 @@ export class QueryService { q.setParameters(mutingInstanceQuery.getParameters()); } + @bindThis public generateMutedUserQueryForUsers(q: SelectQueryBuilder, me: { id: User['id'] }): void { const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') .select('muting.muteeId') @@ -196,6 +204,7 @@ export class QueryService { q.setParameters(mutingQuery.getParameters()); } + @bindThis public generateRepliesQuery(q: SelectQueryBuilder, me?: Pick | null): void { if (me == null) { q.andWhere(new Brackets(qb => { qb @@ -221,6 +230,7 @@ export class QueryService { } } + @bindThis 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) { diff --git a/packages/backend/src/core/QueueService.ts b/packages/backend/src/core/QueueService.ts index a27d68ee19..7956a3a8f9 100644 --- a/packages/backend/src/core/QueueService.ts +++ b/packages/backend/src/core/QueueService.ts @@ -8,6 +8,7 @@ import { DI } from '@/di-symbols.js'; import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from './QueueModule.js'; import type { ThinUser } from '../queue/types.js'; import type httpSignature from '@peertube/http-signature'; +import { bindThis } from '@/decorators.js'; @Injectable() export class QueueService { @@ -24,6 +25,7 @@ export class QueueService { @Inject('queue:webhookDeliver') public webhookDeliverQueue: WebhookDeliverQueue, ) {} + @bindThis public deliver(user: ThinUser, content: IActivity | null, to: string | null) { if (content == null) return null; if (to == null) return null; @@ -47,6 +49,7 @@ export class QueueService { }); } + @bindThis public inbox(activity: IActivity, signature: httpSignature.IParsedSignature) { const data = { activity: activity, @@ -64,6 +67,7 @@ export class QueueService { }); } + @bindThis public createDeleteDriveFilesJob(user: ThinUser) { return this.dbQueue.add('deleteDriveFiles', { user: user, @@ -73,6 +77,7 @@ export class QueueService { }); } + @bindThis public createExportCustomEmojisJob(user: ThinUser) { return this.dbQueue.add('exportCustomEmojis', { user: user, @@ -82,6 +87,7 @@ export class QueueService { }); } + @bindThis public createExportNotesJob(user: ThinUser) { return this.dbQueue.add('exportNotes', { user: user, @@ -91,6 +97,7 @@ export class QueueService { }); } + @bindThis public createExportFollowingJob(user: ThinUser, excludeMuting = false, excludeInactive = false) { return this.dbQueue.add('exportFollowing', { user: user, @@ -102,6 +109,7 @@ export class QueueService { }); } + @bindThis public createExportMuteJob(user: ThinUser) { return this.dbQueue.add('exportMuting', { user: user, @@ -111,6 +119,7 @@ export class QueueService { }); } + @bindThis public createExportBlockingJob(user: ThinUser) { return this.dbQueue.add('exportBlocking', { user: user, @@ -120,6 +129,7 @@ export class QueueService { }); } + @bindThis public createExportUserListsJob(user: ThinUser) { return this.dbQueue.add('exportUserLists', { user: user, @@ -129,6 +139,7 @@ export class QueueService { }); } + @bindThis public createImportFollowingJob(user: ThinUser, fileId: DriveFile['id']) { return this.dbQueue.add('importFollowing', { user: user, @@ -139,6 +150,7 @@ export class QueueService { }); } + @bindThis public createImportMutingJob(user: ThinUser, fileId: DriveFile['id']) { return this.dbQueue.add('importMuting', { user: user, @@ -149,6 +161,7 @@ export class QueueService { }); } + @bindThis public createImportBlockingJob(user: ThinUser, fileId: DriveFile['id']) { return this.dbQueue.add('importBlocking', { user: user, @@ -159,6 +172,7 @@ export class QueueService { }); } + @bindThis public createImportUserListsJob(user: ThinUser, fileId: DriveFile['id']) { return this.dbQueue.add('importUserLists', { user: user, @@ -169,6 +183,7 @@ export class QueueService { }); } + @bindThis public createImportCustomEmojisJob(user: ThinUser, fileId: DriveFile['id']) { return this.dbQueue.add('importCustomEmojis', { user: user, @@ -179,6 +194,7 @@ export class QueueService { }); } + @bindThis public createDeleteAccountJob(user: ThinUser, opts: { soft?: boolean; } = {}) { return this.dbQueue.add('deleteAccount', { user: user, @@ -189,6 +205,7 @@ export class QueueService { }); } + @bindThis public createDeleteObjectStorageFileJob(key: string) { return this.objectStorageQueue.add('deleteFile', { key: key, @@ -198,6 +215,7 @@ export class QueueService { }); } + @bindThis public createCleanRemoteFilesJob() { return this.objectStorageQueue.add('cleanRemoteFiles', {}, { removeOnComplete: true, @@ -205,6 +223,7 @@ export class QueueService { }); } + @bindThis public webhookDeliver(webhook: Webhook, type: typeof webhookEventTypes[number], content: unknown) { const data = { type, @@ -228,6 +247,7 @@ export class QueueService { }); } + @bindThis public destroy() { this.deliverQueue.once('cleaned', (jobs, status) => { //deliverLogger.succ(`Cleaned ${jobs.length} ${status} jobs`); diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index 7a9724e7dd..b02c990566 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -49,6 +49,7 @@ type DecodedReaction = { */ host?: string | null; }; +import { bindThis } from '@/decorators.js'; @Injectable() export class ReactionService { @@ -81,6 +82,7 @@ export class ReactionService { ) { } + @bindThis public async create(user: { id: User['id']; host: User['host']; }, note: Note, reaction?: string) { // Check blocking if (note.userId !== user.id) { @@ -196,6 +198,7 @@ export class ReactionService { //#endregion } + @bindThis public async delete(user: { id: User['id']; host: User['host']; }, note: Note) { // if already unreacted const exist = await this.noteReactionsRepository.findOneBy({ @@ -244,11 +247,13 @@ export class ReactionService { //#endregion } + @bindThis public async getFallbackReaction(): Promise { const meta = await this.metaService.fetch(); return meta.useStarForReactionFallback ? '⭐' : '👍'; } + @bindThis public convertLegacyReactions(reactions: Record) { const _reactions = {} as Record; @@ -279,6 +284,7 @@ export class ReactionService { return _reactions2; } + @bindThis public async toDbReaction(reaction?: string | null, reacterHost?: string | null): Promise { if (reaction == null) return await this.getFallbackReaction(); @@ -311,6 +317,7 @@ export class ReactionService { return await this.getFallbackReaction(); } + @bindThis public decodeReaction(str: string): DecodedReaction { const custom = str.match(/^:([\w+-]+)(?:@([\w.-]+))?:$/); @@ -332,6 +339,7 @@ export class ReactionService { }; } + @bindThis public convertLegacyReaction(reaction: string): string { reaction = this.decodeReaction(reaction).reaction; if (Object.keys(legacies).includes(reaction)) return legacies[reaction]; diff --git a/packages/backend/src/core/RelayService.ts b/packages/backend/src/core/RelayService.ts index 7951edddcb..5fb853f25a 100644 --- a/packages/backend/src/core/RelayService.ts +++ b/packages/backend/src/core/RelayService.ts @@ -12,6 +12,7 @@ import { DI } from '@/di-symbols.js'; import { deepClone } from '@/misc/clone.js'; const ACTOR_USERNAME = 'relay.actor' as const; +import { bindThis } from '@/decorators.js'; @Injectable() export class RelayService { @@ -32,6 +33,7 @@ export class RelayService { this.relaysCache = new Cache(1000 * 60 * 10); } + @bindThis private async getRelayActor(): Promise { const user = await this.usersRepository.findOneBy({ host: IsNull(), @@ -44,6 +46,7 @@ export class RelayService { return created as ILocalUser; } + @bindThis public async addRelay(inbox: string): Promise { const relay = await this.relaysRepository.insert({ id: this.idService.genId(), @@ -59,6 +62,7 @@ export class RelayService { return relay; } + @bindThis public async removeRelay(inbox: string): Promise { const relay = await this.relaysRepository.findOneBy({ inbox, @@ -77,11 +81,13 @@ export class RelayService { await this.relaysRepository.delete(relay.id); } + @bindThis public async listRelay(): Promise { const relays = await this.relaysRepository.find(); return relays; } + @bindThis public async relayAccepted(id: string): Promise { const result = await this.relaysRepository.update(id, { status: 'accepted', @@ -90,6 +96,7 @@ export class RelayService { return JSON.stringify(result); } + @bindThis public async relayRejected(id: string): Promise { const result = await this.relaysRepository.update(id, { status: 'rejected', @@ -98,6 +105,7 @@ export class RelayService { return JSON.stringify(result); } + @bindThis public async deliverToRelays(user: { id: User['id']; host: null; }, activity: any): Promise { if (activity == null) return; diff --git a/packages/backend/src/core/RemoteLoggerService.ts b/packages/backend/src/core/RemoteLoggerService.ts index 68246466c8..0ea5d7b42f 100644 --- a/packages/backend/src/core/RemoteLoggerService.ts +++ b/packages/backend/src/core/RemoteLoggerService.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { LoggerService } from '@/core/LoggerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class RemoteLoggerService { diff --git a/packages/backend/src/core/RemoteUserResolveService.ts b/packages/backend/src/core/RemoteUserResolveService.ts index 809b50f6e9..dde4098624 100644 --- a/packages/backend/src/core/RemoteUserResolveService.ts +++ b/packages/backend/src/core/RemoteUserResolveService.ts @@ -11,6 +11,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { WebfingerService } from '@/core/WebfingerService.js'; import { RemoteLoggerService } from '@/core/RemoteLoggerService.js'; import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class RemoteUserResolveService { @@ -31,6 +32,7 @@ export class RemoteUserResolveService { this.logger = this.remoteLoggerService.logger.createSubLogger('resolve-user'); } + @bindThis public async resolveUser(username: string, host: string | null): Promise { const usernameLower = username.toLowerCase(); @@ -116,6 +118,7 @@ export class RemoteUserResolveService { return user; } + @bindThis private async resolveSelf(acctLower: string) { this.logger.info(`WebFinger for ${chalk.yellow(acctLower)}`); const finger = await this.webfingerService.webfinger(acctLower).catch(err => { diff --git a/packages/backend/src/core/S3Service.ts b/packages/backend/src/core/S3Service.ts index 1374ee06c8..0ce69aaa74 100644 --- a/packages/backend/src/core/S3Service.ts +++ b/packages/backend/src/core/S3Service.ts @@ -5,6 +5,7 @@ import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type { Meta } from '@/models/entities/Meta.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class S3Service { @@ -16,6 +17,7 @@ export class S3Service { ) { } + @bindThis public getS3(meta: Meta) { const u = meta.objectStorageEndpoint != null ? `${meta.objectStorageUseSSL ? 'https://' : 'http://'}${meta.objectStorageEndpoint}` diff --git a/packages/backend/src/core/SignupService.ts b/packages/backend/src/core/SignupService.ts index 1e34d9e4f8..9cf203566d 100644 --- a/packages/backend/src/core/SignupService.ts +++ b/packages/backend/src/core/SignupService.ts @@ -14,6 +14,7 @@ import generateUserToken from '@/misc/generate-native-user-token.js'; import UsersChart from './chart/charts/users.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UtilityService } from './UtilityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class SignupService { @@ -37,6 +38,7 @@ export class SignupService { ) { } + @bindThis public async signup(opts: { username: User['username']; password?: string | null; diff --git a/packages/backend/src/core/TwoFactorAuthenticationService.ts b/packages/backend/src/core/TwoFactorAuthenticationService.ts index 54e87ec36f..150047fd22 100644 --- a/packages/backend/src/core/TwoFactorAuthenticationService.ts +++ b/packages/backend/src/core/TwoFactorAuthenticationService.ts @@ -103,6 +103,7 @@ function PEMString(pemBuffer: Buffer, type = 'CERTIFICATE') { `\n-----END ${type}-----\n` ); } +import { bindThis } from '@/decorators.js'; @Injectable() export class TwoFactorAuthenticationService { @@ -115,6 +116,7 @@ export class TwoFactorAuthenticationService { ) { } + @bindThis public hash(data: Buffer) { return crypto .createHash('sha256') @@ -122,6 +124,7 @@ export class TwoFactorAuthenticationService { .digest(); } + @bindThis public verifySignin({ publicKey, authenticatorData, @@ -159,6 +162,7 @@ export class TwoFactorAuthenticationService { .verify(PEMString(publicKey), signature); } + @bindThis public getProcedures() { return { none: { diff --git a/packages/backend/src/core/UserBlockingService.ts b/packages/backend/src/core/UserBlockingService.ts index 3399bb510f..d411768dc7 100644 --- a/packages/backend/src/core/UserBlockingService.ts +++ b/packages/backend/src/core/UserBlockingService.ts @@ -14,6 +14,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { LoggerService } from '@/core/LoggerService.js'; import { WebhookService } from '@/core/WebhookService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UserBlockingService { @@ -50,6 +51,7 @@ export class UserBlockingService { this.logger = this.loggerService.getLogger('user-block'); } + @bindThis public async block(blocker: User, blockee: User) { await Promise.all([ this.cancelRequest(blocker, blockee), @@ -76,6 +78,7 @@ export class UserBlockingService { } } + @bindThis private async cancelRequest(follower: User, followee: User) { const request = await this.followRequestsRepository.findOneBy({ followeeId: followee.id, @@ -126,6 +129,7 @@ export class UserBlockingService { } } + @bindThis private async unFollow(follower: User, followee: User) { const following = await this.followingsRepository.findOneBy({ followerId: follower.id, @@ -167,6 +171,7 @@ export class UserBlockingService { } } + @bindThis private async removeFromList(listOwner: User, user: User) { const userLists = await this.userListsRepository.findBy({ userId: listOwner.id, @@ -180,6 +185,7 @@ export class UserBlockingService { } } + @bindThis public async unblock(blocker: CacheableUser, blockee: CacheableUser) { const blocking = await this.blockingsRepository.findOneBy({ blockerId: blocker.id, diff --git a/packages/backend/src/core/UserCacheService.ts b/packages/backend/src/core/UserCacheService.ts index 25a600a8da..423c8993e3 100644 --- a/packages/backend/src/core/UserCacheService.ts +++ b/packages/backend/src/core/UserCacheService.ts @@ -6,6 +6,7 @@ import type { CacheableLocalUser, CacheableUser, ILocalUser } from '@/models/ent import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UserCacheService implements OnApplicationShutdown { @@ -23,7 +24,7 @@ export class UserCacheService implements OnApplicationShutdown { private userEntityService: UserEntityService, ) { - this.onMessage = this.onMessage.bind(this); + //this.onMessage = this.onMessage.bind(this); this.userByIdCache = new Cache(Infinity); this.localUserByNativeTokenCache = new Cache(Infinity); @@ -33,6 +34,7 @@ export class UserCacheService implements OnApplicationShutdown { this.redisSubscriber.on('message', this.onMessage); } + @bindThis private async onMessage(_: string, data: string): Promise { const obj = JSON.parse(data); @@ -68,6 +70,7 @@ export class UserCacheService implements OnApplicationShutdown { } } + @bindThis public onApplicationShutdown(signal?: string | undefined) { this.redisSubscriber.off('message', this.onMessage); } diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index 2f51e2a9df..a6cb042c7d 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -31,6 +31,7 @@ type Remote = IRemoteUser | { inbox: IRemoteUser['inbox']; }; type Both = Local | Remote; +import { bindThis } from '@/decorators.js'; @Injectable() export class UserFollowingService { @@ -66,6 +67,7 @@ export class UserFollowingService { ) { } + @bindThis 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 }), @@ -140,6 +142,7 @@ export class UserFollowingService { } } + @bindThis private async insertFollowingDoc( followee: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'] @@ -253,6 +256,7 @@ export class UserFollowingService { } } + @bindThis public async unfollow( follower: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; @@ -305,6 +309,7 @@ export class UserFollowingService { } } + @bindThis private async decrementFollowing( follower: {id: User['id']; host: User['host']; }, followee: { id: User['id']; host: User['host']; }, @@ -333,6 +338,7 @@ export class UserFollowingService { this.perUserFollowingChart.update(follower, followee, false); } + @bindThis public async createFollowRequest( follower: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; @@ -396,6 +402,7 @@ export class UserFollowingService { } } + @bindThis public async cancelFollowRequest( followee: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox'] @@ -431,6 +438,7 @@ export class UserFollowingService { }).then(packed => this.globalEventServie.publishMainStream(followee.id, 'meUpdated', packed)); } + @bindThis public async acceptFollowRequest( followee: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; @@ -458,6 +466,7 @@ export class UserFollowingService { }).then(packed => this.globalEventServie.publishMainStream(followee.id, 'meUpdated', packed)); } + @bindThis public async acceptAllFollowRequests( user: { id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; @@ -476,6 +485,7 @@ export class UserFollowingService { /** * API following/request/reject */ + @bindThis public async rejectFollowRequest(user: Local, follower: Both): Promise { if (this.userEntityService.isRemoteUser(follower)) { this.deliverReject(user, follower); @@ -491,6 +501,7 @@ export class UserFollowingService { /** * API following/reject */ + @bindThis public async rejectFollow(user: Local, follower: Both): Promise { if (this.userEntityService.isRemoteUser(follower)) { this.deliverReject(user, follower); @@ -506,6 +517,7 @@ export class UserFollowingService { /** * AP Reject/Follow */ + @bindThis public async remoteReject(actor: Remote, follower: Local): Promise { await this.removeFollowRequest(actor, follower); await this.removeFollow(actor, follower); @@ -515,6 +527,7 @@ export class UserFollowingService { /** * Remove follow request record */ + @bindThis private async removeFollowRequest(followee: Both, follower: Both): Promise { const request = await this.followRequestsRepository.findOneBy({ followeeId: followee.id, @@ -529,6 +542,7 @@ export class UserFollowingService { /** * Remove follow record */ + @bindThis private async removeFollow(followee: Both, follower: Both): Promise { const following = await this.followingsRepository.findOneBy({ followeeId: followee.id, @@ -544,6 +558,7 @@ export class UserFollowingService { /** * Deliver Reject to remote */ + @bindThis private async deliverReject(followee: Local, follower: Remote): Promise { const request = await this.followRequestsRepository.findOneBy({ followeeId: followee.id, @@ -557,6 +572,7 @@ export class UserFollowingService { /** * Publish unfollow to local */ + @bindThis private async publishUnfollow(followee: Both, follower: Local): Promise { const packedFollowee = await this.userEntityService.pack(followee.id, follower, { detail: true, diff --git a/packages/backend/src/core/UserKeypairStoreService.ts b/packages/backend/src/core/UserKeypairStoreService.ts index 8eca03a10b..1d3cc87c8d 100644 --- a/packages/backend/src/core/UserKeypairStoreService.ts +++ b/packages/backend/src/core/UserKeypairStoreService.ts @@ -4,6 +4,7 @@ import type { UserKeypairsRepository } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; import type { UserKeypair } from '@/models/entities/UserKeypair.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UserKeypairStoreService { @@ -16,6 +17,7 @@ export class UserKeypairStoreService { this.cache = new Cache(Infinity); } + @bindThis public async getUserKeypair(userId: User['id']): Promise { return await this.cache.fetch(userId, () => this.userKeypairsRepository.findOneByOrFail({ userId: userId })); } diff --git a/packages/backend/src/core/UserListService.ts b/packages/backend/src/core/UserListService.ts index 1d1ead5a1f..054387ff8e 100644 --- a/packages/backend/src/core/UserListService.ts +++ b/packages/backend/src/core/UserListService.ts @@ -9,6 +9,7 @@ import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ProxyAccountService } from '@/core/ProxyAccountService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UserListService { @@ -27,6 +28,7 @@ export class UserListService { ) { } + @bindThis public async push(target: User, list: UserList) { await this.userListJoiningsRepository.insert({ id: this.idService.genId(), diff --git a/packages/backend/src/core/UserMutingService.ts b/packages/backend/src/core/UserMutingService.ts index 4c09e450c8..3029d02c00 100644 --- a/packages/backend/src/core/UserMutingService.ts +++ b/packages/backend/src/core/UserMutingService.ts @@ -5,6 +5,7 @@ import { QueueService } from '@/core/QueueService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import type { User } from '@/models/entities/User.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UserMutingService { @@ -21,6 +22,7 @@ export class UserMutingService { ) { } + @bindThis public async mute(user: User, target: User): Promise { await this.mutingsRepository.insert({ id: this.idService.genId(), diff --git a/packages/backend/src/core/UserSuspendService.ts b/packages/backend/src/core/UserSuspendService.ts index 02f686bab6..df1664942f 100644 --- a/packages/backend/src/core/UserSuspendService.ts +++ b/packages/backend/src/core/UserSuspendService.ts @@ -8,6 +8,7 @@ import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UserSuspendService { @@ -28,6 +29,7 @@ export class UserSuspendService { ) { } + @bindThis public async doPostSuspend(user: { id: User['id']; host: User['host'] }): Promise { this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true }); @@ -57,6 +59,7 @@ export class UserSuspendService { } } + @bindThis public async doPostUnsuspend(user: User): Promise { this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false }); diff --git a/packages/backend/src/core/UtilityService.ts b/packages/backend/src/core/UtilityService.ts index 15dd684286..1412e6e9aa 100644 --- a/packages/backend/src/core/UtilityService.ts +++ b/packages/backend/src/core/UtilityService.ts @@ -3,6 +3,7 @@ import { toASCII } from 'punycode'; import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UtilityService { @@ -12,24 +13,29 @@ export class UtilityService { ) { } + @bindThis public getFullApAccount(username: string, host: string | null): string { return host ? `${username}@${this.toPuny(host)}` : `${username}@${this.toPuny(this.config.host)}`; } + @bindThis public isSelfHost(host: string | null): boolean { if (host == null) return true; return this.toPuny(this.config.host) === this.toPuny(host); } + @bindThis public extractDbHost(uri: string): string { const url = new URL(uri); return this.toPuny(url.hostname); } + @bindThis public toPuny(host: string): string { return toASCII(host.toLowerCase()); } + @bindThis public toPunyNullable(host: string | null | undefined): string | null { if (host == null) return null; return toASCII(host.toLowerCase()); diff --git a/packages/backend/src/core/VideoProcessingService.ts b/packages/backend/src/core/VideoProcessingService.ts index af4036a291..2807960cb7 100644 --- a/packages/backend/src/core/VideoProcessingService.ts +++ b/packages/backend/src/core/VideoProcessingService.ts @@ -5,6 +5,7 @@ import type { Config } from '@/config.js'; import { ImageProcessingService } from '@/core/ImageProcessingService.js'; import type { IImage } from '@/core/ImageProcessingService.js'; import { createTempDir } from '@/misc/create-temp.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class VideoProcessingService { @@ -16,6 +17,7 @@ export class VideoProcessingService { ) { } + @bindThis public async generateVideoThumbnail(source: string): Promise { const [dir, cleanup] = await createTempDir(); diff --git a/packages/backend/src/core/WebfingerService.ts b/packages/backend/src/core/WebfingerService.ts index d2a88be583..2d7afe5c88 100644 --- a/packages/backend/src/core/WebfingerService.ts +++ b/packages/backend/src/core/WebfingerService.ts @@ -14,6 +14,7 @@ type IWebFinger = { links: ILink[]; subject: string; }; +import { bindThis } from '@/decorators.js'; @Injectable() export class WebfingerService { @@ -25,12 +26,14 @@ export class WebfingerService { ) { } + @bindThis public async webfinger(query: string): Promise { const url = this.genUrl(query); return await this.httpRequestService.getJson(url, 'application/jrd+json, application/json') as IWebFinger; } + @bindThis private genUrl(query: string): string { if (query.match(/^https?:\/\//)) { const u = new URL(query); diff --git a/packages/backend/src/core/WebhookService.ts b/packages/backend/src/core/WebhookService.ts index 1a690314f8..91a39f1359 100644 --- a/packages/backend/src/core/WebhookService.ts +++ b/packages/backend/src/core/WebhookService.ts @@ -4,6 +4,7 @@ import type { WebhooksRepository } from '@/models/index.js'; import type { Webhook } from '@/models/entities/Webhook.js'; import { DI } from '@/di-symbols.js'; import type { OnApplicationShutdown } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; @Injectable() export class WebhookService implements OnApplicationShutdown { @@ -17,10 +18,11 @@ export class WebhookService implements OnApplicationShutdown { @Inject(DI.webhooksRepository) private webhooksRepository: WebhooksRepository, ) { - this.onMessage = this.onMessage.bind(this); + //this.onMessage = this.onMessage.bind(this); this.redisSubscriber.on('message', this.onMessage); } + @bindThis public async getActiveWebhooks() { if (!this.webhooksFetched) { this.webhooks = await this.webhooksRepository.findBy({ @@ -32,6 +34,7 @@ export class WebhookService implements OnApplicationShutdown { return this.webhooks; } + @bindThis private async onMessage(_: string, data: string): Promise { const obj = JSON.parse(data); @@ -64,6 +67,7 @@ export class WebhookService implements OnApplicationShutdown { } } + @bindThis public onApplicationShutdown(signal?: string | undefined) { this.redisSubscriber.off('message', this.onMessage); } diff --git a/packages/backend/src/core/activitypub/ApAudienceService.ts b/packages/backend/src/core/activitypub/ApAudienceService.ts index 744017aa3a..64f01644a7 100644 --- a/packages/backend/src/core/activitypub/ApAudienceService.ts +++ b/packages/backend/src/core/activitypub/ApAudienceService.ts @@ -4,6 +4,7 @@ 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 '@/misc/prelude/array.js'; +import { bindThis } from '@/decorators.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'; @@ -24,6 +25,7 @@ export class ApAudienceService { ) { } + @bindThis 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); @@ -66,6 +68,7 @@ export class ApAudienceService { }; } + @bindThis private groupingAudience(ids: string[], actor: CacheableRemoteUser) { const groups = { public: [] as string[], @@ -88,6 +91,7 @@ export class ApAudienceService { return groups; } + @bindThis private isPublic(id: string) { return [ 'https://www.w3.org/ns/activitystreams#Public', @@ -96,6 +100,7 @@ export class ApAudienceService { ].includes(id); } + @bindThis private isFollowers(id: string, actor: CacheableRemoteUser) { return ( id === (actor.followersUri ?? `${actor.uri}/followers`) diff --git a/packages/backend/src/core/activitypub/ApDbResolverService.ts b/packages/backend/src/core/activitypub/ApDbResolverService.ts index 77d200c3c8..1f28fb3a07 100644 --- a/packages/backend/src/core/activitypub/ApDbResolverService.ts +++ b/packages/backend/src/core/activitypub/ApDbResolverService.ts @@ -9,6 +9,7 @@ import type { UserPublickey } from '@/models/entities/UserPublickey.js'; import { UserCacheService } from '@/core/UserCacheService.js'; import type { Note } from '@/models/entities/Note.js'; import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; +import { bindThis } from '@/decorators.js'; import { getApId } from './type.js'; import { ApPersonService } from './models/ApPersonService.js'; import type { IObject } from './type.js'; @@ -57,6 +58,7 @@ export class ApDbResolverService { this.publicKeyByUserIdCache = new Cache(Infinity); } + @bindThis public parseUri(value: string | IObject): UriParseResult { const uri = getApId(value); @@ -82,6 +84,7 @@ export class ApDbResolverService { /** * AP Note => Misskey Note in DB */ + @bindThis public async getNoteFromApId(value: string | IObject): Promise { const parsed = this.parseUri(value); @@ -98,6 +101,7 @@ export class ApDbResolverService { } } + @bindThis public async getMessageFromApId(value: string | IObject): Promise { const parsed = this.parseUri(value); @@ -117,6 +121,7 @@ export class ApDbResolverService { /** * AP Person => Misskey User in DB */ + @bindThis public async getUserFromApId(value: string | IObject): Promise { const parsed = this.parseUri(value); @@ -136,6 +141,7 @@ export class ApDbResolverService { /** * AP KeyId => Misskey User and Key */ + @bindThis public async getAuthUserFromKeyId(keyId: string): Promise<{ user: CacheableRemoteUser; key: UserPublickey; @@ -161,6 +167,7 @@ export class ApDbResolverService { /** * AP Actor id => Misskey User and Key */ + @bindThis public async getAuthUserFromApId(uri: string): Promise<{ user: CacheableRemoteUser; key: UserPublickey | null; diff --git a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts index 6fc75a0397..256cf12651 100644 --- a/packages/backend/src/core/activitypub/ApDeliverManagerService.ts +++ b/packages/backend/src/core/activitypub/ApDeliverManagerService.ts @@ -6,6 +6,7 @@ import type { Config } from '@/config.js'; import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; import { QueueService } from '@/core/QueueService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; interface IRecipe { type: string; @@ -48,6 +49,7 @@ export class ApDeliverManagerService { * @param activity Activity * @param from Followee */ + @bindThis public async deliverToFollowers(actor: { id: ILocalUser['id']; host: null; }, activity: any) { const manager = new DeliverManager( this.userEntityService, @@ -65,6 +67,7 @@ export class ApDeliverManagerService { * @param activity Activity * @param to Target user */ + @bindThis public async deliverToUser(actor: { id: ILocalUser['id']; host: null; }, activity: any, to: IRemoteUser) { const manager = new DeliverManager( this.userEntityService, @@ -77,6 +80,7 @@ export class ApDeliverManagerService { await manager.execute(); } + @bindThis public createDeliverManager(actor: { id: User['id']; host: null; }, activity: any) { return new DeliverManager( this.userEntityService, @@ -114,6 +118,7 @@ class DeliverManager { /** * Add recipe for followers deliver */ + @bindThis public addFollowersRecipe() { const deliver = { type: 'Followers', @@ -126,6 +131,7 @@ class DeliverManager { * Add recipe for direct deliver * @param to To */ + @bindThis public addDirectRecipe(to: IRemoteUser) { const recipe = { type: 'Direct', @@ -139,6 +145,7 @@ class DeliverManager { * Add recipe * @param recipe Recipe */ + @bindThis public addRecipe(recipe: IRecipe) { this.recipes.push(recipe); } @@ -146,6 +153,7 @@ class DeliverManager { /** * Execute delivers */ + @bindThis public async execute() { if (!this.userEntityService.isLocalUser(this.actor)) return; diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts index 3da384ec2d..79a917426a 100644 --- a/packages/backend/src/core/activitypub/ApInboxService.ts +++ b/packages/backend/src/core/activitypub/ApInboxService.ts @@ -32,6 +32,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ApInboxService { @@ -85,6 +86,7 @@ export class ApInboxService { this.logger = this.apLoggerService.logger; } + @bindThis public async performActivity(actor: CacheableRemoteUser, activity: IObject) { if (isCollectionOrOrderedCollection(activity)) { const resolver = this.apResolverService.createResolver(); @@ -112,6 +114,7 @@ export class ApInboxService { } } + @bindThis public async performOneActivity(actor: CacheableRemoteUser, activity: IObject): Promise { if (actor.isSuspended) return; @@ -148,6 +151,7 @@ export class ApInboxService { } } + @bindThis private async follow(actor: CacheableRemoteUser, activity: IFollow): Promise { const followee = await this.apDbResolverService.getUserFromApId(activity.object); @@ -163,6 +167,7 @@ export class ApInboxService { return 'ok'; } + @bindThis private async like(actor: CacheableRemoteUser, activity: ILike): Promise { const targetUri = getApId(activity.object); @@ -180,6 +185,7 @@ export class ApInboxService { }).then(() => 'ok'); } + @bindThis private async read(actor: CacheableRemoteUser, activity: IRead): Promise { const id = await getApId(activity.object); @@ -202,6 +208,7 @@ export class ApInboxService { return `ok: mark as read (${message.userId} => ${message.recipientId} ${message.id})`; } + @bindThis private async accept(actor: CacheableRemoteUser, activity: IAccept): Promise { const uri = activity.id ?? activity; @@ -219,6 +226,7 @@ export class ApInboxService { return `skip: Unknown Accept type: ${getApType(object)}`; } + @bindThis private async acceptFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある @@ -242,6 +250,7 @@ export class ApInboxService { return 'ok'; } + @bindThis private async add(actor: CacheableRemoteUser, activity: IAdd): Promise { if ('actor' in activity && actor.uri !== activity.actor) { throw new Error('invalid actor'); @@ -261,6 +270,7 @@ export class ApInboxService { throw new Error(`unknown target: ${activity.target}`); } + @bindThis private async announce(actor: CacheableRemoteUser, activity: IAnnounce): Promise { const uri = getApId(activity); @@ -271,6 +281,7 @@ export class ApInboxService { this.announceNote(actor, activity, targetUri); } + @bindThis private async announceNote(actor: CacheableRemoteUser, activity: IAnnounce, targetUri: string): Promise { const uri = getApId(activity); @@ -330,6 +341,7 @@ export class ApInboxService { } } + @bindThis private async block(actor: CacheableRemoteUser, activity: IBlock): Promise { // ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず @@ -347,6 +359,7 @@ export class ApInboxService { return 'ok'; } + @bindThis private async create(actor: CacheableRemoteUser, activity: ICreate): Promise { const uri = getApId(activity); @@ -382,6 +395,7 @@ export class ApInboxService { } } + @bindThis private async createNote(resolver: Resolver, actor: CacheableRemoteUser, note: IObject, silent = false, activity?: ICreate): Promise { const uri = getApId(note); @@ -416,6 +430,7 @@ export class ApInboxService { } } + @bindThis private async delete(actor: CacheableRemoteUser, activity: IDelete): Promise { if ('actor' in activity && actor.uri !== activity.actor) { throw new Error('invalid actor'); @@ -457,6 +472,7 @@ export class ApInboxService { } } + @bindThis private async deleteActor(actor: CacheableRemoteUser, uri: string): Promise { this.logger.info(`Deleting the Actor: ${uri}`); @@ -478,6 +494,7 @@ export class ApInboxService { return `ok: queued ${job.name} ${job.id}`; } + @bindThis private async deleteNote(actor: CacheableRemoteUser, uri: string): Promise { this.logger.info(`Deleting the Note: ${uri}`); @@ -510,6 +527,7 @@ export class ApInboxService { } } + @bindThis private async flag(actor: CacheableRemoteUser, activity: IFlag): Promise { // objectは `(User|Note) | (User|Note)[]` だけど、全パターンDBスキーマと対応させられないので // 対象ユーザーは一番最初のユーザー として あとはコメントとして格納する @@ -534,6 +552,7 @@ export class ApInboxService { return 'ok'; } + @bindThis private async reject(actor: CacheableRemoteUser, activity: IReject): Promise { const uri = activity.id ?? activity; @@ -551,6 +570,7 @@ export class ApInboxService { return `skip: Unknown Reject type: ${getApType(object)}`; } + @bindThis private async rejectFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある @@ -574,6 +594,7 @@ export class ApInboxService { return 'ok'; } + @bindThis private async remove(actor: CacheableRemoteUser, activity: IRemove): Promise { if ('actor' in activity && actor.uri !== activity.actor) { throw new Error('invalid actor'); @@ -593,6 +614,7 @@ export class ApInboxService { throw new Error(`unknown target: ${activity.target}`); } + @bindThis private async undo(actor: CacheableRemoteUser, activity: IUndo): Promise { if ('actor' in activity && actor.uri !== activity.actor) { throw new Error('invalid actor'); @@ -618,6 +640,7 @@ export class ApInboxService { return `skip: unknown object type ${getApType(object)}`; } + @bindThis private async undoAccept(actor: CacheableRemoteUser, activity: IAccept): Promise { const follower = await this.apDbResolverService.getUserFromApId(activity.object); if (follower == null) { @@ -637,6 +660,7 @@ export class ApInboxService { return 'skip: フォローされていない'; } + @bindThis private async undoAnnounce(actor: CacheableRemoteUser, activity: IAnnounce): Promise { const uri = getApId(activity); @@ -651,6 +675,7 @@ export class ApInboxService { return 'ok: deleted'; } + @bindThis private async undoBlock(actor: CacheableRemoteUser, activity: IBlock): Promise { const blockee = await this.apDbResolverService.getUserFromApId(activity.object); @@ -666,6 +691,7 @@ export class ApInboxService { return 'ok'; } + @bindThis private async undoFollow(actor: CacheableRemoteUser, activity: IFollow): Promise { const followee = await this.apDbResolverService.getUserFromApId(activity.object); if (followee == null) { @@ -699,6 +725,7 @@ export class ApInboxService { return 'skip: リクエストもフォローもされていない'; } + @bindThis private async undoLike(actor: CacheableRemoteUser, activity: ILike): Promise { const targetUri = getApId(activity.object); @@ -713,6 +740,7 @@ export class ApInboxService { return 'ok'; } + @bindThis private async update(actor: CacheableRemoteUser, activity: IUpdate): Promise { if ('actor' in activity && actor.uri !== activity.actor) { return 'skip: invalid actor'; diff --git a/packages/backend/src/core/activitypub/ApLoggerService.ts b/packages/backend/src/core/activitypub/ApLoggerService.ts index a742cc42da..b9bf1e4054 100644 --- a/packages/backend/src/core/activitypub/ApLoggerService.ts +++ b/packages/backend/src/core/activitypub/ApLoggerService.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { RemoteLoggerService } from '@/core/RemoteLoggerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ApLoggerService { diff --git a/packages/backend/src/core/activitypub/ApMfmService.ts b/packages/backend/src/core/activitypub/ApMfmService.ts index 8804fde64a..6116822f7a 100644 --- a/packages/backend/src/core/activitypub/ApMfmService.ts +++ b/packages/backend/src/core/activitypub/ApMfmService.ts @@ -6,6 +6,7 @@ import { MfmService } from '@/core/MfmService.js'; import type { Note } from '@/models/entities/Note.js'; import { extractApHashtagObjects } from './models/tag.js'; import type { IObject } from './type.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ApMfmService { @@ -17,12 +18,14 @@ export class ApMfmService { ) { } + @bindThis 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); } + @bindThis 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/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index 38a92567c3..1800840ee9 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -25,6 +25,7 @@ import { LdSignatureService } from './LdSignatureService.js'; import { ApMfmService } from './ApMfmService.js'; import type { IActivity, IObject } from './type.js'; import type { IIdentifier } from './models/identifier.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ApRendererService { @@ -59,6 +60,7 @@ export class ApRendererService { ) { } + @bindThis public renderAccept(object: any, user: { id: User['id']; host: null }) { return { type: 'Accept', @@ -67,6 +69,7 @@ export class ApRendererService { }; } + @bindThis public renderAdd(user: ILocalUser, target: any, object: any) { return { type: 'Add', @@ -76,6 +79,7 @@ export class ApRendererService { }; } + @bindThis public renderAnnounce(object: any, note: Note) { const attributedTo = `${this.config.url}/users/${note.userId}`; @@ -108,6 +112,7 @@ export class ApRendererService { * * @param block The block to be rendered. The blockee relation must be loaded. */ + @bindThis public renderBlock(block: Blocking) { if (block.blockee?.uri == null) { throw new Error('renderBlock: missing blockee uri'); @@ -121,6 +126,7 @@ export class ApRendererService { }; } + @bindThis public renderCreate(object: any, note: Note) { const activity = { id: `${this.config.url}/notes/${note.id}/activity`, @@ -136,6 +142,7 @@ export class ApRendererService { return activity; } + @bindThis public renderDelete(object: any, user: { id: User['id']; host: null }) { return { type: 'Delete', @@ -145,6 +152,7 @@ export class ApRendererService { }; } + @bindThis public renderDocument(file: DriveFile) { return { type: 'Document', @@ -154,6 +162,7 @@ export class ApRendererService { }; } + @bindThis public renderEmoji(emoji: Emoji) { return { id: `${this.config.url}/emojis/${emoji.name}`, @@ -170,6 +179,7 @@ export class ApRendererService { // to anonymise reporters, the reporting actor must be a system user // object has to be a uri or array of uris + @bindThis public renderFlag(user: ILocalUser, object: [string], content: string) { return { type: 'Flag', @@ -179,6 +189,7 @@ export class ApRendererService { }; } + @bindThis public renderFollowRelay(relay: Relay, relayActor: ILocalUser) { const follow = { id: `${this.config.url}/activities/follow-relay/${relay.id}`, @@ -194,11 +205,13 @@ export class ApRendererService { * Convert (local|remote)(Follower|Followee)ID to URL * @param id Follower|Followee ID */ + @bindThis public async renderFollowUser(id: User['id']) { const user = await this.usersRepository.findOneByOrFail({ id: id }); return this.userEntityService.isLocalUser(user) ? `${this.config.url}/users/${user.id}` : user.uri; } + @bindThis public renderFollow( follower: { id: User['id']; host: User['host']; uri: User['host'] }, followee: { id: User['id']; host: User['host']; uri: User['host'] }, @@ -214,6 +227,7 @@ export class ApRendererService { return follow; } + @bindThis public renderHashtag(tag: string) { return { type: 'Hashtag', @@ -222,6 +236,7 @@ export class ApRendererService { }; } + @bindThis public renderImage(file: DriveFile) { return { type: 'Image', @@ -231,6 +246,7 @@ export class ApRendererService { }; } + @bindThis public renderKey(user: ILocalUser, key: UserKeypair, postfix?: string) { return { id: `${this.config.url}/users/${user.id}${postfix ?? '/publickey'}`, @@ -243,6 +259,7 @@ export class ApRendererService { }; } + @bindThis public async renderLike(noteReaction: NoteReaction, note: { uri: string | null }) { const reaction = noteReaction.reaction; @@ -268,6 +285,7 @@ export class ApRendererService { return object; } + @bindThis public renderMention(mention: User) { return { type: 'Mention', @@ -276,6 +294,7 @@ export class ApRendererService { }; } + @bindThis public async renderNote(note: Note, dive = true, isTalk = false): Promise { const getPromisedFiles = async (ids: string[]) => { if (!ids || ids.length === 0) return []; @@ -420,6 +439,7 @@ export class ApRendererService { }; } + @bindThis public async renderPerson(user: ILocalUser) { const id = `${this.config.url}/users/${user.id}`; const isSystem = !!user.username.match(/\./); @@ -496,6 +516,7 @@ export class ApRendererService { return person; } + @bindThis public async renderQuestion(user: { id: User['id'] }, note: Note, poll: Poll) { const question = { type: 'Question', @@ -515,6 +536,7 @@ export class ApRendererService { return question; } + @bindThis public renderRead(user: { id: User['id'] }, message: MessagingMessage) { return { type: 'Read', @@ -523,6 +545,7 @@ export class ApRendererService { }; } + @bindThis public renderReject(object: any, user: { id: User['id'] }) { return { type: 'Reject', @@ -531,6 +554,7 @@ export class ApRendererService { }; } + @bindThis public renderRemove(user: { id: User['id'] }, target: any, object: any) { return { type: 'Remove', @@ -540,6 +564,7 @@ export class ApRendererService { }; } + @bindThis public renderTombstone(id: string) { return { id, @@ -547,6 +572,7 @@ export class ApRendererService { }; } + @bindThis 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; @@ -560,6 +586,7 @@ export class ApRendererService { }; } + @bindThis public renderUpdate(object: any, user: { id: User['id'] }) { const activity = { id: `${this.config.url}/users/${user.id}#updates/${new Date().getTime()}`, @@ -573,6 +600,7 @@ export class ApRendererService { return activity; } + @bindThis 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`, @@ -591,6 +619,7 @@ export class ApRendererService { }; } + @bindThis public renderActivity(x: any): IActivity | null { if (x == null) return null; @@ -632,6 +661,7 @@ export class ApRendererService { }, x); } + @bindThis public async attachLdSignature(activity: any, user: { id: User['id']; host: null; }): Promise { const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); @@ -651,6 +681,7 @@ export class ApRendererService { * @param prev URL of prev page (optional) * @param next URL of next page (optional) */ + @bindThis public renderOrderedCollectionPage(id: string, totalItems: any, orderedItems: any, partOf: string, prev?: string, next?: string) { const page = { id, @@ -674,6 +705,7 @@ export class ApRendererService { * @param last URL of last page (optional) * @param orderedItems attached objects (optional) */ + @bindThis public renderOrderedCollection(id: string | null, totalItems: any, first?: string, last?: string, orderedItems?: IObject[]) { const page: any = { id, @@ -688,6 +720,7 @@ export class ApRendererService { return page; } + @bindThis private async getEmojis(names: string[]): Promise { if (names == null || names.length === 0) return []; diff --git a/packages/backend/src/core/activitypub/ApRequestService.ts b/packages/backend/src/core/activitypub/ApRequestService.ts index baad46d668..d1edd579fa 100644 --- a/packages/backend/src/core/activitypub/ApRequestService.ts +++ b/packages/backend/src/core/activitypub/ApRequestService.ts @@ -6,6 +6,7 @@ import type { Config } from '@/config.js'; import type { User } from '@/models/entities/User.js'; import { UserKeypairStoreService } from '@/core/UserKeypairStoreService.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; +import { bindThis } from '@/decorators.js'; type Request = { url: string; @@ -36,6 +37,7 @@ export class ApRequestService { ) { } + @bindThis private 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')}`; @@ -61,6 +63,7 @@ export class ApRequestService { }; } + @bindThis private createSignedGet(args: { key: PrivateKey, url: string, additionalHeaders: Record }): Signed { const u = new URL(args.url); @@ -84,6 +87,7 @@ export class ApRequestService { }; } + @bindThis private 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'); @@ -101,6 +105,7 @@ export class ApRequestService { }; } + @bindThis private genSigningString(request: Request, includeHeaders: string[]): string { request.headers = this.lcObjectKey(request.headers); @@ -117,16 +122,19 @@ export class ApRequestService { return results.join('\n'); } + @bindThis private 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; } + @bindThis private objectAssignWithLcKey(a: Record, b: Record): Record { return Object.assign(this.lcObjectKey(a), this.lcObjectKey(b)); } + @bindThis public async signedPost(user: { id: User['id'] }, url: string, object: any) { const body = JSON.stringify(object); @@ -157,6 +165,7 @@ export class ApRequestService { * @param user http-signature user * @param url URL to fetch */ + @bindThis public async signedGet(url: string, user: { id: User['id'] }) { const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts index bcdb9383d1..e96c84f148 100644 --- a/packages/backend/src/core/activitypub/ApResolverService.ts +++ b/packages/backend/src/core/activitypub/ApResolverService.ts @@ -7,58 +7,13 @@ import { MetaService } from '@/core/MetaService.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; import { DI } from '@/di-symbols.js'; import { UtilityService } from '@/core/UtilityService.js'; +import { bindThis } from '@/decorators.js'; import { isCollectionOrOrderedCollection } from './type.js'; import { ApDbResolverService } from './ApDbResolverService.js'; import { ApRendererService } from './ApRendererService.js'; import { ApRequestService } from './ApRequestService.js'; import type { IObject, ICollection, IOrderedCollection } from './type.js'; -@Injectable() -export class ApResolverService { - constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - - @Inject(DI.pollsRepository) - private pollsRepository: PollsRepository, - - @Inject(DI.noteReactionsRepository) - private noteReactionsRepository: NoteReactionsRepository, - - private utilityService: UtilityService, - private instanceActorService: InstanceActorService, - private metaService: MetaService, - private apRequestService: ApRequestService, - private httpRequestService: HttpRequestService, - private apRendererService: ApRendererService, - private apDbResolverService: ApDbResolverService, - ) { - } - - public createResolver(): Resolver { - return new Resolver( - this.config, - this.usersRepository, - this.notesRepository, - this.pollsRepository, - this.noteReactionsRepository, - this.utilityService, - this.instanceActorService, - this.metaService, - this.apRequestService, - this.httpRequestService, - this.apRendererService, - this.apDbResolverService, - ); - } -} - export class Resolver { private history: Set; private user?: ILocalUser; @@ -76,15 +31,17 @@ export class Resolver { private httpRequestService: HttpRequestService, private apRendererService: ApRendererService, private apDbResolverService: ApDbResolverService, - private recursionLimit = 100 + private recursionLimit = 100, ) { this.history = new Set(); } + @bindThis public getHistory(): string[] { return Array.from(this.history); } + @bindThis public async resolveCollection(value: string | IObject): Promise { const collection = typeof value === 'string' ? await this.resolve(value) @@ -97,6 +54,7 @@ export class Resolver { } } + @bindThis public async resolve(value: string | IObject): Promise { if (value == null) { throw new Error('resolvee is null (or undefined)'); @@ -152,6 +110,7 @@ export class Resolver { return object; } + @bindThis private resolveLocal(url: string): Promise { const parsed = this.apDbResolverService.parseUri(url); if (!parsed.local) throw new Error('resolveLocal: not local'); @@ -193,3 +152,50 @@ export class Resolver { } } } + +@Injectable() +export class ApResolverService { + constructor( + @Inject(DI.config) + private config: Config, + + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + + @Inject(DI.pollsRepository) + private pollsRepository: PollsRepository, + + @Inject(DI.noteReactionsRepository) + private noteReactionsRepository: NoteReactionsRepository, + + private utilityService: UtilityService, + private instanceActorService: InstanceActorService, + private metaService: MetaService, + private apRequestService: ApRequestService, + private httpRequestService: HttpRequestService, + private apRendererService: ApRendererService, + private apDbResolverService: ApDbResolverService, + ) { + } + + @bindThis + public createResolver(): Resolver { + return new Resolver( + this.config, + this.usersRepository, + this.notesRepository, + this.pollsRepository, + this.noteReactionsRepository, + this.utilityService, + this.instanceActorService, + this.metaService, + this.apRequestService, + this.httpRequestService, + this.apRendererService, + this.apDbResolverService, + ); + } +} diff --git a/packages/backend/src/core/activitypub/LdSignatureService.ts b/packages/backend/src/core/activitypub/LdSignatureService.ts index ea39f15b2b..b71320ed0b 100644 --- a/packages/backend/src/core/activitypub/LdSignatureService.ts +++ b/packages/backend/src/core/activitypub/LdSignatureService.ts @@ -2,22 +2,11 @@ import * as crypto from 'node:crypto'; import { Inject, Injectable } from '@nestjs/common'; import fetch from 'node-fetch'; import { HttpRequestService } from '@/core/HttpRequestService.js'; +import { bindThis } from '@/decorators.js'; import { CONTEXTS } from './misc/contexts.js'; // RsaSignature2017 based from https://github.com/transmute-industries/RsaSignature2017 -@Injectable() -export class LdSignatureService { - constructor( - private httpRequestService: HttpRequestService, - ) { - } - - public use(): LdSignature { - return new LdSignature(this.httpRequestService); - } -} - class LdSignature { public debug = false; public preLoad = true; @@ -28,6 +17,7 @@ class LdSignature { ) { } + @bindThis public async signRsaSignature2017(data: any, privateKey: string, creator: string, domain?: string, created?: Date): Promise { const options = { type: 'RsaSignature2017', @@ -64,6 +54,7 @@ class LdSignature { }; } + @bindThis public async verifyRsaSignature2017(data: any, publicKey: string): Promise { const toBeSigned = await this.createVerifyData(data, data.signature); const verifier = crypto.createVerify('sha256'); @@ -71,6 +62,7 @@ class LdSignature { return verifier.verify(publicKey, data.signature.signatureValue, 'base64'); } + @bindThis public async createVerifyData(data: any, options: any) { const transformedOptions = { ...options, @@ -90,11 +82,13 @@ class LdSignature { return verifyData; } + @bindThis public async normalize(data: any) { const customLoader = this.getLoader(); return 42; } + @bindThis private getLoader() { return async (url: string): Promise => { if (!url.match('^https?\:\/\/')) throw `Invalid URL ${url}`; @@ -120,6 +114,7 @@ class LdSignature { }; } + @bindThis private async fetchDocument(url: string) { const json = await fetch(url, { headers: { @@ -139,9 +134,23 @@ class LdSignature { return json; } + @bindThis public sha256(data: string): string { const hash = crypto.createHash('sha256'); hash.update(data); return hash.digest('hex'); } } + +@Injectable() +export class LdSignatureService { + constructor( + private httpRequestService: HttpRequestService, + ) { + } + + @bindThis + public use(): LdSignature { + return new LdSignature(this.httpRequestService); + } +} diff --git a/packages/backend/src/core/activitypub/models/ApImageService.ts b/packages/backend/src/core/activitypub/models/ApImageService.ts index 9bf87f19d4..58fcc8cb53 100644 --- a/packages/backend/src/core/activitypub/models/ApImageService.ts +++ b/packages/backend/src/core/activitypub/models/ApImageService.ts @@ -11,6 +11,7 @@ import { DriveService } from '@/core/DriveService.js'; import type Logger from '@/logger.js'; import { ApResolverService } from '../ApResolverService.js'; import { ApLoggerService } from '../ApLoggerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ApImageService { @@ -34,6 +35,7 @@ export class ApImageService { /** * Imageを作成します。 */ + @bindThis public async createImage(actor: CacheableRemoteUser, value: any): Promise { // 投稿者が凍結されていたらスキップ if (actor.isSuspended) { @@ -81,6 +83,7 @@ export class ApImageService { * Misskeyに対象のImageが登録されていればそれを返し、そうでなければ * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 */ + @bindThis public async resolveImage(actor: CacheableRemoteUser, value: any): Promise { // TODO diff --git a/packages/backend/src/core/activitypub/models/ApMentionService.ts b/packages/backend/src/core/activitypub/models/ApMentionService.ts index 1275e24c62..41e6c6b14f 100644 --- a/packages/backend/src/core/activitypub/models/ApMentionService.ts +++ b/packages/backend/src/core/activitypub/models/ApMentionService.ts @@ -9,6 +9,7 @@ import { isMention } from '../type.js'; import { ApResolverService, Resolver } from '../ApResolverService.js'; import { ApPersonService } from './ApPersonService.js'; import type { IObject, IApMention } from '../type.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ApMentionService { @@ -21,6 +22,7 @@ export class ApMentionService { ) { } + @bindThis public async extractApMentions(tags: IObject | IObject[] | null | undefined, resolver: Resolver) { const hrefs = unique(this.extractApMentionObjects(tags).map(x => x.href as string)); @@ -32,6 +34,7 @@ export class ApMentionService { return mentionedUsers; } + @bindThis public extractApMentionObjects(tags: IObject | IObject[] | null | undefined): IApMention[] { if (tags == null) return []; return toArray(tags).filter(isMention); diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index 7cf6725a38..e1d93a08b0 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -32,6 +32,7 @@ import { ApQuestionService } from './ApQuestionService.js'; import { ApImageService } from './ApImageService.js'; import type { Resolver } from '../ApResolverService.js'; import type { IObject, IPost } from '../type.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ApNoteService { @@ -74,6 +75,7 @@ export class ApNoteService { this.logger = this.apLoggerService.logger; } + @bindThis public validateNote(object: any, uri: string) { const expectHost = this.utilityService.extractDbHost(uri); @@ -101,6 +103,7 @@ export class ApNoteService { * * Misskeyに対象のNoteが登録されていればそれを返します。 */ + @bindThis public async fetchNote(object: string | IObject): Promise { return await this.apDbResolverService.getNoteFromApId(object); } @@ -108,6 +111,7 @@ export class ApNoteService { /** * Noteを作成します。 */ + @bindThis public async createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise { if (resolver == null) resolver = this.apResolverService.createResolver(); @@ -313,6 +317,7 @@ export class ApNoteService { * Misskeyに対象のNoteが登録されていればそれを返し、そうでなければ * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 */ + @bindThis 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'); @@ -345,6 +350,7 @@ export class ApNoteService { } } + @bindThis public async extractEmojis(tags: IObject | IObject[], host: string): Promise { host = this.utilityService.toPuny(host); diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index f9d6f42ef6..d5faf37df2 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -72,6 +72,7 @@ function addService(target: { [x: string]: any }, source: IApPropertyValue) { target[source.name.split(':')[2]] = service(id, username); } } +import { bindThis } from '@/decorators.js'; @Injectable() export class ApPersonService implements OnModuleInit { @@ -161,6 +162,7 @@ export class ApPersonService implements OnModuleInit { * @param x Fetched object * @param uri Fetch target URI */ + @bindThis private validateActor(x: IObject, uri: string): IActor { const expectHost = this.utilityService.toPuny(new URL(uri).hostname); @@ -224,6 +226,7 @@ export class ApPersonService implements OnModuleInit { * * Misskeyに対象のPersonが登録されていればそれを返します。 */ + @bindThis public async fetchPerson(uri: string, resolver?: Resolver): Promise { if (typeof uri !== 'string') throw new Error('uri is not string'); @@ -253,6 +256,7 @@ export class ApPersonService implements OnModuleInit { /** * Personを作成します。 */ + @bindThis public async createPerson(uri: string, resolver?: Resolver): Promise { if (typeof uri !== 'string') throw new Error('uri is not string'); @@ -402,6 +406,7 @@ export class ApPersonService implements OnModuleInit { * @param resolver Resolver * @param hint Hint of Person object (この値が正当なPersonの場合、Remote resolveをせずに更新に利用します) */ + @bindThis public async updatePerson(uri: string, resolver?: Resolver | null, hint?: IObject): Promise { if (typeof uri !== 'string') throw new Error('uri is not string'); @@ -512,6 +517,7 @@ export class ApPersonService implements OnModuleInit { * Misskeyに対象のPersonが登録されていればそれを返し、そうでなければ * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 */ + @bindThis public async resolvePerson(uri: string, resolver?: Resolver): Promise { if (typeof uri !== 'string') throw new Error('uri is not string'); @@ -528,6 +534,7 @@ export class ApPersonService implements OnModuleInit { return await this.createPerson(uri, resolver); } + @bindThis public analyzeAttachments(attachments: IObject | IObject[] | undefined) { const fields: { name: string, @@ -551,6 +558,7 @@ export class ApPersonService implements OnModuleInit { return { fields, services }; } + @bindThis public async updateFeatured(userId: User['id'], resolver?: Resolver) { const user = await this.usersRepository.findOneByOrFail({ id: userId }); if (!this.userEntityService.isRemoteUser(user)) return; diff --git a/packages/backend/src/core/activitypub/models/ApQuestionService.ts b/packages/backend/src/core/activitypub/models/ApQuestionService.ts index 5793b98353..13a2f0fa5c 100644 --- a/packages/backend/src/core/activitypub/models/ApQuestionService.ts +++ b/packages/backend/src/core/activitypub/models/ApQuestionService.ts @@ -9,6 +9,7 @@ import { ApLoggerService } from '../ApLoggerService.js'; import { ApResolverService } from '../ApResolverService.js'; import type { Resolver } from '../ApResolverService.js'; import type { IObject, IQuestion } from '../type.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ApQuestionService { @@ -30,6 +31,7 @@ export class ApQuestionService { this.logger = this.apLoggerService.logger; } + @bindThis public async extractPollFromQuestion(source: string | IObject, resolver?: Resolver): Promise { if (resolver == null) resolver = this.apResolverService.createResolver(); @@ -65,6 +67,7 @@ export class ApQuestionService { * @param uri URI of AP Question object * @returns true if updated */ + @bindThis public async updateQuestion(value: any, resolver?: Resolver) { const uri = typeof value === 'string' ? value : value.id; diff --git a/packages/backend/src/core/chart/ChartLoggerService.ts b/packages/backend/src/core/chart/ChartLoggerService.ts index 544a006ac9..d392c6d595 100644 --- a/packages/backend/src/core/chart/ChartLoggerService.ts +++ b/packages/backend/src/core/chart/ChartLoggerService.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { LoggerService } from '@/core/LoggerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ChartLoggerService { diff --git a/packages/backend/src/core/chart/ChartManagementService.ts b/packages/backend/src/core/chart/ChartManagementService.ts index 6476cd6843..13ee06c6c5 100644 --- a/packages/backend/src/core/chart/ChartManagementService.ts +++ b/packages/backend/src/core/chart/ChartManagementService.ts @@ -13,6 +13,7 @@ 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 { OnApplicationShutdown } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ChartManagementService implements OnApplicationShutdown { @@ -49,6 +50,7 @@ export class ChartManagementService implements OnApplicationShutdown { ]; } + @bindThis public async run() { // 20分おきにメモリ情報をDBに書き込み this.saveIntervalId = setInterval(() => { diff --git a/packages/backend/src/core/chart/charts/active-users.ts b/packages/backend/src/core/chart/charts/active-users.ts index 40c60910ea..bc0ba25cbb 100644 --- a/packages/backend/src/core/chart/charts/active-users.ts +++ b/packages/backend/src/core/chart/charts/active-users.ts @@ -3,6 +3,7 @@ import { DataSource } from 'typeorm'; import { AppLockService } from '@/core/AppLockService.js'; import type { User } from '@/models/entities/User.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/active-users.js'; @@ -36,6 +37,7 @@ export default class ActiveUsersChart extends Chart { return {}; } + @bindThis public async read(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise { await this.commit({ 'read': [user.id], @@ -48,6 +50,7 @@ export default class ActiveUsersChart extends Chart { }); } + @bindThis public async write(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise { await this.commit({ 'write': [user.id], diff --git a/packages/backend/src/core/chart/charts/ap-request.ts b/packages/backend/src/core/chart/charts/ap-request.ts index 4b91fbbf18..ce377460c8 100644 --- a/packages/backend/src/core/chart/charts/ap-request.ts +++ b/packages/backend/src/core/chart/charts/ap-request.ts @@ -2,6 +2,7 @@ import { Injectable, Inject } from '@nestjs/common'; import { DataSource } from 'typeorm'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/ap-request.js'; @@ -31,18 +32,21 @@ export default class ApRequestChart extends Chart { return {}; } + @bindThis public async deliverSucc(): Promise { await this.commit({ 'deliverSucceeded': 1, }); } + @bindThis public async deliverFail(): Promise { await this.commit({ 'deliverFailed': 1, }); } + @bindThis public async inbox(): Promise { await this.commit({ 'inboxReceived': 1, diff --git a/packages/backend/src/core/chart/charts/drive.ts b/packages/backend/src/core/chart/charts/drive.ts index 494dfbbe57..da36b944f5 100644 --- a/packages/backend/src/core/chart/charts/drive.ts +++ b/packages/backend/src/core/chart/charts/drive.ts @@ -3,6 +3,7 @@ import { Not, IsNull, DataSource } from 'typeorm'; import type { DriveFile } from '@/models/entities/DriveFile.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/drive.js'; @@ -32,6 +33,7 @@ export default class DriveChart extends Chart { return {}; } + @bindThis public async update(file: DriveFile, isAdditional: boolean): Promise { const fileSizeKb = file.size / 1000; await this.commit(file.userHost === null ? { diff --git a/packages/backend/src/core/chart/charts/federation.ts b/packages/backend/src/core/chart/charts/federation.ts index 21e4cedea3..d9234e8028 100644 --- a/packages/backend/src/core/chart/charts/federation.ts +++ b/packages/backend/src/core/chart/charts/federation.ts @@ -4,6 +4,7 @@ import type { FollowingsRepository, InstancesRepository } from '@/models/index.j import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { MetaService } from '@/core/MetaService.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/federation.js'; @@ -107,6 +108,7 @@ export default class FederationChart extends Chart { }; } + @bindThis public async deliverd(host: string, succeeded: boolean): Promise { await this.commit(succeeded ? { 'deliveredInstances': [host], @@ -115,6 +117,7 @@ export default class FederationChart extends Chart { }); } + @bindThis public async inbox(host: string): Promise { await this.commit({ 'inboxInstances': [host], diff --git a/packages/backend/src/core/chart/charts/hashtag.ts b/packages/backend/src/core/chart/charts/hashtag.ts index 8b8c795cfd..3899b41363 100644 --- a/packages/backend/src/core/chart/charts/hashtag.ts +++ b/packages/backend/src/core/chart/charts/hashtag.ts @@ -4,6 +4,7 @@ import type { User } from '@/models/entities/User.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/hashtag.js'; @@ -34,6 +35,7 @@ export default class HashtagChart extends Chart { return {}; } + @bindThis public async update(hashtag: string, user: { id: User['id'], host: User['host'] }): Promise { await this.commit({ 'local.users': this.userEntityService.isLocalUser(user) ? [user.id] : [], diff --git a/packages/backend/src/core/chart/charts/instance.ts b/packages/backend/src/core/chart/charts/instance.ts index 2e0f4c7126..8ca88d80e3 100644 --- a/packages/backend/src/core/chart/charts/instance.ts +++ b/packages/backend/src/core/chart/charts/instance.ts @@ -6,6 +6,7 @@ import type { Note } from '@/models/entities/Note.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { UtilityService } from '@/core/UtilityService.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/instance.js'; @@ -68,12 +69,14 @@ export default class InstanceChart extends Chart { return {}; } + @bindThis public async requestReceived(host: string): Promise { await this.commit({ 'requests.received': 1, }, this.utilityService.toPuny(host)); } + @bindThis public async requestSent(host: string, isSucceeded: boolean): Promise { await this.commit({ 'requests.succeeded': isSucceeded ? 1 : 0, @@ -81,6 +84,7 @@ export default class InstanceChart extends Chart { }, this.utilityService.toPuny(host)); } + @bindThis public async newUser(host: string): Promise { await this.commit({ 'users.total': 1, @@ -88,6 +92,7 @@ export default class InstanceChart extends Chart { }, this.utilityService.toPuny(host)); } + @bindThis public async updateNote(host: string, note: Note, isAdditional: boolean): Promise { await this.commit({ 'notes.total': isAdditional ? 1 : -1, @@ -100,6 +105,7 @@ export default class InstanceChart extends Chart { }, this.utilityService.toPuny(host)); } + @bindThis public async updateFollowing(host: string, isAdditional: boolean): Promise { await this.commit({ 'following.total': isAdditional ? 1 : -1, @@ -108,6 +114,7 @@ export default class InstanceChart extends Chart { }, this.utilityService.toPuny(host)); } + @bindThis public async updateFollowers(host: string, isAdditional: boolean): Promise { await this.commit({ 'followers.total': isAdditional ? 1 : -1, @@ -116,6 +123,7 @@ export default class InstanceChart extends Chart { }, this.utilityService.toPuny(host)); } + @bindThis public async updateDrive(file: DriveFile, isAdditional: boolean): Promise { const fileSizeKb = file.size / 1000; await this.commit({ diff --git a/packages/backend/src/core/chart/charts/notes.ts b/packages/backend/src/core/chart/charts/notes.ts index 2153cfe4b4..23dc248fec 100644 --- a/packages/backend/src/core/chart/charts/notes.ts +++ b/packages/backend/src/core/chart/charts/notes.ts @@ -4,6 +4,7 @@ import type { NotesRepository } from '@/models/index.js'; import type { Note } from '@/models/entities/Note.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/notes.js'; @@ -44,6 +45,7 @@ export default class NotesChart extends Chart { return {}; } + @bindThis public async update(note: Note, isAdditional: boolean): Promise { const prefix = note.userHost === null ? 'local' : 'remote'; diff --git a/packages/backend/src/core/chart/charts/per-user-drive.ts b/packages/backend/src/core/chart/charts/per-user-drive.ts index a44460bb4e..ffba04b041 100644 --- a/packages/backend/src/core/chart/charts/per-user-drive.ts +++ b/packages/backend/src/core/chart/charts/per-user-drive.ts @@ -5,6 +5,7 @@ import type { DriveFile } from '@/models/entities/DriveFile.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/per-user-drive.js'; @@ -46,6 +47,7 @@ export default class PerUserDriveChart extends Chart { return {}; } + @bindThis public async update(file: DriveFile, isAdditional: boolean): Promise { const fileSizeKb = file.size / 1000; await this.commit({ diff --git a/packages/backend/src/core/chart/charts/per-user-following.ts b/packages/backend/src/core/chart/charts/per-user-following.ts index 5ea08a0872..aea6d44a9a 100644 --- a/packages/backend/src/core/chart/charts/per-user-following.ts +++ b/packages/backend/src/core/chart/charts/per-user-following.ts @@ -5,6 +5,7 @@ import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { FollowingsRepository } from '@/models/index.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/per-user-following.js'; @@ -55,6 +56,7 @@ export default class PerUserFollowingChart extends Chart { return {}; } + @bindThis public async update(follower: { id: User['id']; host: User['host']; }, followee: { id: User['id']; host: User['host']; }, isFollow: boolean): Promise { const prefixFollower = this.userEntityService.isLocalUser(follower) ? 'local' : 'remote'; const prefixFollowee = this.userEntityService.isLocalUser(followee) ? 'local' : 'remote'; diff --git a/packages/backend/src/core/chart/charts/per-user-notes.ts b/packages/backend/src/core/chart/charts/per-user-notes.ts index 5c14309d89..1e2a579dfa 100644 --- a/packages/backend/src/core/chart/charts/per-user-notes.ts +++ b/packages/backend/src/core/chart/charts/per-user-notes.ts @@ -5,6 +5,7 @@ import type { Note } from '@/models/entities/Note.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import type { NotesRepository } from '@/models/index.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/per-user-notes.js'; @@ -43,6 +44,7 @@ export default class PerUserNotesChart extends Chart { return {}; } + @bindThis public async update(user: { id: User['id'] }, note: Note, isAdditional: boolean): Promise { await this.commit({ 'total': isAdditional ? 1 : -1, diff --git a/packages/backend/src/core/chart/charts/per-user-reactions.ts b/packages/backend/src/core/chart/charts/per-user-reactions.ts index 4160219720..7bc6d4b521 100644 --- a/packages/backend/src/core/chart/charts/per-user-reactions.ts +++ b/packages/backend/src/core/chart/charts/per-user-reactions.ts @@ -5,6 +5,7 @@ import type { Note } from '@/models/entities/Note.js'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/per-user-reactions.js'; @@ -35,6 +36,7 @@ export default class PerUserReactionsChart extends Chart { return {}; } + @bindThis public async update(user: { id: User['id'], host: User['host'] }, note: Note): Promise { const prefix = this.userEntityService.isLocalUser(user) ? 'local' : 'remote'; this.commit({ diff --git a/packages/backend/src/core/chart/charts/test-grouped.ts b/packages/backend/src/core/chart/charts/test-grouped.ts index bc215f3942..128967bc65 100644 --- a/packages/backend/src/core/chart/charts/test-grouped.ts +++ b/packages/backend/src/core/chart/charts/test-grouped.ts @@ -3,6 +3,7 @@ import { DataSource } from 'typeorm'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Logger from '@/logger.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { name, schema } from './entities/test-grouped.js'; import type { KVs } from '../core.js'; @@ -35,6 +36,7 @@ export default class TestGroupedChart extends Chart { return {}; } + @bindThis public async increment(group: string): Promise { if (this.total[group] == null) this.total[group] = 0; diff --git a/packages/backend/src/core/chart/charts/test-intersection.ts b/packages/backend/src/core/chart/charts/test-intersection.ts index a074a7dded..6b4eed9062 100644 --- a/packages/backend/src/core/chart/charts/test-intersection.ts +++ b/packages/backend/src/core/chart/charts/test-intersection.ts @@ -3,6 +3,7 @@ import { DataSource } from 'typeorm'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Logger from '@/logger.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { name, schema } from './entities/test-intersection.js'; import type { KVs } from '../core.js'; @@ -31,12 +32,14 @@ export default class TestIntersectionChart extends Chart { return {}; } + @bindThis public async addA(key: string): Promise { await this.commit({ a: [key], }); } + @bindThis public async addB(key: string): Promise { await this.commit({ b: [key], diff --git a/packages/backend/src/core/chart/charts/test-unique.ts b/packages/backend/src/core/chart/charts/test-unique.ts index 4d3e2f2403..5d2b3f8ab1 100644 --- a/packages/backend/src/core/chart/charts/test-unique.ts +++ b/packages/backend/src/core/chart/charts/test-unique.ts @@ -3,6 +3,7 @@ import { DataSource } from 'typeorm'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Logger from '@/logger.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { name, schema } from './entities/test-unique.js'; import type { KVs } from '../core.js'; @@ -31,6 +32,7 @@ export default class TestUniqueChart extends Chart { return {}; } + @bindThis public async uniqueIncrement(key: string): Promise { await this.commit({ foo: [key], diff --git a/packages/backend/src/core/chart/charts/test.ts b/packages/backend/src/core/chart/charts/test.ts index 72caf79e0f..238351d8b3 100644 --- a/packages/backend/src/core/chart/charts/test.ts +++ b/packages/backend/src/core/chart/charts/test.ts @@ -3,6 +3,7 @@ import { DataSource } from 'typeorm'; import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import Logger from '@/logger.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { name, schema } from './entities/test.js'; import type { KVs } from '../core.js'; @@ -35,6 +36,7 @@ export default class TestChart extends Chart { return {}; } + @bindThis public async increment(): Promise { this.total++; @@ -44,6 +46,7 @@ export default class TestChart extends Chart { }); } + @bindThis public async decrement(): Promise { this.total--; diff --git a/packages/backend/src/core/chart/charts/users.ts b/packages/backend/src/core/chart/charts/users.ts index f0359968eb..7bc3602439 100644 --- a/packages/backend/src/core/chart/charts/users.ts +++ b/packages/backend/src/core/chart/charts/users.ts @@ -5,6 +5,7 @@ import { AppLockService } from '@/core/AppLockService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { UsersRepository } from '@/models/index.js'; +import { bindThis } from '@/decorators.js'; import Chart from '../core.js'; import { ChartLoggerService } from '../ChartLoggerService.js'; import { name, schema } from './entities/users.js'; @@ -46,6 +47,7 @@ export default class UsersChart extends Chart { return {}; } + @bindThis public async update(user: { id: User['id'], host: User['host'] }, isAdditional: boolean): Promise { const prefix = this.userEntityService.isLocalUser(user) ? 'local' : 'remote'; diff --git a/packages/backend/src/core/chart/core.ts b/packages/backend/src/core/chart/core.ts index cf5aa48884..2092b13b7e 100644 --- a/packages/backend/src/core/chart/core.ts +++ b/packages/backend/src/core/chart/core.ts @@ -8,6 +8,7 @@ import * as nestedProperty from 'nested-property'; import { EntitySchema, LessThan, Between } from 'typeorm'; import { dateUTC, isTimeSame, isTimeBefore, subtractTime, addTime } from '@/misc/prelude/time.js'; import type Logger from '@/logger.js'; +import { bindThis } from '@/decorators.js'; import type { Repository, DataSource } from 'typeorm'; const columnPrefix = '___' as const; @@ -249,6 +250,7 @@ export default abstract class Chart { this.repositoryForDay = db.getRepository<{ id: number; group?: string | null; date: number; }>(day); } + @bindThis private convertRawRecord(x: RawRecord): KVs { const kvs = {} as Record; for (const k of Object.keys(x).filter((k) => k.startsWith(columnPrefix)) as (keyof Columns)[]) { @@ -257,6 +259,7 @@ export default abstract class Chart { return kvs as KVs; } + @bindThis private getNewLog(latest: KVs | null): KVs { const log = {} as Record; for (const [k, v] of Object.entries(this.schema) as ([keyof typeof this['schema'], this['schema'][string]])[]) { @@ -269,6 +272,7 @@ export default abstract class Chart { return log as KVs; } + @bindThis private getLatestLog(group: string | null, span: 'hour' | 'day'): Promise | null> { const repository = span === 'hour' ? this.repositoryForHour : @@ -288,6 +292,7 @@ export default abstract class Chart { /** * 現在(=今のHour or Day)のログをデータベースから探して、あればそれを返し、なければ作成して返します。 */ + @bindThis private async claimCurrentLog(group: string | null, span: 'hour' | 'day'): Promise> { const [y, m, d, h] = Chart.getCurrentDate(); @@ -380,6 +385,7 @@ export default abstract class Chart { }); } + @bindThis public async save(): Promise { if (this.buffer.length === 0) { this.logger.info(`${this.name}: Write skipped`); @@ -498,6 +504,7 @@ export default abstract class Chart { update(logHour, logDay)))); } + @bindThis public async tick(major: boolean, group: string | null = null): Promise { const data = major ? await this.tickMajor(group) : await this.tickMinor(group); @@ -533,10 +540,12 @@ export default abstract class Chart { update(logHour, logDay)); } + @bindThis public resync(group: string | null = null): Promise { return this.tick(true, group); } + @bindThis public async clean(): Promise { const current = dateUTC(Chart.getCurrentDate()); @@ -572,6 +581,7 @@ export default abstract class Chart { ]); } + @bindThis public async getChartRaw(span: 'hour' | 'day', amount: number, cursor: Date | null, group: string | null = null): Promise> { const [y, m, d, h, _m, _s, _ms] = cursor ? Chart.parseDate(subtractTime(addTime(cursor, 1, span), 1)) : Chart.getCurrentDate(); const [y2, m2, d2, h2] = cursor ? Chart.parseDate(addTime(cursor, 1, span)) : [] as never; @@ -676,6 +686,7 @@ export default abstract class Chart { return res; } + @bindThis public async getChart(span: 'hour' | 'day', amount: number, cursor: Date | null, group: string | null = null): Promise>> { const result = await this.getChartRaw(span, amount, cursor, group); const object = {}; diff --git a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts index 1660894571..7f8240b8b2 100644 --- a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts +++ b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts @@ -4,6 +4,7 @@ import type { AbuseUserReportsRepository } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { AbuseUserReport } from '@/models/entities/AbuseUserReport.js'; import { UserEntityService } from './UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class AbuseUserReportEntityService { @@ -15,6 +16,7 @@ export class AbuseUserReportEntityService { ) { } + @bindThis public async pack( src: AbuseUserReport['id'] | AbuseUserReport, ) { @@ -41,6 +43,7 @@ export class AbuseUserReportEntityService { }); } + @bindThis public packMany( reports: any[], ) { diff --git a/packages/backend/src/core/entities/AntennaEntityService.ts b/packages/backend/src/core/entities/AntennaEntityService.ts index 44110e7364..bc79ce26aa 100644 --- a/packages/backend/src/core/entities/AntennaEntityService.ts +++ b/packages/backend/src/core/entities/AntennaEntityService.ts @@ -4,6 +4,7 @@ import type { AntennaNotesRepository, AntennasRepository, UserGroupJoiningsRepos import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Packed } from '@/misc/schema.js'; import type { Antenna } from '@/models/entities/Antenna.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class AntennaEntityService { @@ -19,6 +20,7 @@ export class AntennaEntityService { ) { } + @bindThis public async pack( src: Antenna['id'] | Antenna, ): Promise> { diff --git a/packages/backend/src/core/entities/AppEntityService.ts b/packages/backend/src/core/entities/AppEntityService.ts index 1cc7ca11dc..781cbdcc6b 100644 --- a/packages/backend/src/core/entities/AppEntityService.ts +++ b/packages/backend/src/core/entities/AppEntityService.ts @@ -6,6 +6,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class AppEntityService { @@ -18,6 +19,7 @@ export class AppEntityService { ) { } + @bindThis public async pack( src: App['id'] | App, me?: { id: User['id'] } | null | undefined, diff --git a/packages/backend/src/core/entities/AuthSessionEntityService.ts b/packages/backend/src/core/entities/AuthSessionEntityService.ts index bf8efa5f78..4a74f9c2f6 100644 --- a/packages/backend/src/core/entities/AuthSessionEntityService.ts +++ b/packages/backend/src/core/entities/AuthSessionEntityService.ts @@ -7,6 +7,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class AuthSessionEntityService { @@ -18,6 +19,7 @@ export class AuthSessionEntityService { ) { } + @bindThis public async pack( src: AuthSession['id'] | AuthSession, me?: { id: User['id'] } | null | undefined, diff --git a/packages/backend/src/core/entities/BlockingEntityService.ts b/packages/backend/src/core/entities/BlockingEntityService.ts index 49a96037ca..c9e15207b9 100644 --- a/packages/backend/src/core/entities/BlockingEntityService.ts +++ b/packages/backend/src/core/entities/BlockingEntityService.ts @@ -6,6 +6,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class BlockingEntityService { @@ -17,6 +18,7 @@ export class BlockingEntityService { ) { } + @bindThis public async pack( src: Blocking['id'] | Blocking, me?: { id: User['id'] } | null | undefined, @@ -33,6 +35,7 @@ export class BlockingEntityService { }); } + @bindThis public packMany( blockings: any[], me: { id: User['id'] }, diff --git a/packages/backend/src/core/entities/ChannelEntityService.ts b/packages/backend/src/core/entities/ChannelEntityService.ts index 860967443e..5e2f019a12 100644 --- a/packages/backend/src/core/entities/ChannelEntityService.ts +++ b/packages/backend/src/core/entities/ChannelEntityService.ts @@ -8,6 +8,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ChannelEntityService { @@ -29,6 +30,7 @@ export class ChannelEntityService { ) { } + @bindThis public async pack( src: Channel['id'] | Channel, me?: { id: User['id'] } | null | undefined, diff --git a/packages/backend/src/core/entities/ClipEntityService.ts b/packages/backend/src/core/entities/ClipEntityService.ts index 7a5d2f7f0a..1e794391e9 100644 --- a/packages/backend/src/core/entities/ClipEntityService.ts +++ b/packages/backend/src/core/entities/ClipEntityService.ts @@ -7,6 +7,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ClipEntityService { @@ -18,6 +19,7 @@ export class ClipEntityService { ) { } + @bindThis public async pack( src: Clip['id'] | Clip, ): Promise> { @@ -34,6 +36,7 @@ export class ClipEntityService { }); } + @bindThis public packMany( clips: Clip[], ) { diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts index e0aeb70dfc..706c8c1186 100644 --- a/packages/backend/src/core/entities/DriveFileEntityService.ts +++ b/packages/backend/src/core/entities/DriveFileEntityService.ts @@ -19,6 +19,7 @@ type PackOptions = { self?: boolean, withUser?: boolean, }; +import { bindThis } from '@/decorators.js'; @Injectable() export class DriveFileEntityService { @@ -44,6 +45,7 @@ export class DriveFileEntityService { ) { } + @bindThis public validateFileName(name: string): boolean { return ( (name.trim().length > 0) && @@ -54,6 +56,7 @@ export class DriveFileEntityService { ); } + @bindThis public getPublicProperties(file: DriveFile): DriveFile['properties'] { if (file.properties.orientation != null) { const properties = deepClone(file.properties); @@ -67,6 +70,7 @@ export class DriveFileEntityService { return file.properties; } + @bindThis public getPublicUrl(file: DriveFile, thumbnail = false): string | null { // リモートかつメディアプロキシ if (file.uri != null && file.userHost != null && this.config.mediaProxy != null) { @@ -90,6 +94,7 @@ export class DriveFileEntityService { return thumbnail ? (file.thumbnailUrl ?? (isImage ? (file.webpublicUrl ?? file.url) : null)) : (file.webpublicUrl ?? file.url); } + @bindThis public async calcDriveUsageOf(user: User['id'] | { id: User['id'] }): Promise { const id = typeof user === 'object' ? user.id : user; @@ -103,6 +108,7 @@ export class DriveFileEntityService { return parseInt(sum, 10) ?? 0; } + @bindThis public async calcDriveUsageOfHost(host: string): Promise { const { sum } = await this.driveFilesRepository .createQueryBuilder('file') @@ -114,6 +120,7 @@ export class DriveFileEntityService { return parseInt(sum, 10) ?? 0; } + @bindThis public async calcDriveUsageOfLocal(): Promise { const { sum } = await this.driveFilesRepository .createQueryBuilder('file') @@ -125,6 +132,7 @@ export class DriveFileEntityService { return parseInt(sum, 10) ?? 0; } + @bindThis public async calcDriveUsageOfRemote(): Promise { const { sum } = await this.driveFilesRepository .createQueryBuilder('file') @@ -136,6 +144,7 @@ export class DriveFileEntityService { return parseInt(sum, 10) ?? 0; } + @bindThis public async pack( src: DriveFile['id'] | DriveFile, options?: PackOptions, @@ -169,6 +178,7 @@ export class DriveFileEntityService { }); } + @bindThis public async packNullable( src: DriveFile['id'] | DriveFile, options?: PackOptions, @@ -203,6 +213,7 @@ export class DriveFileEntityService { }); } + @bindThis public async packMany( files: (DriveFile['id'] | DriveFile)[], options?: PackOptions, diff --git a/packages/backend/src/core/entities/DriveFolderEntityService.ts b/packages/backend/src/core/entities/DriveFolderEntityService.ts index 5761fa37bc..0bb0f1754e 100644 --- a/packages/backend/src/core/entities/DriveFolderEntityService.ts +++ b/packages/backend/src/core/entities/DriveFolderEntityService.ts @@ -7,6 +7,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class DriveFolderEntityService { @@ -19,6 +20,7 @@ export class DriveFolderEntityService { ) { } + @bindThis public async pack( src: DriveFolder['id'] | DriveFolder, options?: { diff --git a/packages/backend/src/core/entities/EmojiEntityService.ts b/packages/backend/src/core/entities/EmojiEntityService.ts index fc09b5a2c7..08d83a2753 100644 --- a/packages/backend/src/core/entities/EmojiEntityService.ts +++ b/packages/backend/src/core/entities/EmojiEntityService.ts @@ -7,6 +7,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class EmojiEntityService { @@ -18,6 +19,7 @@ export class EmojiEntityService { ) { } + @bindThis public async pack( src: Emoji['id'] | Emoji, ): Promise> { @@ -34,6 +36,7 @@ export class EmojiEntityService { }; } + @bindThis public packMany( emojis: any[], ) { diff --git a/packages/backend/src/core/entities/FollowRequestEntityService.ts b/packages/backend/src/core/entities/FollowRequestEntityService.ts index 4a60c1263f..88c91d0f21 100644 --- a/packages/backend/src/core/entities/FollowRequestEntityService.ts +++ b/packages/backend/src/core/entities/FollowRequestEntityService.ts @@ -7,6 +7,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class FollowRequestEntityService { @@ -18,6 +19,7 @@ export class FollowRequestEntityService { ) { } + @bindThis public async pack( src: FollowRequest['id'] | FollowRequest, me?: { id: User['id'] } | null | undefined, diff --git a/packages/backend/src/core/entities/FollowingEntityService.ts b/packages/backend/src/core/entities/FollowingEntityService.ts index c7e040a57b..a833ae719b 100644 --- a/packages/backend/src/core/entities/FollowingEntityService.ts +++ b/packages/backend/src/core/entities/FollowingEntityService.ts @@ -31,6 +31,7 @@ type RemoteFolloweeFollowing = Following & { followeeInbox: string; followeeSharedInbox: string; }; +import { bindThis } from '@/decorators.js'; @Injectable() export class FollowingEntityService { @@ -42,22 +43,27 @@ export class FollowingEntityService { ) { } + @bindThis public isLocalFollower(following: Following): following is LocalFollowerFollowing { return following.followerHost == null; } + @bindThis public isRemoteFollower(following: Following): following is RemoteFollowerFollowing { return following.followerHost != null; } + @bindThis public isLocalFollowee(following: Following): following is LocalFolloweeFollowing { return following.followeeHost == null; } + @bindThis public isRemoteFollowee(following: Following): following is RemoteFolloweeFollowing { return following.followeeHost != null; } + @bindThis public async pack( src: Following['id'] | Following, me?: { id: User['id'] } | null | undefined, @@ -84,6 +90,7 @@ export class FollowingEntityService { }); } + @bindThis public packMany( followings: any[], me?: { id: User['id'] } | null | undefined, diff --git a/packages/backend/src/core/entities/GalleryLikeEntityService.ts b/packages/backend/src/core/entities/GalleryLikeEntityService.ts index 7e599113cc..8b15ffc2bb 100644 --- a/packages/backend/src/core/entities/GalleryLikeEntityService.ts +++ b/packages/backend/src/core/entities/GalleryLikeEntityService.ts @@ -8,6 +8,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class GalleryLikeEntityService { @@ -19,6 +20,7 @@ export class GalleryLikeEntityService { ) { } + @bindThis public async pack( src: GalleryLike['id'] | GalleryLike, me?: any, @@ -31,6 +33,7 @@ export class GalleryLikeEntityService { }; } + @bindThis public packMany( likes: any[], me: any, diff --git a/packages/backend/src/core/entities/GalleryPostEntityService.ts b/packages/backend/src/core/entities/GalleryPostEntityService.ts index ca98687d7b..ab29e7dba1 100644 --- a/packages/backend/src/core/entities/GalleryPostEntityService.ts +++ b/packages/backend/src/core/entities/GalleryPostEntityService.ts @@ -8,6 +8,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class GalleryPostEntityService { @@ -23,6 +24,7 @@ export class GalleryPostEntityService { ) { } + @bindThis public async pack( src: GalleryPost['id'] | GalleryPost, me?: { id: User['id'] } | null | undefined, @@ -47,6 +49,7 @@ export class GalleryPostEntityService { }); } + @bindThis public packMany( posts: GalleryPost[], me?: { id: User['id'] } | null | undefined, diff --git a/packages/backend/src/core/entities/HashtagEntityService.ts b/packages/backend/src/core/entities/HashtagEntityService.ts index 511992c44f..f79b821222 100644 --- a/packages/backend/src/core/entities/HashtagEntityService.ts +++ b/packages/backend/src/core/entities/HashtagEntityService.ts @@ -7,6 +7,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class HashtagEntityService { @@ -18,6 +19,7 @@ export class HashtagEntityService { ) { } + @bindThis public async pack( src: Hashtag, ): Promise> { @@ -32,6 +34,7 @@ export class HashtagEntityService { }; } + @bindThis public packMany( hashtags: Hashtag[], ) { diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts index f4fe9a17d3..5a7ceb89a3 100644 --- a/packages/backend/src/core/entities/InstanceEntityService.ts +++ b/packages/backend/src/core/entities/InstanceEntityService.ts @@ -8,6 +8,7 @@ import type { User } from '@/models/entities/User.js'; import type { Instance } from '@/models/entities/Instance.js'; import { MetaService } from '@/core/MetaService.js'; import { UserEntityService } from './UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class InstanceEntityService { @@ -19,6 +20,7 @@ export class InstanceEntityService { ) { } + @bindThis public async pack( instance: Instance, ): Promise> { @@ -50,6 +52,7 @@ export class InstanceEntityService { }; } + @bindThis public packMany( instances: Instance[], ) { diff --git a/packages/backend/src/core/entities/MessagingMessageEntityService.ts b/packages/backend/src/core/entities/MessagingMessageEntityService.ts index b7c42a5760..cdb752dd81 100644 --- a/packages/backend/src/core/entities/MessagingMessageEntityService.ts +++ b/packages/backend/src/core/entities/MessagingMessageEntityService.ts @@ -9,6 +9,7 @@ import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; import { UserEntityService } from './UserEntityService.js'; import { DriveFileEntityService } from './DriveFileEntityService.js'; import { UserGroupEntityService } from './UserGroupEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class MessagingMessageEntityService { @@ -22,6 +23,7 @@ export class MessagingMessageEntityService { ) { } + @bindThis public async pack( src: MessagingMessage['id'] | MessagingMessage, me?: { id: User['id'] } | null | undefined, diff --git a/packages/backend/src/core/entities/ModerationLogEntityService.ts b/packages/backend/src/core/entities/ModerationLogEntityService.ts index 2f508710b8..ab61797910 100644 --- a/packages/backend/src/core/entities/ModerationLogEntityService.ts +++ b/packages/backend/src/core/entities/ModerationLogEntityService.ts @@ -7,6 +7,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ModerationLogEntityService { @@ -18,6 +19,7 @@ export class ModerationLogEntityService { ) { } + @bindThis public async pack( src: ModerationLog['id'] | ModerationLog, ) { @@ -35,6 +37,7 @@ export class ModerationLogEntityService { }); } + @bindThis public packMany( reports: any[], ) { diff --git a/packages/backend/src/core/entities/MutingEntityService.ts b/packages/backend/src/core/entities/MutingEntityService.ts index 862be009da..4f02ef4087 100644 --- a/packages/backend/src/core/entities/MutingEntityService.ts +++ b/packages/backend/src/core/entities/MutingEntityService.ts @@ -7,6 +7,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class MutingEntityService { @@ -18,6 +19,7 @@ export class MutingEntityService { ) { } + @bindThis public async pack( src: Muting['id'] | Muting, me?: { id: User['id'] } | null | undefined, @@ -35,6 +37,7 @@ export class MutingEntityService { }); } + @bindThis public packMany( mutings: any[], me: { id: User['id'] }, diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 5605cf8ce6..73d3184957 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -16,6 +16,7 @@ import type { CustomEmojiService } from '../CustomEmojiService.js'; import type { ReactionService } from '../ReactionService.js'; import type { UserEntityService } from './UserEntityService.js'; import type { DriveFileEntityService } from './DriveFileEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class NoteEntityService implements OnModuleInit { @@ -68,6 +69,7 @@ export class NoteEntityService implements OnModuleInit { this.reactionService = this.moduleRef.get('ReactionService'); } + @bindThis private async hideNote(packedNote: Packed<'Note'>, meId: User['id'] | null) { // TODO: isVisibleForMe を使うようにしても良さそう(型違うけど) let hide = false; @@ -128,6 +130,7 @@ export class NoteEntityService implements OnModuleInit { } } + @bindThis private async populatePoll(note: Note, meId: User['id'] | null) { const poll = await this.pollsRepository.findOneByOrFail({ noteId: note.id }); const choices = poll.choices.map(c => ({ @@ -166,6 +169,7 @@ export class NoteEntityService implements OnModuleInit { }; } + @bindThis private async populateMyReaction(note: Note, meId: User['id'], _hint_?: { myReactions: Map; }) { @@ -191,6 +195,7 @@ export class NoteEntityService implements OnModuleInit { return undefined; } + @bindThis public async isVisibleForMe(note: Note, meId: User['id'] | null): Promise { // This code must always be synchronized with the checks in generateVisibilityQuery. // visibility が specified かつ自分が指定されていなかったら非表示 @@ -244,6 +249,7 @@ export class NoteEntityService implements OnModuleInit { return true; } + @bindThis public async pack( src: Note['id'] | Note, me?: { id: User['id'] } | null | undefined, @@ -353,6 +359,7 @@ export class NoteEntityService implements OnModuleInit { return packed; } + @bindThis public async packMany( notes: Note[], me?: { id: User['id'] } | null | undefined, @@ -388,6 +395,7 @@ export class NoteEntityService implements OnModuleInit { }))); } + @bindThis public async countSameRenotes(userId: string, renoteId: string, excludeNoteId: string | undefined): Promise { // 指定したユーザーの指定したノートのリノートがいくつあるか数える const query = this.notesRepository.createQueryBuilder('note') diff --git a/packages/backend/src/core/entities/NoteFavoriteEntityService.ts b/packages/backend/src/core/entities/NoteFavoriteEntityService.ts index 1a68a5c628..aa5c354b6d 100644 --- a/packages/backend/src/core/entities/NoteFavoriteEntityService.ts +++ b/packages/backend/src/core/entities/NoteFavoriteEntityService.ts @@ -8,6 +8,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class NoteFavoriteEntityService { @@ -19,6 +20,7 @@ export class NoteFavoriteEntityService { ) { } + @bindThis public async pack( src: NoteFavorite['id'] | NoteFavorite, me?: { id: User['id'] } | null | undefined, @@ -33,6 +35,7 @@ export class NoteFavoriteEntityService { }; } + @bindThis public packMany( favorites: any[], me: { id: User['id'] }, diff --git a/packages/backend/src/core/entities/NoteReactionEntityService.ts b/packages/backend/src/core/entities/NoteReactionEntityService.ts index 47008ee08e..eba6f9d908 100644 --- a/packages/backend/src/core/entities/NoteReactionEntityService.ts +++ b/packages/backend/src/core/entities/NoteReactionEntityService.ts @@ -11,6 +11,7 @@ import type { ReactionService } from '../ReactionService.js'; import type { UserEntityService } from './UserEntityService.js'; import type { NoteEntityService } from './NoteEntityService.js'; import { ModuleRef } from '@nestjs/core'; +import { bindThis } from '@/decorators.js'; @Injectable() export class NoteReactionEntityService implements OnModuleInit { @@ -36,6 +37,7 @@ export class NoteReactionEntityService implements OnModuleInit { this.reactionService = this.moduleRef.get('ReactionService'); } + @bindThis public async pack( src: NoteReaction['id'] | NoteReaction, me?: { id: User['id'] } | null | undefined, diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index c415599fea..346faae6b0 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -13,6 +13,7 @@ import type { CustomEmojiService } from '../CustomEmojiService.js'; import type { UserEntityService } from './UserEntityService.js'; import type { NoteEntityService } from './NoteEntityService.js'; import type { UserGroupInvitationEntityService } from './UserGroupInvitationEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class NotificationEntityService implements OnModuleInit { @@ -47,6 +48,7 @@ export class NotificationEntityService implements OnModuleInit { this.customEmojiService = this.moduleRef.get('CustomEmojiService'); } + @bindThis public async pack( src: Notification['id'] | Notification, options: { @@ -120,6 +122,7 @@ export class NotificationEntityService implements OnModuleInit { }); } + @bindThis public async packMany( notifications: Notification[], meId: User['id'], diff --git a/packages/backend/src/core/entities/PageEntityService.ts b/packages/backend/src/core/entities/PageEntityService.ts index 004443759b..48e45dd019 100644 --- a/packages/backend/src/core/entities/PageEntityService.ts +++ b/packages/backend/src/core/entities/PageEntityService.ts @@ -9,6 +9,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class PageEntityService { @@ -27,6 +28,7 @@ export class PageEntityService { ) { } + @bindThis public async pack( src: Page['id'] | Page, me?: { id: User['id'] } | null | undefined, @@ -99,6 +101,7 @@ export class PageEntityService { }); } + @bindThis public packMany( pages: Page[], me?: { id: User['id'] } | null | undefined, diff --git a/packages/backend/src/core/entities/PageLikeEntityService.ts b/packages/backend/src/core/entities/PageLikeEntityService.ts index 62d9c82ca6..d3e45783dd 100644 --- a/packages/backend/src/core/entities/PageLikeEntityService.ts +++ b/packages/backend/src/core/entities/PageLikeEntityService.ts @@ -8,6 +8,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class PageLikeEntityService { @@ -19,6 +20,7 @@ export class PageLikeEntityService { ) { } + @bindThis public async pack( src: PageLike['id'] | PageLike, me?: { id: User['id'] } | null | undefined, @@ -31,6 +33,7 @@ export class PageLikeEntityService { }; } + @bindThis public packMany( likes: any[], me: { id: User['id'] }, diff --git a/packages/backend/src/core/entities/SigninEntityService.ts b/packages/backend/src/core/entities/SigninEntityService.ts index fd89662f7d..c402644742 100644 --- a/packages/backend/src/core/entities/SigninEntityService.ts +++ b/packages/backend/src/core/entities/SigninEntityService.ts @@ -7,6 +7,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class SigninEntityService { @@ -18,6 +19,7 @@ export class SigninEntityService { ) { } + @bindThis public async pack( src: Signin, ) { diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index c691eaebdf..4a027d1de6 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -41,6 +41,7 @@ function isRemoteUser(user: T): user is T & { function isRemoteUser(user: User | { host: User['host'] }): boolean { return !isLocalUser(user); } +import { bindThis } from '@/decorators.js'; @Injectable() export class UserEntityService implements OnModuleInit { @@ -143,6 +144,7 @@ export class UserEntityService implements OnModuleInit { public isLocalUser = isLocalUser; public isRemoteUser = isRemoteUser; + @bindThis public async getRelation(me: User['id'], target: User['id']) { return awaitAll({ id: target, @@ -198,6 +200,7 @@ export class UserEntityService implements OnModuleInit { }); } + @bindThis public async getHasUnreadMessagingMessage(userId: User['id']): Promise { const mute = await this.mutingsRepository.findBy({ muterId: userId, @@ -227,6 +230,7 @@ export class UserEntityService implements OnModuleInit { return withUser || withGroups.some(x => x); } + @bindThis public async getHasUnreadAnnouncement(userId: User['id']): Promise { const reads = await this.announcementReadsRepository.findBy({ userId: userId, @@ -239,6 +243,7 @@ export class UserEntityService implements OnModuleInit { return count > 0; } + @bindThis public async getHasUnreadAntenna(userId: User['id']): Promise { const myAntennas = (await this.antennaService.getAntennas()).filter(a => a.userId === userId); @@ -250,6 +255,7 @@ export class UserEntityService implements OnModuleInit { return unread != null; } + @bindThis public async getHasUnreadChannel(userId: User['id']): Promise { const channels = await this.channelFollowingsRepository.findBy({ followerId: userId }); @@ -261,6 +267,7 @@ export class UserEntityService implements OnModuleInit { return unread != null; } + @bindThis public async getHasUnreadNotification(userId: User['id']): Promise { const mute = await this.mutingsRepository.findBy({ muterId: userId, @@ -279,6 +286,7 @@ export class UserEntityService implements OnModuleInit { return count > 0; } + @bindThis public async getHasPendingReceivedFollowRequest(userId: User['id']): Promise { const count = await this.followRequestsRepository.countBy({ followeeId: userId, @@ -287,6 +295,7 @@ export class UserEntityService implements OnModuleInit { return count > 0; } + @bindThis public getOnlineStatus(user: User): 'unknown' | 'online' | 'active' | 'offline' { if (user.hideOnlineStatus) return 'unknown'; if (user.lastActiveDate == null) return 'unknown'; @@ -298,6 +307,7 @@ export class UserEntityService implements OnModuleInit { ); } + @bindThis public async getAvatarUrl(user: User): Promise { if (user.avatar) { return this.driveFileEntityService.getPublicUrl(user.avatar, true) ?? this.getIdenticonUrl(user.id); @@ -309,6 +319,7 @@ export class UserEntityService implements OnModuleInit { } } + @bindThis public getAvatarUrlSync(user: User): string { if (user.avatar) { return this.driveFileEntityService.getPublicUrl(user.avatar, true) ?? this.getIdenticonUrl(user.id); @@ -317,6 +328,7 @@ export class UserEntityService implements OnModuleInit { } } + @bindThis public getIdenticonUrl(userId: User['id']): string { return `${this.config.url}/identicon/${userId}`; } diff --git a/packages/backend/src/core/entities/UserGroupEntityService.ts b/packages/backend/src/core/entities/UserGroupEntityService.ts index e399197618..0674a76723 100644 --- a/packages/backend/src/core/entities/UserGroupEntityService.ts +++ b/packages/backend/src/core/entities/UserGroupEntityService.ts @@ -7,6 +7,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UserGroupEntityService { @@ -21,6 +22,7 @@ export class UserGroupEntityService { ) { } + @bindThis public async pack( src: UserGroup['id'] | UserGroup, ): Promise> { diff --git a/packages/backend/src/core/entities/UserGroupInvitationEntityService.ts b/packages/backend/src/core/entities/UserGroupInvitationEntityService.ts index f5c9be3475..0fba1426f4 100644 --- a/packages/backend/src/core/entities/UserGroupInvitationEntityService.ts +++ b/packages/backend/src/core/entities/UserGroupInvitationEntityService.ts @@ -8,6 +8,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UserGroupInvitationEntityService { @@ -19,6 +20,7 @@ export class UserGroupInvitationEntityService { ) { } + @bindThis public async pack( src: UserGroupInvitation['id'] | UserGroupInvitation, ) { @@ -30,6 +32,7 @@ export class UserGroupInvitationEntityService { }; } + @bindThis public packMany( invitations: any[], ) { diff --git a/packages/backend/src/core/entities/UserListEntityService.ts b/packages/backend/src/core/entities/UserListEntityService.ts index e2b0814914..f2e0426928 100644 --- a/packages/backend/src/core/entities/UserListEntityService.ts +++ b/packages/backend/src/core/entities/UserListEntityService.ts @@ -7,6 +7,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UserListEntityService { @@ -21,6 +22,7 @@ export class UserListEntityService { ) { } + @bindThis public async pack( src: UserList['id'] | UserList, ): Promise> { diff --git a/packages/backend/src/daemons/JanitorService.ts b/packages/backend/src/daemons/JanitorService.ts index dbad576abe..8cdfb703f1 100644 --- a/packages/backend/src/daemons/JanitorService.ts +++ b/packages/backend/src/daemons/JanitorService.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { LessThan } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { AttestationChallengesRepository } from '@/models/index.js'; +import { bindThis } from '@/decorators.js'; import type { OnApplicationShutdown } from '@nestjs/common'; const interval = 30 * 60 * 1000; @@ -19,6 +20,7 @@ export class JanitorService implements OnApplicationShutdown { /** * Clean up database occasionally */ + @bindThis public start(): void { const tick = async () => { await this.attestationChallengesRepository.delete({ @@ -31,6 +33,7 @@ export class JanitorService implements OnApplicationShutdown { this.intervalId = setInterval(tick, interval); } + @bindThis public onApplicationShutdown(signal?: string | undefined) { clearInterval(this.intervalId); } diff --git a/packages/backend/src/daemons/QueueStatsService.ts b/packages/backend/src/daemons/QueueStatsService.ts index 931de19067..7b47d78a17 100644 --- a/packages/backend/src/daemons/QueueStatsService.ts +++ b/packages/backend/src/daemons/QueueStatsService.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import Xev from 'xev'; import { DI } from '@/di-symbols.js'; import { QueueService } from '@/core/QueueService.js'; +import { bindThis } from '@/decorators.js'; import type { OnApplicationShutdown } from '@nestjs/common'; const ev = new Xev(); @@ -20,6 +21,7 @@ export class QueueStatsService implements OnApplicationShutdown { /** * Report queue stats regularly */ + @bindThis public start(): void { const log = [] as any[]; @@ -71,6 +73,7 @@ export class QueueStatsService implements OnApplicationShutdown { this.intervalId = setInterval(tick, interval); } + @bindThis public onApplicationShutdown(signal?: string | undefined) { clearInterval(this.intervalId); } diff --git a/packages/backend/src/daemons/ServerStatsService.ts b/packages/backend/src/daemons/ServerStatsService.ts index e40912442d..7971f9e810 100644 --- a/packages/backend/src/daemons/ServerStatsService.ts +++ b/packages/backend/src/daemons/ServerStatsService.ts @@ -3,6 +3,7 @@ import si from 'systeminformation'; import Xev from 'xev'; import * as osUtils from 'os-utils'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; import type { OnApplicationShutdown } from '@nestjs/common'; const ev = new Xev(); @@ -23,6 +24,7 @@ export class ServerStatsService implements OnApplicationShutdown { /** * Report server stats regularly */ + @bindThis public start(): void { const log = [] as any[]; @@ -61,6 +63,7 @@ export class ServerStatsService implements OnApplicationShutdown { this.intervalId = setInterval(tick, interval); } + @bindThis public onApplicationShutdown(signal?: string | undefined) { clearInterval(this.intervalId); } diff --git a/packages/backend/src/decorators.ts b/packages/backend/src/decorators.ts new file mode 100644 index 0000000000..94b1c4be8c --- /dev/null +++ b/packages/backend/src/decorators.ts @@ -0,0 +1,41 @@ +// https://github.com/andreypopp/autobind-decorator + +/** + * Return a descriptor removing the value and returning a getter + * The getter will return a .bind version of the function + * and memoize the result against a symbol on the instance + */ +export function bindThis(target, key, descriptor) { + let fn = descriptor.value; + + if (typeof fn !== 'function') { + throw new TypeError(`@bindThis decorator can only be applied to methods not: ${typeof fn}`); + } + + return { + configurable: true, + get() { + // eslint-disable-next-line no-prototype-builtins + if (this === target.prototype || this.hasOwnProperty(key) || + typeof fn !== 'function') { + return fn; + } + + const boundFn = fn.bind(this); + Object.defineProperty(this, key, { + configurable: true, + get() { + return boundFn; + }, + set(value) { + fn = value; + delete this[key]; + }, + }); + return boundFn; + }, + set(value) { + fn = value; + }, + }; +} diff --git a/packages/backend/src/logger.ts b/packages/backend/src/logger.ts index 6722220681..d09b479c42 100644 --- a/packages/backend/src/logger.ts +++ b/packages/backend/src/logger.ts @@ -2,6 +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 { bindThis } from '@/decorators.js'; import { envOption } from './env.js'; type Domain = { @@ -26,12 +27,14 @@ export default class Logger { this.syslogClient = syslogClient; } + @bindThis public createSubLogger(domain: string, color?: string, store = true): Logger { const logger = new Logger(domain, color, store); logger.parentLogger = this; return logger; } + @bindThis private log(level: Level, message: string, data?: Record | null, important = false, subDomains: Domain[] = [], store = true): void { if (envOption.quiet) return; if (!this.store) store = false; @@ -80,6 +83,7 @@ export default class Logger { } } + @bindThis public error(x: string | Error, data?: Record | null, important = false): void { // 実行を継続できない状況で使う if (x instanceof Error) { data = data ?? {}; @@ -92,20 +96,24 @@ export default class Logger { } } + @bindThis public warn(message: string, data?: Record | null, important = false): void { // 実行を継続できるが改善すべき状況で使う this.log('warning', message, data, important); } + @bindThis public succ(message: string, data?: Record | null, important = false): void { // 何かに成功した状況で使う this.log('success', message, data, important); } + @bindThis public debug(message: string, data?: Record | null, important = false): void { // デバッグ用に使う(開発者に必要だが利用者に不要な情報) if (process.env.NODE_ENV !== 'production' || envOption.verbose) { this.log('debug', message, data, important); } } + @bindThis public info(message: string, data?: Record | null, important = false): void { // それ以外 this.log('info', message, data, important); } diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts index e5b911ed32..69512498f8 100644 --- a/packages/backend/src/misc/cache.ts +++ b/packages/backend/src/misc/cache.ts @@ -1,3 +1,5 @@ +import { bindThis } from '@/decorators.js'; + export class Cache { public cache: Map; private lifetime: number; @@ -7,6 +9,7 @@ export class Cache { this.lifetime = lifetime; } + @bindThis public set(key: string | null, value: T): void { this.cache.set(key, { date: Date.now(), @@ -14,6 +17,7 @@ export class Cache { }); } + @bindThis public get(key: string | null): T | undefined { const cached = this.cache.get(key); if (cached == null) return undefined; @@ -24,6 +28,7 @@ export class Cache { return cached.value; } + @bindThis public delete(key: string | null) { this.cache.delete(key); } @@ -32,6 +37,7 @@ export class Cache { * キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します * optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします */ + @bindThis public async fetch(key: string | null, fetcher: () => Promise, validator?: (cachedValue: T) => boolean): Promise { const cachedValue = this.get(key); if (cachedValue !== undefined) { @@ -56,6 +62,7 @@ export class Cache { * キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します * optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします */ + @bindThis public async fetchMaybe(key: string | null, fetcher: () => Promise, validator?: (cachedValue: T) => boolean): Promise { const cachedValue = this.get(key); if (cachedValue !== undefined) { diff --git a/packages/backend/src/misc/i18n.ts b/packages/backend/src/misc/i18n.ts index 4fa398763a..e304a8adac 100644 --- a/packages/backend/src/misc/i18n.ts +++ b/packages/backend/src/misc/i18n.ts @@ -5,12 +5,13 @@ export class I18n> { this.locale = locale; //#region BIND - this.t = this.t.bind(this); + //this.t = this.t.bind(this); //#endregion } // string にしているのは、ドット区切りでのパス指定を許可するため // なるべくこのメソッド使うよりもlocale直接参照の方がvueのキャッシュ効いてパフォーマンスが良いかも + @bindThis public t(key: string, args?: Record): string { try { let str = key.split('.').reduce((o, i) => o[i], this.locale) as string; diff --git a/packages/backend/src/postgre.ts b/packages/backend/src/postgre.ts index 2beb31e24e..0f9f7a6a96 100644 --- a/packages/backend/src/postgre.ts +++ b/packages/backend/src/postgre.ts @@ -72,6 +72,7 @@ import { Channel } from '@/models/entities/Channel.js'; import { Config } from '@/config.js'; import MisskeyLogger from '@/logger.js'; +import { bindThis } from '@/decorators.js'; import { envOption } from './env.js'; export const dbLogger = new MisskeyLogger('db'); @@ -79,32 +80,39 @@ export const dbLogger = new MisskeyLogger('db'); const sqlLogger = dbLogger.createSubLogger('sql', 'gray', false); class MyCustomLogger implements Logger { + @bindThis private highlight(sql: string) { return highlight.highlight(sql, { language: 'sql', ignoreIllegals: true, }); } + @bindThis public logQuery(query: string, parameters?: any[]) { sqlLogger.info(this.highlight(query).substring(0, 100)); } + @bindThis public logQueryError(error: string, query: string, parameters?: any[]) { sqlLogger.error(this.highlight(query)); } + @bindThis public logQuerySlow(time: number, query: string, parameters?: any[]) { sqlLogger.warn(this.highlight(query)); } + @bindThis public logSchemaBuild(message: string) { sqlLogger.info(message); } + @bindThis public log(message: string) { sqlLogger.info(message); } + @bindThis public logMigration(message: string) { sqlLogger.info(message); } diff --git a/packages/backend/src/queue/DbQueueProcessorsService.ts b/packages/backend/src/queue/DbQueueProcessorsService.ts index 58384c4d1b..e5568ab9bf 100644 --- a/packages/backend/src/queue/DbQueueProcessorsService.ts +++ b/packages/backend/src/queue/DbQueueProcessorsService.ts @@ -16,6 +16,7 @@ import { ImportUserListsProcessorService } from './processors/ImportUserListsPro import { ImportCustomEmojisProcessorService } from './processors/ImportCustomEmojisProcessorService.js'; import { DeleteAccountProcessorService } from './processors/DeleteAccountProcessorService.js'; import type Bull from 'bull'; +import { bindThis } from '@/decorators.js'; @Injectable() export class DbQueueProcessorsService { @@ -39,6 +40,7 @@ export class DbQueueProcessorsService { ) { } + @bindThis public start(q: Bull.Queue): void { q.process('deleteDriveFiles', (job, done) => this.deleteDriveFilesProcessorService.process(job, done)); q.process('exportCustomEmojis', (job, done) => this.exportCustomEmojisProcessorService.process(job, done)); diff --git a/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts b/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts index 3ff3dd090c..c95e1c1ba6 100644 --- a/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts +++ b/packages/backend/src/queue/ObjectStorageQueueProcessorsService.ts @@ -5,6 +5,7 @@ import type { Config } from '@/config.js'; import { CleanRemoteFilesProcessorService } from './processors/CleanRemoteFilesProcessorService.js'; import { DeleteFileProcessorService } from './processors/DeleteFileProcessorService.js'; import type Bull from 'bull'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ObjectStorageQueueProcessorsService { @@ -17,6 +18,7 @@ export class ObjectStorageQueueProcessorsService { ) { } + @bindThis public start(q: Bull.Queue): void { q.process('deleteFile', 16, (job) => this.deleteFileProcessorService.process(job)); q.process('cleanRemoteFiles', 16, (job, done) => this.cleanRemoteFilesProcessorService.process(job, done)); diff --git a/packages/backend/src/queue/QueueLoggerService.ts b/packages/backend/src/queue/QueueLoggerService.ts index a311470cc9..3a8a734f10 100644 --- a/packages/backend/src/queue/QueueLoggerService.ts +++ b/packages/backend/src/queue/QueueLoggerService.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { LoggerService } from '@/core/LoggerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class QueueLoggerService { diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts index 8c300d479c..1d2feb5ef8 100644 --- a/packages/backend/src/queue/QueueProcessorService.ts +++ b/packages/backend/src/queue/QueueProcessorService.ts @@ -13,6 +13,7 @@ import { EndedPollNotificationProcessorService } from './processors/EndedPollNot import { DeliverProcessorService } from './processors/DeliverProcessorService.js'; import { InboxProcessorService } from './processors/InboxProcessorService.js'; import { QueueLoggerService } from './QueueLoggerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class QueueProcessorService { @@ -35,6 +36,7 @@ export class QueueProcessorService { this.logger = this.queueLoggerService.logger; } + @bindThis public start() { function renderError(e: Error): any { if (e) { // 何故かeがundefinedで来ることがある diff --git a/packages/backend/src/queue/SystemQueueProcessorsService.ts b/packages/backend/src/queue/SystemQueueProcessorsService.ts index a8af92b9ba..1ce4152b2c 100644 --- a/packages/backend/src/queue/SystemQueueProcessorsService.ts +++ b/packages/backend/src/queue/SystemQueueProcessorsService.ts @@ -7,6 +7,7 @@ import { CleanChartsProcessorService } from './processors/CleanChartsProcessorSe import { CheckExpiredMutingsProcessorService } from './processors/CheckExpiredMutingsProcessorService.js'; import { CleanProcessorService } from './processors/CleanProcessorService.js'; import type Bull from 'bull'; +import { bindThis } from '@/decorators.js'; @Injectable() export class SystemQueueProcessorsService { @@ -22,6 +23,7 @@ export class SystemQueueProcessorsService { ) { } + @bindThis public start(q: Bull.Queue): void { q.process('tickCharts', (job, done) => this.tickChartsProcessorService.process(job, done)); q.process('resyncCharts', (job, done) => this.resyncChartsProcessorService.process(job, done)); diff --git a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts index e91cba9d10..7a1e3e71be 100644 --- a/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts +++ b/packages/backend/src/queue/processors/CheckExpiredMutingsProcessorService.ts @@ -7,6 +7,7 @@ import type Logger from '@/logger.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; +import { bindThis } from '@/decorators.js'; @Injectable() export class CheckExpiredMutingsProcessorService { @@ -25,6 +26,7 @@ export class CheckExpiredMutingsProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('check-expired-mutings'); } + @bindThis public async process(job: Bull.Job>, done: () => void): Promise { this.logger.info('Checking expired mutings...'); diff --git a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts index e8e90f1422..c57086240a 100644 --- a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts @@ -17,6 +17,7 @@ import PerUserDriveChart from '@/core/chart/charts/per-user-drive.js'; import ApRequestChart from '@/core/chart/charts/ap-request.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; +import { bindThis } from '@/decorators.js'; @Injectable() export class CleanChartsProcessorService { @@ -44,6 +45,7 @@ export class CleanChartsProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('clean-charts'); } + @bindThis public async process(job: Bull.Job>, done: () => void): Promise { this.logger.info('Clean charts...'); diff --git a/packages/backend/src/queue/processors/CleanProcessorService.ts b/packages/backend/src/queue/processors/CleanProcessorService.ts index 6eb457ce9f..8ca39a9677 100644 --- a/packages/backend/src/queue/processors/CleanProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanProcessorService.ts @@ -6,6 +6,7 @@ import type { Config } from '@/config.js'; import type Logger from '@/logger.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; +import { bindThis } from '@/decorators.js'; @Injectable() export class CleanProcessorService { @@ -23,6 +24,7 @@ export class CleanProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('clean'); } + @bindThis public async process(job: Bull.Job>, done: () => void): Promise { this.logger.info('Cleaning...'); diff --git a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts index a4fd8c502d..5a33c27188 100644 --- a/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanRemoteFilesProcessorService.ts @@ -7,6 +7,7 @@ import type Logger from '@/logger.js'; import { DriveService } from '@/core/DriveService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; +import { bindThis } from '@/decorators.js'; @Injectable() export class CleanRemoteFilesProcessorService { @@ -25,6 +26,7 @@ export class CleanRemoteFilesProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('clean-remote-files'); } + @bindThis public async process(job: Bull.Job>, done: () => void): Promise { this.logger.info('Deleting cached remote files...'); diff --git a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts index 5e4c8bdd69..e36a78de6a 100644 --- a/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteAccountProcessorService.ts @@ -11,6 +11,7 @@ import { EmailService } from '@/core/EmailService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserDeleteJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class DeleteAccountProcessorService { @@ -39,6 +40,7 @@ export class DeleteAccountProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('delete-account'); } + @bindThis public async process(job: Bull.Job): Promise { this.logger.info(`Deleting account of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts index 682382b2db..fa0c1733f6 100644 --- a/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteDriveFilesProcessorService.ts @@ -8,6 +8,7 @@ import { DriveService } from '@/core/DriveService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class DeleteDriveFilesProcessorService { @@ -29,6 +30,7 @@ export class DeleteDriveFilesProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('delete-drive-files'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info(`Deleting drive files of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/DeleteFileProcessorService.ts b/packages/backend/src/queue/processors/DeleteFileProcessorService.ts index 6740643fe2..2fb2f56f8d 100644 --- a/packages/backend/src/queue/processors/DeleteFileProcessorService.ts +++ b/packages/backend/src/queue/processors/DeleteFileProcessorService.ts @@ -6,6 +6,7 @@ import { DriveService } from '@/core/DriveService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { ObjectStorageFileJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class DeleteFileProcessorService { @@ -21,6 +22,7 @@ export class DeleteFileProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('delete-file'); } + @bindThis public async process(job: Bull.Job): Promise { const key: string = job.data.key; diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts index 2a4b201a7d..58969d550e 100644 --- a/packages/backend/src/queue/processors/DeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts @@ -18,6 +18,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DeliverJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class DeliverProcessorService { @@ -50,6 +51,7 @@ export class DeliverProcessorService { this.latest = null; } + @bindThis public async process(job: Bull.Job): Promise { const { host } = new URL(job.data.to); diff --git a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts index 2fc7fe219e..21d2dc9efc 100644 --- a/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts +++ b/packages/backend/src/queue/processors/EndedPollNotificationProcessorService.ts @@ -8,6 +8,7 @@ import { CreateNotificationService } from '@/core/CreateNotificationService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { EndedPollNotificationJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class EndedPollNotificationProcessorService { @@ -29,6 +30,7 @@ export class EndedPollNotificationProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('ended-poll-notification'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { const note = await this.notesRepository.findOneBy({ id: job.data.noteId }); if (note == null || !note.hasPoll) { diff --git a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts index db149b68cf..5b3c1a415b 100644 --- a/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportBlockingProcessorService.ts @@ -12,6 +12,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ExportBlockingProcessorService { @@ -34,6 +35,7 @@ export class ExportBlockingProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('export-blocking'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info(`Exporting blocking of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts index f8f261b478..87b23f1891 100644 --- a/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportCustomEmojisProcessorService.ts @@ -14,6 +14,7 @@ import { createTemp, createTempDir } from '@/misc/create-temp.js'; import { DownloadService } from '@/core/DownloadService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ExportCustomEmojisProcessorService { @@ -36,6 +37,7 @@ export class ExportCustomEmojisProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('export-custom-emojis'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info('Exporting custom emojis ...'); diff --git a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts index 1e3fba06b5..064b126e44 100644 --- a/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportFollowingProcessorService.ts @@ -13,6 +13,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ExportFollowingProcessorService { @@ -38,6 +39,7 @@ export class ExportFollowingProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('export-following'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info(`Exporting following of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts index e263c245fd..94c7ea8a46 100644 --- a/packages/backend/src/queue/processors/ExportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportMutingProcessorService.ts @@ -12,6 +12,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ExportMutingProcessorService { @@ -37,6 +38,7 @@ export class ExportMutingProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('export-muting'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info(`Exporting muting of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts index 533d4bd7c6..8431829e91 100644 --- a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts @@ -13,6 +13,7 @@ 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 { bindThis } from '@/decorators.js'; @Injectable() export class ExportNotesProcessorService { @@ -37,6 +38,7 @@ export class ExportNotesProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('export-notes'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info(`Exporting notes of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts index 8c3e3dbe17..a8daa5e5ee 100644 --- a/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportUserListsProcessorService.ts @@ -12,6 +12,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ExportUserListsProcessorService { @@ -37,6 +38,7 @@ export class ExportUserListsProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('export-user-lists'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info(`Exporting user lists of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts index 9442a60d8d..2eed420e96 100644 --- a/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportBlockingProcessorService.ts @@ -12,6 +12,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserImportJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ImportBlockingProcessorService { @@ -39,6 +40,7 @@ export class ImportBlockingProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('import-blocking'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info(`Importing blocking of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts index 492f17f9ff..0061c2a8f7 100644 --- a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts @@ -10,6 +10,7 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { createTempDir } from '@/misc/create-temp.js'; import { DriveService } from '@/core/DriveService.js'; import { DownloadService } from '@/core/DownloadService.js'; +import { bindThis } from '@/decorators.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserImportJobData } from '../types.js'; @@ -43,6 +44,7 @@ export class ImportCustomEmojisProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('import-custom-emojis'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info('Importing custom emojis ...'); diff --git a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts index 667f7279fb..b61846d747 100644 --- a/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportFollowingProcessorService.ts @@ -12,6 +12,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserImportJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ImportFollowingProcessorService { @@ -36,6 +37,7 @@ export class ImportFollowingProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('import-following'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info(`Importing following of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts index f3c16e73d5..21236da2ef 100644 --- a/packages/backend/src/queue/processors/ImportMutingProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportMutingProcessorService.ts @@ -12,6 +12,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserImportJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ImportMutingProcessorService { @@ -36,6 +37,7 @@ export class ImportMutingProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('import-muting'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info(`Importing muting of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts index 1519877c5f..1bec77b837 100644 --- a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts @@ -13,6 +13,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserImportJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ImportUserListsProcessorService { @@ -44,6 +45,7 @@ export class ImportUserListsProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('import-user-lists'); } + @bindThis public async process(job: Bull.Job, done: () => void): Promise { this.logger.info(`Importing user lists of ${job.data.user.id} ...`); diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index 8f1c474020..c032122caf 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -24,6 +24,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; import { LdSignatureService } from '@/core/activitypub/LdSignatureService.js'; import { ApInboxService } from '@/core/activitypub/ApInboxService.js'; +import { bindThis } from '@/decorators.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DeliverJobData, InboxJobData } from '../types.js'; @@ -60,6 +61,7 @@ export class InboxProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('inbox'); } + @bindThis public async process(job: Bull.Job): Promise { const signature = job.data.signature; // HTTP-signature const activity = job.data.activity; diff --git a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts index bf2fdeb7a0..1a8fe65a4f 100644 --- a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts @@ -17,6 +17,7 @@ import PerUserDriveChart from '@/core/chart/charts/per-user-drive.js'; import ApRequestChart from '@/core/chart/charts/ap-request.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ResyncChartsProcessorService { @@ -44,6 +45,7 @@ export class ResyncChartsProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('resync-charts'); } + @bindThis public async process(job: Bull.Job>, done: () => void): Promise { this.logger.info('Resync charts...'); diff --git a/packages/backend/src/queue/processors/TickChartsProcessorService.ts b/packages/backend/src/queue/processors/TickChartsProcessorService.ts index 96607e1d68..323e5227cd 100644 --- a/packages/backend/src/queue/processors/TickChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/TickChartsProcessorService.ts @@ -17,6 +17,7 @@ import PerUserDriveChart from '@/core/chart/charts/per-user-drive.js'; import ApRequestChart from '@/core/chart/charts/ap-request.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; +import { bindThis } from '@/decorators.js'; @Injectable() export class TickChartsProcessorService { @@ -44,6 +45,7 @@ export class TickChartsProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('tick-charts'); } + @bindThis public async process(job: Bull.Job>, done: () => void): Promise { this.logger.info('Tick charts...'); diff --git a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts index 43e3f37201..183ef07477 100644 --- a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts @@ -9,6 +9,7 @@ import { StatusError } from '@/misc/status-error.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { WebhookDeliverJobData } from '../types.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class WebhookDeliverProcessorService { @@ -27,6 +28,7 @@ export class WebhookDeliverProcessorService { this.logger = this.queueLoggerService.logger.createSubLogger('webhook'); } + @bindThis public async process(job: Bull.Job): Promise { try { this.logger.debug(`delivering ${job.data.webhookId}`); diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 015c8f2b4c..94a277f4a4 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -18,6 +18,7 @@ import type { Note } from '@/models/entities/Note.js'; import { QueryService } from '@/core/QueryService.js'; import { UtilityService } from '@/core/UtilityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; import type { FindOptionsWhere } from 'typeorm'; const ACTIVITY_JSON = 'application/activity+json; charset=utf-8'; @@ -57,9 +58,10 @@ export class ActivityPubServerService { private userKeypairStoreService: UserKeypairStoreService, private queryService: QueryService, ) { - this.createServer = this.createServer.bind(this); + //this.createServer = this.createServer.bind(this); } + @bindThis private setResponseType(request: FastifyRequest, reply: FastifyReply): void { const accept = request.accepts().type([ACTIVITY_JSON, LD_JSON]); if (accept === LD_JSON) { @@ -73,6 +75,7 @@ export class ActivityPubServerService { * Pack Create or Announce Activity * @param note Note */ + @bindThis private async packActivity(note: Note): Promise { if (note.renoteId && note.text == null && !note.hasPoll && (note.fileIds == null || note.fileIds.length === 0)) { const renote = await this.notesRepository.findOneByOrFail({ id: note.renoteId }); @@ -82,6 +85,7 @@ export class ActivityPubServerService { return this.apRendererService.renderCreate(await this.apRendererService.renderNote(note, false), note); } + @bindThis private inbox(request: FastifyRequest, reply: FastifyReply) { let signature; @@ -97,6 +101,7 @@ export class ActivityPubServerService { reply.code(202); } + @bindThis private async followers( request: FastifyRequest<{ Params: { user: string; }; Querystring: { cursor?: string; page?: string; }; }>, reply: FastifyReply, @@ -184,6 +189,7 @@ export class ActivityPubServerService { } } + @bindThis private async following( request: FastifyRequest<{ Params: { user: string; }; Querystring: { cursor?: string; page?: string; }; }>, reply: FastifyReply, @@ -271,6 +277,7 @@ export class ActivityPubServerService { } } + @bindThis private async featured(request: FastifyRequest<{ Params: { user: string; }; }>, reply: FastifyReply) { const userId = request.params.user; @@ -304,6 +311,7 @@ export class ActivityPubServerService { return (this.apRendererService.renderActivity(rendered)); } + @bindThis private async outbox( request: FastifyRequest<{ Params: { user: string; }; @@ -390,6 +398,7 @@ export class ActivityPubServerService { } } + @bindThis private async userInfo(request: FastifyRequest, reply: FastifyReply, user: User | null) { if (user == null) { reply.code(404); @@ -401,6 +410,7 @@ export class ActivityPubServerService { return (this.apRendererService.renderActivity(await this.apRendererService.renderPerson(user as ILocalUser))); } + @bindThis public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { fastify.addConstraintStrategy({ name: 'apOrHtml', diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index 088e780d69..b7ab549611 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -19,6 +19,7 @@ import { InternalStorageService } from '@/core/InternalStorageService.js'; import { contentDisposition } from '@/misc/content-disposition.js'; import { FileInfoService } from '@/core/FileInfoService.js'; import { LoggerService } from '@/core/LoggerService.js'; +import { bindThis } from '@/decorators.js'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); @@ -45,9 +46,10 @@ export class FileServerService { ) { this.logger = this.loggerService.getLogger('server', 'gray', false); - this.createServer = this.createServer.bind(this); + //this.createServer = this.createServer.bind(this); } + @bindThis public commonReadableHandlerGenerator(reply: FastifyReply) { return (err: Error): void => { this.logger.error(err); @@ -56,6 +58,7 @@ export class FileServerService { }; } + @bindThis public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { fastify.addHook('onRequest', (request, reply, done) => { reply.header('Content-Security-Policy', 'default-src \'none\'; img-src \'self\'; media-src \'self\'; style-src \'unsafe-inline\''); @@ -80,6 +83,7 @@ export class FileServerService { done(); } + @bindThis private async sendDriveFile(request: FastifyRequest<{ Params: { key: string; } }>, reply: FastifyReply) { const key = request.params.key; diff --git a/packages/backend/src/server/MediaProxyServerService.ts b/packages/backend/src/server/MediaProxyServerService.ts index 4d7bbdf599..733a7feeb5 100644 --- a/packages/backend/src/server/MediaProxyServerService.ts +++ b/packages/backend/src/server/MediaProxyServerService.ts @@ -14,6 +14,7 @@ import { StatusError } from '@/misc/status-error.js'; import type Logger from '@/logger.js'; import { FileInfoService } from '@/core/FileInfoService.js'; import { LoggerService } from '@/core/LoggerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class MediaProxyServerService { @@ -30,9 +31,10 @@ export class MediaProxyServerService { ) { this.logger = this.loggerService.getLogger('server', 'gray', false); - this.createServer = this.createServer.bind(this); + //this.createServer = this.createServer.bind(this); } + @bindThis public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { fastify.addHook('onRequest', (request, reply, done) => { reply.header('Content-Security-Policy', 'default-src \'none\'; img-src \'self\'; media-src \'self\'; style-src \'unsafe-inline\''); @@ -47,6 +49,7 @@ export class MediaProxyServerService { done(); } + @bindThis private async handler(request: FastifyRequest<{ Params: { url: string; }; Querystring: { url?: string; }; }>, reply: FastifyReply) { const url = 'url' in request.query ? request.query.url : 'https://' + request.params.url; diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index b85925f53e..0f3cc36dae 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -8,6 +8,7 @@ import { MetaService } from '@/core/MetaService.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { Cache } from '@/misc/cache.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; const nodeinfo2_1path = '/nodeinfo/2.1'; const nodeinfo2_0path = '/nodeinfo/2.0'; @@ -27,9 +28,10 @@ export class NodeinfoServerService { private userEntityService: UserEntityService, private metaService: MetaService, ) { - this.createServer = this.createServer.bind(this); + //this.createServer = this.createServer.bind(this); } + @bindThis public getLinks() { return [/* (awaiting release) { rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1', @@ -40,6 +42,7 @@ export class NodeinfoServerService { }]; } + @bindThis public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { const nodeinfo2 = async () => { const now = Date.now(); diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index 96159cfc53..075b9cdff7 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -23,6 +23,7 @@ import { WellKnownServerService } from './WellKnownServerService.js'; import { MediaProxyServerService } from './MediaProxyServerService.js'; import { FileServerService } from './FileServerService.js'; import { ClientServerService } from './web/ClientServerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ServerService { @@ -53,6 +54,7 @@ export class ServerService { this.logger = this.loggerService.getLogger('server', 'gray', false); } + @bindThis public launch() { const fastify = Fastify({ trustProxy: true, diff --git a/packages/backend/src/server/WellKnownServerService.ts b/packages/backend/src/server/WellKnownServerService.ts index 412c608313..ea34ad5b0f 100644 --- a/packages/backend/src/server/WellKnownServerService.ts +++ b/packages/backend/src/server/WellKnownServerService.ts @@ -10,6 +10,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class WellKnownServerService { @@ -22,9 +23,10 @@ export class WellKnownServerService { private nodeinfoServerService: NodeinfoServerService, ) { - this.createServer = this.createServer.bind(this); + //this.createServer = this.createServer.bind(this); } + @bindThis public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { const XRD = (...x: { element: string, value?: string, attributes?: Record }[]) => `${x.map(({ element, value, attributes }) => diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index 2e72cdf9f8..fb1e17790c 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -12,6 +12,7 @@ import type Logger from '@/logger.js'; import type { UserIpsRepository } from '@/models/index.js'; import { MetaService } from '@/core/MetaService.js'; import { createTemp } from '@/misc/create-temp.js'; +import { bindThis } from '@/decorators.js'; import { ApiError } from './error.js'; import { RateLimiterService } from './RateLimiterService.js'; import { ApiLoggerService } from './ApiLoggerService.js'; @@ -50,6 +51,7 @@ export class ApiCallService implements OnApplicationShutdown { }, 1000 * 60 * 60); } + @bindThis public handleRequest( endpoint: IEndpoint & { exec: any }, request: FastifyRequest<{ Body: Record, Querystring: Record }>, @@ -90,6 +92,7 @@ export class ApiCallService implements OnApplicationShutdown { }); } + @bindThis public async handleMultipartRequest( endpoint: IEndpoint & { exec: any }, request: FastifyRequest<{ Body: Record, Querystring: Record }>, @@ -140,6 +143,7 @@ export class ApiCallService implements OnApplicationShutdown { }); } + @bindThis private send(reply: FastifyReply, x?: any, y?: ApiError) { if (x == null) { reply.code(204); @@ -160,6 +164,7 @@ export class ApiCallService implements OnApplicationShutdown { } } + @bindThis private async logIp(request: FastifyRequest, user: ILocalUser) { const meta = await this.metaService.fetch(); if (!meta.enableIpLogging) return; @@ -183,6 +188,7 @@ export class ApiCallService implements OnApplicationShutdown { } } + @bindThis private async call( ep: IEndpoint & { exec: any }, user: CacheableLocalUser | null | undefined, @@ -315,6 +321,7 @@ export class ApiCallService implements OnApplicationShutdown { }); } + @bindThis 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 index c4fb25036e..cabd65fd3e 100644 --- a/packages/backend/src/server/api/ApiLoggerService.ts +++ b/packages/backend/src/server/api/ApiLoggerService.ts @@ -1,6 +1,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { LoggerService } from '@/core/LoggerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ApiLoggerService { diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts index cf3f2deebf..b17456d0e2 100644 --- a/packages/backend/src/server/api/ApiServerService.ts +++ b/packages/backend/src/server/api/ApiServerService.ts @@ -14,6 +14,7 @@ import { SigninApiService } from './SigninApiService.js'; import { GithubServerService } from './integration/GithubServerService.js'; import { DiscordServerService } from './integration/DiscordServerService.js'; import { TwitterServerService } from './integration/TwitterServerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ApiServerService { @@ -40,9 +41,10 @@ export class ApiServerService { private discordServerService: DiscordServerService, private twitterServerService: TwitterServerService, ) { - this.createServer = this.createServer.bind(this); + //this.createServer = this.createServer.bind(this); } + @bindThis public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { fastify.register(cors, { origin: '*', diff --git a/packages/backend/src/server/api/AuthenticateService.ts b/packages/backend/src/server/api/AuthenticateService.ts index ad387c4732..8b39f6c924 100644 --- a/packages/backend/src/server/api/AuthenticateService.ts +++ b/packages/backend/src/server/api/AuthenticateService.ts @@ -7,6 +7,7 @@ import { Cache } from '@/misc/cache.js'; import type { App } from '@/models/entities/App.js'; import { UserCacheService } from '@/core/UserCacheService.js'; import isNativeToken from '@/misc/is-native-token.js'; +import { bindThis } from '@/decorators.js'; export class AuthenticationError extends Error { constructor(message: string) { @@ -34,6 +35,7 @@ export class AuthenticateService { this.appCache = new Cache(Infinity); } + @bindThis public async authenticate(token: string | null | undefined): Promise<[CacheableLocalUser | null | undefined, AccessToken | null | undefined]> { if (token == null) { return [null, null]; diff --git a/packages/backend/src/server/api/GetterService.ts b/packages/backend/src/server/api/GetterService.ts index 70ab46ec35..c7f9916f97 100644 --- a/packages/backend/src/server/api/GetterService.ts +++ b/packages/backend/src/server/api/GetterService.ts @@ -5,6 +5,7 @@ import { IdentifiableError } from '@/misc/identifiable-error.js'; import type { User } from '@/models/entities/User.js'; import type { Note } from '@/models/entities/Note.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class GetterService { @@ -22,6 +23,7 @@ export class GetterService { /** * Get note for API processing */ + @bindThis public async getNote(noteId: Note['id']) { const note = await this.notesRepository.findOneBy({ id: noteId }); @@ -35,6 +37,7 @@ export class GetterService { /** * Get user for API processing */ + @bindThis public async getUser(userId: User['id']) { const user = await this.usersRepository.findOneBy({ id: userId }); @@ -48,6 +51,7 @@ export class GetterService { /** * Get remote user for API processing */ + @bindThis public async getRemoteUser(userId: User['id']) { const user = await this.getUser(userId); @@ -61,6 +65,7 @@ export class GetterService { /** * Get local user for API processing */ + @bindThis public async getLocalUser(userId: User['id']) { const user = await this.getUser(userId); diff --git a/packages/backend/src/server/api/RateLimiterService.ts b/packages/backend/src/server/api/RateLimiterService.ts index 35f28bfd63..94a15f94bb 100644 --- a/packages/backend/src/server/api/RateLimiterService.ts +++ b/packages/backend/src/server/api/RateLimiterService.ts @@ -5,6 +5,7 @@ import { DI } from '@/di-symbols.js'; import type Logger from '@/logger.js'; import { LoggerService } from '@/core/LoggerService.js'; import type { IEndpointMeta } from './endpoints.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class RateLimiterService { @@ -19,6 +20,7 @@ export class RateLimiterService { this.logger = this.loggerService.getLogger('limiter'); } + @bindThis public limit(limitation: IEndpointMeta['limit'] & { key: NonNullable }, actor: string) { return new Promise((ok, reject) => { if (process.env.NODE_ENV === 'test') ok(); diff --git a/packages/backend/src/server/api/SigninApiService.ts b/packages/backend/src/server/api/SigninApiService.ts index 8b3d86e5a6..b633c2888f 100644 --- a/packages/backend/src/server/api/SigninApiService.ts +++ b/packages/backend/src/server/api/SigninApiService.ts @@ -13,6 +13,7 @@ import { IdService } from '@/core/IdService.js'; import { TwoFactorAuthenticationService } from '@/core/TwoFactorAuthenticationService.js'; import { RateLimiterService } from './RateLimiterService.js'; import { SigninService } from './SigninService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class SigninApiService { @@ -42,6 +43,7 @@ export class SigninApiService { ) { } + @bindThis public async signin( request: FastifyRequest<{ Body: { diff --git a/packages/backend/src/server/api/SigninService.ts b/packages/backend/src/server/api/SigninService.ts index 18a1d6c088..96a89956f9 100644 --- a/packages/backend/src/server/api/SigninService.ts +++ b/packages/backend/src/server/api/SigninService.ts @@ -7,6 +7,7 @@ import { IdService } from '@/core/IdService.js'; import type { ILocalUser } from '@/models/entities/User.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { SigninEntityService } from '@/core/entities/SigninEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class SigninService { @@ -23,6 +24,7 @@ export class SigninService { ) { } + @bindThis public signin(request: FastifyRequest, reply: FastifyReply, user: ILocalUser, redirect = false) { setImmediate(async () => { // Append signin history diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts index 771858d091..59676426af 100644 --- a/packages/backend/src/server/api/SignupApiService.ts +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -14,6 +14,7 @@ import { EmailService } from '@/core/EmailService.js'; import { ILocalUser } from '@/models/entities/User.js'; import { FastifyReplyError } from '@/misc/fastify-reply-error.js'; import { SigninService } from './SigninService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class SignupApiService { @@ -43,6 +44,7 @@ export class SignupApiService { ) { } + @bindThis public async signup( request: FastifyRequest<{ Body: { @@ -165,6 +167,7 @@ export class SignupApiService { } } + @bindThis public async signupPending(request: FastifyRequest<{ Body: { code: string; } }>, reply: FastifyReply) { const body = request.body; diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts index 46eaf8566e..487eef2d50 100644 --- a/packages/backend/src/server/api/StreamingApiServerService.ts +++ b/packages/backend/src/server/api/StreamingApiServerService.ts @@ -13,6 +13,7 @@ import MainStreamConnection from './stream/index.js'; import { ChannelsService } from './stream/ChannelsService.js'; import type { ParsedUrlQuery } from 'querystring'; import type * as http from 'node:http'; +import { bindThis } from '@/decorators.js'; @Injectable() export class StreamingApiServerService { @@ -49,6 +50,7 @@ export class StreamingApiServerService { ) { } + @bindThis public attachStreamingApi(server: http.Server) { // Init websocket server const ws = new websocket.server({ 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 53de8d9495..9fc1391570 100644 --- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts @@ -8,6 +8,7 @@ import { UserSuspendService } from '@/core/UserSuspendService.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; export const meta = { tags: ['admin'], @@ -79,6 +80,7 @@ export default class extends Endpoint { }); } + @bindThis private async unFollowAll(follower: User) { const followings = await this.followingsRepository.findBy({ followerId: follower.id, @@ -97,6 +99,7 @@ export default class extends Endpoint { } } + @bindThis private async readAllNotify(notifier: User) { await this.notificationsRepository.update({ notifierId: notifier.id, diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index c218ec4642..1068a2eec7 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -15,6 +15,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { UtilityService } from '@/core/UtilityService.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -112,6 +113,7 @@ export default class extends Endpoint { /*** * URIからUserかNoteを解決する */ + @bindThis private async fetchAny(uri: string, me: CacheableLocalUser | null | undefined): Promise | null> { // ブロックしてたら中断 const fetchedMeta = await this.metaService.fetch(); @@ -144,6 +146,7 @@ export default class extends Endpoint { ); } + @bindThis private async mergePack(me: CacheableLocalUser | null | undefined, user: User | null | undefined, note: Note | null | undefined): Promise | null> { if (user != null) { return { diff --git a/packages/backend/src/server/api/integration/DiscordServerService.ts b/packages/backend/src/server/api/integration/DiscordServerService.ts index 93c22a6c0b..a7f39a78d0 100644 --- a/packages/backend/src/server/api/integration/DiscordServerService.ts +++ b/packages/backend/src/server/api/integration/DiscordServerService.ts @@ -14,6 +14,7 @@ import { MetaService } from '@/core/MetaService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { FastifyReplyError } from '@/misc/fastify-reply-error.js'; import { SigninService } from '../SigninService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class DiscordServerService { @@ -36,9 +37,10 @@ export class DiscordServerService { private metaService: MetaService, private signinService: SigninService, ) { - this.create = this.create.bind(this); + //this.create = this.create.bind(this); } + @bindThis public create(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { fastify.get('/disconnect/discord', async (request, reply) => { if (!this.compareOrigin(request)) { @@ -288,10 +290,12 @@ export class DiscordServerService { done(); } + @bindThis private getUserToken(request: FastifyRequest): string | null { return ((request.headers['cookie'] ?? '').match(/igi=(\w+)/) ?? [null, null])[1]; } + @bindThis private compareOrigin(request: FastifyRequest): boolean { function normalizeUrl(url?: string): string { return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; diff --git a/packages/backend/src/server/api/integration/GithubServerService.ts b/packages/backend/src/server/api/integration/GithubServerService.ts index 2fd20bf831..3aa04f72ee 100644 --- a/packages/backend/src/server/api/integration/GithubServerService.ts +++ b/packages/backend/src/server/api/integration/GithubServerService.ts @@ -14,6 +14,7 @@ import { MetaService } from '@/core/MetaService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { FastifyReplyError } from '@/misc/fastify-reply-error.js'; import { SigninService } from '../SigninService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class GithubServerService { @@ -36,9 +37,10 @@ export class GithubServerService { private metaService: MetaService, private signinService: SigninService, ) { - this.create = this.create.bind(this); + //this.create = this.create.bind(this); } + @bindThis public create(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { fastify.get('/disconnect/github', async (request, reply) => { if (!this.compareOrigin(request)) { @@ -260,10 +262,12 @@ export class GithubServerService { done(); } + @bindThis private getUserToken(request: FastifyRequest): string | null { return ((request.headers['cookie'] ?? '').match(/igi=(\w+)/) ?? [null, null])[1]; } + @bindThis private compareOrigin(request: FastifyRequest): boolean { function normalizeUrl(url?: string): string { return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; diff --git a/packages/backend/src/server/api/integration/TwitterServerService.ts b/packages/backend/src/server/api/integration/TwitterServerService.ts index a8447f9d49..7a127fa29a 100644 --- a/packages/backend/src/server/api/integration/TwitterServerService.ts +++ b/packages/backend/src/server/api/integration/TwitterServerService.ts @@ -14,6 +14,7 @@ import { MetaService } from '@/core/MetaService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { FastifyReplyError } from '@/misc/fastify-reply-error.js'; import { SigninService } from '../SigninService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class TwitterServerService { @@ -36,9 +37,10 @@ export class TwitterServerService { private metaService: MetaService, private signinService: SigninService, ) { - this.create = this.create.bind(this); + //this.create = this.create.bind(this); } + @bindThis public create(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { fastify.get('/disconnect/twitter', async (request, reply) => { if (!this.compareOrigin(request)) { @@ -205,10 +207,12 @@ export class TwitterServerService { done(); } + @bindThis private getUserToken(request: FastifyRequest): string | null { return ((request.headers['cookie'] ?? '').match(/igi=(\w+)/) ?? [null, null])[1]; } + @bindThis private compareOrigin(request: FastifyRequest): boolean { function normalizeUrl(url?: string): string { return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts index d6005b1ee8..198fc190d4 100644 --- a/packages/backend/src/server/api/stream/ChannelsService.ts +++ b/packages/backend/src/server/api/stream/ChannelsService.ts @@ -15,6 +15,7 @@ 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'; +import { bindThis } from '@/decorators.js'; @Injectable() export class ChannelsService { @@ -37,6 +38,7 @@ export class ChannelsService { ) { } + @bindThis public getChannelService(name: string) { switch (name) { case 'main': return this.mainChannelService; diff --git a/packages/backend/src/server/api/stream/channel.ts b/packages/backend/src/server/api/stream/channel.ts index 5480c12c09..3e67880b45 100644 --- a/packages/backend/src/server/api/stream/channel.ts +++ b/packages/backend/src/server/api/stream/channel.ts @@ -1,3 +1,4 @@ +import { bindThis } from '@/decorators.js'; import type Connection from '.'; /** @@ -43,6 +44,7 @@ export default abstract class Channel { this.connection = connection; } + @bindThis public send(typeOrPayload: any, payload?: any) { const type = payload === undefined ? typeOrPayload.type : typeOrPayload; const body = payload === undefined ? typeOrPayload.body : payload; diff --git a/packages/backend/src/server/api/stream/channels/admin.ts b/packages/backend/src/server/api/stream/channels/admin.ts index 8c3c0d2adf..210e016a7e 100644 --- a/packages/backend/src/server/api/stream/channels/admin.ts +++ b/packages/backend/src/server/api/stream/channels/admin.ts @@ -1,4 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; class AdminChannel extends Channel { @@ -6,6 +7,7 @@ class AdminChannel extends Channel { public static shouldShare = true; public static requireCredential = true; + @bindThis public async init(params: any) { // Subscribe admin stream this.subscriber.on(`adminStream:${this.user!.id}`, data => { @@ -23,6 +25,7 @@ export class AdminChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): AdminChannel { return new AdminChannel( id, diff --git a/packages/backend/src/server/api/stream/channels/antenna.ts b/packages/backend/src/server/api/stream/channels/antenna.ts index 7c34aef495..44beef2da2 100644 --- a/packages/backend/src/server/api/stream/channels/antenna.ts +++ b/packages/backend/src/server/api/stream/channels/antenna.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { NotesRepository } from '@/models/index.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; import type { StreamMessages } from '../types.js'; @@ -18,9 +19,10 @@ class AntennaChannel extends Channel { connection: Channel['connection'], ) { super(id, connection); - this.onEvent = this.onEvent.bind(this); + //this.onEvent = this.onEvent.bind(this); } + @bindThis public async init(params: any) { this.antennaId = params.antennaId as string; @@ -28,6 +30,7 @@ class AntennaChannel extends Channel { this.subscriber.on(`antennaStream:${this.antennaId}`, this.onEvent); } + @bindThis private async onEvent(data: StreamMessages['antenna']['payload']) { if (data.type === 'note') { const note = await this.noteEntityService.pack(data.body.id, this.user, { detail: true }); @@ -45,6 +48,7 @@ class AntennaChannel extends Channel { } } + @bindThis public dispose() { // Unsubscribe events this.subscriber.off(`antennaStream:${this.antennaId}`, this.onEvent); @@ -61,6 +65,7 @@ export class AntennaChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): AntennaChannel { return new AntennaChannel( this.noteEntityService, diff --git a/packages/backend/src/server/api/stream/channels/channel.ts b/packages/backend/src/server/api/stream/channels/channel.ts index 2ef70e62e9..5ba84e43c4 100644 --- a/packages/backend/src/server/api/stream/channels/channel.ts +++ b/packages/backend/src/server/api/stream/channels/channel.ts @@ -5,6 +5,7 @@ import type { User } from '@/models/entities/User.js'; import type { Packed } from '@/misc/schema.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; import type { StreamMessages } from '../types.js'; @@ -24,10 +25,11 @@ class ChannelChannel extends Channel { connection: Channel['connection'], ) { super(id, connection); - this.onNote = this.onNote.bind(this); - this.emitTypers = this.emitTypers.bind(this); + //this.onNote = this.onNote.bind(this); + //this.emitTypers = this.emitTypers.bind(this); } + @bindThis public async init(params: any) { this.channelId = params.channelId as string; @@ -37,6 +39,7 @@ class ChannelChannel extends Channel { this.emitTypersIntervalId = setInterval(this.emitTypers, 5000); } + @bindThis private async onNote(note: Packed<'Note'>) { if (note.channelId !== this.channelId) return; @@ -63,6 +66,7 @@ class ChannelChannel extends Channel { this.send('note', note); } + @bindThis private onEvent(data: StreamMessages['channel']['payload']) { if (data.type === 'typing') { const id = data.body; @@ -74,6 +78,7 @@ class ChannelChannel extends Channel { } } + @bindThis private async emitTypers() { const now = new Date(); @@ -90,6 +95,7 @@ class ChannelChannel extends Channel { }); } + @bindThis public dispose() { // Unsubscribe events this.subscriber.off('notesStream', this.onNote); @@ -110,6 +116,7 @@ export class ChannelChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): ChannelChannel { return new ChannelChannel( this.noteEntityService, diff --git a/packages/backend/src/server/api/stream/channels/drive.ts b/packages/backend/src/server/api/stream/channels/drive.ts index 80d83cd690..cfcb125b6b 100644 --- a/packages/backend/src/server/api/stream/channels/drive.ts +++ b/packages/backend/src/server/api/stream/channels/drive.ts @@ -1,4 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; class DriveChannel extends Channel { @@ -6,6 +7,7 @@ class DriveChannel extends Channel { public static shouldShare = true; public static requireCredential = true; + @bindThis public async init(params: any) { // Subscribe drive stream this.subscriber.on(`driveStream:${this.user!.id}`, data => { @@ -23,6 +25,7 @@ export class DriveChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): DriveChannel { return new DriveChannel( id, 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 a8617582dc..34f782e580 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -6,6 +6,7 @@ import { isUserRelated } from '@/misc/is-user-related.js'; import type { Packed } from '@/misc/schema.js'; import { MetaService } from '@/core/MetaService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; class GlobalTimelineChannel extends Channel { @@ -21,9 +22,10 @@ class GlobalTimelineChannel extends Channel { connection: Channel['connection'], ) { super(id, connection); - this.onNote = this.onNote.bind(this); + //this.onNote = this.onNote.bind(this); } + @bindThis public async init(params: any) { const meta = await this.metaService.fetch(); if (meta.disableGlobalTimeline) { @@ -34,6 +36,7 @@ class GlobalTimelineChannel extends Channel { this.subscriber.on('notesStream', this.onNote); } + @bindThis private async onNote(note: Packed<'Note'>) { if (note.visibility !== 'public') return; if (note.channelId != null) return; @@ -78,6 +81,7 @@ class GlobalTimelineChannel extends Channel { this.send('note', note); } + @bindThis public dispose() { // Unsubscribe events this.subscriber.off('notesStream', this.onNote); @@ -95,6 +99,7 @@ export class GlobalTimelineChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): GlobalTimelineChannel { return new GlobalTimelineChannel( this.metaService, diff --git a/packages/backend/src/server/api/stream/channels/hashtag.ts b/packages/backend/src/server/api/stream/channels/hashtag.ts index 0f6c081c12..073b737079 100644 --- a/packages/backend/src/server/api/stream/channels/hashtag.ts +++ b/packages/backend/src/server/api/stream/channels/hashtag.ts @@ -4,6 +4,7 @@ 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 '@/core/entities/NoteEntityService.js'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; class HashtagChannel extends Channel { @@ -19,9 +20,10 @@ class HashtagChannel extends Channel { connection: Channel['connection'], ) { super(id, connection); - this.onNote = this.onNote.bind(this); + //this.onNote = this.onNote.bind(this); } + @bindThis public async init(params: any) { this.q = params.q; @@ -31,6 +33,7 @@ class HashtagChannel extends Channel { this.subscriber.on('notesStream', this.onNote); } + @bindThis private async onNote(note: Packed<'Note'>) { const noteTags = note.tags ? note.tags.map((t: string) => t.toLowerCase()) : []; const matched = this.q.some(tags => tags.every(tag => noteTags.includes(normalizeForSearch(tag)))); @@ -53,6 +56,7 @@ class HashtagChannel extends Channel { this.send('note', note); } + @bindThis public dispose() { // Unsubscribe events this.subscriber.off('notesStream', this.onNote); @@ -69,6 +73,7 @@ export class HashtagChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): HashtagChannel { return new HashtagChannel( this.noteEntityService, 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 16e0cebc72..5707ddd821 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -5,6 +5,7 @@ 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 '@/core/entities/NoteEntityService.js'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; class HomeTimelineChannel extends Channel { @@ -19,14 +20,16 @@ class HomeTimelineChannel extends Channel { connection: Channel['connection'], ) { super(id, connection); - this.onNote = this.onNote.bind(this); + //this.onNote = this.onNote.bind(this); } + @bindThis public async init(params: any) { // Subscribe events this.subscriber.on('notesStream', this.onNote); } + @bindThis private async onNote(note: Packed<'Note'>) { if (note.channelId) { if (!this.followingChannels.has(note.channelId)) return; @@ -85,6 +88,7 @@ class HomeTimelineChannel extends Channel { this.send('note', note); } + @bindThis public dispose() { // Unsubscribe events this.subscriber.off('notesStream', this.onNote); @@ -101,6 +105,7 @@ export class HomeTimelineChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): HomeTimelineChannel { return new HomeTimelineChannel( this.noteEntityService, 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 f1ce822583..6c6afb12bf 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -7,6 +7,7 @@ import type { Packed } from '@/misc/schema.js'; import { DI } from '@/di-symbols.js'; import { MetaService } from '@/core/MetaService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; class HybridTimelineChannel extends Channel { @@ -22,9 +23,10 @@ class HybridTimelineChannel extends Channel { connection: Channel['connection'], ) { super(id, connection); - this.onNote = this.onNote.bind(this); + //this.onNote = this.onNote.bind(this); } + @bindThis public async init(params: any): Promise { const meta = await this.metaService.fetch(); if (meta.disableLocalTimeline && !this.user!.isAdmin && !this.user!.isModerator) return; @@ -33,6 +35,7 @@ class HybridTimelineChannel extends Channel { this.subscriber.on('notesStream', this.onNote); } + @bindThis private async onNote(note: Packed<'Note'>) { // チャンネルの投稿ではなく、自分自身の投稿 または // チャンネルの投稿ではなく、その投稿のユーザーをフォローしている または @@ -95,6 +98,7 @@ class HybridTimelineChannel extends Channel { this.send('note', note); } + @bindThis public dispose(): void { // Unsubscribe events this.subscriber.off('notesStream', this.onNote); @@ -112,6 +116,7 @@ export class HybridTimelineChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): HybridTimelineChannel { return new HybridTimelineChannel( this.metaService, 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 5a5a43f845..54388787ef 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -5,6 +5,7 @@ import { isUserRelated } from '@/misc/is-user-related.js'; import type { Packed } from '@/misc/schema.js'; import { MetaService } from '@/core/MetaService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; class LocalTimelineChannel extends Channel { @@ -20,9 +21,10 @@ class LocalTimelineChannel extends Channel { connection: Channel['connection'], ) { super(id, connection); - this.onNote = this.onNote.bind(this); + //this.onNote = this.onNote.bind(this); } + @bindThis public async init(params: any) { const meta = await this.metaService.fetch(); if (meta.disableLocalTimeline) { @@ -33,6 +35,7 @@ class LocalTimelineChannel extends Channel { this.subscriber.on('notesStream', this.onNote); } + @bindThis private async onNote(note: Packed<'Note'>) { if (note.user.host !== null) return; if (note.visibility !== 'public') return; @@ -75,6 +78,7 @@ class LocalTimelineChannel extends Channel { this.send('note', note); } + @bindThis public dispose() { // Unsubscribe events this.subscriber.off('notesStream', this.onNote); @@ -92,6 +96,7 @@ export class LocalTimelineChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): LocalTimelineChannel { return new LocalTimelineChannel( this.metaService, diff --git a/packages/backend/src/server/api/stream/channels/main.ts b/packages/backend/src/server/api/stream/channels/main.ts index 12908e07b4..42f255b8fe 100644 --- a/packages/backend/src/server/api/stream/channels/main.ts +++ b/packages/backend/src/server/api/stream/channels/main.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { NotesRepository } from '@/models/index.js'; import { isInstanceMuted, isUserFromMutedInstance } from '@/misc/is-instance-muted.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; class MainChannel extends Channel { @@ -18,6 +19,7 @@ class MainChannel extends Channel { super(id, connection); } + @bindThis public async init(params: any) { // Subscribe main stream channel this.subscriber.on(`mainStream:${this.user!.id}`, async data => { @@ -66,6 +68,7 @@ export class MainChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): MainChannel { return new MainChannel( this.noteEntityService, 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 bebc07f4ad..66cb79f7a7 100644 --- a/packages/backend/src/server/api/stream/channels/messaging-index.ts +++ b/packages/backend/src/server/api/stream/channels/messaging-index.ts @@ -1,4 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; class MessagingIndexChannel extends Channel { @@ -6,6 +7,7 @@ class MessagingIndexChannel extends Channel { public static shouldShare = true; public static requireCredential = true; + @bindThis public async init(params: any) { // Subscribe messaging index stream this.subscriber.on(`messagingIndexStream:${this.user!.id}`, data => { @@ -23,6 +25,7 @@ export class MessagingIndexChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): MessagingIndexChannel { return new MessagingIndexChannel( id, diff --git a/packages/backend/src/server/api/stream/channels/messaging.ts b/packages/backend/src/server/api/stream/channels/messaging.ts index b6ce6c217e..92af6b591c 100644 --- a/packages/backend/src/server/api/stream/channels/messaging.ts +++ b/packages/backend/src/server/api/stream/channels/messaging.ts @@ -5,6 +5,7 @@ import type { UserGroup } from '@/models/entities/UserGroup.js'; import { MessagingService } from '@/core/MessagingService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; import type { StreamMessages } from '../types.js'; @@ -31,11 +32,12 @@ class MessagingChannel extends Channel { connection: Channel['connection'], ) { super(id, connection); - this.onEvent = this.onEvent.bind(this); - this.onMessage = this.onMessage.bind(this); - this.emitTypers = this.emitTypers.bind(this); + //this.onEvent = this.onEvent.bind(this); + //this.onMessage = this.onMessage.bind(this); + //this.emitTypers = this.emitTypers.bind(this); } + @bindThis public async init(params: any) { this.otherpartyId = params.otherparty; this.otherparty = this.otherpartyId ? await this.usersRepository.findOneByOrFail({ id: this.otherpartyId }) : null; @@ -63,6 +65,7 @@ class MessagingChannel extends Channel { this.subscriber.on(this.subCh, this.onEvent); } + @bindThis private onEvent(data: StreamMessages['messaging']['payload'] | StreamMessages['groupMessaging']['payload']) { if (data.type === 'typing') { const id = data.body; @@ -76,6 +79,7 @@ class MessagingChannel extends Channel { } } + @bindThis public onMessage(type: string, body: any) { switch (type) { case 'read': @@ -95,6 +99,7 @@ class MessagingChannel extends Channel { } } + @bindThis private async emitTypers() { const now = new Date(); @@ -111,6 +116,7 @@ class MessagingChannel extends Channel { }); } + @bindThis public dispose() { this.subscriber.off(this.subCh, this.onEvent); @@ -138,6 +144,7 @@ export class MessagingChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): MessagingChannel { return new MessagingChannel( this.usersRepository, 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 1802c6723b..c773916103 100644 --- a/packages/backend/src/server/api/stream/channels/queue-stats.ts +++ b/packages/backend/src/server/api/stream/channels/queue-stats.ts @@ -1,5 +1,6 @@ import Xev from 'xev'; import { Inject, Injectable } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; const ev = new Xev(); @@ -11,18 +12,21 @@ class QueueStatsChannel extends Channel { constructor(id: string, connection: Channel['connection']) { super(id, connection); - this.onStats = this.onStats.bind(this); - this.onMessage = this.onMessage.bind(this); + //this.onStats = this.onStats.bind(this); + //this.onMessage = this.onMessage.bind(this); } + @bindThis public async init(params: any) { ev.addListener('queueStats', this.onStats); } + @bindThis private onStats(stats: any) { this.send('stats', stats); } + @bindThis public onMessage(type: string, body: any) { switch (type) { case 'requestLog': @@ -37,6 +41,7 @@ class QueueStatsChannel extends Channel { } } + @bindThis public dispose() { ev.removeListener('queueStats', this.onStats); } @@ -51,6 +56,7 @@ export class QueueStatsChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): QueueStatsChannel { return new QueueStatsChannel( id, 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 e2b00de25f..492912dbe6 100644 --- a/packages/backend/src/server/api/stream/channels/server-stats.ts +++ b/packages/backend/src/server/api/stream/channels/server-stats.ts @@ -1,5 +1,6 @@ import Xev from 'xev'; import { Inject, Injectable } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; const ev = new Xev(); @@ -11,18 +12,21 @@ class ServerStatsChannel extends Channel { constructor(id: string, connection: Channel['connection']) { super(id, connection); - this.onStats = this.onStats.bind(this); - this.onMessage = this.onMessage.bind(this); + //this.onStats = this.onStats.bind(this); + //this.onMessage = this.onMessage.bind(this); } + @bindThis public async init(params: any) { ev.addListener('serverStats', this.onStats); } + @bindThis private onStats(stats: any) { this.send('stats', stats); } + @bindThis public onMessage(type: string, body: any) { switch (type) { case 'requestLog': @@ -37,6 +41,7 @@ class ServerStatsChannel extends Channel { } } + @bindThis public dispose() { ev.removeListener('serverStats', this.onStats); } @@ -51,6 +56,7 @@ export class ServerStatsChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): ServerStatsChannel { return new ServerStatsChannel( id, 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 f9f0d02558..16af32868c 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -1,11 +1,11 @@ import { Inject, Injectable } from '@nestjs/common'; -import type { UserListJoiningsRepository, UserListsRepository } from '@/models/index.js'; -import type { NotesRepository } from '@/models/index.js'; +import type { UserListJoiningsRepository, UserListsRepository, NotesRepository } 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 '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; import Channel from '../channel.js'; class UserListChannel extends Channel { @@ -25,10 +25,11 @@ class UserListChannel extends Channel { connection: Channel['connection'], ) { super(id, connection); - this.updateListUsers = this.updateListUsers.bind(this); - this.onNote = this.onNote.bind(this); + //this.updateListUsers = this.updateListUsers.bind(this); + //this.onNote = this.onNote.bind(this); } + @bindThis public async init(params: any) { this.listId = params.listId as string; @@ -48,6 +49,7 @@ class UserListChannel extends Channel { this.listUsersClock = setInterval(this.updateListUsers, 5000); } + @bindThis private async updateListUsers() { const users = await this.userListJoiningsRepository.find({ where: { @@ -59,6 +61,7 @@ class UserListChannel extends Channel { this.listUsers = users.map(x => x.userId); } + @bindThis private async onNote(note: Packed<'Note'>) { if (!this.listUsers.includes(note.userId)) return; @@ -93,6 +96,7 @@ class UserListChannel extends Channel { this.send('note', note); } + @bindThis public dispose() { // Unsubscribe events this.subscriber.off(`userListStream:${this.listId}`, this.send); @@ -118,6 +122,7 @@ export class UserListChannelService { ) { } + @bindThis public create(id: string, connection: Channel['connection']): UserListChannel { return new UserListChannel( this.userListsRepository, diff --git a/packages/backend/src/server/api/stream/index.ts b/packages/backend/src/server/api/stream/index.ts index 0c5066b736..6763953f9d 100644 --- a/packages/backend/src/server/api/stream/index.ts +++ b/packages/backend/src/server/api/stream/index.ts @@ -8,6 +8,7 @@ import type { Packed } from '@/misc/schema.js'; import type { GlobalEventService } from '@/core/GlobalEventService.js'; import type { NoteReadService } from '@/core/NoteReadService.js'; import type { NotificationService } from '@/core/NotificationService.js'; +import { bindThis } from '@/decorators.js'; import type { ChannelsService } from './ChannelsService.js'; import type * as websocket from 'websocket'; import type { EventEmitter } from 'events'; @@ -52,10 +53,10 @@ export default class Connection { if (user) this.user = user; if (token) this.token = token; - this.onWsConnectionMessage = this.onWsConnectionMessage.bind(this); - this.onUserEvent = this.onUserEvent.bind(this); - this.onNoteStreamMessage = this.onNoteStreamMessage.bind(this); - this.onBroadcastMessage = this.onBroadcastMessage.bind(this); + //this.onWsConnectionMessage = this.onWsConnectionMessage.bind(this); + //this.onUserEvent = this.onUserEvent.bind(this); + //this.onNoteStreamMessage = this.onNoteStreamMessage.bind(this); + //this.onBroadcastMessage = this.onBroadcastMessage.bind(this); this.wsConnection.on('message', this.onWsConnectionMessage); @@ -74,6 +75,7 @@ export default class Connection { } } + @bindThis private onUserEvent(data: StreamMessages['user']['payload']) { // { type, body }と展開するとそれぞれ型が分離してしまう switch (data.type) { case 'follow': @@ -119,6 +121,7 @@ export default class Connection { /** * クライアントからメッセージ受信時 */ + @bindThis private async onWsConnectionMessage(data: websocket.Message) { if (data.type !== 'utf8') return; if (data.utf8Data == null) return; @@ -153,10 +156,12 @@ export default class Connection { } } + @bindThis private onBroadcastMessage(data: StreamMessages['broadcast']['payload']) { this.sendMessageToWs(data.type, data.body); } + @bindThis public cacheNote(note: Packed<'Note'>) { const add = (note: Packed<'Note'>) => { const existIndex = this.cachedNotes.findIndex(n => n.id === note.id); @@ -176,6 +181,7 @@ export default class Connection { if (note.renote) add(note.renote); } + @bindThis private readNote(body: any) { const id = body.id; @@ -190,6 +196,7 @@ export default class Connection { } } + @bindThis private onReadNotification(payload: any) { if (!payload.id) return; this.notificationService.readNotification(this.user!.id, [payload.id]); @@ -198,6 +205,7 @@ export default class Connection { /** * 投稿購読要求時 */ + @bindThis private onSubscribeNote(payload: any) { if (!payload.id) return; @@ -215,6 +223,7 @@ export default class Connection { /** * 投稿購読解除要求時 */ + @bindThis private onUnsubscribeNote(payload: any) { if (!payload.id) return; @@ -225,6 +234,7 @@ export default class Connection { } } + @bindThis private async onNoteStreamMessage(data: StreamMessages['note']['payload']) { this.sendMessageToWs('noteUpdated', { id: data.body.id, @@ -236,6 +246,7 @@ export default class Connection { /** * チャンネル接続要求時 */ + @bindThis private onChannelConnectRequested(payload: any) { const { channel, id, params, pong } = payload; this.connectChannel(id, params, channel, pong); @@ -244,6 +255,7 @@ export default class Connection { /** * チャンネル切断要求時 */ + @bindThis private onChannelDisconnectRequested(payload: any) { const { id } = payload; this.disconnectChannel(id); @@ -252,6 +264,7 @@ export default class Connection { /** * クライアントにメッセージ送信 */ + @bindThis public sendMessageToWs(type: string, payload: any) { this.wsConnection.send(JSON.stringify({ type: type, @@ -262,6 +275,7 @@ export default class Connection { /** * チャンネルに接続 */ + @bindThis public connectChannel(id: string, params: any, channel: string, pong = false) { const channelService = this.channelsService.getChannelService(channel); @@ -289,6 +303,7 @@ export default class Connection { * チャンネルから切断 * @param id チャンネルコネクションID */ + @bindThis public disconnectChannel(id: string) { const channel = this.channels.find(c => c.id === id); @@ -302,6 +317,7 @@ export default class Connection { * チャンネルへメッセージ送信要求時 * @param data メッセージ */ + @bindThis private onChannelMessageRequested(data: any) { const channel = this.channels.find(c => c.id === data.id); if (channel != null && channel.onMessage != null) { @@ -309,12 +325,14 @@ export default class Connection { } } + @bindThis private typingOnChannel(channel: ChannelModel['id']) { if (this.user) { this.globalEventService.publishChannelStream(channel, 'typing', this.user.id); } } + @bindThis private typingOnMessaging(param: { partner?: User['id']; group?: UserGroup['id']; }) { if (this.user) { if (param.partner) { @@ -325,6 +343,7 @@ export default class Connection { } } + @bindThis private async updateFollowing() { const followings = await this.followingsRepository.find({ where: { @@ -336,6 +355,7 @@ export default class Connection { this.following = new Set(followings.map(x => x.followeeId)); } + @bindThis private async updateMuting() { const mutings = await this.mutingsRepository.find({ where: { @@ -347,6 +367,7 @@ export default class Connection { this.muting = new Set(mutings.map(x => x.muteeId)); } + @bindThis private async updateBlocking() { // ここでいうBlockingは被Blockingの意 const blockings = await this.blockingsRepository.find({ where: { @@ -358,6 +379,7 @@ export default class Connection { this.blocking = new Set(blockings.map(x => x.blockerId)); } + @bindThis private async updateFollowingChannels() { const followings = await this.channelFollowingsRepository.find({ where: { @@ -369,6 +391,7 @@ export default class Connection { this.followingChannels = new Set(followings.map(x => x.followeeId)); } + @bindThis private async updateUserProfile() { this.userProfile = await this.userProfilesRepository.findOneBy({ userId: this.user!.id, @@ -378,6 +401,7 @@ export default class Connection { /** * ストリームが切れたとき */ + @bindThis public dispose() { for (const c of this.channels.filter(c => c.dispose)) { if (c.dispose) c.dispose(); diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 3fcf8b7c0d..727cf92831 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -23,6 +23,7 @@ import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; import type { ChannelsRepository, ClipsRepository, GalleryPostsRepository, NotesRepository, PagesRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; import { deepClone } from '@/misc/clone.js'; +import { bindThis } from '@/decorators.js'; import manifest from './manifest.json' assert { type: 'json' }; import { FeedService } from './FeedService.js'; import { UrlPreviewService } from './UrlPreviewService.js'; @@ -80,9 +81,10 @@ export class ClientServerService { @Inject('queue:objectStorage') public objectStorageQueue: ObjectStorageQueue, @Inject('queue:webhookDeliver') public webhookDeliverQueue: WebhookDeliverQueue, ) { - this.createServer = this.createServer.bind(this); + //this.createServer = this.createServer.bind(this); } + @bindThis private async manifestHandler(reply: FastifyReply) { const res = deepClone(manifest); @@ -96,6 +98,7 @@ export class ClientServerService { return (res); } + @bindThis public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { /* TODO //#region Bull Dashboard diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts index 1d7d49961d..a14609adf9 100644 --- a/packages/backend/src/server/web/FeedService.ts +++ b/packages/backend/src/server/web/FeedService.ts @@ -7,6 +7,7 @@ import type { Config } from '@/config.js'; import type { User } from '@/models/entities/User.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class FeedService { @@ -31,6 +32,7 @@ export class FeedService { ) { } + @bindThis public async packFeed(user: User) { const author = { link: `${this.config.url}/@${user.username}`, diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts index 69f52cc2f2..69bb232d4a 100644 --- a/packages/backend/src/server/web/UrlPreviewService.ts +++ b/packages/backend/src/server/web/UrlPreviewService.ts @@ -9,6 +9,7 @@ import { HttpRequestService } from '@/core/HttpRequestService.js'; import type Logger from '@/logger.js'; import { query } from '@/misc/prelude/url.js'; import { LoggerService } from '@/core/LoggerService.js'; +import { bindThis } from '@/decorators.js'; @Injectable() export class UrlPreviewService { @@ -28,6 +29,7 @@ export class UrlPreviewService { this.logger = this.loggerService.getLogger('url-preview'); } + @bindThis private wrap(url?: string): string | null { return url != null ? url.match(/^https?:\/\//) @@ -39,6 +41,7 @@ export class UrlPreviewService { : null; } + @bindThis public async handle( request: FastifyRequest<{ Querystring: { url: string; lang: string; } }>, reply: FastifyReply, diff --git a/packages/backend/test/misc/mock-resolver.ts b/packages/backend/test/misc/mock-resolver.ts index cad4d8af6c..9efed267ea 100644 --- a/packages/backend/test/misc/mock-resolver.ts +++ b/packages/backend/test/misc/mock-resolver.ts @@ -15,6 +15,7 @@ export class MockResolver extends Resolver { }); } + @bindThis public async resolve(value: string | IObject): Promise { if (typeof value !== 'string') return value; -- cgit v1.2.3-freya From 9ce13d487b25ae518693b4b75177508101623393 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 4 Dec 2022 17:05:32 +0900 Subject: chore: fix import position --- packages/backend/src/core/AiService.ts | 2 +- packages/backend/src/core/AppLockService.ts | 2 +- packages/backend/src/core/CustomEmojiService.ts | 2 +- packages/backend/src/core/DriveService.ts | 2 +- packages/backend/src/core/FetchInstanceMetadataService.ts | 2 +- packages/backend/src/core/GlobalEventService.ts | 13 +++++++++++++ packages/backend/src/core/InstanceActorService.ts | 2 +- packages/backend/src/core/InternalStorageService.ts | 2 +- packages/backend/src/core/MfmService.ts | 2 +- packages/backend/src/core/NoteCreateService.ts | 2 +- packages/backend/src/core/PushNotificationService.ts | 2 +- packages/backend/src/core/ReactionService.ts | 2 +- packages/backend/src/core/RelayService.ts | 2 +- packages/backend/src/core/TwoFactorAuthenticationService.ts | 2 +- packages/backend/src/core/UserFollowingService.ts | 4 ++-- packages/backend/src/core/WebfingerService.ts | 2 +- 16 files changed, 29 insertions(+), 16 deletions(-) (limited to 'packages/backend/src/core/NoteCreateService.ts') diff --git a/packages/backend/src/core/AiService.ts b/packages/backend/src/core/AiService.ts index aee9504c85..059e335eff 100644 --- a/packages/backend/src/core/AiService.ts +++ b/packages/backend/src/core/AiService.ts @@ -6,13 +6,13 @@ import * as nsfw from 'nsfwjs'; import si from 'systeminformation'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); const REQUIRED_CPU_FLAGS = ['avx2', 'fma']; let isSupportedCpu: undefined | boolean = undefined; -import { bindThis } from '@/decorators.js'; @Injectable() export class AiService { diff --git a/packages/backend/src/core/AppLockService.ts b/packages/backend/src/core/AppLockService.ts index 1f512b5790..5f3072a415 100644 --- a/packages/backend/src/core/AppLockService.ts +++ b/packages/backend/src/core/AppLockService.ts @@ -3,12 +3,12 @@ import { Inject, Injectable } from '@nestjs/common'; import redisLock from 'redis-lock'; import Redis from 'ioredis'; import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; /** * Retry delay (ms) for lock acquisition */ const retryDelay = 100; -import { bindThis } from '@/decorators.js'; @Injectable() export class AppLockService { diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 36f88fd743..ff52ad27d6 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -12,6 +12,7 @@ import type { Note } from '@/models/entities/Note.js'; import type { EmojisRepository } from '@/models/index.js'; import { UtilityService } from '@/core/UtilityService.js'; import { ReactionService } from '@/core/ReactionService.js'; +import { bindThis } from '@/decorators.js'; /** * 添付用絵文字情報 @@ -20,7 +21,6 @@ type PopulatedEmoji = { name: string; url: string; }; -import { bindThis } from '@/decorators.js'; @Injectable() export class CustomEmojiService { diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index b83047dbc2..895073c32c 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -31,6 +31,7 @@ import { InternalStorageService } from '@/core/InternalStorageService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { FileInfoService } from '@/core/FileInfoService.js'; +import { bindThis } from '@/decorators.js'; import type S3 from 'aws-sdk/clients/s3.js'; type AddFileArgs = { @@ -71,7 +72,6 @@ type UploadFromUrlArgs = { requestIp?: string | null; requestHeaders?: Record | null; }; -import { bindThis } from '@/decorators.js'; @Injectable() export class DriveService { diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts index 4d4945ca7f..7eea45200e 100644 --- a/packages/backend/src/core/FetchInstanceMetadataService.ts +++ b/packages/backend/src/core/FetchInstanceMetadataService.ts @@ -10,6 +10,7 @@ import type Logger from '@/logger.js'; import { DI } from '@/di-symbols.js'; import { LoggerService } from '@/core/LoggerService.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; +import { bindThis } from '@/decorators.js'; import type { DOMWindow } from 'jsdom'; type NodeInfo = { @@ -30,7 +31,6 @@ type NodeInfo = { themeColor?: unknown; }; }; -import { bindThis } from '@/decorators.js'; @Injectable() export class FetchInstanceMetadataService { diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index 90f911027e..784612149d 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -50,26 +50,32 @@ export class GlobalEventService { })); } + @bindThis public publishInternalEvent(type: K, value?: InternalStreamTypes[K]): void { this.publish('internal', type, typeof value === 'undefined' ? null : value); } + @bindThis public publishUserEvent(userId: User['id'], type: K, value?: UserStreamTypes[K]): void { this.publish(`user:${userId}`, type, typeof value === 'undefined' ? null : value); } + @bindThis public publishBroadcastStream(type: K, value?: BroadcastTypes[K]): void { this.publish('broadcast', type, typeof value === 'undefined' ? null : value); } + @bindThis public publishMainStream(userId: User['id'], type: K, value?: MainStreamTypes[K]): void { this.publish(`mainStream:${userId}`, type, typeof value === 'undefined' ? null : value); } + @bindThis public publishDriveStream(userId: User['id'], type: K, value?: DriveStreamTypes[K]): void { this.publish(`driveStream:${userId}`, type, typeof value === 'undefined' ? null : value); } + @bindThis public publishNoteStream(noteId: Note['id'], type: K, value?: NoteStreamTypes[K]): void { this.publish(`noteStream:${noteId}`, type, { id: noteId, @@ -77,26 +83,32 @@ export class GlobalEventService { }); } + @bindThis public publishChannelStream(channelId: Channel['id'], type: K, value?: ChannelStreamTypes[K]): void { this.publish(`channelStream:${channelId}`, type, typeof value === 'undefined' ? null : value); } + @bindThis public publishUserListStream(listId: UserList['id'], type: K, value?: UserListStreamTypes[K]): void { this.publish(`userListStream:${listId}`, type, typeof value === 'undefined' ? null : value); } + @bindThis public publishAntennaStream(antennaId: Antenna['id'], type: K, value?: AntennaStreamTypes[K]): void { this.publish(`antennaStream:${antennaId}`, type, typeof value === 'undefined' ? null : value); } + @bindThis 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); } + @bindThis public publishGroupMessagingStream(groupId: UserGroup['id'], type: K, value?: GroupMessagingStreamTypes[K]): void { this.publish(`messagingStream:${groupId}`, type, typeof value === 'undefined' ? null : value); } + @bindThis public publishMessagingIndexStream(userId: User['id'], type: K, value?: MessagingIndexStreamTypes[K]): void { this.publish(`messagingIndexStream:${userId}`, type, typeof value === 'undefined' ? null : value); } @@ -106,6 +118,7 @@ export class GlobalEventService { this.publish('notesStream', null, note); } + @bindThis 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/core/InstanceActorService.ts b/packages/backend/src/core/InstanceActorService.ts index abd6685d61..0b4a83c634 100644 --- a/packages/backend/src/core/InstanceActorService.ts +++ b/packages/backend/src/core/InstanceActorService.ts @@ -5,9 +5,9 @@ import type { UsersRepository } from '@/models/index.js'; import { Cache } from '@/misc/cache.js'; import { DI } from '@/di-symbols.js'; import { CreateSystemUserService } from '@/core/CreateSystemUserService.js'; +import { bindThis } from '@/decorators.js'; const ACTOR_USERNAME = 'instance.actor' as const; -import { bindThis } from '@/decorators.js'; @Injectable() export class InstanceActorService { diff --git a/packages/backend/src/core/InternalStorageService.ts b/packages/backend/src/core/InternalStorageService.ts index e32cbf645c..7c03af7de7 100644 --- a/packages/backend/src/core/InternalStorageService.ts +++ b/packages/backend/src/core/InternalStorageService.ts @@ -5,12 +5,12 @@ import { dirname } from 'node:path'; import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; +import { bindThis } from '@/decorators.js'; const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); const path = Path.resolve(_dirname, '../../../../files'); -import { bindThis } from '@/decorators.js'; @Injectable() export class InternalStorageService { diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts index d53623baaa..6c40ba25a1 100644 --- a/packages/backend/src/core/MfmService.ts +++ b/packages/backend/src/core/MfmService.ts @@ -7,6 +7,7 @@ import type { UsersRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; import { intersperse } from '@/misc/prelude/array.js'; import type { IMentionedRemoteUsers } from '@/models/entities/Note.js'; +import { bindThis } from '@/decorators.js'; import * as TreeAdapter from '../../node_modules/parse5/dist/tree-adapters/default.js'; import type * as mfm from 'mfm-js'; @@ -14,7 +15,6 @@ const treeAdapter = TreeAdapter.defaultTreeAdapter; const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/; const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/; -import { bindThis } from '@/decorators.js'; @Injectable() export class MfmService { diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index a41df28050..b0a8f03af1 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -40,6 +40,7 @@ import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; import { NoteReadService } from '@/core/NoteReadService.js'; import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js'; +import { bindThis } from '@/decorators.js'; const mutedWordsCache = new Cache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5); @@ -132,7 +133,6 @@ type Option = { url?: string | null; app?: App | null; }; -import { bindThis } from '@/decorators.js'; @Injectable() export class NoteCreateService { diff --git a/packages/backend/src/core/PushNotificationService.ts b/packages/backend/src/core/PushNotificationService.ts index bffb24cf4c..512b6025b3 100644 --- a/packages/backend/src/core/PushNotificationService.ts +++ b/packages/backend/src/core/PushNotificationService.ts @@ -6,6 +6,7 @@ import type { Packed } from '@/misc/schema'; import { getNoteSummary } from '@/misc/get-note-summary.js'; import type { SwSubscriptionsRepository } from '@/models/index.js'; import { MetaService } from '@/core/MetaService.js'; +import { bindThis } from '@/decorators.js'; // Defined also packages/sw/types.ts#L14-L21 type pushNotificationsTypes = { @@ -37,7 +38,6 @@ function truncateNotification(notification: Packed<'Notification'>): any { return notification; } -import { bindThis } from '@/decorators.js'; @Injectable() export class PushNotificationService { diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index b02c990566..09022b57ab 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -17,6 +17,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { MetaService } from '@/core/MetaService.js'; +import { bindThis } from '@/decorators.js'; import { UtilityService } from './UtilityService.js'; const legacies: Record = { @@ -49,7 +50,6 @@ type DecodedReaction = { */ host?: string | null; }; -import { bindThis } from '@/decorators.js'; @Injectable() export class ReactionService { diff --git a/packages/backend/src/core/RelayService.ts b/packages/backend/src/core/RelayService.ts index 5fb853f25a..a7408649b8 100644 --- a/packages/backend/src/core/RelayService.ts +++ b/packages/backend/src/core/RelayService.ts @@ -10,9 +10,9 @@ import { CreateSystemUserService } from '@/core/CreateSystemUserService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { DI } from '@/di-symbols.js'; import { deepClone } from '@/misc/clone.js'; +import { bindThis } from '@/decorators.js'; const ACTOR_USERNAME = 'relay.actor' as const; -import { bindThis } from '@/decorators.js'; @Injectable() export class RelayService { diff --git a/packages/backend/src/core/TwoFactorAuthenticationService.ts b/packages/backend/src/core/TwoFactorAuthenticationService.ts index 150047fd22..dda78236e9 100644 --- a/packages/backend/src/core/TwoFactorAuthenticationService.ts +++ b/packages/backend/src/core/TwoFactorAuthenticationService.ts @@ -4,6 +4,7 @@ import * as jsrsasign from 'jsrsasign'; import { DI } from '@/di-symbols.js'; import type { UsersRepository } from '@/models/index.js'; import type { Config } from '@/config.js'; +import { bindThis } from '@/decorators.js'; const ECC_PRELUDE = Buffer.from([0x04]); const NULL_BYTE = Buffer.from([0]); @@ -103,7 +104,6 @@ function PEMString(pemBuffer: Buffer, type = 'CERTIFICATE') { `\n-----END ${type}-----\n` ); } -import { bindThis } from '@/decorators.js'; @Injectable() export class TwoFactorAuthenticationService { diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index a6cb042c7d..074aae86c8 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -13,9 +13,10 @@ import { WebhookService } from '@/core/WebhookService.js'; import { CreateNotificationService } from '@/core/CreateNotificationService.js'; import { DI } from '@/di-symbols.js'; import type { BlockingsRepository, FollowingsRepository, FollowRequestsRepository, InstancesRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; -import Logger from '../logger.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; +import { bindThis } from '@/decorators.js'; +import Logger from '../logger.js'; const logger = new Logger('following/create'); @@ -31,7 +32,6 @@ type Remote = IRemoteUser | { inbox: IRemoteUser['inbox']; }; type Both = Local | Remote; -import { bindThis } from '@/decorators.js'; @Injectable() export class UserFollowingService { diff --git a/packages/backend/src/core/WebfingerService.ts b/packages/backend/src/core/WebfingerService.ts index 2d7afe5c88..4c91ab8438 100644 --- a/packages/backend/src/core/WebfingerService.ts +++ b/packages/backend/src/core/WebfingerService.ts @@ -4,6 +4,7 @@ import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { query as urlQuery } from '@/misc/prelude/url.js'; import { HttpRequestService } from '@/core/HttpRequestService.js'; +import { bindThis } from '@/decorators.js'; type ILink = { href: string; @@ -14,7 +15,6 @@ type IWebFinger = { links: ILink[]; subject: string; }; -import { bindThis } from '@/decorators.js'; @Injectable() export class WebfingerService { -- cgit v1.2.3-freya From fe158339da85c0dc9674509d416d132435cbbdcf Mon Sep 17 00:00:00 2001 From: CyberRex Date: Wed, 21 Dec 2022 10:23:03 +0900 Subject: improve(backend): Skip note score incrementing when bots reacted (#9367) fix Improved code quality fix small fix --- packages/backend/src/core/NoteCreateService.ts | 4 +++- packages/backend/src/core/NoteDeleteService.ts | 4 ++-- packages/backend/src/core/ReactionService.ts | 8 ++++---- 3 files changed, 9 insertions(+), 7 deletions(-) (limited to 'packages/backend/src/core/NoteCreateService.ts') diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index b0a8f03af1..e9c2252314 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -198,6 +198,7 @@ export class NoteCreateService { host: User['host']; isSilenced: User['isSilenced']; createdAt: User['createdAt']; + isBot: User['isBot']; }, data: Option, silent = false): Promise { // チャンネル外にリプライしたら対象のスコープに合わせる // (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで) @@ -415,6 +416,7 @@ export class NoteCreateService { host: User['host']; isSilenced: User['isSilenced']; createdAt: User['createdAt']; + isBot: User['isBot']; }, data: Option, silent: boolean, tags: string[], mentionedUsers: MinimumUser[]) { // 統計を更新 this.notesChart.update(note, true); @@ -484,7 +486,7 @@ export class NoteCreateService { // この投稿を除く指定したユーザーによる指定したノートのリノートが存在しないとき if (data.renote && (await this.noteEntityService.countSameRenotes(user.id, data.renote.id, note.id) === 0)) { - this.incRenoteCount(data.renote); + if (!user.isBot) this.incRenoteCount(data.renote); } if (data.poll && data.poll.expiresAt) { diff --git a/packages/backend/src/core/NoteDeleteService.ts b/packages/backend/src/core/NoteDeleteService.ts index 7331d03552..1313fcbd3d 100644 --- a/packages/backend/src/core/NoteDeleteService.ts +++ b/packages/backend/src/core/NoteDeleteService.ts @@ -49,13 +49,13 @@ export class NoteDeleteService { * @param user 投稿者 * @param note 投稿 */ - async delete(user: { id: User['id']; uri: User['uri']; host: User['host']; }, note: Note, quiet = false) { + async delete(user: { id: User['id']; uri: User['uri']; host: User['host']; isBot: User['isBot']; }, note: Note, quiet = false) { const deletedAt = new Date(); // この投稿を除く指定したユーザーによる指定したノートのリノートが存在しないとき if (note.renoteId && (await this.noteEntityService.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 (!user.isBot) this.notesRepository.decrement({ id: note.renoteId }, 'score', 1); } if (note.replyId) { diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index 09022b57ab..1775d54228 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -83,7 +83,7 @@ export class ReactionService { } @bindThis - public async create(user: { id: User['id']; host: User['host']; }, note: Note, reaction?: string) { + public async create(user: { id: User['id']; host: User['host']; isBot: User['isBot'] }, note: Note, reaction?: string) { // Check blocking if (note.userId !== user.id) { const block = await this.blockingsRepository.findOneBy({ @@ -139,7 +139,7 @@ export class ReactionService { await this.notesRepository.createQueryBuilder().update() .set({ reactions: () => sql, - score: () => '"score" + 1', + ... (!user.isBot ? { score: () => '"score" + 1' } : {}), }) .where('id = :id', { id: note.id }) .execute(); @@ -199,7 +199,7 @@ export class ReactionService { } @bindThis - public async delete(user: { id: User['id']; host: User['host']; }, note: Note) { + public async delete(user: { id: User['id']; host: User['host']; isBot: User['isBot']; }, note: Note) { // if already unreacted const exist = await this.noteReactionsRepository.findOneBy({ noteId: note.id, @@ -226,7 +226,7 @@ export class ReactionService { .where('id = :id', { id: note.id }) .execute(); - this.notesRepository.decrement({ id: note.id }, 'score', 1); + if (!user.isBot) this.notesRepository.decrement({ id: note.id }, 'score', 1); this.globalEventServie.publishNoteStream(note.id, 'unreacted', { reaction: this.decodeReaction(exist.reaction).reaction, -- cgit v1.2.3-freya From 94b1c99c86197807e2bc50bfa7642a0d60b86f01 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 25 Dec 2022 14:28:51 +0900 Subject: fix(server): trim long text of note from ap Fix #9407 --- CHANGELOG.md | 1 + packages/backend/src/core/NoteCreateService.ts | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'packages/backend/src/core/NoteCreateService.ts') diff --git a/CHANGELOG.md b/CHANGELOG.md index d58ff7691f..64b47883d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ You should also include the user name that made the change. - Server: 引用内の文章がnyaizeされてしまう問題を修正 @kabo2468 - Server: Bug fix for Pinned Users lookup on instance @squidicuzz - Server: Fix peers API returning suspended instances @ineffyble +- Server: trim long text of note from ap @syuilo - Client: case insensitive emoji search @saschanaz - Client: InAppウィンドウが操作できなくなることがあるのを修正 @tamaina - Client: use proxied image for instance icon @syuilo diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index e9c2252314..cc6c213446 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -41,6 +41,7 @@ import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerServ import { NoteReadService } from '@/core/NoteReadService.js'; import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js'; import { bindThis } from '@/decorators.js'; +import { DB_MAX_NOTE_TEXT_LENGTH } from '@/const.js'; const mutedWordsCache = new Cache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5); @@ -259,6 +260,9 @@ export class NoteCreateService { } if (data.text) { + if (data.text.length > DB_MAX_NOTE_TEXT_LENGTH) { + data.text = data.text.slice(0, DB_MAX_NOTE_TEXT_LENGTH); + } data.text = data.text.trim(); } else { data.text = null; -- cgit v1.2.3-freya From 4cc71d24439375488655904048a3651455e44ad3 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 3 Jan 2023 09:32:36 +0900 Subject: :v: --- .../backend/src/core/FederatedInstanceService.ts | 15 ++++++++++++- packages/backend/src/core/NoteCreateService.ts | 2 +- packages/backend/src/core/NoteDeleteService.ts | 2 +- packages/backend/src/core/UserFollowingService.ts | 8 +++---- .../src/core/activitypub/models/ApPersonService.ts | 2 +- .../queue/processors/DeliverProcessorService.ts | 26 +++++++++++++++------- .../src/queue/processors/InboxProcessorService.ts | 5 ++++- .../server/api/endpoints/federation/instances.ts | 2 ++ 8 files changed, 45 insertions(+), 17 deletions(-) (limited to 'packages/backend/src/core/NoteCreateService.ts') diff --git a/packages/backend/src/core/FederatedInstanceService.ts b/packages/backend/src/core/FederatedInstanceService.ts index 48e77caaee..d517117da6 100644 --- a/packages/backend/src/core/FederatedInstanceService.ts +++ b/packages/backend/src/core/FederatedInstanceService.ts @@ -22,7 +22,7 @@ export class FederatedInstanceService { } @bindThis - public async registerOrFetchInstanceDoc(host: string): Promise { + public async fetch(host: string): Promise { host = this.utilityService.toPuny(host); const cached = this.cache.get(host); @@ -44,4 +44,17 @@ export class FederatedInstanceService { return index; } } + + @bindThis + public async updateCachePartial(host: string, data: Partial): Promise { + host = this.utilityService.toPuny(host); + + const cached = this.cache.get(host); + if (cached == null) return; + + this.cache.set(host, { + ...cached, + ...data, + }); + } } diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index cc6c213446..6038840406 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -428,7 +428,7 @@ export class NoteCreateService { // Register host if (this.userEntityService.isRemoteUser(user)) { - this.federatedInstanceService.registerOrFetchInstanceDoc(user.host).then(i => { + this.federatedInstanceService.fetch(user.host).then(i => { this.instancesRepository.increment({ id: i.id }, 'notesCount', 1); this.instanceChart.updateNote(i.host, note, true); }); diff --git a/packages/backend/src/core/NoteDeleteService.ts b/packages/backend/src/core/NoteDeleteService.ts index 1313fcbd3d..b1f16b6e8a 100644 --- a/packages/backend/src/core/NoteDeleteService.ts +++ b/packages/backend/src/core/NoteDeleteService.ts @@ -100,7 +100,7 @@ export class NoteDeleteService { this.perUserNotesChart.update(user, note, false); if (this.userEntityService.isRemoteUser(user)) { - this.federatedInstanceService.registerOrFetchInstanceDoc(user.host).then(i => { + this.federatedInstanceService.fetch(user.host).then(i => { this.instancesRepository.decrement({ id: i.id }, 'notesCount', 1); this.instanceChart.updateNote(i.host, note, false); }); diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index 074aae86c8..52834c375e 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -205,12 +205,12 @@ export class UserFollowingService { //#region Update instance stats if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { - this.federatedInstanceService.registerOrFetchInstanceDoc(follower.host).then(i => { + this.federatedInstanceService.fetch(follower.host).then(i => { this.instancesRepository.increment({ id: i.id }, 'followingCount', 1); this.instanceChart.updateFollowing(i.host, true); }); } else if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { - this.federatedInstanceService.registerOrFetchInstanceDoc(followee.host).then(i => { + this.federatedInstanceService.fetch(followee.host).then(i => { this.instancesRepository.increment({ id: i.id }, 'followersCount', 1); this.instanceChart.updateFollowers(i.host, true); }); @@ -323,12 +323,12 @@ export class UserFollowingService { //#region Update instance stats if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { - this.federatedInstanceService.registerOrFetchInstanceDoc(follower.host).then(i => { + this.federatedInstanceService.fetch(follower.host).then(i => { this.instancesRepository.decrement({ id: i.id }, 'followingCount', 1); this.instanceChart.updateFollowing(i.host, false); }); } else if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { - this.federatedInstanceService.registerOrFetchInstanceDoc(followee.host).then(i => { + this.federatedInstanceService.fetch(followee.host).then(i => { this.instancesRepository.decrement({ id: i.id }, 'followersCount', 1); this.instanceChart.updateFollowers(i.host, false); }); diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index d5faf37df2..e08f33c906 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -348,7 +348,7 @@ export class ApPersonService implements OnModuleInit { } // Register host - this.federatedInstanceService.registerOrFetchInstanceDoc(host).then(i => { + this.federatedInstanceService.fetch(host).then(i => { this.instancesRepository.increment({ id: i.id }, 'usersCount', 1); this.instanceChart.newUser(i.host); this.fetchInstanceMetadataService.fetchInstanceMetadata(i); diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts index fcff3a0e2a..d62e1f643a 100644 --- a/packages/backend/src/queue/processors/DeliverProcessorService.ts +++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts @@ -83,10 +83,15 @@ export class DeliverProcessorService { 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, { - isNotResponding: false, - }); + this.federatedInstanceService.fetch(host).then(i => { + if (i.isNotResponding) { + this.instancesRepository.update(i.id, { + isNotResponding: false, + }); + this.federatedInstanceService.updateCachePartial(host, { + isNotResponding: false, + }); + } this.fetchInstanceMetadataService.fetchInstanceMetadata(i); @@ -98,10 +103,15 @@ export class DeliverProcessorService { return 'Success'; } catch (res) { // Update stats - this.federatedInstanceService.registerOrFetchInstanceDoc(host).then(i => { - this.instancesRepository.update(i.id, { - isNotResponding: true, - }); + this.federatedInstanceService.fetch(host).then(i => { + if (!i.isNotResponding) { + this.instancesRepository.update(i.id, { + isNotResponding: true, + }); + this.federatedInstanceService.updateCachePartial(host, { + isNotResponding: true, + }); + } this.instanceChart.requestSent(i.host, false); this.apRequestChart.deliverFail(); diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts index 56058638c9..d033637849 100644 --- a/packages/backend/src/queue/processors/InboxProcessorService.ts +++ b/packages/backend/src/queue/processors/InboxProcessorService.ts @@ -176,11 +176,14 @@ export class InboxProcessorService { } // Update stats - this.federatedInstanceService.registerOrFetchInstanceDoc(authUser.user.host).then(i => { + this.federatedInstanceService.fetch(authUser.user.host).then(i => { this.instancesRepository.update(i.id, { latestRequestReceivedAt: new Date(), isNotResponding: false, }); + this.federatedInstanceService.updateCachePartial(host, { + isNotResponding: false, + }); this.fetchInstanceMetadataService.fetchInstanceMetadata(i); diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts index 6672434fb5..180887285a 100644 --- a/packages/backend/src/server/api/endpoints/federation/instances.ts +++ b/packages/backend/src/server/api/endpoints/federation/instances.ts @@ -64,6 +64,8 @@ export default class extends Endpoint { 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 '+latestRequestReceivedAt': query.orderBy('instance.latestRequestReceivedAt', 'DESC'); break; + case '-latestRequestReceivedAt': query.orderBy('instance.latestRequestReceivedAt', 'ASC'); break; default: query.orderBy('instance.id', 'DESC'); break; } -- cgit v1.2.3-freya From 2470afaa2e200fb2fc748e0f8eef5e2c215369b6 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 12 Jan 2023 21:02:26 +0900 Subject: Role (#9437) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * wip * Update CHANGELOG.md * wip * wip * wip * Update create.ts * wip * wip * Update CHANGELOG.md * wip * wip * wip * wip * wip * wip * wip * Update CHANGELOG.md * wip * wip * Update delete.ts * Update delete.ts * wip * wip * wip * Update account-info.vue * wip * wip * Update settings.vue * Update user-info.vue * wip * Update show-file.ts * Update show-user.ts * wip * wip * Update delete.ts * wip * wip * Update overview.moderators.vue * Create 1673500412259-Role.js * wip * wip * Update roles.vue * 色 * Update roles.vue * integrate silence * wip * wip --- CHANGELOG.md | 8 +- locales/ja-JP.yml | 29 +++ packages/backend/migration/1673500412259-Role.js | 37 ++++ .../backend/migration/1673515526953-RoleColor.js | 11 ++ .../backend/migration/1673522856499-RoleIroiro.js | 13 ++ .../migration/1673524604156-RoleLastUsedAt.js | 13 ++ packages/backend/src/core/CoreModule.ts | 12 ++ .../backend/src/core/CreateSystemUserService.ts | 2 +- packages/backend/src/core/DeleteAccountService.ts | 3 + packages/backend/src/core/DriveService.ts | 19 +- packages/backend/src/core/NoteCreateService.ts | 11 +- packages/backend/src/core/RoleService.ts | 201 +++++++++++++++++++++ packages/backend/src/core/SignupService.ts | 6 +- packages/backend/src/core/UserCacheService.ts | 4 +- .../backend/src/core/entities/RoleEntityService.ts | 80 ++++++++ .../backend/src/core/entities/UserEntityService.ts | 19 +- packages/backend/src/di-symbols.ts | 2 + packages/backend/src/models/RepositoryModule.ts | 18 +- packages/backend/src/models/entities/Meta.ts | 21 +-- packages/backend/src/models/entities/Role.ts | 66 +++++++ .../backend/src/models/entities/RoleAssignment.ts | 42 +++++ packages/backend/src/models/entities/User.ts | 22 +-- packages/backend/src/models/index.ts | 6 + packages/backend/src/postgre.ts | 4 + .../backend/src/server/NodeinfoServerService.ts | 7 +- packages/backend/src/server/api/ApiCallService.ts | 57 +++--- packages/backend/src/server/api/EndpointsModule.ts | 52 ++++-- packages/backend/src/server/api/endpoints.ts | 34 ++-- .../server/api/endpoints/admin/accounts/create.ts | 2 +- .../server/api/endpoints/admin/accounts/delete.ts | 10 +- .../endpoints/admin/delete-all-files-of-a-user.ts | 2 +- .../api/endpoints/admin/drive-capacity-override.ts | 61 ------- .../server/api/endpoints/admin/drive/show-file.ts | 9 +- .../server/api/endpoints/admin/get-index-stats.ts | 2 +- .../server/api/endpoints/admin/get-table-stats.ts | 2 +- .../src/server/api/endpoints/admin/get-user-ips.ts | 2 +- .../backend/src/server/api/endpoints/admin/meta.ts | 9 +- .../server/api/endpoints/admin/moderators/add.ts | 49 ----- .../api/endpoints/admin/moderators/remove.ts | 45 ----- .../server/api/endpoints/admin/reset-password.ts | 4 +- .../src/server/api/endpoints/admin/roles/assign.ts | 96 ++++++++++ .../src/server/api/endpoints/admin/roles/create.ts | 75 ++++++++ .../src/server/api/endpoints/admin/roles/delete.ts | 53 ++++++ .../src/server/api/endpoints/admin/roles/list.ts | 39 ++++ .../src/server/api/endpoints/admin/roles/show.ts | 50 +++++ .../server/api/endpoints/admin/roles/unassign.ts | 101 +++++++++++ .../admin/roles/update-default-role-override.ts | 42 +++++ .../src/server/api/endpoints/admin/roles/update.ts | 82 +++++++++ .../src/server/api/endpoints/admin/show-user.ts | 21 ++- .../src/server/api/endpoints/admin/show-users.ts | 26 ++- .../src/server/api/endpoints/admin/silence-user.ts | 55 ------ .../src/server/api/endpoints/admin/suspend-user.ts | 10 +- .../server/api/endpoints/admin/unsilence-user.ts | 51 ------ .../src/server/api/endpoints/admin/update-meta.ts | 15 -- .../src/server/api/endpoints/antennas/create.ts | 4 +- packages/backend/src/server/api/endpoints/drive.ts | 6 +- .../src/server/api/endpoints/drive/files/delete.ts | 4 +- .../src/server/api/endpoints/drive/files/show.ts | 4 +- .../src/server/api/endpoints/drive/files/update.ts | 4 +- packages/backend/src/server/api/endpoints/meta.ts | 20 +- .../src/server/api/endpoints/notes/delete.ts | 6 +- .../server/api/endpoints/notes/global-timeline.ts | 10 +- .../server/api/endpoints/notes/hybrid-timeline.ts | 6 +- .../server/api/endpoints/notes/local-timeline.ts | 10 +- packages/backend/src/server/api/endpoints/users.ts | 5 +- .../src/server/api/endpoints/users/report-abuse.ts | 14 +- .../backend/src/server/api/endpoints/users/show.ts | 8 +- .../server/api/stream/channels/global-timeline.ts | 10 +- .../server/api/stream/channels/hybrid-timeline.ts | 8 +- .../server/api/stream/channels/local-timeline.ts | 10 +- packages/backend/src/server/api/stream/types.ts | 34 ++-- .../backend/src/server/web/ClientServerService.ts | 9 +- packages/backend/src/server/web/boot.js | 4 + packages/frontend/src/components/MkRolePreview.vue | 32 ++++ .../frontend/src/components/MkUserCardMini.vue | 2 +- packages/frontend/src/directives/adaptive-bg.ts | 24 +++ packages/frontend/src/directives/index.ts | 2 + packages/frontend/src/pages/admin/index.vue | 5 + packages/frontend/src/pages/admin/roles.edit.vue | 65 +++++++ packages/frontend/src/pages/admin/roles.editor.vue | 193 ++++++++++++++++++++ packages/frontend/src/pages/admin/roles.role.vue | 121 +++++++++++++ packages/frontend/src/pages/admin/roles.vue | 115 ++++++++++++ packages/frontend/src/pages/admin/settings.vue | 35 +--- packages/frontend/src/pages/admin/users.vue | 1 - packages/frontend/src/pages/timeline.vue | 4 +- packages/frontend/src/pages/user-info.vue | 121 +++++++------ packages/frontend/src/pages/user/home.vue | 4 +- packages/frontend/src/router.ts | 17 ++ packages/frontend/src/scripts/get-user-menu.ts | 32 +--- packages/frontend/src/ui/deck/tl-column.vue | 4 +- 90 files changed, 2027 insertions(+), 638 deletions(-) create mode 100644 packages/backend/migration/1673500412259-Role.js create mode 100644 packages/backend/migration/1673515526953-RoleColor.js create mode 100644 packages/backend/migration/1673522856499-RoleIroiro.js create mode 100644 packages/backend/migration/1673524604156-RoleLastUsedAt.js create mode 100644 packages/backend/src/core/RoleService.ts create mode 100644 packages/backend/src/core/entities/RoleEntityService.ts create mode 100644 packages/backend/src/models/entities/Role.ts create mode 100644 packages/backend/src/models/entities/RoleAssignment.ts delete mode 100644 packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts delete mode 100644 packages/backend/src/server/api/endpoints/admin/moderators/add.ts delete mode 100644 packages/backend/src/server/api/endpoints/admin/moderators/remove.ts create mode 100644 packages/backend/src/server/api/endpoints/admin/roles/assign.ts create mode 100644 packages/backend/src/server/api/endpoints/admin/roles/create.ts create mode 100644 packages/backend/src/server/api/endpoints/admin/roles/delete.ts create mode 100644 packages/backend/src/server/api/endpoints/admin/roles/list.ts create mode 100644 packages/backend/src/server/api/endpoints/admin/roles/show.ts create mode 100644 packages/backend/src/server/api/endpoints/admin/roles/unassign.ts create mode 100644 packages/backend/src/server/api/endpoints/admin/roles/update-default-role-override.ts create mode 100644 packages/backend/src/server/api/endpoints/admin/roles/update.ts delete mode 100644 packages/backend/src/server/api/endpoints/admin/silence-user.ts delete mode 100644 packages/backend/src/server/api/endpoints/admin/unsilence-user.ts create mode 100644 packages/frontend/src/components/MkRolePreview.vue create mode 100644 packages/frontend/src/directives/adaptive-bg.ts create mode 100644 packages/frontend/src/pages/admin/roles.edit.vue create mode 100644 packages/frontend/src/pages/admin/roles.editor.vue create mode 100644 packages/frontend/src/pages/admin/roles.role.vue create mode 100644 packages/frontend/src/pages/admin/roles.vue (limited to 'packages/backend/src/core/NoteCreateService.ts') diff --git a/CHANGELOG.md b/CHANGELOG.md index 066faa7e05..256df462d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ You should also include the user name that made the change. ## 13.0.0 (unreleased) ### TL;DR -- New features (Play, new widgets, new charts, 🍪👈, etc) +- New features (Role system, Misskey Play, New widgets, New charts, 🍪👈, etc) - Rewriten backend - Better performance (backend and frontend) - Various usability improvements @@ -27,6 +27,11 @@ You should also include the user name that made the change. - 代わりに今後任意の検索プロバイダを設定できる仕組みを構想しています。その仕組みを使えば今まで通りElasticsearchも利用できます - Migrate to Yarn Berry (v3.2.1) @ThatOneCalculator - You may have to `yarn run clean-all`, `sudo corepack enable` and `yarn set version berry` before running `yarn install` if you're still on yarn classic +- 従来のモデレーターフラグは廃止され、より高度なロール機能が導入されました + - これに伴い、アップデートを行うと全てのモデレーターフラグは失われます。そのため、予めモデレーター一覧を記録しておき、アップデート後にモデレーターロールを作りアサインし直してください。 + - サイレンスはロールに統合されました + - ユーザーごとのドライブ容量設定はロールに統合されました + - LTL/GTLの解放状態はロールに統合されました #### For users - ノートのウォッチ機能が削除されました @@ -52,6 +57,7 @@ You should also include the user name that made the change. - API: `instance`エンティティに`latestStatus`、`lastCommunicatedAt`、`latestRequestSentAt`プロパティが含まれなくなりました ### Improvements +- Role system @syuilo - Misskey Play @syuilo - Introduce retention-rate aggregation @syuilo - Make possible to export favorited notes @syuilo diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index aac89d8fe4..3dd770c60f 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -924,6 +924,35 @@ neverShow: "今後表示しない" remindMeLater: "また後で" didYouLikeMisskey: "Misskeyを気に入っていただけましたか?" pleaseDonate: "Misskeyは{host}が使用している無料のソフトウェアです。これからも開発を続けられるように、ぜひ寄付をお願いします!" +roles: "ロール" +role: "ロール" +noramlUser: "一般ユーザー" +undefined: "未定義" +assign: "アサイン" +unassign: "アサインを解除" +color: "色" + +_role: + new: "ロールの作成" + edit: "ロールの編集" + name: "ロール名" + description: "ロールの説明" + type: "ロールの種類" + descriptionOfType: "モデレーターは基本的なモデレーションに関する操作を行えます。\n管理者はインスタンスの全ての設定を変更できます。" + isPublic: "ロールを公開" + descriptionOfIsPublic: "ロールにアサインされたユーザーを誰でも見ることができます。また、ユーザーのプロフィールでこのロールが表示されます。" + options: "オプション" + baseRole: "ベースロール" + useBaseValue: "ベースロールの値を使用" + chooseRoleToAssign: "アサインするロールを選択" + canEditMembersByModerator: "モデレーターのメンバー編集を許可" + descriptionOfCanEditMembersByModerator: "オンにすると、管理者に加えてモデレーターもこのロールへユーザーをアサイン/アサイン解除できるようになります。オフにすると管理者のみが行えます。" + _options: + gtlAvailable: "グローバルタイムラインの閲覧" + ltlAvailable: "ローカルタイムラインの閲覧" + canPublicNote: "パブリック投稿の許可" + driveCapacity: "ドライブ容量" + antennaMax: "アンテナの作成可能数" _sensitiveMediaDetection: description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。" diff --git a/packages/backend/migration/1673500412259-Role.js b/packages/backend/migration/1673500412259-Role.js new file mode 100644 index 0000000000..a8acedf5b7 --- /dev/null +++ b/packages/backend/migration/1673500412259-Role.js @@ -0,0 +1,37 @@ +export class Role1673500412259 { + name = 'Role1673500412259' + + async up(queryRunner) { + await queryRunner.query(`CREATE TABLE "role" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, "name" character varying(256) NOT NULL, "description" character varying(1024) NOT NULL, "isPublic" boolean NOT NULL DEFAULT false, "isModerator" boolean NOT NULL DEFAULT false, "isAdministrator" boolean NOT NULL DEFAULT false, "options" jsonb NOT NULL DEFAULT '{}', CONSTRAINT "PK_b36bcfe02fc8de3c57a8b2391c2" PRIMARY KEY ("id")); COMMENT ON COLUMN "role"."createdAt" IS 'The created date of the Role.'; COMMENT ON COLUMN "role"."updatedAt" IS 'The updated date of the Role.'`); + await queryRunner.query(`CREATE TABLE "role_assignment" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "roleId" character varying(32) NOT NULL, CONSTRAINT "PK_7e79671a8a5db18936173148cb4" PRIMARY KEY ("id")); COMMENT ON COLUMN "role_assignment"."createdAt" IS 'The created date of the RoleAssignment.'; COMMENT ON COLUMN "role_assignment"."userId" IS 'The user ID.'; COMMENT ON COLUMN "role_assignment"."roleId" IS 'The role ID.'`); + await queryRunner.query(`CREATE INDEX "IDX_db5b72c16227c97ca88734d5c2" ON "role_assignment" ("userId") `); + await queryRunner.query(`CREATE INDEX "IDX_f0de67fd09cd3cd0aabca79994" ON "role_assignment" ("roleId") `); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0953deda7ce6e1448e935859e5" ON "role_assignment" ("userId", "roleId") `); + await queryRunner.query(`ALTER TABLE "user" RENAME COLUMN "isAdmin" TO "isRoot"`); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "isModerator"`); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "driveCapacityOverrideMb"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "disableLocalTimeline"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "disableGlobalTimeline"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "localDriveCapacityMb"`); + await queryRunner.query(`ALTER TABLE "meta" ADD "defaultRoleOverride" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "role_assignment" ADD CONSTRAINT "FK_db5b72c16227c97ca88734d5c2b" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "role_assignment" ADD CONSTRAINT "FK_f0de67fd09cd3cd0aabca79994d" FOREIGN KEY ("roleId") REFERENCES "role"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "role_assignment" DROP CONSTRAINT "FK_f0de67fd09cd3cd0aabca79994d"`); + await queryRunner.query(`ALTER TABLE "role_assignment" DROP CONSTRAINT "FK_db5b72c16227c97ca88734d5c2b"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "defaultRoleOverride"`); + await queryRunner.query(`ALTER TABLE "meta" ADD "localDriveCapacityMb" integer NOT NULL DEFAULT '1024'`); + await queryRunner.query(`ALTER TABLE "meta" ADD "disableGlobalTimeline" boolean NOT NULL DEFAULT false`); + await queryRunner.query(`ALTER TABLE "meta" ADD "disableLocalTimeline" boolean NOT NULL DEFAULT false`); + await queryRunner.query(`ALTER TABLE "user" ADD "driveCapacityOverrideMb" integer`); + await queryRunner.query(`ALTER TABLE "user" ADD "isModerator" boolean NOT NULL DEFAULT false`); + await queryRunner.query(`ALTER TABLE "user" RENAME COLUMN "isRoot" TO "isAdmin"`); + await queryRunner.query(`DROP INDEX "public"."IDX_0953deda7ce6e1448e935859e5"`); + await queryRunner.query(`DROP INDEX "public"."IDX_f0de67fd09cd3cd0aabca79994"`); + await queryRunner.query(`DROP INDEX "public"."IDX_db5b72c16227c97ca88734d5c2"`); + await queryRunner.query(`DROP TABLE "role_assignment"`); + await queryRunner.query(`DROP TABLE "role"`); + } +} diff --git a/packages/backend/migration/1673515526953-RoleColor.js b/packages/backend/migration/1673515526953-RoleColor.js new file mode 100644 index 0000000000..343eedf346 --- /dev/null +++ b/packages/backend/migration/1673515526953-RoleColor.js @@ -0,0 +1,11 @@ +export class RoleColor1673515526953 { + name = 'RoleColor1673515526953' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "role" ADD "color" character varying(256)`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "color"`); + } +} diff --git a/packages/backend/migration/1673522856499-RoleIroiro.js b/packages/backend/migration/1673522856499-RoleIroiro.js new file mode 100644 index 0000000000..a1e64d49fe --- /dev/null +++ b/packages/backend/migration/1673522856499-RoleIroiro.js @@ -0,0 +1,13 @@ +export class RoleIroiro1673522856499 { + name = 'RoleIroiro1673522856499' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "isSilenced"`); + await queryRunner.query(`ALTER TABLE "role" ADD "canEditMembersByModerator" boolean NOT NULL DEFAULT false`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "canEditMembersByModerator"`); + await queryRunner.query(`ALTER TABLE "user" ADD "isSilenced" boolean NOT NULL DEFAULT false`); + } +} diff --git a/packages/backend/migration/1673524604156-RoleLastUsedAt.js b/packages/backend/migration/1673524604156-RoleLastUsedAt.js new file mode 100644 index 0000000000..786ef07f5e --- /dev/null +++ b/packages/backend/migration/1673524604156-RoleLastUsedAt.js @@ -0,0 +1,13 @@ +export class RoleLastUsedAt1673524604156 { + name = 'RoleLastUsedAt1673524604156' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "role" ADD "lastUsedAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`COMMENT ON COLUMN "role"."lastUsedAt" IS 'The last used date of the Role.'`); + } + + async down(queryRunner) { + await queryRunner.query(`COMMENT ON COLUMN "role"."lastUsedAt" IS 'The last used date of the Role.'`); + await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "lastUsedAt"`); + } +} diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index 2f17fa389a..0ae1ee32b2 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -35,6 +35,7 @@ import { PushNotificationService } from './PushNotificationService.js'; import { QueryService } from './QueryService.js'; import { ReactionService } from './ReactionService.js'; import { RelayService } from './RelayService.js'; +import { RoleService } from './RoleService.js'; import { S3Service } from './S3Service.js'; import { SignupService } from './SignupService.js'; import { TwoFactorAuthenticationService } from './TwoFactorAuthenticationService.js'; @@ -97,6 +98,7 @@ import { UserGroupInvitationEntityService } from './entities/UserGroupInvitation import { UserListEntityService } from './entities/UserListEntityService.js'; import { FlashEntityService } from './entities/FlashEntityService.js'; import { FlashLikeEntityService } from './entities/FlashLikeEntityService.js'; +import { RoleEntityService } from './entities/RoleEntityService.js'; import { ApAudienceService } from './activitypub/ApAudienceService.js'; import { ApDbResolverService } from './activitypub/ApDbResolverService.js'; import { ApDeliverManagerService } from './activitypub/ApDeliverManagerService.js'; @@ -158,6 +160,7 @@ const $PushNotificationService: Provider = { provide: 'PushNotificationService', const $QueryService: Provider = { provide: 'QueryService', useExisting: QueryService }; const $ReactionService: Provider = { provide: 'ReactionService', useExisting: ReactionService }; const $RelayService: Provider = { provide: 'RelayService', useExisting: RelayService }; +const $RoleService: Provider = { provide: 'RoleService', useExisting: RoleService }; const $S3Service: Provider = { provide: 'S3Service', useExisting: S3Service }; const $SignupService: Provider = { provide: 'SignupService', useExisting: SignupService }; const $TwoFactorAuthenticationService: Provider = { provide: 'TwoFactorAuthenticationService', useExisting: TwoFactorAuthenticationService }; @@ -220,6 +223,7 @@ const $UserGroupInvitationEntityService: Provider = { provide: 'UserGroupInvitat const $UserListEntityService: Provider = { provide: 'UserListEntityService', useExisting: UserListEntityService }; const $FlashEntityService: Provider = { provide: 'FlashEntityService', useExisting: FlashEntityService }; const $FlashLikeEntityService: Provider = { provide: 'FlashLikeEntityService', useExisting: FlashLikeEntityService }; +const $RoleEntityService: Provider = { provide: 'RoleEntityService', useExisting: RoleEntityService }; const $ApAudienceService: Provider = { provide: 'ApAudienceService', useExisting: ApAudienceService }; const $ApDbResolverService: Provider = { provide: 'ApDbResolverService', useExisting: ApDbResolverService }; @@ -283,6 +287,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting QueryService, ReactionService, RelayService, + RoleService, S3Service, SignupService, TwoFactorAuthenticationService, @@ -344,6 +349,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting UserListEntityService, FlashEntityService, FlashLikeEntityService, + RoleEntityService, ApAudienceService, ApDbResolverService, ApDeliverManagerService, @@ -402,6 +408,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $QueryService, $ReactionService, $RelayService, + $RoleService, $S3Service, $SignupService, $TwoFactorAuthenticationService, @@ -463,6 +470,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $UserListEntityService, $FlashEntityService, $FlashLikeEntityService, + $RoleEntityService, $ApAudienceService, $ApDbResolverService, $ApDeliverManagerService, @@ -522,6 +530,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting QueryService, ReactionService, RelayService, + RoleService, S3Service, SignupService, TwoFactorAuthenticationService, @@ -582,6 +591,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting UserListEntityService, FlashEntityService, FlashLikeEntityService, + RoleEntityService, ApAudienceService, ApDbResolverService, ApDeliverManagerService, @@ -640,6 +650,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $QueryService, $ReactionService, $RelayService, + $RoleService, $S3Service, $SignupService, $TwoFactorAuthenticationService, @@ -700,6 +711,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $UserListEntityService, $FlashEntityService, $FlashLikeEntityService, + $RoleEntityService, $ApAudienceService, $ApDbResolverService, $ApDeliverManagerService, diff --git a/packages/backend/src/core/CreateSystemUserService.ts b/packages/backend/src/core/CreateSystemUserService.ts index 1e753f65cc..8f887d90f9 100644 --- a/packages/backend/src/core/CreateSystemUserService.ts +++ b/packages/backend/src/core/CreateSystemUserService.ts @@ -53,7 +53,7 @@ export class CreateSystemUserService { usernameLower: username.toLowerCase(), host: null, token: secret, - isAdmin: false, + isRoot: false, isLocked: true, isExplorable: false, isBot: true, diff --git a/packages/backend/src/core/DeleteAccountService.ts b/packages/backend/src/core/DeleteAccountService.ts index e42c738707..0ac12857c9 100644 --- a/packages/backend/src/core/DeleteAccountService.ts +++ b/packages/backend/src/core/DeleteAccountService.ts @@ -23,6 +23,9 @@ export class DeleteAccountService { id: string; host: string | null; }): Promise { + const _user = await this.usersRepository.findOneByOrFail({ id: user.id }); + if (_user.isRoot) throw new Error('cannot delete a root account'); + // 物理削除する前にDelete activityを送信する await this.userSuspendService.doPostSuspend(user).catch(e => {}); diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index bbdb5fae83..5954abba91 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -32,11 +32,12 @@ import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.j import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { FileInfoService } from '@/core/FileInfoService.js'; import { bindThis } from '@/decorators.js'; +import { RoleService } from '@/core/RoleService.js'; import type S3 from 'aws-sdk/clients/s3.js'; type AddFileArgs = { /** User who wish to add file */ - user: { id: User['id']; host: User['host']; driveCapacityOverrideMb: User['driveCapacityOverrideMb'] } | null; + user: { id: User['id']; host: User['host'] } | null; /** File path */ path: string; /** Name */ @@ -62,7 +63,7 @@ type AddFileArgs = { type UploadFromUrlArgs = { url: string; - user: { id: User['id']; host: User['host']; driveCapacityOverrideMb: User['driveCapacityOverrideMb'] } | null; + user: { id: User['id']; host: User['host'] } | null; folderId?: DriveFolder['id'] | null; uri?: string | null; sensitive?: boolean; @@ -106,6 +107,7 @@ export class DriveService { private videoProcessingService: VideoProcessingService, private globalEventService: GlobalEventService, private queueService: QueueService, + private roleService: RoleService, private driveChart: DriveChart, private perUserDriveChart: PerUserDriveChart, private instanceChart: InstanceChart, @@ -463,15 +465,16 @@ export class DriveService { //#region Check drive usage if (user && !isLink) { 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 * (this.userEntityService.isLocalUser(user) ? instance.localDriveCapacityMb : instance.remoteDriveCapacityMb); - - if (this.userEntityService.isLocalUser(user) && u?.driveCapacityOverrideMb != null) { - driveCapacity = 1024 * 1024 * u.driveCapacityOverrideMb; + let driveCapacity: number; + if (this.userEntityService.isLocalUser(user)) { + const role = await this.roleService.getUserRoleOptions(user.id); + driveCapacity = 1024 * 1024 * role.driveCapacityMb; this.registerLogger.debug('drive capacity override applied'); this.registerLogger.debug(`overrideCap: ${driveCapacity}bytes, usage: ${usage}bytes, u+s: ${usage + info.size}bytes`); + } else { + const instance = await this.metaService.fetch(); + driveCapacity = 1024 * 1024 * instance.remoteDriveCapacityMb; } this.registerLogger.debug(`drive usage is ${usage} (max: ${driveCapacity})`); diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 6038840406..1c2add5d64 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -42,6 +42,7 @@ import { NoteReadService } from '@/core/NoteReadService.js'; import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js'; import { bindThis } from '@/decorators.js'; import { DB_MAX_NOTE_TEXT_LENGTH } from '@/const.js'; +import { RoleService } from '@/core/RoleService.js'; const mutedWordsCache = new Cache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5); @@ -186,6 +187,7 @@ export class NoteCreateService { private remoteUserResolveService: RemoteUserResolveService, private apDeliverManagerService: ApDeliverManagerService, private apRendererService: ApRendererService, + private roleService: RoleService, private notesChart: NotesChart, private perUserNotesChart: PerUserNotesChart, private activeUsersChart: ActiveUsersChart, @@ -197,7 +199,6 @@ export class NoteCreateService { id: User['id']; username: User['username']; host: User['host']; - isSilenced: User['isSilenced']; createdAt: User['createdAt']; isBot: User['isBot']; }, data: Option, silent = false): Promise { @@ -224,9 +225,10 @@ export class NoteCreateService { 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'; + if (data.visibility === 'public' && data.channel == null) { + if ((await this.roleService.getUserRoleOptions(user.id)).canPublicNote) { + data.visibility = 'home'; + } } // Renote対象が「ホームまたは全体」以外の公開範囲ならreject @@ -418,7 +420,6 @@ export class NoteCreateService { id: User['id']; username: User['username']; host: User['host']; - isSilenced: User['isSilenced']; createdAt: User['createdAt']; isBot: User['isBot']; }, data: Option, silent: boolean, tags: string[], mentionedUsers: MinimumUser[]) { diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts new file mode 100644 index 0000000000..6ce7f431ca --- /dev/null +++ b/packages/backend/src/core/RoleService.ts @@ -0,0 +1,201 @@ +import { Inject, Injectable } from '@nestjs/common'; +import Redis from 'ioredis'; +import { In } from 'typeorm'; +import type { Role, RoleAssignment, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/index.js'; +import { Cache } from '@/misc/cache.js'; +import type { CacheableLocalUser, CacheableUser, ILocalUser, User } from '@/models/entities/User.js'; +import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; +import { MetaService } from '@/core/MetaService.js'; +import type { OnApplicationShutdown } from '@nestjs/common'; + +export type RoleOptions = { + gtlAvailable: boolean; + ltlAvailable: boolean; + canPublicNote: boolean; + driveCapacityMb: number; + antennaLimit: number; +}; + +export const DEFAULT_ROLE: RoleOptions = { + gtlAvailable: true, + ltlAvailable: true, + canPublicNote: true, + driveCapacityMb: 100, + antennaLimit: 5, +}; + +@Injectable() +export class RoleService implements OnApplicationShutdown { + private rolesCache: Cache; + private roleAssignmentByUserIdCache: Cache; + + constructor( + @Inject(DI.redisSubscriber) + private redisSubscriber: Redis.Redis, + + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.rolesRepository) + private rolesRepository: RolesRepository, + + @Inject(DI.roleAssignmentsRepository) + private roleAssignmentsRepository: RoleAssignmentsRepository, + + private metaService: MetaService, + ) { + //this.onMessage = this.onMessage.bind(this); + + this.rolesCache = new Cache(Infinity); + this.roleAssignmentByUserIdCache = new Cache(Infinity); + + this.redisSubscriber.on('message', this.onMessage); + } + + @bindThis + private async onMessage(_: string, data: string): Promise { + const obj = JSON.parse(data); + + if (obj.channel === 'internal') { + const { type, body } = obj.message; + switch (type) { + case 'roleCreated': { + const cached = this.rolesCache.get(null); + if (cached) { + body.createdAt = new Date(body.createdAt); + body.updatedAt = new Date(body.updatedAt); + body.lastUsedAt = new Date(body.lastUsedAt); + cached.push(body); + } + break; + } + case 'roleUpdated': { + const cached = this.rolesCache.get(null); + if (cached) { + const i = cached.findIndex(x => x.id === body.id); + if (i > -1) { + body.createdAt = new Date(body.createdAt); + body.updatedAt = new Date(body.updatedAt); + body.lastUsedAt = new Date(body.lastUsedAt); + cached[i] = body; + } + } + break; + } + case 'roleDeleted': { + const cached = this.rolesCache.get(null); + if (cached) { + this.rolesCache.set(null, cached.filter(x => x.id !== body.id)); + } + break; + } + case 'userRoleAssigned': { + const cached = this.roleAssignmentByUserIdCache.get(body.userId); + if (cached) { + body.createdAt = new Date(body.createdAt); + cached.push(body); + } + break; + } + case 'userRoleUnassigned': { + const cached = this.roleAssignmentByUserIdCache.get(body.userId); + if (cached) { + this.roleAssignmentByUserIdCache.set(body.userId, cached.filter(x => x.id !== body.id)); + } + break; + } + default: + break; + } + } + } + + @bindThis + public async getUserRoles(userId: User['id']) { + const assigns = await this.roleAssignmentByUserIdCache.fetch(userId, () => this.roleAssignmentsRepository.findBy({ userId })); + const assignedRoleIds = assigns.map(x => x.roleId); + const roles = await this.rolesCache.fetch(null, () => this.rolesRepository.findBy({})); + return roles.filter(r => assignedRoleIds.includes(r.id)); + } + + @bindThis + public async getUserRoleOptions(userId: User['id'] | null): Promise { + const meta = await this.metaService.fetch(); + const baseRoleOptions = { ...DEFAULT_ROLE, ...meta.defaultRoleOverride }; + + if (userId == null) return baseRoleOptions; + + const roles = await this.getUserRoles(userId); + + function getOptionValues(option: keyof RoleOptions) { + if (roles.length === 0) return [baseRoleOptions[option]]; + return roles.map(role => (role.options[option] && (role.options[option].useDefault !== true)) ? role.options[option].value : baseRoleOptions[option]); + } + + return { + gtlAvailable: getOptionValues('gtlAvailable').some(x => x === true), + ltlAvailable: getOptionValues('ltlAvailable').some(x => x === true), + canPublicNote: getOptionValues('canPublicNote').some(x => x === true), + driveCapacityMb: Math.max(...getOptionValues('driveCapacityMb')), + antennaLimit: Math.max(...getOptionValues('antennaLimit')), + }; + } + + @bindThis + public async isModerator(user: { id: User['id']; isRoot: User['isRoot'] } | null): Promise { + if (user == null) return false; + return user.isRoot || (await this.getUserRoles(user.id)).some(r => r.isModerator || r.isAdministrator); + } + + @bindThis + public async isAdministrator(user: { id: User['id']; isRoot: User['isRoot'] } | null): Promise { + if (user == null) return false; + return user.isRoot || (await this.getUserRoles(user.id)).some(r => r.isAdministrator); + } + + @bindThis + public async getModeratorIds(includeAdmins = true): Promise { + const roles = await this.rolesCache.fetch(null, () => this.rolesRepository.findBy({})); + const moderatorRoles = includeAdmins ? roles.filter(r => r.isModerator || r.isAdministrator) : roles.filter(r => r.isModerator); + const assigns = moderatorRoles.length > 0 ? await this.roleAssignmentsRepository.findBy({ + roleId: In(moderatorRoles.map(r => r.id)), + }) : []; + // TODO: isRootなアカウントも含める + return assigns.map(a => a.userId); + } + + @bindThis + public async getModerators(includeAdmins = true): Promise { + const ids = await this.getModeratorIds(includeAdmins); + const users = ids.length > 0 ? await this.usersRepository.findBy({ + id: In(ids), + }) : []; + return users; + } + + @bindThis + public async getAdministratorIds(): Promise { + const roles = await this.rolesCache.fetch(null, () => this.rolesRepository.findBy({})); + const administratorRoles = roles.filter(r => r.isAdministrator); + const assigns = administratorRoles.length > 0 ? await this.roleAssignmentsRepository.findBy({ + roleId: In(administratorRoles.map(r => r.id)), + }) : []; + // TODO: isRootなアカウントも含める + return assigns.map(a => a.userId); + } + + @bindThis + public async getAdministrators(): Promise { + const ids = await this.getAdministratorIds(); + const users = ids.length > 0 ? await this.usersRepository.findBy({ + id: In(ids), + }) : []; + return users; + } + + @bindThis + public onApplicationShutdown(signal?: string | undefined) { + this.redisSubscriber.off('message', this.onMessage); + } +} diff --git a/packages/backend/src/core/SignupService.ts b/packages/backend/src/core/SignupService.ts index 9cf203566d..90a7186909 100644 --- a/packages/backend/src/core/SignupService.ts +++ b/packages/backend/src/core/SignupService.ts @@ -11,10 +11,10 @@ import { IdService } from '@/core/IdService.js'; 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 '@/core/entities/UserEntityService.js'; -import { UtilityService } from './UtilityService.js'; import { bindThis } from '@/decorators.js'; +import UsersChart from './chart/charts/users.js'; +import { UtilityService } from './UtilityService.js'; @Injectable() export class SignupService { @@ -112,7 +112,7 @@ export class SignupService { usernameLower: username.toLowerCase(), host: this.utilityService.toPunyNullable(host), token: secret, - isAdmin: (await this.usersRepository.countBy({ + isRoot: (await this.usersRepository.countBy({ host: IsNull(), })) === 0, })); diff --git a/packages/backend/src/core/UserCacheService.ts b/packages/backend/src/core/UserCacheService.ts index 423c8993e3..3f735c0c53 100644 --- a/packages/backend/src/core/UserCacheService.ts +++ b/packages/backend/src/core/UserCacheService.ts @@ -5,8 +5,8 @@ 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 '@/core/entities/UserEntityService.js'; -import type { OnApplicationShutdown } from '@nestjs/common'; import { bindThis } from '@/decorators.js'; +import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() export class UserCacheService implements OnApplicationShutdown { @@ -42,8 +42,6 @@ export class UserCacheService implements OnApplicationShutdown { const { type, body } = obj.message; switch (type) { case 'userChangeSuspendedState': - case 'userChangeSilencedState': - case 'userChangeModeratorState': case 'remoteUserUpdated': { const user = await this.usersRepository.findOneByOrFail({ id: body.id }); this.userByIdCache.set(user.id, user); diff --git a/packages/backend/src/core/entities/RoleEntityService.ts b/packages/backend/src/core/entities/RoleEntityService.ts new file mode 100644 index 0000000000..22c4cdff81 --- /dev/null +++ b/packages/backend/src/core/entities/RoleEntityService.ts @@ -0,0 +1,80 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { DI } from '@/di-symbols.js'; +import type { RoleAssignmentsRepository, RolesRepository } from '@/models/index.js'; +import { awaitAll } from '@/misc/prelude/await-all.js'; +import type { Packed } from '@/misc/schema.js'; +import type { User } from '@/models/entities/User.js'; +import type { Role } from '@/models/entities/Role.js'; +import { bindThis } from '@/decorators.js'; +import { DEFAULT_ROLE } from '@/core/RoleService.js'; +import { UserEntityService } from './UserEntityService.js'; + +@Injectable() +export class RoleEntityService { + constructor( + @Inject(DI.rolesRepository) + private rolesRepository: RolesRepository, + + @Inject(DI.roleAssignmentsRepository) + private roleAssignmentsRepository: RoleAssignmentsRepository, + + private userEntityService: UserEntityService, + ) { + } + + @bindThis + public async pack( + src: Role['id'] | Role, + me?: { id: User['id'] } | null | undefined, + options?: { + detail?: boolean; + }, + ) { + const opts = Object.assign({ + detail: true, + }, options); + + const role = typeof src === 'object' ? src : await this.rolesRepository.findOneByOrFail({ id: src }); + + const assigns = await this.roleAssignmentsRepository.findBy({ + roleId: role.id, + }); + + const roleOptions = { ...role.options }; + for (const [k, v] of Object.entries(DEFAULT_ROLE)) { + if (roleOptions[k] == null) roleOptions[k] = { + useDefault: true, + value: v, + }; + } + + return await awaitAll({ + id: role.id, + createdAt: role.createdAt.toISOString(), + updatedAt: role.updatedAt.toISOString(), + name: role.name, + description: role.description, + color: role.color, + isPublic: role.isPublic, + isAdministrator: role.isAdministrator, + isModerator: role.isModerator, + canEditMembersByModerator: role.canEditMembersByModerator, + options: roleOptions, + ...(opts.detail ? { + users: this.userEntityService.packMany(assigns.map(x => x.userId), me), + } : {}), + }); + } + + @bindThis + public packMany( + roles: any[], + me: { id: User['id'] }, + options?: { + detail?: boolean; + }, + ) { + return Promise.all(roles.map(x => this.pack(x, me, options))); + } +} + diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index a123746220..9a90aec456 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -13,6 +13,8 @@ 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 { UsersRepository, UserSecurityKeysRepository, FollowingsRepository, FollowRequestsRepository, BlockingsRepository, MutingsRepository, DriveFilesRepository, NoteUnreadsRepository, ChannelFollowingsRepository, NotificationsRepository, UserNotePiningsRepository, UserProfilesRepository, InstancesRepository, AnnouncementReadsRepository, MessagingMessagesRepository, UserGroupJoiningsRepository, AnnouncementsRepository, AntennaNotesRepository, PagesRepository } from '@/models/index.js'; +import { bindThis } from '@/decorators.js'; +import { RoleService } from '@/core/RoleService.js'; import type { OnModuleInit } from '@nestjs/common'; import type { AntennaService } from '../AntennaService.js'; import type { CustomEmojiService } from '../CustomEmojiService.js'; @@ -41,7 +43,6 @@ function isRemoteUser(user: T): user is T & { function isRemoteUser(user: User | { host: User['host'] }): boolean { return !isLocalUser(user); } -import { bindThis } from '@/decorators.js'; @Injectable() export class UserEntityService implements OnModuleInit { @@ -50,6 +51,7 @@ export class UserEntityService implements OnModuleInit { private pageEntityService: PageEntityService; private customEmojiService: CustomEmojiService; private antennaService: AntennaService; + private roleService: RoleService; private userInstanceCache: Cache; constructor( @@ -120,6 +122,7 @@ export class UserEntityService implements OnModuleInit { //private pageEntityService: PageEntityService, //private customEmojiService: CustomEmojiService, //private antennaService: AntennaService, + //private roleService: RoleService, ) { this.userInstanceCache = new Cache(1000 * 60 * 60 * 3); } @@ -130,6 +133,7 @@ export class UserEntityService implements OnModuleInit { this.pageEntityService = this.moduleRef.get('PageEntityService'); this.customEmojiService = this.moduleRef.get('CustomEmojiService'); this.antennaService = this.moduleRef.get('AntennaService'); + this.roleService = this.moduleRef.get('RoleService'); } //#region Validators @@ -383,6 +387,9 @@ export class UserEntityService implements OnModuleInit { (profile.ffVisibility === 'followers') && (relation && relation.isFollowing) ? user.followersCount : null; + const isModerator = isMe && opts.detail ? this.roleService.isModerator(user) : null; + const isAdmin = isMe && opts.detail ? this.roleService.isAdministrator(user) : null; + const falsy = opts.detail ? false : undefined; const packed = { @@ -392,8 +399,6 @@ export class UserEntityService implements OnModuleInit { host: user.host, avatarUrl: this.getAvatarUrlSync(user), avatarBlurhash: user.avatar?.blurhash ?? null, - isAdmin: user.isAdmin ?? falsy, - isModerator: user.isModerator ?? falsy, isBot: user.isBot ?? falsy, isCat: user.isCat ?? falsy, instance: user.host ? this.userInstanceCache.fetch(user.host, @@ -418,7 +423,7 @@ export class UserEntityService implements OnModuleInit { bannerUrl: user.banner ? this.driveFileEntityService.getPublicUrl(user.banner, false) : null, bannerBlurhash: user.banner?.blurhash ?? null, isLocked: user.isLocked, - isSilenced: user.isSilenced ?? falsy, + isSilenced: this.roleService.getUserRoleOptions(user.id).then(r => !r.canPublicNote), isSuspended: user.isSuspended ?? falsy, description: profile!.description, location: profile!.location, @@ -443,14 +448,13 @@ export class UserEntityService implements OnModuleInit { userId: user.id, }).then(result => result >= 1) : false, - ...(isMe || opts.includeSecrets ? { - driveCapacityOverrideMb: user.driveCapacityOverrideMb, - } : {}), } : {}), ...(opts.detail && isMe ? { avatarId: user.avatarId, bannerId: user.bannerId, + isModerator: isModerator, + isAdmin: isAdmin, injectFeaturedNote: profile!.injectFeaturedNote, receiveAnnouncementEmail: profile!.receiveAnnouncementEmail, alwaysMarkNsfw: profile!.alwaysMarkNsfw, @@ -484,6 +488,7 @@ export class UserEntityService implements OnModuleInit { } : {}), ...(opts.includeSecrets ? { + role: this.roleService.getUserRoleOptions(user.id), email: profile!.email, emailVerified: profile!.emailVerified, securityKeysList: profile!.twoFactorEnabled diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts index 9719d773ca..3fb0cd4dae 100644 --- a/packages/backend/src/di-symbols.ts +++ b/packages/backend/src/di-symbols.ts @@ -69,6 +69,8 @@ export const DI = { adsRepository: Symbol('adsRepository'), passwordResetRequestsRepository: Symbol('passwordResetRequestsRepository'), retentionAggregationsRepository: Symbol('retentionAggregationsRepository'), + rolesRepository: Symbol('rolesRepository'), + roleAssignmentsRepository: Symbol('roleAssignmentsRepository'), flashsRepository: Symbol('flashsRepository'), flashLikesRepository: Symbol('flashLikesRepository'), //#endregion diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts index a5d5a63931..2a235bc6fc 100644 --- a/packages/backend/src/models/RepositoryModule.ts +++ b/packages/backend/src/models/RepositoryModule.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { User, Note, Announcement, AnnouncementRead, App, NoteFavorite, NoteThreadMuting, NoteReaction, NoteUnread, Notification, Poll, PollVote, UserProfile, UserKeypair, UserPending, AttestationChallenge, UserSecurityKey, UserPublickey, UserList, UserListJoining, UserGroup, UserGroupJoining, UserGroupInvitation, UserNotePining, UserIp, UsedUsername, Following, FollowRequest, Instance, Emoji, DriveFile, DriveFolder, Meta, Muting, Blocking, SwSubscription, Hashtag, AbuseUserReport, RegistrationTicket, AuthSession, AccessToken, Signin, MessagingMessage, Page, PageLike, GalleryPost, GalleryLike, ModerationLog, Clip, ClipNote, Antenna, AntennaNote, PromoNote, PromoRead, Relay, MutedNote, Channel, ChannelFollowing, ChannelNotePining, RegistryItem, Webhook, Ad, PasswordResetRequest, RetentionAggregation, FlashLike, Flash } from './index.js'; +import { User, Note, Announcement, AnnouncementRead, App, NoteFavorite, NoteThreadMuting, NoteReaction, NoteUnread, Notification, Poll, PollVote, UserProfile, UserKeypair, UserPending, AttestationChallenge, UserSecurityKey, UserPublickey, UserList, UserListJoining, UserGroup, UserGroupJoining, UserGroupInvitation, UserNotePining, UserIp, UsedUsername, Following, FollowRequest, Instance, Emoji, DriveFile, DriveFolder, Meta, Muting, Blocking, SwSubscription, Hashtag, AbuseUserReport, RegistrationTicket, AuthSession, AccessToken, Signin, MessagingMessage, Page, PageLike, GalleryPost, GalleryLike, ModerationLog, Clip, ClipNote, Antenna, AntennaNote, PromoNote, PromoRead, Relay, MutedNote, Channel, ChannelFollowing, ChannelNotePining, RegistryItem, Webhook, Ad, PasswordResetRequest, RetentionAggregation, FlashLike, Flash, Role, RoleAssignment } from './index.js'; import type { DataSource } from 'typeorm'; import type { Provider } from '@nestjs/common'; @@ -400,6 +400,18 @@ const $flashLikesRepository: Provider = { inject: [DI.db], }; +const $rolesRepository: Provider = { + provide: DI.rolesRepository, + useFactory: (db: DataSource) => db.getRepository(Role), + inject: [DI.db], +}; + +const $roleAssignmentsRepository: Provider = { + provide: DI.roleAssignmentsRepository, + useFactory: (db: DataSource) => db.getRepository(RoleAssignment), + inject: [DI.db], +}; + @Module({ imports: [ ], @@ -468,6 +480,8 @@ const $flashLikesRepository: Provider = { $adsRepository, $passwordResetRequestsRepository, $retentionAggregationsRepository, + $rolesRepository, + $roleAssignmentsRepository, $flashsRepository, $flashLikesRepository, ], @@ -536,6 +550,8 @@ const $flashLikesRepository: Provider = { $adsRepository, $passwordResetRequestsRepository, $retentionAggregationsRepository, + $rolesRepository, + $roleAssignmentsRepository, $flashsRepository, $flashLikesRepository, ], diff --git a/packages/backend/src/models/entities/Meta.ts b/packages/backend/src/models/entities/Meta.ts index fb25e370d2..0d65a8d17a 100644 --- a/packages/backend/src/models/entities/Meta.ts +++ b/packages/backend/src/models/entities/Meta.ts @@ -42,16 +42,6 @@ export class Meta { }) public disableRegistration: boolean; - @Column('boolean', { - default: false, - }) - public disableLocalTimeline: boolean; - - @Column('boolean', { - default: false, - }) - public disableGlobalTimeline: boolean; - @Column('boolean', { default: false, }) @@ -227,12 +217,6 @@ export class Meta { }) 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)', @@ -476,4 +460,9 @@ export class Meta { default: true, }) public enableActiveEmailValidation: boolean; + + @Column('jsonb', { + default: { }, + }) + public defaultRoleOverride: Record; } diff --git a/packages/backend/src/models/entities/Role.ts b/packages/backend/src/models/entities/Role.ts new file mode 100644 index 0000000000..34dbc2ce41 --- /dev/null +++ b/packages/backend/src/models/entities/Role.ts @@ -0,0 +1,66 @@ +import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; +import { id } from '../id.js'; + +@Entity() +export class Role { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the Role.', + }) + public createdAt: Date; + + @Column('timestamp with time zone', { + comment: 'The updated date of the Role.', + }) + public updatedAt: Date; + + @Column('timestamp with time zone', { + comment: 'The last used date of the Role.', + }) + public lastUsedAt: Date; + + @Column('varchar', { + length: 256, + }) + public name: string; + + @Column('varchar', { + length: 1024, + }) + public description: string; + + @Column('varchar', { + length: 256, nullable: true, + }) + public color: string | null; + + @Column('boolean', { + default: false, + }) + public isPublic: boolean; + + @Column('boolean', { + default: false, + }) + public isModerator: boolean; + + @Column('boolean', { + default: false, + }) + public isAdministrator: boolean; + + @Column('boolean', { + default: false, + }) + public canEditMembersByModerator: boolean; + + @Column('jsonb', { + default: { }, + }) + public options: Record; +} diff --git a/packages/backend/src/models/entities/RoleAssignment.ts b/packages/backend/src/models/entities/RoleAssignment.ts new file mode 100644 index 0000000000..e86f2a8999 --- /dev/null +++ b/packages/backend/src/models/entities/RoleAssignment.ts @@ -0,0 +1,42 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { id } from '../id.js'; +import { Role } from './Role.js'; +import { User } from './User.js'; + +@Entity() +@Index(['userId', 'roleId'], { unique: true }) +export class RoleAssignment { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + comment: 'The created date of the RoleAssignment.', + }) + 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 role ID.', + }) + public roleId: Role['id']; + + @ManyToOne(type => Role, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public role: Role | null; +} diff --git a/packages/backend/src/models/entities/User.ts b/packages/backend/src/models/entities/User.ts index 73736f0150..8bd5c9700d 100644 --- a/packages/backend/src/models/entities/User.ts +++ b/packages/backend/src/models/entities/User.ts @@ -112,12 +112,6 @@ export class User { }) 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.', @@ -138,15 +132,9 @@ export class User { @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.', + comment: 'Whether the User is the root.', }) - public isModerator: boolean; + public isRoot: boolean; @Index() @Column('boolean', { @@ -218,12 +206,6 @@ export class User { }) 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; diff --git a/packages/backend/src/models/index.ts b/packages/backend/src/models/index.ts index b132475747..50697597ad 100644 --- a/packages/backend/src/models/index.ts +++ b/packages/backend/src/models/index.ts @@ -62,6 +62,8 @@ import { UserSecurityKey } from '@/models/entities/UserSecurityKey.js'; import { Webhook } from '@/models/entities/Webhook.js'; import { Channel } from '@/models/entities/Channel.js'; import { RetentionAggregation } from '@/models/entities/RetentionAggregation.js'; +import { Role } from '@/models/entities/Role.js'; +import { RoleAssignment } from '@/models/entities/RoleAssignment.js'; import { Flash } from '@/models/entities/Flash.js'; import { FlashLike } from '@/models/entities/FlashLike.js'; import type { Repository } from 'typeorm'; @@ -131,6 +133,8 @@ export { Webhook, Channel, RetentionAggregation, + Role, + RoleAssignment, Flash, FlashLike, }; @@ -199,5 +203,7 @@ export type UserSecurityKeysRepository = Repository; export type WebhooksRepository = Repository; export type ChannelsRepository = Repository; export type RetentionAggregationsRepository = Repository; +export type RolesRepository = Repository; +export type RoleAssignmentsRepository = Repository; export type FlashsRepository = Repository; export type FlashLikesRepository = Repository; diff --git a/packages/backend/src/postgre.ts b/packages/backend/src/postgre.ts index 4f6b157d80..c55cb78a6a 100644 --- a/packages/backend/src/postgre.ts +++ b/packages/backend/src/postgre.ts @@ -70,6 +70,8 @@ import { UserSecurityKey } from '@/models/entities/UserSecurityKey.js'; import { Webhook } from '@/models/entities/Webhook.js'; import { Channel } from '@/models/entities/Channel.js'; import { RetentionAggregation } from '@/models/entities/RetentionAggregation.js'; +import { Role } from '@/models/entities/Role.js'; +import { RoleAssignment } from '@/models/entities/RoleAssignment.js'; import { Flash } from '@/models/entities/Flash.js'; import { FlashLike } from '@/models/entities/FlashLike.js'; @@ -186,6 +188,8 @@ export const entities = [ Webhook, UserIp, RetentionAggregation, + Role, + RoleAssignment, Flash, FlashLike, ...charts, diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index c4236c8752..19380d13a4 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -10,6 +10,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; import NotesChart from '@/core/chart/charts/notes.js'; import UsersChart from '@/core/chart/charts/users.js'; +import { DEFAULT_ROLE } from '@/core/RoleService.js'; import type { FastifyInstance, FastifyPluginOptions } from 'fastify'; const nodeinfo2_1path = '/nodeinfo/2.1'; @@ -73,6 +74,8 @@ export class NodeinfoServerService { const proxyAccount = meta.proxyAccountId ? await this.userEntityService.pack(meta.proxyAccountId).catch(() => null) : null; + const baseRoleOptions = { ...DEFAULT_ROLE, ...meta.defaultRoleOverride }; + return { software: { name: 'misskey', @@ -102,8 +105,8 @@ export class NodeinfoServerService { repositoryUrl: meta.repositoryUrl, feedbackUrl: meta.feedbackUrl, disableRegistration: meta.disableRegistration, - disableLocalTimeline: meta.disableLocalTimeline, - disableGlobalTimeline: meta.disableGlobalTimeline, + disableLocalTimeline: !baseRoleOptions.ltlAvailable, + disableGlobalTimeline: !baseRoleOptions.gtlAvailable, emailRequiredForSignup: meta.emailRequiredForSignup, enableHcaptcha: meta.enableHcaptcha, enableRecaptcha: meta.enableRecaptcha, diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index 68f43c7dfc..415fbf08dd 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -12,6 +12,7 @@ import type { UserIpsRepository } from '@/models/index.js'; import { MetaService } from '@/core/MetaService.js'; import { createTemp } from '@/misc/create-temp.js'; import { bindThis } from '@/decorators.js'; +import { RoleService } from '@/core/RoleService.js'; import { ApiError } from './error.js'; import { RateLimiterService } from './RateLimiterService.js'; import { ApiLoggerService } from './ApiLoggerService.js'; @@ -41,6 +42,7 @@ export class ApiCallService implements OnApplicationShutdown { private metaService: MetaService, private authenticateService: AuthenticateService, private rateLimiterService: RateLimiterService, + private roleService: RoleService, private apiLoggerService: ApiLoggerService, ) { this.logger = this.apiLoggerService.logger; @@ -202,7 +204,6 @@ export class ApiCallService implements OnApplicationShutdown { request: FastifyRequest<{ Body: Record | undefined, Querystring: Record }>, ) { const isSecure = user != null && token == null; - const isModerator = user != null && (user.isModerator || user.isAdmin); if (ep.meta.secure && !isSecure) { throw new ApiError(accessDenied); @@ -234,30 +235,40 @@ export class ApiCallService implements OnApplicationShutdown { }); } - 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.requireCredential || ep.meta.requireModerator || ep.meta.requireAdmin) { + if (user == null) { + throw new ApiError({ + message: 'Credential required.', + code: 'CREDENTIAL_REQUIRED', + id: '1384574d-a912-4b81-8601-c7b1c4085df1', + httpStatusCode: 401, + }); + } else if (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.requireModerator && !isModerator) { - throw new ApiError(accessDenied, { reason: 'You are not a moderator.' }); + if ((ep.meta.requireModerator || ep.meta.requireAdmin) && !user!.isRoot) { + const myRoles = await this.roleService.getUserRoles(user!.id); + if (ep.meta.requireModerator && !myRoles.some(r => r.isModerator || r.isAdministrator)) { + throw new ApiError({ + message: 'You are not assigned to a moderator role.', + code: 'ROLE_PERMISSION_DENIED', + id: 'd33d5333-db36-423d-a8f9-1a2b9549da41', + }); + } + if (ep.meta.requireAdmin && !myRoles.some(r => r.isAdministrator)) { + throw new ApiError({ + message: 'You are not assigned to an administrator role.', + code: 'ROLE_PERMISSION_DENIED', + id: 'c3d38592-54c0-429d-be96-5636b0431a61', + }); + } } if (token && ep.meta.kind && !token.permission.some(p => p === ep.meta.kind)) { diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index ab9349966d..c226c4e93c 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -38,8 +38,6 @@ 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'; @@ -55,13 +53,19 @@ 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___admin_roles_create from './endpoints/admin/roles/create.js'; +import * as ep___admin_roles_delete from './endpoints/admin/roles/delete.js'; +import * as ep___admin_roles_list from './endpoints/admin/roles/list.js'; +import * as ep___admin_roles_show from './endpoints/admin/roles/show.js'; +import * as ep___admin_roles_update from './endpoints/admin/roles/update.js'; +import * as ep___admin_roles_assign from './endpoints/admin/roles/assign.js'; +import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js'; +import * as ep___admin_roles_updateDefaultRoleOverride from './endpoints/admin/roles/update-default-role-override.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'; @@ -326,7 +330,6 @@ 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'; import * as ep___retention from './endpoints/retention.js'; import { GetterService } from './GetterService.js'; import { ApiLoggerService } from './ApiLoggerService.js'; @@ -369,8 +372,6 @@ const $admin_getIndexStats: Provider = { provide: 'ep:admin/get-index-stats', us 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 }; @@ -386,13 +387,19 @@ const $admin_serverInfo: Provider = { provide: 'ep:admin/server-info', useClass: 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 $admin_roles_create: Provider = { provide: 'ep:admin/roles/create', useClass: ep___admin_roles_create.default }; +const $admin_roles_delete: Provider = { provide: 'ep:admin/roles/delete', useClass: ep___admin_roles_delete.default }; +const $admin_roles_list: Provider = { provide: 'ep:admin/roles/list', useClass: ep___admin_roles_list.default }; +const $admin_roles_show: Provider = { provide: 'ep:admin/roles/show', useClass: ep___admin_roles_show.default }; +const $admin_roles_update: Provider = { provide: 'ep:admin/roles/update', useClass: ep___admin_roles_update.default }; +const $admin_roles_assign: Provider = { provide: 'ep:admin/roles/assign', useClass: ep___admin_roles_assign.default }; +const $admin_roles_unassign: Provider = { provide: 'ep:admin/roles/unassign', useClass: ep___admin_roles_unassign.default }; +const $admin_roles_updateDefaultRoleOverride: Provider = { provide: 'ep:admin/roles/update-default-role-override', useClass: ep___admin_roles_updateDefaultRoleOverride.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 }; @@ -656,7 +663,6 @@ const $users_searchByUsernameAndHost: Provider = { provide: 'ep:users/search-by- 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 }; const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention.default }; @@ -704,8 +710,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_getTableStats, $admin_getUserIps, $admin_invite, - $admin_moderators_add, - $admin_moderators_remove, $admin_promo_create, $admin_queue_clear, $admin_queue_deliverDelayed, @@ -721,13 +725,19 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_showModerationLogs, $admin_showUser, $admin_showUsers, - $admin_silenceUser, $admin_suspendUser, - $admin_unsilenceUser, $admin_unsuspendUser, $admin_updateMeta, $admin_deleteAccount, $admin_updateUserNote, + $admin_roles_create, + $admin_roles_delete, + $admin_roles_list, + $admin_roles_show, + $admin_roles_update, + $admin_roles_assign, + $admin_roles_unassign, + $admin_roles_updateDefaultRoleOverride, $announcements, $antennas_create, $antennas_delete, @@ -991,7 +1001,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $users_search, $users_show, $users_stats, - $admin_driveCapOverride, $fetchRss, $retention, ], @@ -1033,8 +1042,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_getTableStats, $admin_getUserIps, $admin_invite, - $admin_moderators_add, - $admin_moderators_remove, $admin_promo_create, $admin_queue_clear, $admin_queue_deliverDelayed, @@ -1050,13 +1057,19 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_showModerationLogs, $admin_showUser, $admin_showUsers, - $admin_silenceUser, $admin_suspendUser, - $admin_unsilenceUser, $admin_unsuspendUser, $admin_updateMeta, $admin_deleteAccount, $admin_updateUserNote, + $admin_roles_create, + $admin_roles_delete, + $admin_roles_list, + $admin_roles_show, + $admin_roles_update, + $admin_roles_assign, + $admin_roles_unassign, + $admin_roles_updateDefaultRoleOverride, $announcements, $antennas_create, $antennas_delete, @@ -1318,7 +1331,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $users_search, $users_show, $users_stats, - $admin_driveCapOverride, $fetchRss, $retention, ], diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index f9749ad660..1df3240e41 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -37,8 +37,6 @@ 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'; @@ -54,13 +52,19 @@ 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___admin_roles_create from './endpoints/admin/roles/create.js'; +import * as ep___admin_roles_delete from './endpoints/admin/roles/delete.js'; +import * as ep___admin_roles_list from './endpoints/admin/roles/list.js'; +import * as ep___admin_roles_show from './endpoints/admin/roles/show.js'; +import * as ep___admin_roles_update from './endpoints/admin/roles/update.js'; +import * as ep___admin_roles_assign from './endpoints/admin/roles/assign.js'; +import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js'; +import * as ep___admin_roles_updateDefaultRoleOverride from './endpoints/admin/roles/update-default-role-override.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'; @@ -325,7 +329,6 @@ 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'; import * as ep___retention from './endpoints/retention.js'; const eps = [ @@ -366,8 +369,6 @@ const eps = [ ['admin/get-table-stats', ep___admin_getTableStats], ['admin/get-user-ips', ep___admin_getUserIps], ['admin/invite', ep___admin_invite], - ['admin/moderators/add', ep___admin_moderators_add], - ['admin/moderators/remove', ep___admin_moderators_remove], ['admin/promo/create', ep___admin_promo_create], ['admin/queue/clear', ep___admin_queue_clear], ['admin/queue/deliver-delayed', ep___admin_queue_deliverDelayed], @@ -383,13 +384,19 @@ const eps = [ ['admin/show-moderation-logs', ep___admin_showModerationLogs], ['admin/show-user', ep___admin_showUser], ['admin/show-users', ep___admin_showUsers], - ['admin/silence-user', ep___admin_silenceUser], ['admin/suspend-user', ep___admin_suspendUser], - ['admin/unsilence-user', ep___admin_unsilenceUser], ['admin/unsuspend-user', ep___admin_unsuspendUser], ['admin/update-meta', ep___admin_updateMeta], ['admin/delete-account', ep___admin_deleteAccount], ['admin/update-user-note', ep___admin_updateUserNote], + ['admin/roles/create', ep___admin_roles_create], + ['admin/roles/delete', ep___admin_roles_delete], + ['admin/roles/list', ep___admin_roles_list], + ['admin/roles/show', ep___admin_roles_show], + ['admin/roles/update', ep___admin_roles_update], + ['admin/roles/assign', ep___admin_roles_assign], + ['admin/roles/unassign', ep___admin_roles_unassign], + ['admin/roles/update-default-role-override', ep___admin_roles_updateDefaultRoleOverride], ['announcements', ep___announcements], ['antennas/create', ep___antennas_create], ['antennas/delete', ep___antennas_delete], @@ -653,7 +660,6 @@ const eps = [ ['users/search', ep___users_search], ['users/show', ep___users_show], ['users/stats', ep___users_stats], - ['admin/drive-capacity-override', ep___admin_driveCapOverride], ['fetch-rss', ep___fetchRss], ['retention', ep___retention], ]; @@ -680,14 +686,14 @@ export interface IEndpointMeta { readonly requireCredential?: boolean; /** - * 管理者のみ使えるエンドポイントか否か + * isModeratorなロールを必要とするか */ - readonly requireAdmin?: boolean; + readonly requireModerator?: boolean; /** - * 管理者またはモデレーターのみ使えるエンドポイントか否か + * isAdministratorなロールを必要とするか */ - readonly requireModerator?: boolean; + readonly requireAdmin?: boolean; /** * エンドポイントのリミテーションに関するやつ 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 c76ece9e05..bac8ae16e5 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts @@ -47,7 +47,7 @@ export default class extends Endpoint { const noUsers = (await this.usersRepository.countBy({ host: IsNull(), })) === 0; - if (!noUsers && !me?.isAdmin) throw new Error('access denied'); + if (!noUsers && !me?.isRoot) throw new Error('access denied'); const { account, secret } = await this.signupService.signup({ username: ps.username, 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 b7081987ca..e9f72676f0 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts @@ -11,7 +11,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireModerator: true, + requireAdmin: true, } as const; export const paramDef = { @@ -41,12 +41,8 @@ export default class extends Endpoint { throw new Error('user not found'); } - if (user.isAdmin) { - throw new Error('cannot suspend admin'); - } - - if (user.isModerator) { - throw new Error('cannot suspend moderator'); + if (user.isRoot) { + throw new Error('cannot delete a root account'); } if (this.userEntityService.isLocalUser(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 22b78bf19d..c193ed3fb3 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 @@ -8,7 +8,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireModerator: true, + requireAdmin: true, } as const; export const paramDef = { 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 deleted file mode 100644 index 665e2a8cce..0000000000 --- a/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository } from '@/models/index.js'; -import { ModerationLogService } from '@/core/ModerationLogService.js'; -import { DI } from '@/di-symbols.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; - -export const meta = { - tags: ['admin'], - - requireCredential: true, - requireModerator: true, -} as const; - -export const paramDef = { - type: 'object', - properties: { - userId: { type: 'string', format: 'misskey:id' }, - overrideMb: { type: 'number', nullable: true }, - }, - required: ['userId', 'overrideMb'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint { - constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - private userEntityService: UserEntityService, - private moderationLogService: ModerationLogService, - ) { - super(meta, paramDef, async (ps, me) => { - const user = await this.usersRepository.findOneBy({ id: ps.userId }); - - if (user == null) { - throw new Error('user not found'); - } - - if (!this.userEntityService.isLocalUser(user)) { - throw new Error('user is not local user'); - } - - /*if (user.isAdmin) { - throw new Error('cannot suspend admin'); - } - if (user.isModerator) { - throw new Error('cannot suspend moderator'); - }*/ - - await this.usersRepository.update(user.id, { - driveCapacityOverrideMb: ps.overrideMb, - }); - - this.moderationLogService.insertModerationLog(me, 'change-drive-capacity-override', { - targetId: user.id, - }); - }); - } -} 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 6180eeae2b..6376cb153c 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 @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type { DriveFilesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; +import { RoleService } from '@/core/RoleService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -159,6 +160,8 @@ export default class extends Endpoint { constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, + + private roleService: RoleService, ) { super(meta, paramDef, async (ps, me) => { const file = ps.fileId ? await this.driveFilesRepository.findOneBy({ id: ps.fileId }) : await this.driveFilesRepository.findOne({ @@ -175,6 +178,8 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchFile); } + const isModerator = await this.roleService.isModerator(me); + return { id: file.id, userId: file.userId, @@ -202,8 +207,8 @@ export default class extends Endpoint { name: file.name, md5: file.md5, createdAt: file.createdAt.toISOString(), - requestIp: me.isAdmin ? file.requestIp : null, - requestHeaders: me.isAdmin ? file.requestHeaders : null, + requestIp: isModerator ? file.requestIp : null, + requestHeaders: isModerator ? file.requestHeaders : 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 e53d0bfcea..8ffd2b01e7 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 @@ -5,7 +5,7 @@ import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, - requireModerator: true, + requireAdmin: true, tags: ['admin'], } as const; 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 41014cb167..09d61bd741 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 @@ -5,7 +5,7 @@ import { DI } from '@/di-symbols.js'; export const meta = { requireCredential: true, - requireModerator: true, + requireAdmin: true, tags: ['admin'], 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 947a673def..bfcc8a700b 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 @@ -7,7 +7,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireAdmin: true, + requireModerator: true, } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 794ea3d5c9..33f162acf9 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -4,6 +4,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { MetaService } from '@/core/MetaService.js'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; +import { DEFAULT_ROLE } from '@/core/RoleService.js'; export const meta = { tags: ['meta'], @@ -15,10 +16,6 @@ export const meta = { type: 'object', optional: false, nullable: false, properties: { - driveCapacityPerLocalUserMb: { - type: 'number', - optional: false, nullable: false, - }, driveCapacityPerRemoteUserMb: { type: 'number', optional: false, nullable: false, @@ -377,9 +374,6 @@ export default class extends Endpoint { 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, @@ -451,6 +445,7 @@ export default class extends Endpoint { deeplIsPro: instance.deeplIsPro, enableIpLogging: instance.enableIpLogging, enableActiveEmailValidation: instance.enableActiveEmailValidation, + baseRole: { ...DEFAULT_ROLE, ...instance.defaultRoleOverride }, }; }); } diff --git a/packages/backend/src/server/api/endpoints/admin/moderators/add.ts b/packages/backend/src/server/api/endpoints/admin/moderators/add.ts deleted file mode 100644 index 2fc5a35e8e..0000000000 --- a/packages/backend/src/server/api/endpoints/admin/moderators/add.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository } from '@/models/index.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { DI } from '@/di-symbols.js'; - -export const meta = { - tags: ['admin'], - - requireCredential: true, - requireAdmin: true, -} as const; - -export const paramDef = { - type: 'object', - properties: { - userId: { type: 'string', format: 'misskey:id' }, - }, - required: ['userId'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint { - constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - private globalEventService: GlobalEventService, - ) { - super(meta, paramDef, async (ps) => { - const user = await this.usersRepository.findOneBy({ id: ps.userId }); - - if (user == null) { - throw new Error('user not found'); - } - - if (user.isAdmin) { - throw new Error('cannot mark as moderator if admin user'); - } - - await this.usersRepository.update(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 deleted file mode 100644 index f0d7a3f12d..0000000000 --- a/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository } from '@/models/index.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { DI } from '@/di-symbols.js'; - -export const meta = { - tags: ['admin'], - - requireCredential: true, - requireAdmin: true, -} as const; - -export const paramDef = { - type: 'object', - properties: { - userId: { type: 'string', format: 'misskey:id' }, - }, - required: ['userId'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint { - constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - private globalEventService: GlobalEventService, - ) { - super(meta, paramDef, async (ps) => { - const user = await this.usersRepository.findOneBy({ id: ps.userId }); - - if (user == null) { - throw new Error('user not found'); - } - - await this.usersRepository.update(user.id, { - isModerator: false, - }); - - this.globalEventService.publishInternalEvent('userChangeModeratorState', { id: 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 f7d27be9cb..d263f99f6e 100644 --- a/packages/backend/src/server/api/endpoints/admin/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/admin/reset-password.ts @@ -50,8 +50,8 @@ export default class extends Endpoint { throw new Error('user not found'); } - if (user.isAdmin) { - throw new Error('cannot reset password of admin'); + if (user.isRoot) { + throw new Error('cannot reset password of root'); } const passwd = rndstr('a-zA-Z0-9', 8); diff --git a/packages/backend/src/server/api/endpoints/admin/roles/assign.ts b/packages/backend/src/server/api/endpoints/admin/roles/assign.ts new file mode 100644 index 0000000000..7bfb2f6625 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/roles/assign.ts @@ -0,0 +1,96 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '@/server/api/error.js'; +import { IdService } from '@/core/IdService.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { RoleService } from '@/core/RoleService.js'; + +export const meta = { + tags: ['admin', 'role'], + + requireCredential: true, + requireModerator: true, + + errors: { + noSuchRole: { + message: 'No such role.', + code: 'NO_SUCH_ROLE', + id: '6503c040-6af4-4ed9-bf07-f2dd16678eab', + }, + + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '558ea170-f653-4700-94d0-5a818371d0df', + }, + + accessDenied: { + message: 'Only administrators can edit members of the role.', + code: 'ACCESS_DENIED', + id: '25b5bc31-dc79-4ebd-9bd2-c84978fd052c', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + roleId: { type: 'string', format: 'misskey:id' }, + userId: { type: 'string', format: 'misskey:id' }, + }, + required: [ + 'roleId', + 'userId', + ], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.rolesRepository) + private rolesRepository: RolesRepository, + + @Inject(DI.roleAssignmentsRepository) + private roleAssignmentsRepository: RoleAssignmentsRepository, + + private globalEventService: GlobalEventService, + private roleService: RoleService, + private idService: IdService, + ) { + super(meta, paramDef, async (ps, me) => { + const role = await this.rolesRepository.findOneBy({ id: ps.roleId }); + if (role == null) { + throw new ApiError(meta.errors.noSuchRole); + } + + if (!role.canEditMembersByModerator && !(await this.roleService.isAdministrator(me))) { + throw new ApiError(meta.errors.accessDenied); + } + + const user = await this.usersRepository.findOneBy({ id: ps.userId }); + if (user == null) { + throw new ApiError(meta.errors.noSuchUser); + } + + const date = new Date(); + const created = await this.roleAssignmentsRepository.insert({ + id: this.idService.genId(), + createdAt: date, + roleId: role.id, + userId: user.id, + }).then(x => this.roleAssignmentsRepository.findOneByOrFail(x.identifiers[0])); + + this.rolesRepository.update(ps.roleId, { + lastUsedAt: new Date(), + }); + + this.globalEventService.publishInternalEvent('userRoleAssigned', created); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/roles/create.ts b/packages/backend/src/server/api/endpoints/admin/roles/create.ts new file mode 100644 index 0000000000..b04188fac6 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/roles/create.ts @@ -0,0 +1,75 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { RolesRepository } from '@/models/index.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; +import { IdService } from '@/core/IdService.js'; +import { RoleEntityService } from '@/core/entities/RoleEntityService.js'; + +export const meta = { + tags: ['admin', 'role'], + + requireCredential: true, + requireAdmin: true, +} as const; + +export const paramDef = { + type: 'object', + properties: { + name: { type: 'string' }, + description: { type: 'string' }, + color: { type: 'string', nullable: true }, + isPublic: { type: 'boolean' }, + isModerator: { type: 'boolean' }, + isAdministrator: { type: 'boolean' }, + canEditMembersByModerator: { type: 'boolean' }, + options: { + type: 'object', + }, + }, + required: [ + 'name', + 'description', + 'color', + 'isPublic', + 'isModerator', + 'isAdministrator', + 'canEditMembersByModerator', + 'options', + ], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.rolesRepository) + private rolesRepository: RolesRepository, + + private globalEventService: GlobalEventService, + private idService: IdService, + private roleEntityService: RoleEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const date = new Date(); + const created = await this.rolesRepository.insert({ + id: this.idService.genId(), + createdAt: date, + updatedAt: date, + lastUsedAt: date, + name: ps.name, + description: ps.description, + color: ps.color, + isPublic: ps.isPublic, + isAdministrator: ps.isAdministrator, + isModerator: ps.isModerator, + canEditMembersByModerator: ps.canEditMembersByModerator, + options: ps.options, + }).then(x => this.rolesRepository.findOneByOrFail(x.identifiers[0])); + + this.globalEventService.publishInternalEvent('roleCreated', created); + + return await this.roleEntityService.pack(created, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/roles/delete.ts b/packages/backend/src/server/api/endpoints/admin/roles/delete.ts new file mode 100644 index 0000000000..b56ebdb3ee --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/roles/delete.ts @@ -0,0 +1,53 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { RolesRepository } from '@/models/index.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '@/server/api/error.js'; + +export const meta = { + tags: ['admin', 'role'], + + requireCredential: true, + requireAdmin: true, + + errors: { + noSuchRole: { + message: 'No such role.', + code: 'NO_SUCH_ROLE', + id: 'de0d6ecd-8e0a-4253-88ff-74bc89ae3d45', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + roleId: { type: 'string', format: 'misskey:id' }, + }, + required: [ + 'roleId', + ], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.rolesRepository) + private rolesRepository: RolesRepository, + + private globalEventService: GlobalEventService, + ) { + super(meta, paramDef, async (ps) => { + const role = await this.rolesRepository.findOneBy({ id: ps.roleId }); + if (role == null) { + throw new ApiError(meta.errors.noSuchRole); + } + await this.rolesRepository.delete({ + id: ps.roleId, + }); + this.globalEventService.publishInternalEvent('roleDeleted', role); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/roles/list.ts b/packages/backend/src/server/api/endpoints/admin/roles/list.ts new file mode 100644 index 0000000000..458a8d535b --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/roles/list.ts @@ -0,0 +1,39 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { RolesRepository } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '@/server/api/error.js'; +import { RoleEntityService } from '@/core/entities/RoleEntityService.js'; + +export const meta = { + tags: ['admin', 'role'], + + requireCredential: true, + requireModerator: true, +} as const; + +export const paramDef = { + type: 'object', + properties: { + }, + required: [ + ], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.rolesRepository) + private rolesRepository: RolesRepository, + + private roleEntityService: RoleEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const roles = await this.rolesRepository.find({ + order: { lastUsedAt: 'DESC' }, + }); + return await this.roleEntityService.packMany(roles, me, { detail: false }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/roles/show.ts b/packages/backend/src/server/api/endpoints/admin/roles/show.ts new file mode 100644 index 0000000000..c83f96191d --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/roles/show.ts @@ -0,0 +1,50 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { RolesRepository } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '@/server/api/error.js'; +import { RoleEntityService } from '@/core/entities/RoleEntityService.js'; + +export const meta = { + tags: ['admin', 'role'], + + requireCredential: true, + requireModerator: true, + + errors: { + noSuchRole: { + message: 'No such role.', + code: 'NO_SUCH_ROLE', + id: '07dc7d34-c0d8-49b7-96c6-db3ce64ee0b3', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + roleId: { type: 'string', format: 'misskey:id' }, + }, + required: [ + 'roleId', + ], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.rolesRepository) + private rolesRepository: RolesRepository, + + private roleEntityService: RoleEntityService, + ) { + super(meta, paramDef, async (ps) => { + const role = await this.rolesRepository.findOneBy({ id: ps.roleId }); + if (role == null) { + throw new ApiError(meta.errors.noSuchRole); + } + return await this.roleEntityService.pack(role); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts b/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts new file mode 100644 index 0000000000..141cc5ee89 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts @@ -0,0 +1,101 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '@/server/api/error.js'; +import { IdService } from '@/core/IdService.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { RoleService } from '@/core/RoleService.js'; + +export const meta = { + tags: ['admin', 'role'], + + requireCredential: true, + requireModerator: true, + + errors: { + noSuchRole: { + message: 'No such role.', + code: 'NO_SUCH_ROLE', + id: '6e519036-a70d-4c76-b679-bc8fb18194e2', + }, + + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '2b730f78-1179-461b-88ad-d24c9af1a5ce', + }, + + notAssigned: { + message: 'Not assigned.', + code: 'NOT_ASSIGNED', + id: 'b9060ac7-5c94-4da4-9f55-2047c953df44', + }, + + accessDenied: { + message: 'Only administrators can edit members of the role.', + code: 'ACCESS_DENIED', + id: '24636eee-e8c1-493e-94b2-e16ad401e262', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + roleId: { type: 'string', format: 'misskey:id' }, + userId: { type: 'string', format: 'misskey:id' }, + }, + required: [ + 'roleId', + 'userId', + ], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.rolesRepository) + private rolesRepository: RolesRepository, + + @Inject(DI.roleAssignmentsRepository) + private roleAssignmentsRepository: RoleAssignmentsRepository, + + private globalEventService: GlobalEventService, + private roleService: RoleService, + private idService: IdService, + ) { + super(meta, paramDef, async (ps, me) => { + const role = await this.rolesRepository.findOneBy({ id: ps.roleId }); + if (role == null) { + throw new ApiError(meta.errors.noSuchRole); + } + + if (!role.canEditMembersByModerator && !(await this.roleService.isAdministrator(me))) { + throw new ApiError(meta.errors.accessDenied); + } + + const user = await this.usersRepository.findOneBy({ id: ps.userId }); + if (user == null) { + throw new ApiError(meta.errors.noSuchUser); + } + + const roleAssignment = await this.roleAssignmentsRepository.findOneBy({ userId: user.id, roleId: role.id }); + if (roleAssignment == null) { + throw new ApiError(meta.errors.notAssigned); + } + + await this.roleAssignmentsRepository.delete(roleAssignment.id); + + this.rolesRepository.update(ps.roleId, { + lastUsedAt: new Date(), + }); + + this.globalEventService.publishInternalEvent('userRoleUnassigned', roleAssignment); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update-default-role-override.ts b/packages/backend/src/server/api/endpoints/admin/roles/update-default-role-override.ts new file mode 100644 index 0000000000..35da04efd2 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/roles/update-default-role-override.ts @@ -0,0 +1,42 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { RolesRepository } from '@/models/index.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '@/server/api/error.js'; +import { MetaService } from '@/core/MetaService.js'; + +export const meta = { + tags: ['admin', 'role'], + + requireCredential: true, + requireAdmin: true, +} as const; + +export const paramDef = { + type: 'object', + properties: { + options: { + type: 'object', + }, + }, + required: [ + 'options', + ], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + private metaService: MetaService, + private globalEventService: GlobalEventService, + ) { + super(meta, paramDef, async (ps) => { + await this.metaService.update({ + defaultRoleOverride: ps.options, + }); + this.globalEventService.publishInternalEvent('defaultRoleOverrideUpdated', ps.options); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update.ts b/packages/backend/src/server/api/endpoints/admin/roles/update.ts new file mode 100644 index 0000000000..7d97d68e14 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/roles/update.ts @@ -0,0 +1,82 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { RolesRepository } from '@/models/index.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '@/server/api/error.js'; + +export const meta = { + tags: ['admin', 'role'], + + requireCredential: true, + requireAdmin: true, + + errors: { + noSuchRole: { + message: 'No such role.', + code: 'NO_SUCH_ROLE', + id: 'cd23ef55-09ad-428a-ac61-95a45e124b32', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + roleId: { type: 'string', format: 'misskey:id' }, + name: { type: 'string' }, + description: { type: 'string' }, + color: { type: 'string', nullable: true }, + isPublic: { type: 'boolean' }, + isModerator: { type: 'boolean' }, + isAdministrator: { type: 'boolean' }, + canEditMembersByModerator: { type: 'boolean' }, + options: { + type: 'object', + }, + }, + required: [ + 'roleId', + 'name', + 'description', + 'color', + 'isPublic', + 'isModerator', + 'isAdministrator', + 'canEditMembersByModerator', + 'options', + ], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + @Inject(DI.rolesRepository) + private rolesRepository: RolesRepository, + + private globalEventService: GlobalEventService, + ) { + super(meta, paramDef, async (ps) => { + const role = await this.rolesRepository.findOneBy({ id: ps.roleId }); + if (role == null) { + throw new ApiError(meta.errors.noSuchRole); + } + + const date = new Date(); + await this.rolesRepository.update(ps.roleId, { + updatedAt: date, + name: ps.name, + description: ps.description, + color: ps.color, + isPublic: ps.isPublic, + isModerator: ps.isModerator, + isAdministrator: ps.isAdministrator, + canEditMembersByModerator: ps.canEditMembersByModerator, + options: ps.options, + }); + const updated = await this.rolesRepository.findOneByOrFail({ id: ps.roleId }); + this.globalEventService.publishInternalEvent('roleUpdated', updated); + }); + } +} 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 e4031cf960..3f4ec299af 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -2,6 +2,8 @@ import { Inject, Injectable } from '@nestjs/common'; import type { UsersRepository, SigninsRepository, UserProfilesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; +import { RoleService } from '@/core/RoleService.js'; +import { RoleEntityService } from '@/core/entities/RoleEntityService.js'; export const meta = { tags: ['admin'], @@ -35,6 +37,9 @@ export default class extends Endpoint { @Inject(DI.signinsRepository) private signinsRepository: SigninsRepository, + + private roleService: RoleService, + private roleEntityService: RoleEntityService, ) { super(meta, paramDef, async (ps, me) => { const [user, profile] = await Promise.all([ @@ -46,15 +51,16 @@ export default class extends Endpoint { throw new Error('user not found'); } + const isModerator = await this.roleService.isModerator(user); + const isSilenced = !(await this.roleService.getUserRoleOptions(user.id)).canPublicNote; + const _me = await this.usersRepository.findOneByOrFail({ id: me.id }); - if ((_me.isModerator && !_me.isAdmin) && user.isAdmin) { + if (!await this.roleService.isAdministrator(_me) && await this.roleService.isAdministrator(user)) { throw new Error('cannot show info of admin'); } - if (!_me.isAdmin) { + if (!await this.roleService.isAdministrator(_me)) { return { - isModerator: user.isModerator, - isSilenced: user.isSilenced, isSuspended: user.isSuspended, }; } @@ -66,6 +72,8 @@ export default class extends Endpoint { const signins = await this.signinsRepository.findBy({ userId: user.id }); + const roles = await this.roleService.getUserRoles(user.id); + return { email: profile.email, emailVerified: profile.emailVerified, @@ -80,12 +88,13 @@ export default class extends Endpoint { mutedWords: profile.mutedWords, mutedInstances: profile.mutedInstances, mutingNotificationTypes: profile.mutingNotificationTypes, - isModerator: user.isModerator, - isSilenced: user.isSilenced, + isModerator: isModerator, + isSilenced: isSilenced, isSuspended: user.isSuspended, lastActiveDate: user.lastActiveDate, moderationNote: profile.moderationNote, signins, + roles: await this.roleEntityService.packMany(roles, me, { detail: false }), }; }); } 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 5a67cf522a..426973f282 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-users.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts @@ -4,6 +4,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { sqlLikeEscape } from '@/misc/sql-like-escape.js'; +import { RoleService } from '@/core/RoleService.js'; export const meta = { tags: ['admin'], @@ -28,7 +29,7 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, offset: { type: 'integer', default: 0 }, sort: { type: 'string', enum: ['+follower', '-follower', '+createdAt', '-createdAt', '+updatedAt', '-updatedAt', '+lastActiveDate', '-lastActiveDate'] }, - state: { type: 'string', enum: ['all', 'alive', 'available', 'admin', 'moderator', 'adminOrModerator', 'silenced', 'suspended'], default: 'all' }, + state: { type: 'string', enum: ['all', 'alive', 'available', 'admin', 'moderator', 'adminOrModerator', 'suspended'], default: 'all' }, origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: 'combined' }, username: { type: 'string', nullable: true, default: null }, hostname: { @@ -49,18 +50,33 @@ export default class extends Endpoint { private usersRepository: UsersRepository, private userEntityService: UserEntityService, + private roleService: RoleService, ) { super(meta, paramDef, async (ps, me) => { const query = this.usersRepository.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; + case 'admin': { + const adminIds = await this.roleService.getAdministratorIds(); + if (adminIds.length === 0) return []; + query.where('user.id IN (:...adminIds)', { adminIds: adminIds }); + break; + } + case 'moderator': { + const moderatorIds = await this.roleService.getModeratorIds(false); + if (moderatorIds.length === 0) return []; + query.where('user.id IN (:...moderatorIds)', { moderatorIds: moderatorIds }); + break; + } + case 'adminOrModerator': { + const adminOrModeratorIds = await this.roleService.getModeratorIds(); + if (adminOrModeratorIds.length === 0) return []; + query.where('user.id IN (:...adminOrModeratorIds)', { adminOrModeratorIds: adminOrModeratorIds }); + break; + } } switch (ps.origin) { diff --git a/packages/backend/src/server/api/endpoints/admin/silence-user.ts b/packages/backend/src/server/api/endpoints/admin/silence-user.ts deleted file mode 100644 index b9dbd211e0..0000000000 --- a/packages/backend/src/server/api/endpoints/admin/silence-user.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import { ModerationLogService } from '@/core/ModerationLogService.js'; -import type { UsersRepository } from '@/models/index.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { DI } from '@/di-symbols.js'; - -export const meta = { - tags: ['admin'], - - requireCredential: true, - requireModerator: true, -} as const; - -export const paramDef = { - type: 'object', - properties: { - userId: { type: 'string', format: 'misskey:id' }, - }, - required: ['userId'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint { - constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - private moderationLogService: ModerationLogService, - private globalEventService: GlobalEventService, - ) { - super(meta, paramDef, async (ps, me) => { - const user = await this.usersRepository.findOneBy({ id: ps.userId }); - - if (user == null) { - throw new Error('user not found'); - } - - if (user.isAdmin) { - throw new Error('cannot silence admin'); - } - - await this.usersRepository.update(user.id, { - isSilenced: true, - }); - - this.globalEventService.publishInternalEvent('userChangeSilencedState', { id: user.id, isSilenced: true }); - - 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 9fc1391570..3ad6c7c484 100644 --- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts @@ -9,6 +9,7 @@ import { UserFollowingService } from '@/core/UserFollowingService.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; +import { RoleService } from '@/core/RoleService.js'; export const meta = { tags: ['admin'], @@ -41,6 +42,7 @@ export default class extends Endpoint { private userEntityService: UserEntityService, private userFollowingService: UserFollowingService, private userSuspendService: UserSuspendService, + private roleService: RoleService, private moderationLogService: ModerationLogService, private globalEventService: GlobalEventService, ) { @@ -51,12 +53,8 @@ export default class extends Endpoint { throw new Error('user not found'); } - if (user.isAdmin) { - throw new Error('cannot suspend admin'); - } - - if (user.isModerator) { - throw new Error('cannot suspend moderator'); + if (await this.roleService.isModerator(user)) { + throw new Error('cannot suspend moderator account'); } await this.usersRepository.update(user.id, { diff --git a/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts b/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts deleted file mode 100644 index 3a9d410de0..0000000000 --- a/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { UsersRepository } from '@/models/index.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { ModerationLogService } from '@/core/ModerationLogService.js'; -import { DI } from '@/di-symbols.js'; - -export const meta = { - tags: ['admin'], - - requireCredential: true, - requireModerator: true, -} as const; - -export const paramDef = { - type: 'object', - properties: { - userId: { type: 'string', format: 'misskey:id' }, - }, - required: ['userId'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint { - constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - private moderationLogService: ModerationLogService, - private globalEventService: GlobalEventService, - ) { - super(meta, paramDef, async (ps, me) => { - const user = await this.usersRepository.findOneBy({ id: ps.userId }); - - if (user == null) { - throw new Error('user not found'); - } - - await this.usersRepository.update(user.id, { - isSilenced: false, - }); - - this.globalEventService.publishInternalEvent('userChangeSilencedState', { id: user.id, isSilenced: false }); - - this.moderationLogService.insertModerationLog(me, 'unsilence', { - targetId: user.id, - }); - }); - } -} 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 795b8460f3..c766494e6b 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -19,8 +19,6 @@ export const paramDef = { type: 'object', properties: { disableRegistration: { type: 'boolean', nullable: true }, - disableLocalTimeline: { type: 'boolean', nullable: true }, - disableGlobalTimeline: { type: 'boolean', nullable: true }, useStarForReactionFallback: { type: 'boolean', nullable: true }, pinnedUsers: { type: 'array', nullable: true, items: { type: 'string', @@ -42,7 +40,6 @@ export const paramDef = { description: { type: 'string', nullable: true }, defaultLightTheme: { type: 'string', nullable: true }, defaultDarkTheme: { type: 'string', nullable: true }, - localDriveCapacityMb: { type: 'integer' }, remoteDriveCapacityMb: { type: 'integer' }, cacheRemoteFiles: { type: 'boolean' }, emailRequiredForSignup: { type: 'boolean' }, @@ -130,14 +127,6 @@ export default class extends Endpoint { set.disableRegistration = ps.disableRegistration; } - if (typeof ps.disableLocalTimeline === 'boolean') { - set.disableLocalTimeline = ps.disableLocalTimeline; - } - - if (typeof ps.disableGlobalTimeline === 'boolean') { - set.disableGlobalTimeline = ps.disableGlobalTimeline; - } - if (typeof ps.useStarForReactionFallback === 'boolean') { set.useStarForReactionFallback = ps.useStarForReactionFallback; } @@ -194,10 +183,6 @@ export default class extends Endpoint { set.defaultDarkTheme = ps.defaultDarkTheme; } - if (ps.localDriveCapacityMb !== undefined) { - set.localDriveCapacityMb = ps.localDriveCapacityMb; - } - if (ps.remoteDriveCapacityMb !== undefined) { set.remoteDriveCapacityMb = ps.remoteDriveCapacityMb; } diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index 2378660ec8..08625250c8 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -5,6 +5,7 @@ import type { UserListsRepository, UserGroupJoiningsRepository, AntennasReposito import { GlobalEventService } from '@/core/GlobalEventService.js'; import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js'; import { DI } from '@/di-symbols.js'; +import { RoleService } from '@/core/RoleService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -83,6 +84,7 @@ export default class extends Endpoint { private userGroupJoiningsRepository: UserGroupJoiningsRepository, private antennaEntityService: AntennaEntityService, + private roleService: RoleService, private idService: IdService, private globalEventService: GlobalEventService, ) { @@ -90,7 +92,7 @@ export default class extends Endpoint { const currentAntennasCount = await this.antennasRepository.countBy({ userId: me.id, }); - if (currentAntennasCount > 5) { + if (currentAntennasCount > (await this.roleService.getUserRoleOptions(me.id)).antennaLimit) { throw new ApiError(meta.errors.tooManyAntennas); } diff --git a/packages/backend/src/server/api/endpoints/drive.ts b/packages/backend/src/server/api/endpoints/drive.ts index 6f40225f15..2a06792dcf 100644 --- a/packages/backend/src/server/api/endpoints/drive.ts +++ b/packages/backend/src/server/api/endpoints/drive.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { MetaService } from '@/core/MetaService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; +import { RoleService } from '@/core/RoleService.js'; export const meta = { tags: ['drive', 'account'], @@ -38,6 +39,7 @@ export default class extends Endpoint { constructor( private metaService: MetaService, private driveFileEntityService: DriveFileEntityService, + private roleService: RoleService, ) { super(meta, paramDef, async (ps, me) => { const instance = await this.metaService.fetch(true); @@ -45,8 +47,10 @@ export default class extends Endpoint { // Calculate drive usage const usage = await this.driveFileEntityService.calcDriveUsageOf(me.id); + const myRole = await this.roleService.getUserRoleOptions(me.id); + return { - capacity: 1024 * 1024 * (me.driveCapacityOverrideMb ?? instance.localDriveCapacityMb), + capacity: 1024 * 1024 * myRole.driveCapacityMb, usage: usage, }; }); 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 be7b050907..2ced97ee02 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 { DriveFilesRepository } from '@/models/index.js'; import { DriveService } from '@/core/DriveService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; +import { RoleService } from '@/core/RoleService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -46,6 +47,7 @@ export default class extends Endpoint { private driveFilesRepository: DriveFilesRepository, private driveService: DriveService, + private roleService: RoleService, private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { @@ -55,7 +57,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchFile); } - if ((!me.isAdmin && !me.isModerator) && (file.userId !== me.id)) { + if (!await this.roleService.isModerator(me) && (file.userId !== me.id)) { throw new ApiError(meta.errors.accessDenied); } 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 474d599cb6..e0a07a3640 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 { DriveFilesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { DI } from '@/di-symbols.js'; +import { RoleService } from '@/core/RoleService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -62,6 +63,7 @@ export default class extends Endpoint { private driveFilesRepository: DriveFilesRepository, private driveFileEntityService: DriveFileEntityService, + private roleService: RoleService, ) { super(meta, paramDef, async (ps, me) => { let file: DriveFile | null = null; @@ -84,7 +86,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchFile); } - if ((!me.isAdmin && !me.isModerator) && (file.userId !== me.id)) { + if (!await this.roleService.isModerator(me) && (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 9e2c767277..0fe57de6a8 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts @@ -5,6 +5,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; +import { RoleService } from '@/core/RoleService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -72,6 +73,7 @@ export default class extends Endpoint { private driveFoldersRepository: DriveFoldersRepository, private driveFileEntityService: DriveFileEntityService, + private roleService: RoleService, private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { @@ -81,7 +83,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.noSuchFile); } - if ((!me.isAdmin && !me.isModerator) && (file.userId !== me.id)) { + if (!await this.roleService.isModerator(me) && (file.userId !== me.id)) { throw new ApiError(meta.errors.accessDenied); } diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index c44d63d64b..f87fca63e3 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -7,6 +7,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { MetaService } from '@/core/MetaService.js'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; +import { DEFAULT_ROLE } from '@/core/RoleService.js'; export const meta = { tags: ['meta'], @@ -77,18 +78,6 @@ export const meta = { type: 'boolean', optional: false, nullable: false, }, - disableLocalTimeline: { - type: 'boolean', - optional: false, nullable: false, - }, - disableGlobalTimeline: { - type: 'boolean', - optional: false, nullable: false, - }, - driveCapacityPerLocalUserMb: { - type: 'number', - optional: false, nullable: false, - }, driveCapacityPerRemoteUserMb: { type: 'number', optional: false, nullable: false, @@ -314,9 +303,6 @@ export default class extends Endpoint { 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, @@ -353,6 +339,8 @@ export default class extends Endpoint { translatorAvailable: instance.deeplAuthKey != null, + baseRole: { ...DEFAULT_ROLE, ...instance.defaultRoleOverride }, + ...(ps.detail ? { pinnedPages: instance.pinnedPages, pinnedClipId: instance.pinnedClipId, @@ -369,8 +357,6 @@ export default class extends Endpoint { response.proxyAccountName = proxyAccount ? proxyAccount.username : null; response.features = { registration: !instance.disableRegistration, - localTimeLine: !instance.disableLocalTimeline, - globalTimeLine: !instance.disableGlobalTimeline, emailRequiredForSignup: instance.emailRequiredForSignup, elasticsearch: this.config.elasticsearch ? true : false, hcaptcha: instance.enableHcaptcha, diff --git a/packages/backend/src/server/api/endpoints/notes/delete.ts b/packages/backend/src/server/api/endpoints/notes/delete.ts index 3c6e7bf768..16c4c01387 100644 --- a/packages/backend/src/server/api/endpoints/notes/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/delete.ts @@ -4,8 +4,9 @@ import type { UsersRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteDeleteService } from '@/core/NoteDeleteService.js'; import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../error.js'; import { GetterService } from '@/server/api/GetterService.js'; +import { RoleService } from '@/core/RoleService.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['notes'], @@ -51,6 +52,7 @@ export default class extends Endpoint { private usersRepository: UsersRepository, private getterService: GetterService, + private roleService: RoleService, private noteDeleteService: NoteDeleteService, ) { super(meta, paramDef, async (ps, me) => { @@ -59,7 +61,7 @@ export default class extends Endpoint { throw err; }); - if ((!me.isAdmin && !me.isModerator) && (note.userId !== me.id)) { + if (!await this.roleService.isModerator(me) && (note.userId !== me.id)) { throw new ApiError(meta.errors.accessDenied); } 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 b6eaccb5ac..081563493d 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -6,6 +6,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { MetaService } from '@/core/MetaService.js'; import ActiveUsersChart from '@/core/chart/charts/active-users.js'; import { DI } from '@/di-symbols.js'; +import { RoleService } from '@/core/RoleService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -57,14 +58,13 @@ export default class extends Endpoint { private noteEntityService: NoteEntityService, private queryService: QueryService, private metaService: MetaService, + private roleService: RoleService, private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { - const m = await this.metaService.fetch(); - if (m.disableGlobalTimeline) { - if (me == null || (!me.isAdmin && !me.isModerator)) { - throw new ApiError(meta.errors.gtlDisabled); - } + const role = await this.roleService.getUserRoleOptions(me ? me.id : null); + if (!role.gtlAvailable) { + throw new ApiError(meta.errors.gtlDisabled); } //#region Construct query 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 58bbf223a1..b2c504448e 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -7,6 +7,7 @@ import ActiveUsersChart from '@/core/chart/charts/active-users.js'; import { MetaService } from '@/core/MetaService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; +import { RoleService } from '@/core/RoleService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -66,11 +67,12 @@ export default class extends Endpoint { private noteEntityService: NoteEntityService, private queryService: QueryService, private metaService: MetaService, + private roleService: RoleService, private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { - const m = await this.metaService.fetch(); - if (m.disableLocalTimeline && (!me.isAdmin && !me.isModerator)) { + const role = await this.roleService.getUserRoleOptions(me.id); + if (!role.ltlAvailable) { 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 d3594814b0..6361edc310 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -7,6 +7,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { MetaService } from '@/core/MetaService.js'; import ActiveUsersChart from '@/core/chart/charts/active-users.js'; import { DI } from '@/di-symbols.js'; +import { RoleService } from '@/core/RoleService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -62,14 +63,13 @@ export default class extends Endpoint { private noteEntityService: NoteEntityService, private queryService: QueryService, private metaService: MetaService, + private roleService: RoleService, private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { - const m = await this.metaService.fetch(); - if (m.disableLocalTimeline) { - if (me == null || (!me.isAdmin && !me.isModerator)) { - throw new ApiError(meta.errors.ltlDisabled); - } + const role = await this.roleService.getUserRoleOptions(me ? me.id : null); + if (!role.ltlAvailable) { + throw new ApiError(meta.errors.ltlDisabled); } //#region Construct query diff --git a/packages/backend/src/server/api/endpoints/users.ts b/packages/backend/src/server/api/endpoints/users.ts index b015129a7a..8becb68a34 100644 --- a/packages/backend/src/server/api/endpoints/users.ts +++ b/packages/backend/src/server/api/endpoints/users.ts @@ -27,7 +27,7 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, offset: { type: 'integer', default: 0 }, sort: { type: 'string', enum: ['+follower', '-follower', '+createdAt', '-createdAt', '+updatedAt', '-updatedAt'] }, - state: { type: 'string', enum: ['all', 'admin', 'moderator', 'adminOrModerator', 'alive'], default: 'all' }, + state: { type: 'string', enum: ['all', 'alive'], default: 'all' }, origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: 'local' }, hostname: { type: 'string', @@ -54,9 +54,6 @@ export default class extends Endpoint { 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; } 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 13badab727..d19d4007d6 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -7,8 +7,9 @@ import { GlobalEventService } from '@/core/GlobalEventService.js'; import { MetaService } from '@/core/MetaService.js'; import { EmailService } from '@/core/EmailService.js'; import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../error.js'; import { GetterService } from '@/server/api/GetterService.js'; +import { RoleService } from '@/core/RoleService.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['users'], @@ -61,6 +62,7 @@ export default class extends Endpoint { private metaService: MetaService, private emailService: EmailService, private getterService: GetterService, + private roleService: RoleService, private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { @@ -74,7 +76,7 @@ export default class extends Endpoint { throw new ApiError(meta.errors.cannotReportYourself); } - if (user.isAdmin) { + if (await this.roleService.isAdministrator(user)) { throw new ApiError(meta.errors.cannotReportAdmin); } @@ -90,13 +92,7 @@ export default class extends Endpoint { // Publish event to moderators setImmediate(async () => { - const moderators = await this.usersRepository.find({ - where: [{ - isAdmin: true, - }, { - isModerator: true, - }], - }); + const moderators = await this.roleService.getModerators(); for (const moderator of moderators) { this.globalEventService.publishAdminStream(moderator.id, 'newAbuseUserReport', { diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts index fcdaeae1c9..70258ef009 100644 --- a/packages/backend/src/server/api/endpoints/users/show.ts +++ b/packages/backend/src/server/api/endpoints/users/show.ts @@ -7,6 +7,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js'; import { DI } from '@/di-symbols.js'; import PerUserPvChart from '@/core/chart/charts/per-user-pv.js'; +import { RoleService } from '@/core/RoleService.js'; import { ApiError } from '../../error.js'; import { ApiLoggerService } from '../../ApiLoggerService.js'; import type { FindOptionsWhere } from 'typeorm'; @@ -91,20 +92,21 @@ export default class extends Endpoint { private userEntityService: UserEntityService, private remoteUserResolveService: RemoteUserResolveService, + private roleService: RoleService, private perUserPvChart: PerUserPvChart, private apiLoggerService: ApiLoggerService, ) { super(meta, paramDef, async (ps, me, _1, _2, _3, ip) => { let user; - const isAdminOrModerator = me && (me.isAdmin || me.isModerator); + const isModerator = await this.roleService.isModerator(me); if (ps.userIds) { if (ps.userIds.length === 0) { return []; } - const users = await this.usersRepository.findBy(isAdminOrModerator ? { + const users = await this.usersRepository.findBy(isModerator ? { id: In(ps.userIds), } : { id: In(ps.userIds), @@ -135,7 +137,7 @@ export default class extends Endpoint { user = await this.usersRepository.findOneBy(q); } - if (user == null || (!isAdminOrModerator && user.isSuspended)) { + if (user == null || (!isModerator && user.isSuspended)) { throw new ApiError(meta.errors.noSuchUser); } 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 34f782e580..185c813869 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -7,6 +7,7 @@ import type { Packed } from '@/misc/schema.js'; import { MetaService } from '@/core/MetaService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; +import { RoleService } from '@/core/RoleService.js'; import Channel from '../channel.js'; class GlobalTimelineChannel extends Channel { @@ -16,6 +17,7 @@ class GlobalTimelineChannel extends Channel { constructor( private metaService: MetaService, + private roleService: RoleService, private noteEntityService: NoteEntityService, id: string, @@ -27,10 +29,8 @@ class GlobalTimelineChannel extends Channel { @bindThis public async init(params: any) { - const meta = await this.metaService.fetch(); - if (meta.disableGlobalTimeline) { - if (this.user == null || (!this.user.isAdmin && !this.user.isModerator)) return; - } + const role = await this.roleService.getUserRoleOptions(this.user ? this.user.id : null); + if (!role.gtlAvailable) return; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -95,6 +95,7 @@ export class GlobalTimelineChannelService { constructor( private metaService: MetaService, + private roleService: RoleService, private noteEntityService: NoteEntityService, ) { } @@ -103,6 +104,7 @@ export class GlobalTimelineChannelService { public create(id: string, connection: Channel['connection']): GlobalTimelineChannel { return new GlobalTimelineChannel( this.metaService, + this.roleService, 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 6c6afb12bf..a0f75f202c 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -8,6 +8,7 @@ import { DI } from '@/di-symbols.js'; import { MetaService } from '@/core/MetaService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; +import { RoleService } from '@/core/RoleService.js'; import Channel from '../channel.js'; class HybridTimelineChannel extends Channel { @@ -17,6 +18,7 @@ class HybridTimelineChannel extends Channel { constructor( private metaService: MetaService, + private roleService: RoleService, private noteEntityService: NoteEntityService, id: string, @@ -28,8 +30,8 @@ class HybridTimelineChannel extends Channel { @bindThis public async init(params: any): Promise { - const meta = await this.metaService.fetch(); - if (meta.disableLocalTimeline && !this.user!.isAdmin && !this.user!.isModerator) return; + const role = await this.roleService.getUserRoleOptions(this.user ? this.user.id : null); + if (!role.ltlAvailable) return; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -112,6 +114,7 @@ export class HybridTimelineChannelService { constructor( private metaService: MetaService, + private roleService: RoleService, private noteEntityService: NoteEntityService, ) { } @@ -120,6 +123,7 @@ export class HybridTimelineChannelService { public create(id: string, connection: Channel['connection']): HybridTimelineChannel { return new HybridTimelineChannel( this.metaService, + this.roleService, 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 54388787ef..7d76f42fe7 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -6,6 +6,7 @@ import type { Packed } from '@/misc/schema.js'; import { MetaService } from '@/core/MetaService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; +import { RoleService } from '@/core/RoleService.js'; import Channel from '../channel.js'; class LocalTimelineChannel extends Channel { @@ -15,6 +16,7 @@ class LocalTimelineChannel extends Channel { constructor( private metaService: MetaService, + private roleService: RoleService, private noteEntityService: NoteEntityService, id: string, @@ -26,10 +28,8 @@ class LocalTimelineChannel extends Channel { @bindThis public async init(params: any) { - const meta = await this.metaService.fetch(); - if (meta.disableLocalTimeline) { - if (this.user == null || (!this.user.isAdmin && !this.user.isModerator)) return; - } + const role = await this.roleService.getUserRoleOptions(this.user ? this.user.id : null); + if (!role.ltlAvailable) return; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -92,6 +92,7 @@ export class LocalTimelineChannelService { constructor( private metaService: MetaService, + private roleService: RoleService, private noteEntityService: NoteEntityService, ) { } @@ -100,6 +101,7 @@ export class LocalTimelineChannelService { public create(id: string, connection: Channel['connection']): LocalTimelineChannel { return new LocalTimelineChannel( this.metaService, + this.roleService, this.noteEntityService, id, connection, diff --git a/packages/backend/src/server/api/stream/types.ts b/packages/backend/src/server/api/stream/types.ts index ec05be56ee..3bc844f949 100644 --- a/packages/backend/src/server/api/stream/types.ts +++ b/packages/backend/src/server/api/stream/types.ts @@ -14,23 +14,33 @@ 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 { Meta } from '@/models/entities/Meta.js'; +import { Role, RoleAssignment } from '@/models'; import type Emitter from 'strict-event-emitter-types'; import type { EventEmitter } from 'events'; +// redis通すとDateのインスタンスはstringに変換されるので +type Serialized = { + [K in keyof T]: T[K] extends Date ? string : T[K]; +}; + //#region Stream type-body definitions export interface InternalStreamTypes { - userChangeSuspendedState: { id: User['id']; isSuspended: User['isSuspended']; }; - userChangeSilencedState: { id: User['id']; isSilenced: User['isSilenced']; }; - userChangeModeratorState: { id: User['id']; isModerator: User['isModerator']; }; - userTokenRegenerated: { id: User['id']; oldToken: User['token']; newToken: User['token']; }; - remoteUserUpdated: { id: User['id']; }; - webhookCreated: Webhook; - webhookDeleted: Webhook; - webhookUpdated: Webhook; - antennaCreated: Antenna; - antennaDeleted: Antenna; - antennaUpdated: Antenna; - metaUpdated: Meta, + userChangeSuspendedState: Serialized<{ id: User['id']; isSuspended: User['isSuspended']; }>; + userTokenRegenerated: Serialized<{ id: User['id']; oldToken: User['token']; newToken: User['token']; }>; + remoteUserUpdated: Serialized<{ id: User['id']; }>; + defaultRoleOverrideUpdated: Serialized; + roleCreated: Serialized; + roleDeleted: Serialized; + roleUpdated: Serialized; + userRoleAssigned: Serialized; + userRoleUnassigned: Serialized; + webhookCreated: Serialized; + webhookDeleted: Serialized; + webhookUpdated: Serialized; + antennaCreated: Serialized; + antennaDeleted: Serialized; + antennaUpdated: Serialized; + metaUpdated: Serialized; } export interface BroadcastTypes { diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 5c29224019..2a764a25b0 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -29,6 +29,7 @@ import type { ChannelsRepository, ClipsRepository, EmojisRepository, FlashsRepos import { deepClone } from '@/misc/clone.js'; import { bindThis } from '@/decorators.js'; import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; +import { RoleService } from '@/core/RoleService.js'; import manifest from './manifest.json' assert { type: 'json' }; import { FeedService } from './FeedService.js'; import { UrlPreviewService } from './UrlPreviewService.js'; @@ -83,6 +84,7 @@ export class ClientServerService { private metaService: MetaService, private urlPreviewService: UrlPreviewService, private feedService: FeedService, + private roleService: RoleService, @Inject('queue:system') public systemQueue: SystemQueue, @Inject('queue:endedPollNotification') public endedPollNotificationQueue: EndedPollNotificationQueue, @@ -125,7 +127,12 @@ export class ClientServerService { throw new Error('login required'); } const user = await this.usersRepository.findOneBy({ token }); - if (user == null || !(user.isAdmin || user.isModerator)) { + if (user == null) { + reply.code(403); + throw new Error('no such user'); + } + const isAdministrator = await this.roleService.isAdministrator(user); + if (!isAdministrator) { reply.code(403); throw new Error('access denied'); } diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js index 86df3308ec..e2fc27fecd 100644 --- a/packages/backend/src/server/web/boot.js +++ b/packages/backend/src/server/web/boot.js @@ -301,6 +301,10 @@ const meta = await res.json(); + if (meta.version == null) { + throw new Error('failed to fetch instance metadata'); + } + if (meta.version != v) { localStorage.setItem('v', meta.version); refresh(); diff --git a/packages/frontend/src/components/MkRolePreview.vue b/packages/frontend/src/components/MkRolePreview.vue new file mode 100644 index 0000000000..ddd7dbb250 --- /dev/null +++ b/packages/frontend/src/components/MkRolePreview.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/packages/frontend/src/components/MkUserCardMini.vue b/packages/frontend/src/components/MkUserCardMini.vue index 1a4c494987..be8a4c408e 100644 --- a/packages/frontend/src/components/MkUserCardMini.vue +++ b/packages/frontend/src/components/MkUserCardMini.vue @@ -1,5 +1,5 @@ + - - - - - - - - - + + +
@@ -180,12 +181,16 @@ import { definePageMetadata } from '@/scripts/page-metadata'; import { i18n } from '@/i18n'; import { iAmAdmin, iAmModerator } from '@/account'; import { instance } from '@/instance'; +import MkRolePreview from '@/components/MkRolePreview.vue'; -const props = defineProps<{ +const props = withDefaults(defineProps<{ userId: string; -}>(); + initialTab?: string; +}>(), { + initialTab: 'overview', +}); -let tab = $ref('overview'); +let tab = $ref(props.initialTab); let chartSrc = $ref('per-user-notes'); let user = $ref(); let init = $ref>(); @@ -195,7 +200,6 @@ let ap = $ref(null); let moderator = $ref(false); let silenced = $ref(false); let suspended = $ref(false); -let driveCapacityOverrideMb: number | null = $ref(0); let moderationNote = $ref(''); const filesPagination = { endpoint: 'admin/drive/files' as const, @@ -220,7 +224,6 @@ function createFetcher() { moderator = info.isModerator; silenced = info.isSilenced; suspended = info.isSuspended; - driveCapacityOverrideMb = user.driveCapacityOverrideMb; moderationNote = info.moderationNote; watch($$(moderationNote), async () => { @@ -257,19 +260,6 @@ async function resetPassword() { }); } -async function toggleSilence(v) { - const confirm = await os.confirm({ - type: 'warning', - text: v ? i18n.ts.silenceConfirm : i18n.ts.unsilenceConfirm, - }); - if (confirm.canceled) { - silenced = !v; - } else { - await os.api(v ? 'admin/silence-user' : 'admin/unsilence-user', { userId: user.id }); - await refreshUser(); - } -} - async function toggleSuspend(v) { const confirm = await os.confirm({ type: 'warning', @@ -283,11 +273,6 @@ async function toggleSuspend(v) { } } -async function toggleModerator(v) { - await os.api(v ? 'admin/moderators/add' : 'admin/moderators/remove', { userId: user.id }); - await refreshUser(); -} - async function deleteAllFiles() { const confirm = await os.confirm({ type: 'warning', @@ -307,22 +292,6 @@ async function deleteAllFiles() { await refreshUser(); } -async function applyDriveCapacityOverride() { - let driveCapOrMb = driveCapacityOverrideMb; - if (driveCapacityOverrideMb && driveCapacityOverrideMb < 0) { - driveCapOrMb = null; - } - try { - await os.apiWithDialog('admin/drive-capacity-override', { userId: user.id, overrideMb: driveCapOrMb }); - await refreshUser(); - } catch (err) { - os.alert({ - type: 'error', - text: err.toString(), - }); - } -} - async function deleteAccount() { const confirm = await os.confirm({ type: 'warning', @@ -347,6 +316,31 @@ async function deleteAccount() { } } +async function assignRole() { + const roles = await os.api('admin/roles/list'); + + const { canceled, result: roleId } = await os.select({ + title: i18n.ts._role.chooseRoleToAssign, + items: roles.map(r => ({ text: r.name, value: r.id })), + }); + if (canceled) return; + + await os.apiWithDialog('admin/roles/assign', { roleId, userId: user.id }); + refreshUser(); +} + +async function unassignRole(role, ev) { + os.popupMenu([{ + text: i18n.ts.unassign, + icon: 'ti ti-x', + danger: true, + action: async () => { + await os.apiWithDialog('admin/roles/unassign', { roleId: role.id, userId: user.id }); + refreshUser(); + }, + }], ev.currentTarget ?? ev.target); +} + watch(() => props.userId, () => { init = createFetcher(); }, { @@ -484,4 +478,19 @@ definePageMetadata(computed(() => ({ margin-left: auto; } } + +.roleItem { + display: flex; +} + +.role { + flex: 1; +} + +.roleUnassign { + width: 32px; + height: 32px; + margin-left: 8px; + align-self: center; +} diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 07d34a794d..eea4d20094 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -18,7 +18,6 @@
-
@@ -35,7 +34,6 @@
-
@@ -189,7 +187,7 @@ onMounted(() => { const bd = parseInt(props.user.birthday.split('-')[2]); if (m === bm && d === bd) { confetti({ - duration: 1000 * 4 + duration: 1000 * 4, }); } } diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts index 4b9f49f8fd..05dcd7806e 100644 --- a/packages/frontend/src/router.ts +++ b/packages/frontend/src/router.ts @@ -37,6 +37,7 @@ export const routes = [{ }, { path: '/user-info/:userId', component: page(() => import('./pages/user-info.vue')), + hash: 'initialTab', }, { path: '/instance-info/:host', component: page(() => import('./pages/instance-info.vue')), @@ -351,6 +352,22 @@ export const routes = [{ path: '/ads', name: 'ads', component: page(() => import('./pages/admin/ads.vue')), + }, { + path: '/roles/:id/edit', + name: 'roles', + component: page(() => import('./pages/admin/roles.edit.vue')), + }, { + path: '/roles/new', + name: 'roles', + component: page(() => import('./pages/admin/roles.edit.vue')), + }, { + path: '/roles/:id', + name: 'roles', + component: page(() => import('./pages/admin/roles.role.vue')), + }, { + path: '/roles', + name: 'roles', + component: page(() => import('./pages/admin/roles.vue')), }, { path: '/database', name: 'database', diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts index 7ede64c327..74bd61fd78 100644 --- a/packages/frontend/src/scripts/get-user-menu.ts +++ b/packages/frontend/src/scripts/get-user-menu.ts @@ -108,26 +108,6 @@ export function getUserMenu(user, router: Router = mainRouter) { }); } - async function toggleSilence() { - if (!await getConfirmed(i18n.t(user.isSilenced ? 'unsilenceConfirm' : 'silenceConfirm'))) return; - - os.apiWithDialog(user.isSilenced ? 'admin/unsilence-user' : 'admin/silence-user', { - userId: user.id, - }).then(() => { - user.isSilenced = !user.isSilenced; - }); - } - - async function toggleSuspend() { - if (!await getConfirmed(i18n.t(user.isSuspended ? 'unsuspendConfirm' : 'suspendConfirm'))) return; - - os.apiWithDialog(user.isSuspended ? 'admin/unsuspend-user' : 'admin/suspend-user', { - userId: user.id, - }).then(() => { - user.isSuspended = !user.isSuspended; - }); - } - function reportAbuse() { os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), { user: user, @@ -218,13 +198,11 @@ export function getUserMenu(user, router: Router = mainRouter) { if (iAmModerator) { menu = menu.concat([null, { - icon: 'ti ti-microphone-2-off', - text: user.isSilenced ? i18n.ts.unsilence : i18n.ts.silence, - action: toggleSilence, - }, { - icon: 'ti ti-snowflake', - text: user.isSuspended ? i18n.ts.unsuspend : i18n.ts.suspend, - action: toggleSuspend, + icon: 'ti ti-user-exclamation', + text: i18n.ts.moderation, + action: () => { + router.push('/user-info/' + user.id + '#moderation'); + }, }]); } } diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue index f75e526939..b8a0a504a3 100644 --- a/packages/frontend/src/ui/deck/tl-column.vue +++ b/packages/frontend/src/ui/deck/tl-column.vue @@ -45,9 +45,7 @@ onMounted(() => { if (props.column.tl == null) { setType(); } else if ($i) { - disabled = !$i.isModerator && !$i.isAdmin && ( - instance.disableLocalTimeline && ['local', 'social'].includes(props.column.tl) || - instance.disableGlobalTimeline && ['global'].includes(props.column.tl)); + disabled = false; // TODO } }); -- cgit v1.2.3-freya From ba349fc62fe9383b8de2bf7d6850fd6e767eb37d Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 12 Jan 2023 16:33:00 +0000 Subject: Fix #9534 --- packages/backend/src/core/NoteCreateService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/backend/src/core/NoteCreateService.ts') diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 1c2add5d64..112b84fdf9 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -226,7 +226,7 @@ export class NoteCreateService { if (data.channel != null) data.localOnly = true; if (data.visibility === 'public' && data.channel == null) { - if ((await this.roleService.getUserRoleOptions(user.id)).canPublicNote) { + if ((await this.roleService.getUserRoleOptions(user.id)).canPublicNote === false) { data.visibility = 'home'; } } -- cgit v1.2.3-freya From b2117ba3a18addea9f94fdda92029a6c0263cdab Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 12 Jan 2023 16:53:55 +0000 Subject: リモートユーザーはcanPublicNoteがfalseでもhomeにしないように https://github.com/misskey-dev/misskey/issues/9534#issuecomment-1380645073 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/NoteCreateService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/backend/src/core/NoteCreateService.ts') diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 112b84fdf9..f460a6f4ab 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -225,7 +225,7 @@ export class NoteCreateService { if (data.channel != null) data.visibleUsers = []; if (data.channel != null) data.localOnly = true; - if (data.visibility === 'public' && data.channel == null) { + if (data.visibility === 'public' && this.userEntityService.isLocalUser(user) && data.channel == null) { if ((await this.roleService.getUserRoleOptions(user.id)).canPublicNote === false) { data.visibility = 'home'; } -- cgit v1.2.3-freya From 4e39e690b6b6eda436a1af2af16a6d36f73a6d33 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 13 Jan 2023 11:14:23 +0900 Subject: Revert "リモートユーザーはcanPublicNoteがfalseでもhomeにしないように" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit b2117ba3a18addea9f94fdda92029a6c0263cdab. --- packages/backend/src/core/NoteCreateService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/backend/src/core/NoteCreateService.ts') diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index f460a6f4ab..112b84fdf9 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -225,7 +225,7 @@ export class NoteCreateService { if (data.channel != null) data.visibleUsers = []; if (data.channel != null) data.localOnly = true; - if (data.visibility === 'public' && this.userEntityService.isLocalUser(user) && data.channel == null) { + if (data.visibility === 'public' && data.channel == null) { if ((await this.roleService.getUserRoleOptions(user.id)).canPublicNote === false) { data.visibility = 'home'; } -- cgit v1.2.3-freya From 81f11d8f860803cf01fbb2cfd106bd3344db98f2 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 15 Jan 2023 20:52:53 +0900 Subject: refactor: rename role.options -> role.policies --- locales/ja-JP.yml | 1 + .../backend/migration/1673783015567-Policies.js | 13 ++ packages/backend/src/core/DriveService.ts | 4 +- packages/backend/src/core/NoteCreateService.ts | 2 +- packages/backend/src/core/NotePiningService.ts | 2 +- packages/backend/src/core/RoleService.ts | 26 ++-- packages/backend/src/core/UserListService.ts | 2 +- .../backend/src/core/entities/RoleEntityService.ts | 10 +- .../backend/src/core/entities/UserEntityService.ts | 4 +- packages/backend/src/models/entities/Meta.ts | 2 +- packages/backend/src/models/entities/Role.ts | 2 +- .../backend/src/server/NodeinfoServerService.ts | 8 +- packages/backend/src/server/api/ApiCallService.ts | 8 +- packages/backend/src/server/api/EndpointsModule.ts | 8 +- packages/backend/src/server/api/endpoints.ts | 6 +- .../api/endpoints/admin/emoji/add-aliases-bulk.ts | 2 +- .../src/server/api/endpoints/admin/emoji/add.ts | 2 +- .../src/server/api/endpoints/admin/emoji/copy.ts | 2 +- .../api/endpoints/admin/emoji/delete-bulk.ts | 2 +- .../src/server/api/endpoints/admin/emoji/delete.ts | 2 +- .../server/api/endpoints/admin/emoji/import-zip.ts | 2 +- .../api/endpoints/admin/emoji/list-remote.ts | 2 +- .../src/server/api/endpoints/admin/emoji/list.ts | 2 +- .../endpoints/admin/emoji/remove-aliases-bulk.ts | 2 +- .../api/endpoints/admin/emoji/set-aliases-bulk.ts | 2 +- .../api/endpoints/admin/emoji/set-category-bulk.ts | 2 +- .../src/server/api/endpoints/admin/emoji/update.ts | 2 +- .../backend/src/server/api/endpoints/admin/meta.ts | 4 +- .../src/server/api/endpoints/admin/roles/create.ts | 6 +- .../admin/roles/update-default-policies.ts | 42 ++++++ .../admin/roles/update-default-role-override.ts | 42 ------ .../src/server/api/endpoints/admin/roles/update.ts | 6 +- .../src/server/api/endpoints/admin/show-user.ts | 3 +- .../src/server/api/endpoints/antennas/create.ts | 2 +- .../src/server/api/endpoints/clips/add-note.ts | 2 +- .../src/server/api/endpoints/clips/create.ts | 2 +- packages/backend/src/server/api/endpoints/drive.ts | 4 +- .../backend/src/server/api/endpoints/i/update.ts | 2 +- .../src/server/api/endpoints/i/webhooks/create.ts | 2 +- .../backend/src/server/api/endpoints/invite.ts | 2 +- packages/backend/src/server/api/endpoints/meta.ts | 4 +- .../server/api/endpoints/notes/global-timeline.ts | 4 +- .../server/api/endpoints/notes/hybrid-timeline.ts | 4 +- .../server/api/endpoints/notes/local-timeline.ts | 4 +- .../src/server/api/endpoints/users/lists/create.ts | 2 +- .../server/api/stream/channels/global-timeline.ts | 4 +- .../server/api/stream/channels/hybrid-timeline.ts | 4 +- .../server/api/stream/channels/local-timeline.ts | 4 +- packages/backend/src/server/api/stream/types.ts | 2 +- packages/frontend/src/pages/admin/roles.editor.vue | 142 ++++++++++----------- packages/frontend/src/pages/admin/roles.vue | 119 ++++++++--------- packages/frontend/src/pages/timeline.vue | 4 +- packages/frontend/src/pages/user-info.vue | 20 ++- packages/frontend/src/ui/_common_/common.ts | 4 +- 54 files changed, 292 insertions(+), 270 deletions(-) create mode 100644 packages/backend/migration/1673783015567-Policies.js create mode 100644 packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts delete mode 100644 packages/backend/src/server/api/endpoints/admin/roles/update-default-role-override.ts (limited to 'packages/backend/src/core/NoteCreateService.ts') diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 79856447a0..786d41ca41 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -952,6 +952,7 @@ _role: isPublic: "ロールを公開" descriptionOfIsPublic: "ロールにアサインされたユーザーを誰でも見ることができます。また、ユーザーのプロフィールでこのロールが表示されます。" options: "オプション" + policies: "ポリシー" baseRole: "ベースロール" useBaseValue: "ベースロールの値を使用" chooseRoleToAssign: "アサインするロールを選択" diff --git a/packages/backend/migration/1673783015567-Policies.js b/packages/backend/migration/1673783015567-Policies.js new file mode 100644 index 0000000000..8b36921d41 --- /dev/null +++ b/packages/backend/migration/1673783015567-Policies.js @@ -0,0 +1,13 @@ +export class Policies1673783015567 { + name = 'Policies1673783015567' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "role" RENAME COLUMN "options" TO "policies"`); + await queryRunner.query(`ALTER TABLE "meta" RENAME COLUMN "defaultRoleOverride" TO "policies"`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" RENAME COLUMN "policies" TO "defaultRoleOverride"`); + await queryRunner.query(`ALTER TABLE "role" RENAME COLUMN "policies" TO "options"`); + } +} diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts index 9002c96a65..598a457e83 100644 --- a/packages/backend/src/core/DriveService.ts +++ b/packages/backend/src/core/DriveService.ts @@ -479,8 +479,8 @@ export class DriveService { if (user && !isLink) { const usage = await this.driveFileEntityService.calcDriveUsageOf(user); - const role = await this.roleService.getUserRoleOptions(user.id); - const driveCapacity = 1024 * 1024 * role.driveCapacityMb; + const policies = await this.roleService.getUserPolicies(user.id); + const driveCapacity = 1024 * 1024 * policies.driveCapacityMb; this.registerLogger.debug('drive capacity override applied'); this.registerLogger.debug(`overrideCap: ${driveCapacity}bytes, usage: ${usage}bytes, u+s: ${usage + info.size}bytes`); diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 112b84fdf9..3dc44a25fe 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -226,7 +226,7 @@ export class NoteCreateService { if (data.channel != null) data.localOnly = true; if (data.visibility === 'public' && data.channel == null) { - if ((await this.roleService.getUserRoleOptions(user.id)).canPublicNote === false) { + if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) { data.visibility = 'home'; } } diff --git a/packages/backend/src/core/NotePiningService.ts b/packages/backend/src/core/NotePiningService.ts index bc038e17a7..bb6def1edb 100644 --- a/packages/backend/src/core/NotePiningService.ts +++ b/packages/backend/src/core/NotePiningService.ts @@ -57,7 +57,7 @@ export class NotePiningService { const pinings = await this.userNotePiningsRepository.findBy({ userId: user.id }); - if (pinings.length >= (await this.roleService.getUserRoleOptions(user.id)).pinLimit) { + if (pinings.length >= (await this.roleService.getUserPolicies(user.id)).pinLimit) { throw new IdentifiableError('15a018eb-58e5-4da1-93be-330fcc5e4e1a', 'You can not pin notes any more.'); } diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 42b477d9ed..abad058303 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -13,7 +13,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { StreamMessages } from '@/server/api/stream/types.js'; import type { OnApplicationShutdown } from '@nestjs/common'; -export type RoleOptions = { +export type RolePolicies = { gtlAvailable: boolean; ltlAvailable: boolean; canPublicNote: boolean; @@ -31,7 +31,7 @@ export type RoleOptions = { rateLimitFactor: number; }; -export const DEFAULT_ROLE: RoleOptions = { +export const DEFAULT_POLICIES: RolePolicies = { gtlAvailable: true, ltlAvailable: true, canPublicNote: true, @@ -195,26 +195,26 @@ export class RoleService implements OnApplicationShutdown { } @bindThis - public async getUserRoleOptions(userId: User['id'] | null): Promise { + public async getUserPolicies(userId: User['id'] | null): Promise { const meta = await this.metaService.fetch(); - const baseRoleOptions = { ...DEFAULT_ROLE, ...meta.defaultRoleOverride }; + const basePolicies = { ...DEFAULT_POLICIES, ...meta.policies }; - if (userId == null) return baseRoleOptions; + if (userId == null) return basePolicies; const roles = await this.getUserRoles(userId); - function calc(name: T, aggregate: (values: RoleOptions[T][]) => RoleOptions[T]) { - if (roles.length === 0) return baseRoleOptions[name]; + function calc(name: T, aggregate: (values: RolePolicies[T][]) => RolePolicies[T]) { + if (roles.length === 0) return basePolicies[name]; - const options = roles.map(role => role.options[name] ?? { priority: 0, useDefault: true }); + const policies = roles.map(role => role.policies[name] ?? { priority: 0, useDefault: true }); - const p2 = options.filter(option => option.priority === 2); - if (p2.length > 0) return aggregate(p2.map(option => option.useDefault ? baseRoleOptions[name] : option.value)); + const p2 = policies.filter(policy => policy.priority === 2); + if (p2.length > 0) return aggregate(p2.map(policy => policy.useDefault ? basePolicies[name] : policy.value)); - const p1 = options.filter(option => option.priority === 1); - if (p1.length > 0) return aggregate(p2.map(option => option.useDefault ? baseRoleOptions[name] : option.value)); + const p1 = policies.filter(policy => policy.priority === 1); + if (p1.length > 0) return aggregate(p2.map(policy => policy.useDefault ? basePolicies[name] : policy.value)); - return aggregate(options.map(option => option.useDefault ? baseRoleOptions[name] : option.value)); + return aggregate(policies.map(policy => policy.useDefault ? basePolicies[name] : policy.value)); } return { diff --git a/packages/backend/src/core/UserListService.ts b/packages/backend/src/core/UserListService.ts index 18c9787fa8..fc48738307 100644 --- a/packages/backend/src/core/UserListService.ts +++ b/packages/backend/src/core/UserListService.ts @@ -35,7 +35,7 @@ export class UserListService { const currentCount = await this.userListJoiningsRepository.countBy({ userListId: list.id, }); - if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).userEachUserListsLimit) { + if (currentCount > (await this.roleService.getUserPolicies(me.id)).userEachUserListsLimit) { throw new Error('Too many users'); } diff --git a/packages/backend/src/core/entities/RoleEntityService.ts b/packages/backend/src/core/entities/RoleEntityService.ts index 6a14775653..52f3374468 100644 --- a/packages/backend/src/core/entities/RoleEntityService.ts +++ b/packages/backend/src/core/entities/RoleEntityService.ts @@ -6,7 +6,7 @@ import type { Packed } from '@/misc/schema.js'; import type { User } from '@/models/entities/User.js'; import type { Role } from '@/models/entities/Role.js'; import { bindThis } from '@/decorators.js'; -import { DEFAULT_ROLE } from '@/core/RoleService.js'; +import { DEFAULT_POLICIES } from '@/core/RoleService.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() @@ -40,9 +40,9 @@ export class RoleEntityService { roleId: role.id, }); - const roleOptions = { ...role.options }; - for (const [k, v] of Object.entries(DEFAULT_ROLE)) { - if (roleOptions[k] == null) roleOptions[k] = { + const policies = { ...role.policies }; + for (const [k, v] of Object.entries(DEFAULT_POLICIES)) { + if (policies[k] == null) policies[k] = { useDefault: true, priority: 0, value: v, @@ -62,7 +62,7 @@ export class RoleEntityService { isAdministrator: role.isAdministrator, isModerator: role.isModerator, canEditMembersByModerator: role.canEditMembersByModerator, - options: roleOptions, + policies: policies, usersCount: assigns.length, ...(opts.detail ? { users: this.userEntityService.packMany(assigns.map(x => x.userId), me), diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 6b754150cf..bf6f6f4553 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -423,7 +423,7 @@ export class UserEntityService implements OnModuleInit { bannerUrl: user.banner ? this.driveFileEntityService.getPublicUrl(user.banner, false) : null, bannerBlurhash: user.banner?.blurhash ?? null, isLocked: user.isLocked, - isSilenced: this.roleService.getUserRoleOptions(user.id).then(r => !r.canPublicNote), + isSilenced: this.roleService.getUserPolicies(user.id).then(r => !r.canPublicNote), isSuspended: user.isSuspended ?? falsy, description: profile!.description, location: profile!.location, @@ -496,7 +496,7 @@ export class UserEntityService implements OnModuleInit { } : {}), ...(opts.includeSecrets ? { - role: this.roleService.getUserRoleOptions(user.id), + policies: this.roleService.getUserPolicies(user.id), email: profile!.email, emailVerified: profile!.emailVerified, securityKeysList: profile!.twoFactorEnabled diff --git a/packages/backend/src/models/entities/Meta.ts b/packages/backend/src/models/entities/Meta.ts index e724ba9a49..5d222a6da1 100644 --- a/packages/backend/src/models/entities/Meta.ts +++ b/packages/backend/src/models/entities/Meta.ts @@ -458,5 +458,5 @@ export class Meta { @Column('jsonb', { default: { }, }) - public defaultRoleOverride: Record; + public policies: Record; } diff --git a/packages/backend/src/models/entities/Role.ts b/packages/backend/src/models/entities/Role.ts index d8d203493b..abd5f864a2 100644 --- a/packages/backend/src/models/entities/Role.ts +++ b/packages/backend/src/models/entities/Role.ts @@ -136,7 +136,7 @@ export class Role { @Column('jsonb', { default: { }, }) - public options: Record null) : null; - const baseRoleOptions = { ...DEFAULT_ROLE, ...meta.defaultRoleOverride }; + const basePolicies = { ...DEFAULT_POLICIES, ...meta.policies }; return { software: { @@ -105,8 +105,8 @@ export class NodeinfoServerService { repositoryUrl: meta.repositoryUrl, feedbackUrl: meta.feedbackUrl, disableRegistration: meta.disableRegistration, - disableLocalTimeline: !baseRoleOptions.ltlAvailable, - disableGlobalTimeline: !baseRoleOptions.gtlAvailable, + disableLocalTimeline: !basePolicies.ltlAvailable, + disableGlobalTimeline: !basePolicies.gtlAvailable, emailRequiredForSignup: meta.emailRequiredForSignup, enableHcaptcha: meta.enableHcaptcha, enableRecaptcha: meta.enableRecaptcha, diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index dcc9342a82..395a1c468a 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -225,7 +225,7 @@ export class ApiCallService implements OnApplicationShutdown { } // TODO: 毎リクエスト計算するのもあれだしキャッシュしたい - const factor = user ? (await this.roleService.getUserRoleOptions(user.id)).rateLimitFactor : 1; + const factor = user ? (await this.roleService.getUserPolicies(user.id)).rateLimitFactor : 1; // Rate limit await this.rateLimiterService.limit(limit as IEndpointMeta['limit'] & { key: NonNullable }, limitActor, factor).catch(err => { @@ -274,9 +274,9 @@ export class ApiCallService implements OnApplicationShutdown { } } - if (ep.meta.requireRoleOption != null && !user!.isRoot) { - const myRole = await this.roleService.getUserRoleOptions(user!.id); - if (!myRole[ep.meta.requireRoleOption]) { + if (ep.meta.requireRolePolicy != null && !user!.isRoot) { + const policies = await this.roleService.getUserPolicies(user!.id); + if (!policies[ep.meta.requireRolePolicy]) { throw new ApiError({ message: 'You are not assigned to a required role.', code: 'ROLE_PERMISSION_DENIED', diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index aa88a9dd13..14927da7d6 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -65,7 +65,7 @@ import * as ep___admin_roles_show from './endpoints/admin/roles/show.js'; import * as ep___admin_roles_update from './endpoints/admin/roles/update.js'; import * as ep___admin_roles_assign from './endpoints/admin/roles/assign.js'; import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js'; -import * as ep___admin_roles_updateDefaultRoleOverride from './endpoints/admin/roles/update-default-role-override.js'; +import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.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'; @@ -399,7 +399,7 @@ const $admin_roles_show: Provider = { provide: 'ep:admin/roles/show', useClass: const $admin_roles_update: Provider = { provide: 'ep:admin/roles/update', useClass: ep___admin_roles_update.default }; const $admin_roles_assign: Provider = { provide: 'ep:admin/roles/assign', useClass: ep___admin_roles_assign.default }; const $admin_roles_unassign: Provider = { provide: 'ep:admin/roles/unassign', useClass: ep___admin_roles_unassign.default }; -const $admin_roles_updateDefaultRoleOverride: Provider = { provide: 'ep:admin/roles/update-default-role-override', useClass: ep___admin_roles_updateDefaultRoleOverride.default }; +const $admin_roles_updateDefaultPolicies: Provider = { provide: 'ep:admin/roles/update-default-policies', useClass: ep___admin_roles_updateDefaultPolicies.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 }; @@ -737,7 +737,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_roles_update, $admin_roles_assign, $admin_roles_unassign, - $admin_roles_updateDefaultRoleOverride, + $admin_roles_updateDefaultPolicies, $announcements, $antennas_create, $antennas_delete, @@ -1069,7 +1069,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_roles_update, $admin_roles_assign, $admin_roles_unassign, - $admin_roles_updateDefaultRoleOverride, + $admin_roles_updateDefaultPolicies, $announcements, $antennas_create, $antennas_delete, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index f50a3b5dd2..54c4206ea4 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -64,7 +64,7 @@ import * as ep___admin_roles_show from './endpoints/admin/roles/show.js'; import * as ep___admin_roles_update from './endpoints/admin/roles/update.js'; import * as ep___admin_roles_assign from './endpoints/admin/roles/assign.js'; import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js'; -import * as ep___admin_roles_updateDefaultRoleOverride from './endpoints/admin/roles/update-default-role-override.js'; +import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.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'; @@ -396,7 +396,7 @@ const eps = [ ['admin/roles/update', ep___admin_roles_update], ['admin/roles/assign', ep___admin_roles_assign], ['admin/roles/unassign', ep___admin_roles_unassign], - ['admin/roles/update-default-role-override', ep___admin_roles_updateDefaultRoleOverride], + ['admin/roles/update-default-policies', ep___admin_roles_updateDefaultPolicies], ['announcements', ep___announcements], ['antennas/create', ep___antennas_create], ['antennas/delete', ep___antennas_delete], @@ -695,7 +695,7 @@ export interface IEndpointMeta { */ readonly requireAdmin?: boolean; - readonly requireRoleOption?: string; + readonly requireRolePolicy?: string; /** * エンドポイントのリミテーションに関するやつ 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 d114fd3d55..9b6c774f0c 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 @@ -8,7 +8,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', } as const; export const paramDef = { 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 52ccb74447..abca1d169d 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -14,7 +14,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', 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 4d1fdd989d..b4fc7fd6f5 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -14,7 +14,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', errors: { noSuchEmoji: { 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 27aa4fb1b1..ae45105b28 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 @@ -9,7 +9,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', } as const; export const paramDef = { 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 2531246569..e237d87d34 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts @@ -10,7 +10,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', 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 15f468c180..b4a07324bb 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 @@ -5,7 +5,7 @@ import { QueueService } from '@/core/QueueService.js'; export const meta = { secure: true, requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', } as const; export const paramDef = { 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 131c9ef223..d9ce97194a 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 @@ -11,7 +11,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', res: { type: 'array', 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 ef2bc936c3..1a6096f36f 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts @@ -11,7 +11,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', res: { type: 'array', 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 a70cd8d787..5fc9e024bf 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 @@ -8,7 +8,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', } as const; export const paramDef = { 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 b33e5662bb..8b5ba8fbf4 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 @@ -8,7 +8,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', } as const; export const paramDef = { 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 05834bc572..827b5ace7a 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 @@ -8,7 +8,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', } as const; export const paramDef = { 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 19645cb515..fb0ef12878 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -9,7 +9,7 @@ export const meta = { tags: ['admin'], requireCredential: true, - requireRoleOption: 'canManageCustomEmojis', + requireRolePolicy: 'canManageCustomEmojis', errors: { noSuchEmoji: { diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index fd08a5f847..b393827054 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -4,7 +4,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { MetaService } from '@/core/MetaService.js'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; -import { DEFAULT_ROLE } from '@/core/RoleService.js'; +import { DEFAULT_POLICIES } from '@/core/RoleService.js'; export const meta = { tags: ['meta'], @@ -440,7 +440,7 @@ export default class extends Endpoint { deeplIsPro: instance.deeplIsPro, enableIpLogging: instance.enableIpLogging, enableActiveEmailValidation: instance.enableActiveEmailValidation, - baseRole: { ...DEFAULT_ROLE, ...instance.defaultRoleOverride }, + policies: { ...DEFAULT_POLICIES, ...instance.policies }, }; }); } diff --git a/packages/backend/src/server/api/endpoints/admin/roles/create.ts b/packages/backend/src/server/api/endpoints/admin/roles/create.ts index a9216a6386..f136c6d624 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/create.ts @@ -25,7 +25,7 @@ export const paramDef = { isModerator: { type: 'boolean' }, isAdministrator: { type: 'boolean' }, canEditMembersByModerator: { type: 'boolean' }, - options: { + policies: { type: 'object', }, }, @@ -39,7 +39,7 @@ export const paramDef = { 'isModerator', 'isAdministrator', 'canEditMembersByModerator', - 'options', + 'policies', ], } as const; @@ -70,7 +70,7 @@ export default class extends Endpoint { isAdministrator: ps.isAdministrator, isModerator: ps.isModerator, canEditMembersByModerator: ps.canEditMembersByModerator, - options: ps.options, + policies: ps.policies, }).then(x => this.rolesRepository.findOneByOrFail(x.identifiers[0])); this.globalEventService.publishInternalEvent('roleCreated', created); diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts b/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts new file mode 100644 index 0000000000..6006816bcb --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts @@ -0,0 +1,42 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { RolesRepository } from '@/models/index.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '@/server/api/error.js'; +import { MetaService } from '@/core/MetaService.js'; + +export const meta = { + tags: ['admin', 'role'], + + requireCredential: true, + requireAdmin: true, +} as const; + +export const paramDef = { + type: 'object', + properties: { + policies: { + type: 'object', + }, + }, + required: [ + 'policies', + ], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint { + constructor( + private metaService: MetaService, + private globalEventService: GlobalEventService, + ) { + super(meta, paramDef, async (ps) => { + await this.metaService.update({ + policies: ps.policies, + }); + this.globalEventService.publishInternalEvent('policiesUpdated', ps.policies); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update-default-role-override.ts b/packages/backend/src/server/api/endpoints/admin/roles/update-default-role-override.ts deleted file mode 100644 index 35da04efd2..0000000000 --- a/packages/backend/src/server/api/endpoints/admin/roles/update-default-role-override.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { RolesRepository } from '@/models/index.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { DI } from '@/di-symbols.js'; -import { ApiError } from '@/server/api/error.js'; -import { MetaService } from '@/core/MetaService.js'; - -export const meta = { - tags: ['admin', 'role'], - - requireCredential: true, - requireAdmin: true, -} as const; - -export const paramDef = { - type: 'object', - properties: { - options: { - type: 'object', - }, - }, - required: [ - 'options', - ], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint { - constructor( - private metaService: MetaService, - private globalEventService: GlobalEventService, - ) { - super(meta, paramDef, async (ps) => { - await this.metaService.update({ - defaultRoleOverride: ps.options, - }); - this.globalEventService.publishInternalEvent('defaultRoleOverrideUpdated', ps.options); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update.ts b/packages/backend/src/server/api/endpoints/admin/roles/update.ts index 4ca5124eda..fc4c3d8f11 100644 --- a/packages/backend/src/server/api/endpoints/admin/roles/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/roles/update.ts @@ -33,7 +33,7 @@ export const paramDef = { isModerator: { type: 'boolean' }, isAdministrator: { type: 'boolean' }, canEditMembersByModerator: { type: 'boolean' }, - options: { + policies: { type: 'object', }, }, @@ -48,7 +48,7 @@ export const paramDef = { 'isModerator', 'isAdministrator', 'canEditMembersByModerator', - 'options', + 'policies', ], } as const; @@ -79,7 +79,7 @@ export default class extends Endpoint { isModerator: ps.isModerator, isAdministrator: ps.isAdministrator, canEditMembersByModerator: ps.canEditMembersByModerator, - options: ps.options, + policies: ps.policies, }); const updated = await this.rolesRepository.findOneByOrFail({ id: ps.roleId }); this.globalEventService.publishInternalEvent('roleUpdated', updated); 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 3f4ec299af..94603cc91a 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -52,7 +52,7 @@ export default class extends Endpoint { } const isModerator = await this.roleService.isModerator(user); - const isSilenced = !(await this.roleService.getUserRoleOptions(user.id)).canPublicNote; + const isSilenced = !(await this.roleService.getUserPolicies(user.id)).canPublicNote; const _me = await this.usersRepository.findOneByOrFail({ id: me.id }); if (!await this.roleService.isAdministrator(_me) && await this.roleService.isAdministrator(user)) { @@ -94,6 +94,7 @@ export default class extends Endpoint { lastActiveDate: user.lastActiveDate, moderationNote: profile.moderationNote, signins, + policies: await this.roleService.getUserPolicies(user.id), roles: await this.roleEntityService.packMany(roles, me, { detail: false }), }; }); diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index 08625250c8..a1553b6a80 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -92,7 +92,7 @@ export default class extends Endpoint { const currentAntennasCount = await this.antennasRepository.countBy({ userId: me.id, }); - if (currentAntennasCount > (await this.roleService.getUserRoleOptions(me.id)).antennaLimit) { + if (currentAntennasCount > (await this.roleService.getUserPolicies(me.id)).antennaLimit) { throw new ApiError(meta.errors.tooManyAntennas); } 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 3cf096c242..f3f9c3477f 100644 --- a/packages/backend/src/server/api/endpoints/clips/add-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/add-note.ts @@ -97,7 +97,7 @@ export default class extends Endpoint { const currentCount = await this.clipNotesRepository.countBy({ clipId: clip.id, }); - if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).noteEachClipsLimit) { + if (currentCount > (await this.roleService.getUserPolicies(me.id)).noteEachClipsLimit) { throw new ApiError(meta.errors.tooManyClipNotes); } diff --git a/packages/backend/src/server/api/endpoints/clips/create.ts b/packages/backend/src/server/api/endpoints/clips/create.ts index abc0288c89..c095de702c 100644 --- a/packages/backend/src/server/api/endpoints/clips/create.ts +++ b/packages/backend/src/server/api/endpoints/clips/create.ts @@ -54,7 +54,7 @@ export default class extends Endpoint { const currentCount = await this.clipsRepository.countBy({ userId: me.id, }); - if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).clipLimit) { + if (currentCount > (await this.roleService.getUserPolicies(me.id)).clipLimit) { throw new ApiError(meta.errors.tooManyClips); } diff --git a/packages/backend/src/server/api/endpoints/drive.ts b/packages/backend/src/server/api/endpoints/drive.ts index 2a06792dcf..e5bbfecbcf 100644 --- a/packages/backend/src/server/api/endpoints/drive.ts +++ b/packages/backend/src/server/api/endpoints/drive.ts @@ -47,10 +47,10 @@ export default class extends Endpoint { // Calculate drive usage const usage = await this.driveFileEntityService.calcDriveUsageOf(me.id); - const myRole = await this.roleService.getUserRoleOptions(me.id); + const policies = await this.roleService.getUserPolicies(me.id); return { - capacity: 1024 * 1024 * myRole.driveCapacityMb, + capacity: 1024 * 1024 * policies.driveCapacityMb, usage: usage, }; }); diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index fe09eca674..b1eaab3908 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -173,7 +173,7 @@ export default class extends Endpoint { if (ps.mutedWords !== undefined) { // TODO: ちゃんと数える const length = JSON.stringify(ps.mutedWords).length; - if (length > (await this.roleService.getUserRoleOptions(user.id)).wordMuteLimit) { + if (length > (await this.roleService.getUserPolicies(user.id)).wordMuteLimit) { throw new ApiError(meta.errors.tooManyMutedWords); } 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 3d89b77a7b..51fcce6cf0 100644 --- a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts +++ b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts @@ -54,7 +54,7 @@ export default class extends Endpoint { const currentWebhooksCount = await this.webhooksRepository.countBy({ userId: me.id, }); - if (currentWebhooksCount > (await this.roleService.getUserRoleOptions(me.id)).webhookLimit) { + if (currentWebhooksCount > (await this.roleService.getUserPolicies(me.id)).webhookLimit) { throw new ApiError(meta.errors.tooManyWebhooks); } diff --git a/packages/backend/src/server/api/endpoints/invite.ts b/packages/backend/src/server/api/endpoints/invite.ts index 9b03cf4bb6..5d2c479e79 100644 --- a/packages/backend/src/server/api/endpoints/invite.ts +++ b/packages/backend/src/server/api/endpoints/invite.ts @@ -9,7 +9,7 @@ export const meta = { tags: ['meta'], requireCredential: true, - requireRoleOption: 'canInvite', + requireRolePolicy: 'canInvite', res: { type: 'object', diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index f46a32dfe7..89fa503173 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -7,7 +7,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { MetaService } from '@/core/MetaService.js'; import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; -import { DEFAULT_ROLE } from '@/core/RoleService.js'; +import { DEFAULT_POLICIES } from '@/core/RoleService.js'; export const meta = { tags: ['meta'], @@ -334,7 +334,7 @@ export default class extends Endpoint { translatorAvailable: instance.deeplAuthKey != null, - baseRole: { ...DEFAULT_ROLE, ...instance.defaultRoleOverride }, + policies: { ...DEFAULT_POLICIES, ...instance.policies }, ...(ps.detail ? { pinnedPages: instance.pinnedPages, 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 081563493d..5d0cdc3fca 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -62,8 +62,8 @@ export default class extends Endpoint { private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { - const role = await this.roleService.getUserRoleOptions(me ? me.id : null); - if (!role.gtlAvailable) { + const policies = await this.roleService.getUserPolicies(me ? me.id : null); + if (!policies.gtlAvailable) { throw new ApiError(meta.errors.gtlDisabled); } 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 b2c504448e..2819abb125 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -71,8 +71,8 @@ export default class extends Endpoint { private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { - const role = await this.roleService.getUserRoleOptions(me.id); - if (!role.ltlAvailable) { + const policies = await this.roleService.getUserPolicies(me.id); + if (!policies.ltlAvailable) { 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 6361edc310..f396f7e584 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -67,8 +67,8 @@ export default class extends Endpoint { private activeUsersChart: ActiveUsersChart, ) { super(meta, paramDef, async (ps, me) => { - const role = await this.roleService.getUserRoleOptions(me ? me.id : null); - if (!role.ltlAvailable) { + const policies = await this.roleService.getUserPolicies(me ? me.id : null); + if (!policies.ltlAvailable) { throw new ApiError(meta.errors.ltlDisabled); } 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 22e5e3ce78..a840c1a04e 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts @@ -55,7 +55,7 @@ export default class extends Endpoint { const currentCount = await this.userListsRepository.countBy({ userId: me.id, }); - if (currentCount > (await this.roleService.getUserRoleOptions(me.id)).userListLimit) { + if (currentCount > (await this.roleService.getUserPolicies(me.id)).userListLimit) { throw new ApiError(meta.errors.tooManyUserLists); } 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 185c813869..43d8907fc9 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -29,8 +29,8 @@ class GlobalTimelineChannel extends Channel { @bindThis public async init(params: any) { - const role = await this.roleService.getUserRoleOptions(this.user ? this.user.id : null); - if (!role.gtlAvailable) return; + const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); + if (!policies.gtlAvailable) return; // Subscribe events this.subscriber.on('notesStream', this.onNote); 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 a0f75f202c..340f677815 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -30,8 +30,8 @@ class HybridTimelineChannel extends Channel { @bindThis public async init(params: any): Promise { - const role = await this.roleService.getUserRoleOptions(this.user ? this.user.id : null); - if (!role.ltlAvailable) return; + const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); + if (!policies.ltlAvailable) return; // Subscribe events this.subscriber.on('notesStream', this.onNote); 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 7d76f42fe7..ea29e30d63 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -28,8 +28,8 @@ class LocalTimelineChannel extends Channel { @bindThis public async init(params: any) { - const role = await this.roleService.getUserRoleOptions(this.user ? this.user.id : null); - if (!role.ltlAvailable) return; + const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); + if (!policies.ltlAvailable) return; // Subscribe events this.subscriber.on('notesStream', this.onNote); diff --git a/packages/backend/src/server/api/stream/types.ts b/packages/backend/src/server/api/stream/types.ts index 03837baefb..a442529bb3 100644 --- a/packages/backend/src/server/api/stream/types.ts +++ b/packages/backend/src/server/api/stream/types.ts @@ -30,7 +30,7 @@ export interface InternalStreamTypes { remoteUserUpdated: Serialized<{ id: User['id']; }>; follow: Serialized<{ followerId: User['id']; followeeId: User['id']; }>; unfollow: Serialized<{ followerId: User['id']; followeeId: User['id']; }>; - defaultRoleOverrideUpdated: Serialized; + policiesUpdated: Serialized; roleCreated: Serialized; roleDeleted: Serialized; roleUpdated: Serialized; diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index 0f67cec0b2..d0be0a8c39 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -36,20 +36,20 @@ - +
- +
- + - + - +
@@ -57,15 +57,15 @@ - +
- + - + - +
@@ -73,15 +73,15 @@ - +
- + - + - +
@@ -89,15 +89,15 @@ - +
- + - + - +
@@ -105,15 +105,15 @@ - +
- + - + - +
@@ -121,15 +121,15 @@ - +
- + - + - +
@@ -137,15 +137,15 @@ - +
- + - + - +
@@ -153,14 +153,14 @@ - +
- + - + - +
@@ -168,14 +168,14 @@ - +
- + - + - +
@@ -183,15 +183,15 @@ - +
- + - + - +
@@ -199,14 +199,14 @@ - +
- + - + - +
@@ -214,14 +214,14 @@ - +
- + - + - +
@@ -229,14 +229,14 @@ - +
- + - + - +
@@ -244,14 +244,14 @@ - +
- + - + - +
@@ -259,14 +259,14 @@ - +
- + - + - +
@@ -306,7 +306,7 @@ import * as os from '@/os'; import { i18n } from '@/i18n'; import { instance } from '@/instance'; -const ROLE_OPTIONS = [ +const ROLE_POLICIES = [ 'gtlAvailable', 'ltlAvailable', 'canPublicNote', @@ -345,13 +345,13 @@ let condFormula = $ref(role?.condFormula ?? { id: uuid(), type: 'isRemote' }); let isPublic = $ref(role?.isPublic ?? false); let canEditMembersByModerator = $ref(role?.canEditMembersByModerator ?? false); -const options = reactive>({}); -for (const ROLE_OPTION of ROLE_OPTIONS) { - const _options = role?.options ?? {}; - options[ROLE_OPTION] = { - useDefault: _options[ROLE_OPTION]?.useDefault ?? true, - priority: _options[ROLE_OPTION]?.priority ?? 0, - value: _options[ROLE_OPTION]?.value ?? instance.baseRole[ROLE_OPTION], +const policies = reactive>({}); +for (const ROLE_POLICY of ROLE_POLICIES) { + const _policies = role?.policies ?? {}; + policies[ROLE_POLICY] = { + useDefault: _policies[ROLE_POLICY]?.useDefault ?? true, + priority: _policies[ROLE_POLICY]?.priority ?? 0, + value: _policies[ROLE_POLICY]?.value ?? instance.policies[ROLE_POLICY], }; } @@ -381,7 +381,7 @@ async function save() { isModerator: rolePermission === 'moderator', isPublic, canEditMembersByModerator, - options, + policies, }); emit('updated'); } else { @@ -395,7 +395,7 @@ async function save() { isModerator: rolePermission === 'moderator', isPublic, canEditMembersByModerator, - options, + policies, }); emit('created', created); } diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index 1ceead8b6c..f074069df0 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -10,114 +10,114 @@
- - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -134,7 +134,7 @@