refactor: introduce bindThis decorator to bind this automaticaly
This commit is contained in:
		
							parent
							
								
									e73581f715
								
							
						
					
					
						commit
						bbb49457f9
					
				
					 199 changed files with 969 additions and 96 deletions
				
			
		|  | @ -7,6 +7,7 @@ import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; | ||||||
| import { RelayService } from '@/core/RelayService.js'; | import { RelayService } from '@/core/RelayService.js'; | ||||||
| import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; | import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; | ||||||
| import { UserEntityService } from '@/core/entities/UserEntityService.js'; | import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class AccountUpdateService { | export class AccountUpdateService { | ||||||
|  | @ -24,6 +25,7 @@ export class AccountUpdateService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async publishToFollowers(userId: User['id']) { | 	public async publishToFollowers(userId: User['id']) { | ||||||
| 		const user = await this.usersRepository.findOneBy({ id: userId }); | 		const user = await this.usersRepository.findOneBy({ id: userId }); | ||||||
| 		if (user == null) throw new Error('user not found'); | 		if (user == null) throw new Error('user not found'); | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ const _dirname = dirname(_filename); | ||||||
| 
 | 
 | ||||||
| const REQUIRED_CPU_FLAGS = ['avx2', 'fma']; | const REQUIRED_CPU_FLAGS = ['avx2', 'fma']; | ||||||
| let isSupportedCpu: undefined | boolean = undefined; | let isSupportedCpu: undefined | boolean = undefined; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class AiService { | export class AiService { | ||||||
|  | @ -23,6 +24,7 @@ export class AiService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async detectSensitive(path: string): Promise<nsfw.predictionType[] | null> { | 	public async detectSensitive(path: string): Promise<nsfw.predictionType[] | null> { | ||||||
| 		try { | 		try { | ||||||
| 			if (isSupportedCpu === undefined) { | 			if (isSupportedCpu === undefined) { | ||||||
|  | @ -53,6 +55,7 @@ export class AiService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async getCpuFlags(): Promise<string[]> { | 	private async getCpuFlags(): Promise<string[]> { | ||||||
| 		const str = await si.cpuFlags(); | 		const str = await si.cpuFlags(); | ||||||
| 		return str.split(/\s+/); | 		return str.split(/\s+/); | ||||||
|  |  | ||||||
|  | @ -13,6 +13,7 @@ import { DI } from '@/di-symbols.js'; | ||||||
| import type { 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 '@/core/UtilityService.js'; | import { UtilityService } from '@/core/UtilityService.js'; | ||||||
| import type { OnApplicationShutdown } from '@nestjs/common'; | import type { OnApplicationShutdown } from '@nestjs/common'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class AntennaService implements OnApplicationShutdown { | export class AntennaService implements OnApplicationShutdown { | ||||||
|  | @ -56,10 +57,12 @@ export class AntennaService implements OnApplicationShutdown { | ||||||
| 		this.redisSubscriber.on('message', this.onRedisMessage); | 		this.redisSubscriber.on('message', this.onRedisMessage); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public onApplicationShutdown(signal?: string | undefined) { | 	public onApplicationShutdown(signal?: string | undefined) { | ||||||
| 		this.redisSubscriber.off('message', this.onRedisMessage); | 		this.redisSubscriber.off('message', this.onRedisMessage); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async onRedisMessage(_: string, data: string): Promise<void> { | 	private async onRedisMessage(_: string, data: string): Promise<void> { | ||||||
| 		const obj = JSON.parse(data); | 		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<void> { | 	public async addNoteToAntenna(antenna: Antenna, note: Note, noteUser: { id: User['id']; }): Promise<void> { | ||||||
| 		// 通知しない設定になっているか、自分自身の投稿なら既読にする
 | 		// 通知しない設定になっているか、自分自身の投稿なら既読にする
 | ||||||
| 		const read = !antenna.notify || (antenna.userId === noteUser.id); | 		const read = !antenna.notify || (antenna.userId === noteUser.id); | ||||||
|  | @ -133,6 +137,7 @@ export class AntennaService implements OnApplicationShutdown { | ||||||
| 	/** | 	/** | ||||||
| 	 * noteUserFollowers / antennaUserFollowing はどちらか一方が指定されていればよい | 	 * 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<boolean> { | 	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<boolean> { | ||||||
| 		if (note.visibility === 'specified') return false; | 		if (note.visibility === 'specified') return false; | ||||||
| 	 | 	 | ||||||
|  | @ -217,6 +222,7 @@ export class AntennaService implements OnApplicationShutdown { | ||||||
| 		return true; | 		return true; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async getAntennas() { | 	public async getAntennas() { | ||||||
| 		if (!this.antennasFetched) { | 		if (!this.antennasFetched) { | ||||||
| 			this.antennas = await this.antennasRepository.find(); | 			this.antennas = await this.antennasRepository.find(); | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ import { DI } from '@/di-symbols.js'; | ||||||
|  * Retry delay (ms) for lock acquisition |  * Retry delay (ms) for lock acquisition | ||||||
|  */ |  */ | ||||||
| const retryDelay = 100; | const retryDelay = 100; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class AppLockService { | export class AppLockService { | ||||||
|  | @ -26,14 +27,17 @@ export class AppLockService { | ||||||
| 	 * @param timeout Lock timeout (ms), The timeout releases previous lock. | 	 * @param timeout Lock timeout (ms), The timeout releases previous lock. | ||||||
| 	 * @returns Unlock function | 	 * @returns Unlock function | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public getApLock(uri: string, timeout = 30 * 1000): Promise<() => void> { | 	public getApLock(uri: string, timeout = 30 * 1000): Promise<() => void> { | ||||||
| 		return this.lock(`ap-object:${uri}`, timeout); | 		return this.lock(`ap-object:${uri}`, timeout); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public getFetchInstanceMetadataLock(host: string, timeout = 30 * 1000): Promise<() => void> { | 	public getFetchInstanceMetadataLock(host: string, timeout = 30 * 1000): Promise<() => void> { | ||||||
| 		return this.lock(`instance:${host}`, timeout); | 		return this.lock(`instance:${host}`, timeout); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public getChartInsertLock(lockKey: string, timeout = 30 * 1000): Promise<() => void> { | 	public getChartInsertLock(lockKey: string, timeout = 30 * 1000): Promise<() => void> { | ||||||
| 		return this.lock(`chart-insert:${lockKey}`, timeout); | 		return this.lock(`chart-insert:${lockKey}`, timeout); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ import { DI } from '@/di-symbols.js'; | ||||||
| import type { UsersRepository } from '@/models/index.js'; | import type { UsersRepository } from '@/models/index.js'; | ||||||
| import type { Config } from '@/config.js'; | import type { Config } from '@/config.js'; | ||||||
| import { HttpRequestService } from '@/core/HttpRequestService.js'; | import { HttpRequestService } from '@/core/HttpRequestService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| type CaptchaResponse = { | type CaptchaResponse = { | ||||||
| 	success: boolean; | 	success: boolean; | ||||||
|  | @ -19,6 +20,7 @@ export class CaptchaService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async getCaptchaResponse(url: string, secret: string, response: string): Promise<CaptchaResponse> { | 	private async getCaptchaResponse(url: string, secret: string, response: string): Promise<CaptchaResponse> { | ||||||
| 		const params = new URLSearchParams({ | 		const params = new URLSearchParams({ | ||||||
| 			secret, | 			secret, | ||||||
|  | @ -45,6 +47,7 @@ export class CaptchaService { | ||||||
| 		return await res.json() as CaptchaResponse; | 		return await res.json() as CaptchaResponse; | ||||||
| 	}	 | 	}	 | ||||||
| 	 | 	 | ||||||
|  | 	@bindThis | ||||||
| 	public async verifyRecaptcha(secret: string, response: string | null | undefined): Promise<void> { | 	public async verifyRecaptcha(secret: string, response: string | null | undefined): Promise<void> { | ||||||
| 		if (response == null) { | 		if (response == null) { | ||||||
| 			throw 'recaptcha-failed: no response provided'; | 			throw 'recaptcha-failed: no response provided'; | ||||||
|  | @ -60,6 +63,7 @@ export class CaptchaService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async verifyHcaptcha(secret: string, response: string | null | undefined): Promise<void> { | 	public async verifyHcaptcha(secret: string, response: string | null | undefined): Promise<void> { | ||||||
| 		if (response == null) { | 		if (response == null) { | ||||||
| 			throw 'hcaptcha-failed: no response provided'; | 			throw 'hcaptcha-failed: no response provided'; | ||||||
|  | @ -75,6 +79,7 @@ export class CaptchaService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async verifyTurnstile(secret: string, response: string | null | undefined): Promise<void> { | 	public async verifyTurnstile(secret: string, response: string | null | undefined): Promise<void> { | ||||||
| 		if (response == null) { | 		if (response == null) { | ||||||
| 			throw 'turnstile-failed: no response provided'; | 			throw 'turnstile-failed: no response provided'; | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ import { IdService } from '@/core/IdService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import { NotificationEntityService } from '@/core/entities/NotificationEntityService.js'; | import { NotificationEntityService } from '@/core/entities/NotificationEntityService.js'; | ||||||
| import { PushNotificationService } from '@/core/PushNotificationService.js'; | import { PushNotificationService } from '@/core/PushNotificationService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class CreateNotificationService { | export class CreateNotificationService { | ||||||
|  | @ -30,6 +31,7 @@ export class CreateNotificationService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async createNotification( | 	public async createNotification( | ||||||
| 		notifieeId: User['id'], | 		notifieeId: User['id'], | ||||||
| 		type: Notification['type'], | 		type: Notification['type'], | ||||||
|  | @ -90,6 +92,7 @@ export class CreateNotificationService { | ||||||
| 
 | 
 | ||||||
| 	// TODO: locale ファイルをクライアント用とサーバー用で分けたい
 | 	// TODO: locale ファイルをクライアント用とサーバー用で分けたい
 | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async emailNotificationFollow(userId: User['id'], follower: User) { | 	private async emailNotificationFollow(userId: User['id'], follower: User) { | ||||||
| 		/* | 		/* | ||||||
| 		const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); | 		const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); | ||||||
|  | @ -101,6 +104,7 @@ export class CreateNotificationService { | ||||||
| 		*/ | 		*/ | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	@bindThis | ||||||
| 	private async emailNotificationReceiveFollowRequest(userId: User['id'], follower: User) { | 	private async emailNotificationReceiveFollowRequest(userId: User['id'], follower: User) { | ||||||
| 		/* | 		/* | ||||||
| 		const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); | 		const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ import { UserKeypair } from '@/models/entities/UserKeypair.js'; | ||||||
| import { UsedUsername } from '@/models/entities/UsedUsername.js'; | import { UsedUsername } from '@/models/entities/UsedUsername.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import generateNativeUserToken from '@/misc/generate-native-user-token.js'; | import generateNativeUserToken from '@/misc/generate-native-user-token.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class CreateSystemUserService { | export class CreateSystemUserService { | ||||||
|  | @ -21,6 +22,7 @@ export class CreateSystemUserService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async createSystemUser(username: string): Promise<User> { | 	public async createSystemUser(username: string): Promise<User> { | ||||||
| 		const password = uuid(); | 		const password = uuid(); | ||||||
| 	 | 	 | ||||||
|  |  | ||||||
|  | @ -20,6 +20,7 @@ type PopulatedEmoji = { | ||||||
| 	name: string; | 	name: string; | ||||||
| 	url: string; | 	url: string; | ||||||
| }; | }; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class CustomEmojiService { | export class CustomEmojiService { | ||||||
|  | @ -43,6 +44,7 @@ export class CustomEmojiService { | ||||||
| 		this.cache = new Cache<Emoji | null>(1000 * 60 * 60 * 12); | 		this.cache = new Cache<Emoji | null>(1000 * 60 * 60 * 12); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async add(data: { | 	public async add(data: { | ||||||
| 		driveFile: DriveFile; | 		driveFile: DriveFile; | ||||||
| 		name: string; | 		name: string; | ||||||
|  | @ -67,6 +69,7 @@ export class CustomEmojiService { | ||||||
| 		return emoji; | 		return emoji; | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	@bindThis | ||||||
| 	private normalizeHost(src: string | undefined, noteUserHost: string | null): string | null { | 	private normalizeHost(src: string | undefined, noteUserHost: string | null): string | null { | ||||||
| 	// クエリに使うホスト
 | 	// クエリに使うホスト
 | ||||||
| 		let host = src === '.' ? null	// .はローカルホスト (ここがマッチするのはリアクションのみ)
 | 		let host = src === '.' ? null	// .はローカルホスト (ここがマッチするのはリアクションのみ)
 | ||||||
|  | @ -79,6 +82,7 @@ export class CustomEmojiService { | ||||||
| 		return host; | 		return host; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private parseEmojiStr(emojiName: string, noteUserHost: string | null) { | 	private parseEmojiStr(emojiName: string, noteUserHost: string | null) { | ||||||
| 		const match = emojiName.match(/^(\w+)(?:@([\w.-]+))?$/); | 		const match = emojiName.match(/^(\w+)(?:@([\w.-]+))?$/); | ||||||
| 		if (!match) return { name: null, host: null }; | 		if (!match) return { name: null, host: null }; | ||||||
|  | @ -97,6 +101,7 @@ export class CustomEmojiService { | ||||||
|  * @param noteUserHost ノートやユーザープロフィールの所有者のホスト |  * @param noteUserHost ノートやユーザープロフィールの所有者のホスト | ||||||
|  * @returns 絵文字情報, nullは未マッチを意味する |  * @returns 絵文字情報, nullは未マッチを意味する | ||||||
|  */ |  */ | ||||||
|  | 	@bindThis | ||||||
| 	public async populateEmoji(emojiName: string, noteUserHost: string | null): Promise<PopulatedEmoji | null> { | 	public async populateEmoji(emojiName: string, noteUserHost: string | null): Promise<PopulatedEmoji | null> { | ||||||
| 		const { name, host } = this.parseEmojiStr(emojiName, noteUserHost); | 		const { name, host } = this.parseEmojiStr(emojiName, noteUserHost); | ||||||
| 		if (name == null) return null; | 		if (name == null) return null; | ||||||
|  | @ -123,11 +128,13 @@ export class CustomEmojiService { | ||||||
| 	/** | 	/** | ||||||
|  * 複数の添付用絵文字情報を解決する (キャシュ付き, 存在しないものは結果から除外される) |  * 複数の添付用絵文字情報を解決する (キャシュ付き, 存在しないものは結果から除外される) | ||||||
|  */ |  */ | ||||||
|  | 	@bindThis | ||||||
| 	public async populateEmojis(emojiNames: string[], noteUserHost: string | null): Promise<PopulatedEmoji[]> { | 	public async populateEmojis(emojiNames: string[], noteUserHost: string | null): Promise<PopulatedEmoji[]> { | ||||||
| 		const emojis = await Promise.all(emojiNames.map(x => this.populateEmoji(x, noteUserHost))); | 		const emojis = await Promise.all(emojiNames.map(x => this.populateEmoji(x, noteUserHost))); | ||||||
| 		return emojis.filter((x): x is PopulatedEmoji => x != null); | 		return emojis.filter((x): x is PopulatedEmoji => x != null); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public aggregateNoteEmojis(notes: Note[]) { | 	public aggregateNoteEmojis(notes: Note[]) { | ||||||
| 		let emojis: { name: string | null; host: string | null; }[] = []; | 		let emojis: { name: string | null; host: string | null; }[] = []; | ||||||
| 		for (const note of notes) { | 		for (const note of notes) { | ||||||
|  | @ -154,6 +161,7 @@ export class CustomEmojiService { | ||||||
| 	/** | 	/** | ||||||
|  * 与えられた絵文字のリストをデータベースから取得し、キャッシュに追加します |  * 与えられた絵文字のリストをデータベースから取得し、キャッシュに追加します | ||||||
|  */ |  */ | ||||||
|  | 	@bindThis | ||||||
| 	public async prefetchEmojis(emojis: { name: string; host: string | null; }[]): Promise<void> { | 	public async prefetchEmojis(emojis: { name: string; host: string | null; }[]): Promise<void> { | ||||||
| 		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 emojisQuery: any[] = []; | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ import { QueueService } from '@/core/QueueService.js'; | ||||||
| import { UserSuspendService } from '@/core/UserSuspendService.js'; | import { UserSuspendService } from '@/core/UserSuspendService.js'; | ||||||
| import { GlobalEventService } from '@/core/GlobalEventService.js'; | import { GlobalEventService } from '@/core/GlobalEventService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class DeleteAccountService { | export class DeleteAccountService { | ||||||
|  | @ -17,6 +18,7 @@ export class DeleteAccountService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async deleteAccount(user: { | 	public async deleteAccount(user: { | ||||||
| 		id: string; | 		id: string; | ||||||
| 		host: string | null; | 		host: string | null; | ||||||
|  |  | ||||||
|  | @ -15,6 +15,7 @@ import { LoggerService } from '@/core/LoggerService.js'; | ||||||
| import type Logger from '@/logger.js'; | import type Logger from '@/logger.js'; | ||||||
| 
 | 
 | ||||||
| const pipeline = util.promisify(stream.pipeline); | const pipeline = util.promisify(stream.pipeline); | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class DownloadService { | export class DownloadService { | ||||||
|  | @ -30,6 +31,7 @@ export class DownloadService { | ||||||
| 		this.logger = this.loggerService.getLogger('download'); | 		this.logger = this.loggerService.getLogger('download'); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async downloadUrl(url: string, path: string): Promise<void> { | 	public async downloadUrl(url: string, path: string): Promise<void> { | ||||||
| 		this.logger.info(`Downloading ${chalk.cyan(url)} ...`); | 		this.logger.info(`Downloading ${chalk.cyan(url)} ...`); | ||||||
| 	 | 	 | ||||||
|  | @ -94,6 +96,7 @@ export class DownloadService { | ||||||
| 		this.logger.succ(`Download finished: ${chalk.cyan(url)}`); | 		this.logger.succ(`Download finished: ${chalk.cyan(url)}`); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async downloadTextFile(url: string): Promise<string> { | 	public async downloadTextFile(url: string): Promise<string> { | ||||||
| 		// Create temp file
 | 		// Create temp file
 | ||||||
| 		const [path, cleanup] = await createTemp(); | 		const [path, cleanup] = await createTemp(); | ||||||
|  | @ -112,6 +115,7 @@ export class DownloadService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	@bindThis | ||||||
| 	private isPrivateIp(ip: string): boolean { | 	private isPrivateIp(ip: string): boolean { | ||||||
| 		for (const net of this.config.allowedPrivateNetworks ?? []) { | 		for (const net of this.config.allowedPrivateNetworks ?? []) { | ||||||
| 			const cidr = new IPCIDR(net); | 			const cidr = new IPCIDR(net); | ||||||
|  |  | ||||||
|  | @ -71,6 +71,7 @@ type UploadFromUrlArgs = { | ||||||
| 	requestIp?: string | null; | 	requestIp?: string | null; | ||||||
| 	requestHeaders?: Record<string, string> | null; | 	requestHeaders?: Record<string, string> | null; | ||||||
| }; | }; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class DriveService { | export class DriveService { | ||||||
|  | @ -122,6 +123,7 @@ export class DriveService { | ||||||
| 	 * @param hash Hash for original | 	 * @param hash Hash for original | ||||||
| 	 * @param size Size for original | 	 * @param size Size for original | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	private async save(file: DriveFile, path: string, name: string, type: string, hash: string, size: number): Promise<DriveFile> { | 	private async save(file: DriveFile, path: string, name: string, type: string, hash: string, size: number): Promise<DriveFile> { | ||||||
| 	// thunbnail, webpublic を必要なら生成
 | 	// thunbnail, webpublic を必要なら生成
 | ||||||
| 		const alts = await this.generateAlts(path, type, !file.uri); | 		const alts = await this.generateAlts(path, type, !file.uri); | ||||||
|  | @ -242,6 +244,7 @@ export class DriveService { | ||||||
| 	 * @param type Content-Type for original | 	 * @param type Content-Type for original | ||||||
| 	 * @param generateWeb Generate webpublic or not | 	 * @param generateWeb Generate webpublic or not | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async generateAlts(path: string, type: string, generateWeb: boolean) { | 	public async generateAlts(path: string, type: string, generateWeb: boolean) { | ||||||
| 		if (type.startsWith('video/')) { | 		if (type.startsWith('video/')) { | ||||||
| 			try { | 			try { | ||||||
|  | @ -345,6 +348,7 @@ export class DriveService { | ||||||
| 	/** | 	/** | ||||||
| 	 * Upload to ObjectStorage | 	 * Upload to ObjectStorage | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	private 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 (type === 'image/apng') type = 'image/png'; | ||||||
| 		if (!FILE_TYPE_BROWSERSAFE.includes(type)) type = 'application/octet-stream'; | 		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}`); | 		if (result) this.registerLogger.debug(`Uploaded: ${result.Bucket}/${result.Key} => ${result.Location}`); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async deleteOldFile(user: IRemoteUser) { | 	private async deleteOldFile(user: IRemoteUser) { | ||||||
| 		const q = this.driveFilesRepository.createQueryBuilder('file') | 		const q = this.driveFilesRepository.createQueryBuilder('file') | ||||||
| 			.where('file.userId = :userId', { userId: user.id }) | 			.where('file.userId = :userId', { userId: user.id }) | ||||||
|  | @ -398,6 +403,7 @@ export class DriveService { | ||||||
| 	 * Add file to drive | 	 * Add file to drive | ||||||
| 	 * | 	 * | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async addFile({ | 	public async addFile({ | ||||||
| 		user, | 		user, | ||||||
| 		path, | 		path, | ||||||
|  | @ -601,6 +607,7 @@ export class DriveService { | ||||||
| 		return file; | 		return file; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async deleteFile(file: DriveFile, isExpired = false) { | 	public async deleteFile(file: DriveFile, isExpired = false) { | ||||||
| 		if (file.storedInternal) { | 		if (file.storedInternal) { | ||||||
| 			this.internalStorageService.del(file.accessKey!); | 			this.internalStorageService.del(file.accessKey!); | ||||||
|  | @ -627,6 +634,7 @@ export class DriveService { | ||||||
| 		this.deletePostProcess(file, isExpired); | 		this.deletePostProcess(file, isExpired); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async deleteFileSync(file: DriveFile, isExpired = false) { | 	public async deleteFileSync(file: DriveFile, isExpired = false) { | ||||||
| 		if (file.storedInternal) { | 		if (file.storedInternal) { | ||||||
| 			this.internalStorageService.del(file.accessKey!); | 			this.internalStorageService.del(file.accessKey!); | ||||||
|  | @ -657,6 +665,7 @@ export class DriveService { | ||||||
| 		this.deletePostProcess(file, isExpired); | 		this.deletePostProcess(file, isExpired); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async deletePostProcess(file: DriveFile, isExpired = false) { | 	private async deletePostProcess(file: DriveFile, isExpired = false) { | ||||||
| 	// リモートファイル期限切れ削除後は直リンクにする
 | 	// リモートファイル期限切れ削除後は直リンクにする
 | ||||||
| 		if (isExpired && file.userHost !== null && file.uri != null) { | 		if (isExpired && file.userHost !== null && file.uri != null) { | ||||||
|  | @ -683,6 +692,7 @@ export class DriveService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async deleteObjectStorageFile(key: string) { | 	public async deleteObjectStorageFile(key: string) { | ||||||
| 		const meta = await this.metaService.fetch(); | 		const meta = await this.metaService.fetch(); | ||||||
| 
 | 
 | ||||||
|  | @ -694,6 +704,7 @@ export class DriveService { | ||||||
| 		}).promise(); | 		}).promise(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async uploadFromUrl({ | 	public async uploadFromUrl({ | ||||||
| 		url, | 		url, | ||||||
| 		user, | 		user, | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ import type { Config } from '@/config.js'; | ||||||
| import type Logger from '@/logger.js'; | import type Logger from '@/logger.js'; | ||||||
| import type { UserProfilesRepository } from '@/models/index.js'; | import type { UserProfilesRepository } from '@/models/index.js'; | ||||||
| import { LoggerService } from '@/core/LoggerService.js'; | import { LoggerService } from '@/core/LoggerService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class EmailService { | export class EmailService { | ||||||
|  | @ -25,6 +26,7 @@ export class EmailService { | ||||||
| 		this.logger = this.loggerService.getLogger('email'); | 		this.logger = this.loggerService.getLogger('email'); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async sendEmail(to: string, subject: string, html: string, text: string) { | 	public async sendEmail(to: string, subject: string, html: string, text: string) { | ||||||
| 		const meta = await this.metaService.fetch(true); | 		const meta = await this.metaService.fetch(true); | ||||||
| 	 | 	 | ||||||
|  | @ -141,6 +143,7 @@ export class EmailService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async validateEmailForAccount(emailAddress: string): Promise<{ | 	public async validateEmailForAccount(emailAddress: string): Promise<{ | ||||||
| 		available: boolean; | 		available: boolean; | ||||||
| 		reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp'; | 		reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp'; | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import { Cache } from '@/misc/cache.js'; | ||||||
| import { IdService } from '@/core/IdService.js'; | import { IdService } from '@/core/IdService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import { UtilityService } from '@/core/UtilityService.js'; | import { UtilityService } from '@/core/UtilityService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class FederatedInstanceService { | export class FederatedInstanceService { | ||||||
|  | @ -20,6 +21,7 @@ export class FederatedInstanceService { | ||||||
| 		this.cache = new Cache<Instance>(1000 * 60 * 60); | 		this.cache = new Cache<Instance>(1000 * 60 * 60); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async registerOrFetchInstanceDoc(host: string): Promise<Instance> { | 	public async registerOrFetchInstanceDoc(host: string): Promise<Instance> { | ||||||
| 		host = this.utilityService.toPuny(host); | 		host = this.utilityService.toPuny(host); | ||||||
| 	 | 	 | ||||||
|  |  | ||||||
|  | @ -30,6 +30,7 @@ type NodeInfo = { | ||||||
| 		themeColor?: unknown; | 		themeColor?: unknown; | ||||||
| 	}; | 	}; | ||||||
| }; | }; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class FetchInstanceMetadataService { | export class FetchInstanceMetadataService { | ||||||
|  | @ -46,6 +47,7 @@ export class FetchInstanceMetadataService { | ||||||
| 		this.logger = this.loggerService.getLogger('metadata', 'cyan'); | 		this.logger = this.loggerService.getLogger('metadata', 'cyan'); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async fetchInstanceMetadata(instance: Instance, force = false): Promise<void> { | 	public async fetchInstanceMetadata(instance: Instance, force = false): Promise<void> { | ||||||
| 		const unlock = await this.appLockService.getFetchInstanceMetadataLock(instance.host); | 		const unlock = await this.appLockService.getFetchInstanceMetadataLock(instance.host); | ||||||
| 	 | 	 | ||||||
|  | @ -105,6 +107,7 @@ export class FetchInstanceMetadataService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async fetchNodeinfo(instance: Instance): Promise<NodeInfo> { | 	private async fetchNodeinfo(instance: Instance): Promise<NodeInfo> { | ||||||
| 		this.logger.info(`Fetching nodeinfo of ${instance.host} ...`); | 		this.logger.info(`Fetching nodeinfo of ${instance.host} ...`); | ||||||
| 	 | 	 | ||||||
|  | @ -148,6 +151,7 @@ export class FetchInstanceMetadataService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async fetchDom(instance: Instance): Promise<DOMWindow['document']> { | 	private async fetchDom(instance: Instance): Promise<DOMWindow['document']> { | ||||||
| 		this.logger.info(`Fetching HTML of ${instance.host} ...`); | 		this.logger.info(`Fetching HTML of ${instance.host} ...`); | ||||||
| 	 | 	 | ||||||
|  | @ -161,6 +165,7 @@ export class FetchInstanceMetadataService { | ||||||
| 		return doc; | 		return doc; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async fetchManifest(instance: Instance): Promise<Record<string, unknown> | null> { | 	private async fetchManifest(instance: Instance): Promise<Record<string, unknown> | null> { | ||||||
| 		const url = 'https://' + instance.host; | 		const url = 'https://' + instance.host; | ||||||
| 	 | 	 | ||||||
|  | @ -171,6 +176,7 @@ export class FetchInstanceMetadataService { | ||||||
| 		return manifest; | 		return manifest; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async fetchFaviconUrl(instance: Instance, doc: DOMWindow['document'] | null): Promise<string | null> { | 	private async fetchFaviconUrl(instance: Instance, doc: DOMWindow['document'] | null): Promise<string | null> { | ||||||
| 		const url = 'https://' + instance.host; | 		const url = 'https://' + instance.host; | ||||||
| 	 | 	 | ||||||
|  | @ -198,6 +204,7 @@ export class FetchInstanceMetadataService { | ||||||
| 		return null; | 		return null; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async fetchIconUrl(instance: Instance, doc: DOMWindow['document'] | null, manifest: Record<string, any> | null): Promise<string | null> { | 	private async fetchIconUrl(instance: Instance, doc: DOMWindow['document'] | null, manifest: Record<string, any> | null): Promise<string | null> { | ||||||
| 		if (manifest && manifest.icons && manifest.icons.length > 0 && manifest.icons[0].src) { | 		if (manifest && manifest.icons && manifest.icons.length > 0 && manifest.icons[0].src) { | ||||||
| 			const url = 'https://' + instance.host; | 			const url = 'https://' + instance.host; | ||||||
|  | @ -226,6 +233,7 @@ export class FetchInstanceMetadataService { | ||||||
| 		return null; | 		return null; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async getThemeColor(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record<string, any> | null): Promise<string | null> { | 	private async getThemeColor(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record<string, any> | null): Promise<string | null> { | ||||||
| 		const themeColor = info?.metadata?.themeColor ?? doc?.querySelector('meta[name="theme-color"]')?.getAttribute('content') ?? manifest?.theme_color; | 		const themeColor = info?.metadata?.themeColor ?? doc?.querySelector('meta[name="theme-color"]')?.getAttribute('content') ?? manifest?.theme_color; | ||||||
| 	 | 	 | ||||||
|  | @ -237,6 +245,7 @@ export class FetchInstanceMetadataService { | ||||||
| 		return null; | 		return null; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async getSiteName(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record<string, any> | null): Promise<string | null> { | 	private async getSiteName(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record<string, any> | null): Promise<string | null> { | ||||||
| 		if (info && info.metadata) { | 		if (info && info.metadata) { | ||||||
| 			if (typeof info.metadata.nodeName === 'string') { | 			if (typeof info.metadata.nodeName === 'string') { | ||||||
|  | @ -261,6 +270,7 @@ export class FetchInstanceMetadataService { | ||||||
| 		return null; | 		return null; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async getDescription(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record<string, any> | null): Promise<string | null> { | 	private async getDescription(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record<string, any> | null): Promise<string | null> { | ||||||
| 		if (info && info.metadata) { | 		if (info && info.metadata) { | ||||||
| 			if (typeof info.metadata.nodeDescription === 'string') { | 			if (typeof info.metadata.nodeDescription === 'string') { | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ import sharp from 'sharp'; | ||||||
| import { encode } from 'blurhash'; | import { encode } from 'blurhash'; | ||||||
| import { createTempDir } from '@/misc/create-temp.js'; | import { createTempDir } from '@/misc/create-temp.js'; | ||||||
| import { AiService } from '@/core/AiService.js'; | import { AiService } from '@/core/AiService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| const pipeline = util.promisify(stream.pipeline); | const pipeline = util.promisify(stream.pipeline); | ||||||
| 
 | 
 | ||||||
|  | @ -42,6 +43,7 @@ const TYPE_SVG = { | ||||||
| 	mime: 'image/svg+xml', | 	mime: 'image/svg+xml', | ||||||
| 	ext: 'svg', | 	ext: 'svg', | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class FileInfoService { | export class FileInfoService { | ||||||
| 	constructor( | 	constructor( | ||||||
|  | @ -52,6 +54,7 @@ export class FileInfoService { | ||||||
| 	/** | 	/** | ||||||
| 	 * Get file information | 	 * Get file information | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async getFileInfo(path: string, opts: { | 	public async getFileInfo(path: string, opts: { | ||||||
| 		skipSensitiveDetection: boolean; | 		skipSensitiveDetection: boolean; | ||||||
| 		sensitiveThreshold?: number; | 		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]> { | 	private async detectSensitivity(source: string, mime: string, sensitiveThreshold: number, sensitiveThresholdForPorn: number, analyzeVideo: boolean): Promise<[sensitive: boolean, porn: boolean]> { | ||||||
| 		let sensitive = false; | 		let sensitive = false; | ||||||
| 		let porn = false; | 		let porn = false; | ||||||
|  | @ -269,6 +273,7 @@ export class FileInfoService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	@bindThis | ||||||
| 	private exists(path: string): Promise<boolean> { | 	private exists(path: string): Promise<boolean> { | ||||||
| 		return fs.promises.access(path).then(() => true, () => false); | 		return fs.promises.access(path).then(() => true, () => false); | ||||||
| 	} | 	} | ||||||
|  | @ -276,6 +281,7 @@ export class FileInfoService { | ||||||
| 	/** | 	/** | ||||||
| 	 * Detect MIME Type and extension | 	 * Detect MIME Type and extension | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async detectType(path: string): Promise<{ | 	public async detectType(path: string): Promise<{ | ||||||
| 	mime: string; | 	mime: string; | ||||||
| 	ext: string | null; | 	ext: string | null; | ||||||
|  | @ -312,6 +318,7 @@ export class FileInfoService { | ||||||
| 	/** | 	/** | ||||||
| 	 * Check the file is SVG or not | 	 * Check the file is SVG or not | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async checkSvg(path: string) { | 	public async checkSvg(path: string) { | ||||||
| 		try { | 		try { | ||||||
| 			const size = await this.getFileSize(path); | 			const size = await this.getFileSize(path); | ||||||
|  | @ -325,6 +332,7 @@ export class FileInfoService { | ||||||
| 	/** | 	/** | ||||||
| 	 * Get file size | 	 * Get file size | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async getFileSize(path: string): Promise<number> { | 	public async getFileSize(path: string): Promise<number> { | ||||||
| 		const getStat = util.promisify(fs.stat); | 		const getStat = util.promisify(fs.stat); | ||||||
| 		return (await getStat(path)).size; | 		return (await getStat(path)).size; | ||||||
|  | @ -333,6 +341,7 @@ export class FileInfoService { | ||||||
| 	/** | 	/** | ||||||
| 	 * Calculate MD5 hash | 	 * Calculate MD5 hash | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	private async calcHash(path: string): Promise<string> { | 	private async calcHash(path: string): Promise<string> { | ||||||
| 		const hash = crypto.createHash('md5').setEncoding('hex'); | 		const hash = crypto.createHash('md5').setEncoding('hex'); | ||||||
| 		await pipeline(fs.createReadStream(path), hash); | 		await pipeline(fs.createReadStream(path), hash); | ||||||
|  | @ -342,6 +351,7 @@ export class FileInfoService { | ||||||
| 	/** | 	/** | ||||||
| 	 * Detect dimensions of image | 	 * Detect dimensions of image | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	private async detectImageSize(path: string): Promise<{ | 	private async detectImageSize(path: string): Promise<{ | ||||||
| 	width: number; | 	width: number; | ||||||
| 	height: number; | 	height: number; | ||||||
|  | @ -358,6 +368,7 @@ export class FileInfoService { | ||||||
| 	/** | 	/** | ||||||
| 	 * Calculate average color of image | 	 * Calculate average color of image | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	private getBlurhash(path: string): Promise<string> { | 	private getBlurhash(path: string): Promise<string> { | ||||||
| 		return new Promise((resolve, reject) => { | 		return new Promise((resolve, reject) => { | ||||||
| 			sharp(path) | 			sharp(path) | ||||||
|  |  | ||||||
|  | @ -25,6 +25,7 @@ import type { | ||||||
| import type { Packed } from '@/misc/schema.js'; | import type { Packed } from '@/misc/schema.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import type { Config } from '@/config.js'; | import type { Config } from '@/config.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class GlobalEventService { | export class GlobalEventService { | ||||||
|  | @ -37,6 +38,7 @@ export class GlobalEventService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private publish(channel: StreamChannels, type: string | null, value?: any): void { | 	private publish(channel: StreamChannels, type: string | null, value?: any): void { | ||||||
| 		const message = type == null ? value : value == null ? | 		const message = type == null ? value : value == null ? | ||||||
| 			{ type: type, body: null } : | 			{ type: type, body: null } : | ||||||
|  | @ -99,6 +101,7 @@ export class GlobalEventService { | ||||||
| 		this.publish(`messagingIndexStream:${userId}`, type, typeof value === 'undefined' ? null : value); | 		this.publish(`messagingIndexStream:${userId}`, type, typeof value === 'undefined' ? null : value); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public publishNotesStream(note: Packed<'Note'>): void { | 	public publishNotesStream(note: Packed<'Note'>): void { | ||||||
| 		this.publish('notesStream', null, note); | 		this.publish('notesStream', null, note); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ import type { Hashtag } from '@/models/entities/Hashtag.js'; | ||||||
| import HashtagChart from '@/core/chart/charts/hashtag.js'; | import HashtagChart from '@/core/chart/charts/hashtag.js'; | ||||||
| import type { HashtagsRepository, UsersRepository } from '@/models/index.js'; | import type { HashtagsRepository, UsersRepository } from '@/models/index.js'; | ||||||
| import { UserEntityService } from '@/core/entities/UserEntityService.js'; | import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class HashtagService { | export class HashtagService { | ||||||
|  | @ -23,12 +24,14 @@ export class HashtagService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async updateHashtags(user: { id: User['id']; host: User['host']; }, tags: string[]) { | 	public async updateHashtags(user: { id: User['id']; host: User['host']; }, tags: string[]) { | ||||||
| 		for (const tag of tags) { | 		for (const tag of tags) { | ||||||
| 			await this.updateHashtag(user, tag); | 			await this.updateHashtag(user, tag); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async updateUsertags(user: User, tags: string[]) { | 	public async updateUsertags(user: User, tags: string[]) { | ||||||
| 		for (const tag of tags) { | 		for (const tag of tags) { | ||||||
| 			await this.updateHashtag(user, tag, true, true); | 			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) { | 	public async updateHashtag(user: { id: User['id']; host: User['host']; }, tag: string, isUserAttached = false, inc = true) { | ||||||
| 		tag = normalizeForSearch(tag); | 		tag = normalizeForSearch(tag); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import type { Config } from '@/config.js'; | import type { Config } from '@/config.js'; | ||||||
| import { StatusError } from '@/misc/status-error.js'; | import { StatusError } from '@/misc/status-error.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import type { Response } from 'node-fetch'; | import type { Response } from 'node-fetch'; | ||||||
| import type { URL } from 'node:url'; | import type { URL } from 'node:url'; | ||||||
| 
 | 
 | ||||||
|  | @ -84,6 +85,7 @@ export class HttpRequestService { | ||||||
| 	 * @param url URL | 	 * @param url URL | ||||||
| 	 * @param bypassProxy Allways bypass proxy | 	 * @param bypassProxy Allways bypass proxy | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public getAgentByUrl(url: URL, bypassProxy = false): http.Agent | https.Agent { | 	public getAgentByUrl(url: URL, bypassProxy = false): http.Agent | https.Agent { | ||||||
| 		if (bypassProxy || (this.config.proxyBypassHosts || []).includes(url.hostname)) { | 		if (bypassProxy || (this.config.proxyBypassHosts || []).includes(url.hostname)) { | ||||||
| 			return url.protocol === 'http:' ? this.http : this.https; | 			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<string, string>): Promise<unknown> { | 	public async getJson(url: string, accept = 'application/json, */*', timeout = 10000, headers?: Record<string, string>): Promise<unknown> { | ||||||
| 		const res = await this.getResponse({ | 		const res = await this.getResponse({ | ||||||
| 			url, | 			url, | ||||||
|  | @ -106,6 +109,7 @@ export class HttpRequestService { | ||||||
| 		return await res.json(); | 		return await res.json(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async getHtml(url: string, accept = 'text/html, */*', timeout = 10000, headers?: Record<string, string>): Promise<string> { | 	public async getHtml(url: string, accept = 'text/html, */*', timeout = 10000, headers?: Record<string, string>): Promise<string> { | ||||||
| 		const res = await this.getResponse({ | 		const res = await this.getResponse({ | ||||||
| 			url, | 			url, | ||||||
|  | @ -120,6 +124,7 @@ export class HttpRequestService { | ||||||
| 		return await res.text(); | 		return await res.text(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async getResponse(args: { | 	public async getResponse(args: { | ||||||
| 		url: string, | 		url: string, | ||||||
| 		method: string, | 		method: string, | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ import { genAid } from '@/misc/id/aid.js'; | ||||||
| import { genMeid } from '@/misc/id/meid.js'; | import { genMeid } from '@/misc/id/meid.js'; | ||||||
| import { genMeidg } from '@/misc/id/meidg.js'; | import { genMeidg } from '@/misc/id/meidg.js'; | ||||||
| import { genObjectId } from '@/misc/id/object-id.js'; | import { genObjectId } from '@/misc/id/object-id.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class IdService { | export class IdService { | ||||||
|  | @ -18,6 +19,7 @@ export class IdService { | ||||||
| 		this.metohd = config.id.toLowerCase(); | 		this.metohd = config.id.toLowerCase(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public genId(date?: Date): string { | 	public genId(date?: Date): string { | ||||||
| 		if (!date || (date > new Date())) date = new Date(); | 		if (!date || (date > new Date())) date = new Date(); | ||||||
| 	 | 	 | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ export type IImage = { | ||||||
| 	ext: string | null; | 	ext: string | null; | ||||||
| 	type: string; | 	type: string; | ||||||
| }; | }; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class ImageProcessingService { | export class ImageProcessingService { | ||||||
|  | @ -21,10 +22,12 @@ export class ImageProcessingService { | ||||||
| 	 * Convert to JPEG | 	 * Convert to JPEG | ||||||
| 	 *   with resize, remove metadata, resolve orientation, stop animation | 	 *   with resize, remove metadata, resolve orientation, stop animation | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async convertToJpeg(path: string, width: number, height: number): Promise<IImage> { | 	public async convertToJpeg(path: string, width: number, height: number): Promise<IImage> { | ||||||
| 		return this.convertSharpToJpeg(await sharp(path), width, height); | 		return this.convertSharpToJpeg(await sharp(path), width, height); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async convertSharpToJpeg(sharp: sharp.Sharp, width: number, height: number): Promise<IImage> { | 	public async convertSharpToJpeg(sharp: sharp.Sharp, width: number, height: number): Promise<IImage> { | ||||||
| 		const data = await sharp | 		const data = await sharp | ||||||
| 			.resize(width, height, { | 			.resize(width, height, { | ||||||
|  | @ -49,10 +52,12 @@ export class ImageProcessingService { | ||||||
| 	 * Convert to WebP | 	 * Convert to WebP | ||||||
| 	 *   with resize, remove metadata, resolve orientation, stop animation | 	 *   with resize, remove metadata, resolve orientation, stop animation | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async convertToWebp(path: string, width: number, height: number, quality = 85): Promise<IImage> { | 	public async convertToWebp(path: string, width: number, height: number, quality = 85): Promise<IImage> { | ||||||
| 		return this.convertSharpToWebp(await sharp(path), width, height, quality); | 		return this.convertSharpToWebp(await sharp(path), width, height, quality); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async convertSharpToWebp(sharp: sharp.Sharp, width: number, height: number, quality = 85): Promise<IImage> { | 	public async convertSharpToWebp(sharp: sharp.Sharp, width: number, height: number, quality = 85): Promise<IImage> { | ||||||
| 		const data = await sharp | 		const data = await sharp | ||||||
| 			.resize(width, height, { | 			.resize(width, height, { | ||||||
|  | @ -76,10 +81,12 @@ export class ImageProcessingService { | ||||||
| 	 * Convert to PNG | 	 * Convert to PNG | ||||||
| 	 *   with resize, remove metadata, resolve orientation, stop animation | 	 *   with resize, remove metadata, resolve orientation, stop animation | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async convertToPng(path: string, width: number, height: number): Promise<IImage> { | 	public async convertToPng(path: string, width: number, height: number): Promise<IImage> { | ||||||
| 		return this.convertSharpToPng(await sharp(path), width, height); | 		return this.convertSharpToPng(await sharp(path), width, height); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async convertSharpToPng(sharp: sharp.Sharp, width: number, height: number): Promise<IImage> { | 	public async convertSharpToPng(sharp: sharp.Sharp, width: number, height: number): Promise<IImage> { | ||||||
| 		const data = await sharp | 		const data = await sharp | ||||||
| 			.resize(width, height, { | 			.resize(width, height, { | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ import { DI } from '@/di-symbols.js'; | ||||||
| import { CreateSystemUserService } from '@/core/CreateSystemUserService.js'; | import { CreateSystemUserService } from '@/core/CreateSystemUserService.js'; | ||||||
| 
 | 
 | ||||||
| const ACTOR_USERNAME = 'instance.actor' as const; | const ACTOR_USERNAME = 'instance.actor' as const; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class InstanceActorService { | export class InstanceActorService { | ||||||
|  | @ -21,6 +22,7 @@ export class InstanceActorService { | ||||||
| 		this.cache = new Cache<ILocalUser>(Infinity); | 		this.cache = new Cache<ILocalUser>(Infinity); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async getInstanceActor(): Promise<ILocalUser> { | 	public async getInstanceActor(): Promise<ILocalUser> { | ||||||
| 		const cached = this.cache.get(null); | 		const cached = this.cache.get(null); | ||||||
| 		if (cached) return cached; | 		if (cached) return cached; | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ const _filename = fileURLToPath(import.meta.url); | ||||||
| const _dirname = dirname(_filename); | const _dirname = dirname(_filename); | ||||||
| 
 | 
 | ||||||
| const path = Path.resolve(_dirname, '../../../../files'); | const path = Path.resolve(_dirname, '../../../../files'); | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class InternalStorageService { | export class InternalStorageService { | ||||||
|  | @ -19,26 +20,31 @@ export class InternalStorageService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public resolvePath(key: string) { | 	public resolvePath(key: string) { | ||||||
| 		return Path.resolve(path, key); | 		return Path.resolve(path, key); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public read(key: string) { | 	public read(key: string) { | ||||||
| 		return fs.createReadStream(this.resolvePath(key)); | 		return fs.createReadStream(this.resolvePath(key)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public saveFromPath(key: string, srcPath: string) { | 	public saveFromPath(key: string, srcPath: string) { | ||||||
| 		fs.mkdirSync(path, { recursive: true }); | 		fs.mkdirSync(path, { recursive: true }); | ||||||
| 		fs.copyFileSync(srcPath, this.resolvePath(key)); | 		fs.copyFileSync(srcPath, this.resolvePath(key)); | ||||||
| 		return `${this.config.url}/files/${key}`; | 		return `${this.config.url}/files/${key}`; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public saveFromBuffer(key: string, data: Buffer) { | 	public saveFromBuffer(key: string, data: Buffer) { | ||||||
| 		fs.mkdirSync(path, { recursive: true }); | 		fs.mkdirSync(path, { recursive: true }); | ||||||
| 		fs.writeFileSync(this.resolvePath(key), data); | 		fs.writeFileSync(this.resolvePath(key), data); | ||||||
| 		return `${this.config.url}/files/${key}`; | 		return `${this.config.url}/files/${key}`; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public del(key: string) { | 	public del(key: string) { | ||||||
| 		fs.unlink(this.resolvePath(key), () => {}); | 		fs.unlink(this.resolvePath(key), () => {}); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ import * as SyslogPro from 'syslog-pro'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import type { Config } from '@/config.js'; | import type { Config } from '@/config.js'; | ||||||
| import Logger from '@/logger.js'; | import Logger from '@/logger.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class LoggerService { | export class LoggerService { | ||||||
|  | @ -27,6 +28,7 @@ export class LoggerService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public getLogger(domain: string, color?: string | undefined, store?: boolean) { | 	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); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||||
| import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; | import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; | ||||||
| import { MessagingMessageEntityService } from '@/core/entities/MessagingMessageEntityService.js'; | import { MessagingMessageEntityService } from '@/core/entities/MessagingMessageEntityService.js'; | ||||||
| import { PushNotificationService } from '@/core/PushNotificationService.js'; | import { PushNotificationService } from '@/core/PushNotificationService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class MessagingService { | 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) { | 	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 = { | 		const message = { | ||||||
| 			id: this.idService.genId(), | 			id: this.idService.genId(), | ||||||
|  | @ -140,11 +142,13 @@ export class MessagingService { | ||||||
| 		return messageObj; | 		return messageObj; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async deleteMessage(message: MessagingMessage) { | 	public async deleteMessage(message: MessagingMessage) { | ||||||
| 		await this.messagingMessagesRepository.delete(message.id); | 		await this.messagingMessagesRepository.delete(message.id); | ||||||
| 		this.postDeleteMessage(message); | 		this.postDeleteMessage(message); | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	@bindThis | ||||||
| 	private async postDeleteMessage(message: MessagingMessage) { | 	private async postDeleteMessage(message: MessagingMessage) { | ||||||
| 		if (message.recipientId) { | 		if (message.recipientId) { | ||||||
| 			const user = await this.usersRepository.findOneByOrFail({ id: message.userId }); | 			const user = await this.usersRepository.findOneByOrFail({ id: message.userId }); | ||||||
|  | @ -165,6 +169,7 @@ export class MessagingService { | ||||||
| 	/** | 	/** | ||||||
| 	 * Mark messages as read | 	 * Mark messages as read | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async readUserMessagingMessage( | 	public async readUserMessagingMessage( | ||||||
| 		userId: User['id'], | 		userId: User['id'], | ||||||
| 		otherpartyId: User['id'], | 		otherpartyId: User['id'], | ||||||
|  | @ -220,6 +225,7 @@ export class MessagingService { | ||||||
| 	/** | 	/** | ||||||
| 	 * Mark messages as read | 	 * Mark messages as read | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async readGroupMessagingMessage( | 	public async readGroupMessagingMessage( | ||||||
| 		userId: User['id'], | 		userId: User['id'], | ||||||
| 		groupId: UserGroup['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[]) { | 	public async deliverReadActivity(user: { id: User['id']; host: null; }, recipient: IRemoteUser, messages: MessagingMessage | MessagingMessage[]) { | ||||||
| 		messages = toArray(messages).filter(x => x.uri); | 		messages = toArray(messages).filter(x => x.uri); | ||||||
| 		const contents = messages.map(x => this.apRendererService.renderRead(user, x)); | 		const contents = messages.map(x => this.apRendererService.renderRead(user, x)); | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import { DI } from '@/di-symbols.js'; | ||||||
| import { Meta } from '@/models/entities/Meta.js'; | import { Meta } from '@/models/entities/Meta.js'; | ||||||
| import { GlobalEventService } from '@/core/GlobalEventService.js'; | import { GlobalEventService } from '@/core/GlobalEventService.js'; | ||||||
| import type { OnApplicationShutdown } from '@nestjs/common'; | import type { OnApplicationShutdown } from '@nestjs/common'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class MetaService implements OnApplicationShutdown { | export class MetaService implements OnApplicationShutdown { | ||||||
|  | @ -20,7 +21,7 @@ export class MetaService implements OnApplicationShutdown { | ||||||
| 
 | 
 | ||||||
| 		private globalEventService: GlobalEventService, | 		private globalEventService: GlobalEventService, | ||||||
| 	) { | 	) { | ||||||
| 		this.onMessage = this.onMessage.bind(this); | 		//this.onMessage = this.onMessage.bind(this);
 | ||||||
| 
 | 
 | ||||||
| 		if (process.env.NODE_ENV !== 'test') { | 		if (process.env.NODE_ENV !== 'test') { | ||||||
| 			this.intervalId = setInterval(() => { | 			this.intervalId = setInterval(() => { | ||||||
|  | @ -34,6 +35,7 @@ export class MetaService implements OnApplicationShutdown { | ||||||
| 		this.redisSubscriber.on('message', this.onMessage); | 		this.redisSubscriber.on('message', this.onMessage); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async onMessage(_: string, data: string): Promise<void> { | 	private async onMessage(_: string, data: string): Promise<void> { | ||||||
| 		const obj = JSON.parse(data); | 		const obj = JSON.parse(data); | ||||||
| 
 | 
 | ||||||
|  | @ -50,6 +52,7 @@ export class MetaService implements OnApplicationShutdown { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async fetch(noCache = false): Promise<Meta> { | 	public async fetch(noCache = false): Promise<Meta> { | ||||||
| 		if (!noCache && this.cache) return this.cache; | 		if (!noCache && this.cache) return this.cache; | ||||||
| 	 | 	 | ||||||
|  | @ -84,6 +87,7 @@ export class MetaService implements OnApplicationShutdown { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async update(data: Partial<Meta>): Promise<Meta> { | 	public async update(data: Partial<Meta>): Promise<Meta> { | ||||||
| 		const updated = await this.db.transaction(async transactionalEntityManager => { | 		const updated = await this.db.transaction(async transactionalEntityManager => { | ||||||
| 			const metas = await transactionalEntityManager.find(Meta, { | 			const metas = await transactionalEntityManager.find(Meta, { | ||||||
|  | @ -114,6 +118,7 @@ export class MetaService implements OnApplicationShutdown { | ||||||
| 		return updated; | 		return updated; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public onApplicationShutdown(signal?: string | undefined) { | 	public onApplicationShutdown(signal?: string | undefined) { | ||||||
| 		clearInterval(this.intervalId); | 		clearInterval(this.intervalId); | ||||||
| 		this.redisSubscriber.off('message', this.onMessage); | 		this.redisSubscriber.off('message', this.onMessage); | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ const treeAdapter = TreeAdapter.defaultTreeAdapter; | ||||||
| 
 | 
 | ||||||
| const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/; | const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/; | ||||||
| const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/; | const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class MfmService { | export class MfmService { | ||||||
|  | @ -23,6 +24,7 @@ export class MfmService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public fromHtml(html: string, hashtagNames?: string[]): string { | 	public fromHtml(html: string, hashtagNames?: string[]): string { | ||||||
| 		// some AP servers like Pixelfed use br tags as well as newlines
 | 		// some AP servers like Pixelfed use br tags as well as newlines
 | ||||||
| 		html = html.replace(/<br\s?\/?>\r?\n/gi, '\n'); | 		html = html.replace(/<br\s?\/?>\r?\n/gi, '\n'); | ||||||
|  | @ -228,6 +230,7 @@ export class MfmService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public toHtml(nodes: mfm.MfmNode[] | null, mentionedRemoteUsers: IMentionedRemoteUsers = []) { | 	public toHtml(nodes: mfm.MfmNode[] | null, mentionedRemoteUsers: IMentionedRemoteUsers = []) { | ||||||
| 		if (nodes == null) { | 		if (nodes == null) { | ||||||
| 			return null; | 			return null; | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ import { DI } from '@/di-symbols.js'; | ||||||
| import type { ModerationLogsRepository } from '@/models/index.js'; | import type { ModerationLogsRepository } from '@/models/index.js'; | ||||||
| import type { User } from '@/models/entities/User.js'; | import type { User } from '@/models/entities/User.js'; | ||||||
| import { IdService } from '@/core/IdService.js'; | import { IdService } from '@/core/IdService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class ModerationLogService { | export class ModerationLogService { | ||||||
|  | @ -14,6 +15,7 @@ export class ModerationLogService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async insertModerationLog(moderator: { id: User['id'] }, type: string, info?: Record<string, any>) { | 	public async insertModerationLog(moderator: { id: User['id'] }, type: string, info?: Record<string, any>) { | ||||||
| 		await this.moderationLogsRepository.insert({ | 		await this.moderationLogsRepository.insert({ | ||||||
| 			id: this.idService.genId(), | 			id: this.idService.genId(), | ||||||
|  |  | ||||||
|  | @ -64,6 +64,7 @@ class NotificationManager { | ||||||
| 		this.queue = []; | 		this.queue = []; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public push(notifiee: ILocalUser['id'], reason: NotificationType) { | 	public push(notifiee: ILocalUser['id'], reason: NotificationType) { | ||||||
| 		// 自分自身へは通知しない
 | 		// 自分自身へは通知しない
 | ||||||
| 		if (this.notifier.id === notifiee) return; | 		if (this.notifier.id === notifiee) return; | ||||||
|  | @ -83,6 +84,7 @@ class NotificationManager { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async deliver() { | 	public async deliver() { | ||||||
| 		for (const x of this.queue) { | 		for (const x of this.queue) { | ||||||
| 			// ミュート情報を取得
 | 			// ミュート情報を取得
 | ||||||
|  | @ -130,6 +132,7 @@ type Option = { | ||||||
| 	url?: string | null; | 	url?: string | null; | ||||||
| 	app?: App | null; | 	app?: App | null; | ||||||
| }; | }; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class NoteCreateService { | export class NoteCreateService { | ||||||
|  | @ -188,6 +191,7 @@ export class NoteCreateService { | ||||||
| 		private instanceChart: InstanceChart, | 		private instanceChart: InstanceChart, | ||||||
| 	) {} | 	) {} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async create(user: { | 	public async create(user: { | ||||||
| 		id: User['id']; | 		id: User['id']; | ||||||
| 		username: User['username']; | 		username: User['username']; | ||||||
|  | @ -307,6 +311,7 @@ export class NoteCreateService { | ||||||
| 		return note; | 		return note; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private 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({ | 		const insert = new Note({ | ||||||
| 			id: this.idService.genId(data.createdAt!), | 			id: this.idService.genId(data.createdAt!), | ||||||
|  | @ -403,6 +408,7 @@ export class NoteCreateService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async postNoteCreated(note: Note, user: { | 	private async postNoteCreated(note: Note, user: { | ||||||
| 		id: User['id']; | 		id: User['id']; | ||||||
| 		username: User['username']; | 		username: User['username']; | ||||||
|  | @ -644,6 +650,7 @@ export class NoteCreateService { | ||||||
| 		this.index(note); | 		this.index(note); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private incRenoteCount(renote: Note) { | 	private incRenoteCount(renote: Note) { | ||||||
| 		this.notesRepository.createQueryBuilder().update() | 		this.notesRepository.createQueryBuilder().update() | ||||||
| 			.set({ | 			.set({ | ||||||
|  | @ -654,6 +661,7 @@ export class NoteCreateService { | ||||||
| 			.execute(); | 			.execute(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private 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))) { | 		for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) { | ||||||
| 			const threadMuted = await this.noteThreadMutingsRepository.findOneBy({ | 			const threadMuted = await this.noteThreadMutingsRepository.findOneBy({ | ||||||
|  | @ -683,10 +691,12 @@ export class NoteCreateService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private saveReply(reply: Note, note: Note) { | 	private saveReply(reply: Note, note: Note) { | ||||||
| 		this.notesRepository.increment({ id: reply.id }, 'repliesCount', 1); | 		this.notesRepository.increment({ id: reply.id }, 'repliesCount', 1); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async renderNoteOrRenoteActivity(data: Option, note: Note) { | 	private async renderNoteOrRenoteActivity(data: Option, note: Note) { | ||||||
| 		if (data.localOnly) return null; | 		if (data.localOnly) return null; | ||||||
| 
 | 
 | ||||||
|  | @ -697,6 +707,7 @@ export class NoteCreateService { | ||||||
| 		return this.apRendererService.renderActivity(content); | 		return this.apRendererService.renderActivity(content); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private index(note: Note) { | 	private index(note: Note) { | ||||||
| 		if (note.text == null || this.config.elasticsearch == null) return; | 		if (note.text == null || this.config.elasticsearch == null) return; | ||||||
| 		/* | 		/* | ||||||
|  | @ -711,6 +722,7 @@ export class NoteCreateService { | ||||||
| 	});*/ | 	});*/ | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private incNotesCountOfUser(user: { id: User['id']; }) { | 	private incNotesCountOfUser(user: { id: User['id']; }) { | ||||||
| 		this.usersRepository.createQueryBuilder().update() | 		this.usersRepository.createQueryBuilder().update() | ||||||
| 			.set({ | 			.set({ | ||||||
|  | @ -721,6 +733,7 @@ export class NoteCreateService { | ||||||
| 			.execute(); | 			.execute(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async extractMentionedUsers(user: { host: User['host']; }, tokens: mfm.MfmNode[]): Promise<User[]> { | 	private async extractMentionedUsers(user: { host: User['host']; }, tokens: mfm.MfmNode[]): Promise<User[]> { | ||||||
| 		if (tokens == null) return []; | 		if (tokens == null) return []; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -15,6 +15,7 @@ import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; | ||||||
| import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; | import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; | ||||||
| import { UserEntityService } from '@/core/entities/UserEntityService.js'; | import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||||
| import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; | import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class NoteDeleteService { | export class NoteDeleteService { | ||||||
|  | @ -112,6 +113,7 @@ export class NoteDeleteService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async findCascadingNotes(note: Note) { | 	private async findCascadingNotes(note: Note) { | ||||||
| 		const cascadingNotes: Note[] = []; | 		const cascadingNotes: Note[] = []; | ||||||
| 
 | 
 | ||||||
|  | @ -134,6 +136,7 @@ export class NoteDeleteService { | ||||||
| 		return cascadingNotes.filter(note => note.userHost === null); // filter out non-local users
 | 		return cascadingNotes.filter(note => note.userHost === null); // filter out non-local users
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async getMentionedRemoteUsers(note: Note) { | 	private async getMentionedRemoteUsers(note: Note) { | ||||||
| 		const where = [] as any[]; | 		const where = [] as any[]; | ||||||
| 
 | 
 | ||||||
|  | @ -159,6 +162,7 @@ export class NoteDeleteService { | ||||||
| 		}) as IRemoteUser[]; | 		}) as IRemoteUser[]; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private 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.apDeliverManagerService.deliverToFollowers(user, content); | ||||||
| 		this.relayService.deliverToRelays(user, content); | 		this.relayService.deliverToRelays(user, content); | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ import type { Config } from '@/config.js'; | ||||||
| import { UserEntityService } from '@/core/entities/UserEntityService.js'; | import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||||
| import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; | import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; | ||||||
| import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; | import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class NotePiningService { | export class NotePiningService { | ||||||
|  | @ -40,6 +41,7 @@ export class NotePiningService { | ||||||
| 	 * @param user | 	 * @param user | ||||||
| 	 * @param noteId | 	 * @param noteId | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async addPinned(user: { id: User['id']; host: User['host']; }, noteId: Note['id']) { | 	public async addPinned(user: { id: User['id']; host: User['host']; }, noteId: Note['id']) { | ||||||
| 	// Fetch pinee
 | 	// Fetch pinee
 | ||||||
| 		const note = await this.notesRepository.findOneBy({ | 		const note = await this.notesRepository.findOneBy({ | ||||||
|  | @ -79,6 +81,7 @@ export class NotePiningService { | ||||||
| 	 * @param user | 	 * @param user | ||||||
| 	 * @param noteId | 	 * @param noteId | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async removePinned(user: { id: User['id']; host: User['host']; }, noteId: Note['id']) { | 	public async removePinned(user: { id: User['id']; host: User['host']; }, noteId: Note['id']) { | ||||||
| 	// Fetch unpinee
 | 	// Fetch unpinee
 | ||||||
| 		const note = await this.notesRepository.findOneBy({ | 		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) { | 	public async deliverPinnedChange(userId: User['id'], noteId: Note['id'], isAddition: boolean) { | ||||||
| 		const user = await this.usersRepository.findOneBy({ id: userId }); | 		const user = await this.usersRepository.findOneBy({ id: userId }); | ||||||
| 		if (user == null) throw new Error('user not found'); | 		if (user == null) throw new Error('user not found'); | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ import type { UsersRepository, NoteUnreadsRepository, MutingsRepository, NoteThr | ||||||
| import { UserEntityService } from '@/core/entities/UserEntityService.js'; | import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||||
| import { NotificationService } from './NotificationService.js'; | import { NotificationService } from './NotificationService.js'; | ||||||
| import { AntennaService } from './AntennaService.js'; | import { AntennaService } from './AntennaService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class NoteReadService { | export class NoteReadService { | ||||||
|  | @ -44,6 +45,7 @@ export class NoteReadService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async insertNoteUnread(userId: User['id'], note: Note, params: { | 	public async insertNoteUnread(userId: User['id'], note: Note, params: { | ||||||
| 		// NOTE: isSpecifiedがtrueならisMentionedは必ずfalse
 | 		// NOTE: isSpecifiedがtrueならisMentionedは必ずfalse
 | ||||||
| 		isSpecified: boolean; | 		isSpecified: boolean; | ||||||
|  | @ -94,6 +96,7 @@ export class NoteReadService { | ||||||
| 		}, 2000); | 		}, 2000); | ||||||
| 	}	 | 	}	 | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async read( | 	public async read( | ||||||
| 		userId: User['id'], | 		userId: User['id'], | ||||||
| 		notes: (Note | Packed<'Note'>)[], | 		notes: (Note | Packed<'Note'>)[], | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ import type { Notification } from '@/models/entities/Notification.js'; | ||||||
| import { UserEntityService } from '@/core/entities/UserEntityService.js'; | import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||||
| import { GlobalEventService } from './GlobalEventService.js'; | import { GlobalEventService } from './GlobalEventService.js'; | ||||||
| import { PushNotificationService } from './PushNotificationService.js'; | import { PushNotificationService } from './PushNotificationService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class NotificationService { | export class NotificationService { | ||||||
|  | @ -21,6 +22,7 @@ export class NotificationService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async readNotification( | 	public async readNotification( | ||||||
| 		userId: User['id'], | 		userId: User['id'], | ||||||
| 		notificationIds: Notification['id'][], | 		notificationIds: Notification['id'][], | ||||||
|  | @ -42,6 +44,7 @@ export class NotificationService { | ||||||
| 		else return this.postReadNotifications(userId, notificationIds); | 		else return this.postReadNotifications(userId, notificationIds); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async readNotificationByQuery( | 	public async readNotificationByQuery( | ||||||
| 		userId: User['id'], | 		userId: User['id'], | ||||||
| 		query: Record<string, any>, | 		query: Record<string, any>, | ||||||
|  | @ -55,11 +58,13 @@ export class NotificationService { | ||||||
| 		return this.readNotification(userId, notificationIds); | 		return this.readNotification(userId, notificationIds); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private postReadAllNotifications(userId: User['id']) { | 	private postReadAllNotifications(userId: User['id']) { | ||||||
| 		this.globalEventService.publishMainStream(userId, 'readAllNotifications'); | 		this.globalEventService.publishMainStream(userId, 'readAllNotifications'); | ||||||
| 		return this.pushNotificationService.pushNotification(userId, 'readAllNotifications', undefined); | 		return this.pushNotificationService.pushNotification(userId, 'readAllNotifications', undefined); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private postReadNotifications(userId: User['id'], notificationIds: Notification['id'][]) { | 	private postReadNotifications(userId: User['id'], notificationIds: Notification['id'][]) { | ||||||
| 		this.globalEventService.publishMainStream(userId, 'readNotifications', notificationIds); | 		this.globalEventService.publishMainStream(userId, 'readNotifications', notificationIds); | ||||||
| 		return this.pushNotificationService.pushNotification(userId, 'readNotifications', { notificationIds }); | 		return this.pushNotificationService.pushNotification(userId, 'readNotifications', { notificationIds }); | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ import { CreateNotificationService } from '@/core/CreateNotificationService.js'; | ||||||
| import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; | import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; | ||||||
| import { UserEntityService } from '@/core/entities/UserEntityService.js'; | import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||||
| import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; | import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class PollService { | export class PollService { | ||||||
|  | @ -40,6 +41,7 @@ export class PollService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async vote(user: CacheableUser, note: Note, choice: number) { | 	public async vote(user: CacheableUser, note: Note, choice: number) { | ||||||
| 		const poll = await this.pollsRepository.findOneBy({ noteId: note.id }); | 		const poll = await this.pollsRepository.findOneBy({ noteId: note.id }); | ||||||
| 	 | 	 | ||||||
|  | @ -99,6 +101,7 @@ export class PollService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async deliverQuestionUpdate(noteId: Note['id']) { | 	public async deliverQuestionUpdate(noteId: Note['id']) { | ||||||
| 		const note = await this.notesRepository.findOneBy({ id: noteId }); | 		const note = await this.notesRepository.findOneBy({ id: noteId }); | ||||||
| 		if (note == null) throw new Error('note not found'); | 		if (note == null) throw new Error('note not found'); | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ import type { UsersRepository } from '@/models/index.js'; | ||||||
| import type { ILocalUser, User } from '@/models/entities/User.js'; | import type { ILocalUser, User } from '@/models/entities/User.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import { MetaService } from '@/core/MetaService.js'; | import { MetaService } from '@/core/MetaService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class ProxyAccountService { | export class ProxyAccountService { | ||||||
|  | @ -14,6 +15,7 @@ export class ProxyAccountService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async fetch(): Promise<ILocalUser | null> { | 	public async fetch(): Promise<ILocalUser | null> { | ||||||
| 		const meta = await this.metaService.fetch(); | 		const meta = await this.metaService.fetch(); | ||||||
| 		if (meta.proxyAccountId == null) return null; | 		if (meta.proxyAccountId == null) return null; | ||||||
|  |  | ||||||
|  | @ -37,6 +37,7 @@ function truncateNotification(notification: Packed<'Notification'>): any { | ||||||
| 
 | 
 | ||||||
| 	return notification; | 	return notification; | ||||||
| } | } | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class PushNotificationService { | export class PushNotificationService { | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ import { DI } from '@/di-symbols.js'; | ||||||
| import type { User } from '@/models/entities/User.js'; | import type { User } from '@/models/entities/User.js'; | ||||||
| import type { UserProfilesRepository, FollowingsRepository, ChannelFollowingsRepository, MutedNotesRepository, BlockingsRepository, NoteThreadMutingsRepository, MutingsRepository } from '@/models/index.js'; | import type { UserProfilesRepository, FollowingsRepository, ChannelFollowingsRepository, MutedNotesRepository, BlockingsRepository, NoteThreadMutingsRepository, MutingsRepository } from '@/models/index.js'; | ||||||
| import type { SelectQueryBuilder } from 'typeorm'; | import type { SelectQueryBuilder } from 'typeorm'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class QueryService { | export class QueryService { | ||||||
|  | @ -59,6 +60,7 @@ export class QueryService { | ||||||
| 	}	 | 	}	 | ||||||
| 	 | 	 | ||||||
| 	// ここでいうBlockedは被Blockedの意
 | 	// ここでいうBlockedは被Blockedの意
 | ||||||
|  | 	@bindThis | ||||||
| 	public generateBlockedUserQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { | 	public generateBlockedUserQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { | ||||||
| 		const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking') | 		const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking') | ||||||
| 			.select('blocking.blockerId') | 			.select('blocking.blockerId') | ||||||
|  | @ -81,6 +83,7 @@ export class QueryService { | ||||||
| 		q.setParameters(blockingQuery.getParameters()); | 		q.setParameters(blockingQuery.getParameters()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public generateBlockQueryForUsers(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { | 	public generateBlockQueryForUsers(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { | ||||||
| 		const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking') | 		const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking') | ||||||
| 			.select('blocking.blockeeId') | 			.select('blocking.blockeeId') | ||||||
|  | @ -97,6 +100,7 @@ export class QueryService { | ||||||
| 		q.setParameters(blockedQuery.getParameters()); | 		q.setParameters(blockedQuery.getParameters()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public generateChannelQuery(q: SelectQueryBuilder<any>, me?: { id: User['id'] } | null): void { | 	public generateChannelQuery(q: SelectQueryBuilder<any>, me?: { id: User['id'] } | null): void { | ||||||
| 		if (me == null) { | 		if (me == null) { | ||||||
| 			q.andWhere('note.channelId IS NULL'); | 			q.andWhere('note.channelId IS NULL'); | ||||||
|  | @ -118,6 +122,7 @@ export class QueryService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public generateMutedNoteQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { | 	public generateMutedNoteQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { | ||||||
| 		const mutedQuery = this.mutedNotesRepository.createQueryBuilder('muted') | 		const mutedQuery = this.mutedNotesRepository.createQueryBuilder('muted') | ||||||
| 			.select('muted.noteId') | 			.select('muted.noteId') | ||||||
|  | @ -128,6 +133,7 @@ export class QueryService { | ||||||
| 		q.setParameters(mutedQuery.getParameters()); | 		q.setParameters(mutedQuery.getParameters()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public generateMutedNoteThreadQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { | 	public generateMutedNoteThreadQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { | ||||||
| 		const mutedQuery = this.noteThreadMutingsRepository.createQueryBuilder('threadMuted') | 		const mutedQuery = this.noteThreadMutingsRepository.createQueryBuilder('threadMuted') | ||||||
| 			.select('threadMuted.threadId') | 			.select('threadMuted.threadId') | ||||||
|  | @ -142,6 +148,7 @@ export class QueryService { | ||||||
| 		q.setParameters(mutedQuery.getParameters()); | 		q.setParameters(mutedQuery.getParameters()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public generateMutedUserQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }, exclude?: User): void { | 	public generateMutedUserQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }, exclude?: User): void { | ||||||
| 		const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') | 		const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') | ||||||
| 			.select('muting.muteeId') | 			.select('muting.muteeId') | ||||||
|  | @ -186,6 +193,7 @@ export class QueryService { | ||||||
| 		q.setParameters(mutingInstanceQuery.getParameters()); | 		q.setParameters(mutingInstanceQuery.getParameters()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public generateMutedUserQueryForUsers(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { | 	public generateMutedUserQueryForUsers(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { | ||||||
| 		const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') | 		const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') | ||||||
| 			.select('muting.muteeId') | 			.select('muting.muteeId') | ||||||
|  | @ -196,6 +204,7 @@ export class QueryService { | ||||||
| 		q.setParameters(mutingQuery.getParameters()); | 		q.setParameters(mutingQuery.getParameters()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public generateRepliesQuery(q: SelectQueryBuilder<any>, me?: Pick<User, 'id' | 'showTimelineReplies'> | null): void { | 	public generateRepliesQuery(q: SelectQueryBuilder<any>, me?: Pick<User, 'id' | 'showTimelineReplies'> | null): void { | ||||||
| 		if (me == null) { | 		if (me == null) { | ||||||
| 			q.andWhere(new Brackets(qb => { qb | 			q.andWhere(new Brackets(qb => { qb | ||||||
|  | @ -221,6 +230,7 @@ export class QueryService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public generateVisibilityQuery(q: SelectQueryBuilder<any>, me?: { id: User['id'] } | null): void { | 	public generateVisibilityQuery(q: SelectQueryBuilder<any>, me?: { id: User['id'] } | null): void { | ||||||
| 		// This code must always be synchronized with the checks in Notes.isVisibleForMe.
 | 		// This code must always be synchronized with the checks in Notes.isVisibleForMe.
 | ||||||
| 		if (me == null) { | 		if (me == null) { | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ import { DI } from '@/di-symbols.js'; | ||||||
| import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from './QueueModule.js'; | import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from './QueueModule.js'; | ||||||
| import type { ThinUser } from '../queue/types.js'; | import type { ThinUser } from '../queue/types.js'; | ||||||
| import type httpSignature from '@peertube/http-signature'; | import type httpSignature from '@peertube/http-signature'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class QueueService { | export class QueueService { | ||||||
|  | @ -24,6 +25,7 @@ export class QueueService { | ||||||
| 		@Inject('queue:webhookDeliver') public webhookDeliverQueue: WebhookDeliverQueue, | 		@Inject('queue:webhookDeliver') public webhookDeliverQueue: WebhookDeliverQueue, | ||||||
| 	) {} | 	) {} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public deliver(user: ThinUser, content: IActivity | null, to: string | null) { | 	public deliver(user: ThinUser, content: IActivity | null, to: string | null) { | ||||||
| 		if (content == null) return null; | 		if (content == null) return null; | ||||||
| 		if (to == null) return null; | 		if (to == null) return null; | ||||||
|  | @ -47,6 +49,7 @@ export class QueueService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public inbox(activity: IActivity, signature: httpSignature.IParsedSignature) { | 	public inbox(activity: IActivity, signature: httpSignature.IParsedSignature) { | ||||||
| 		const data = { | 		const data = { | ||||||
| 			activity: activity, | 			activity: activity, | ||||||
|  | @ -64,6 +67,7 @@ export class QueueService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public createDeleteDriveFilesJob(user: ThinUser) { | 	public createDeleteDriveFilesJob(user: ThinUser) { | ||||||
| 		return this.dbQueue.add('deleteDriveFiles', { | 		return this.dbQueue.add('deleteDriveFiles', { | ||||||
| 			user: user, | 			user: user, | ||||||
|  | @ -73,6 +77,7 @@ export class QueueService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public createExportCustomEmojisJob(user: ThinUser) { | 	public createExportCustomEmojisJob(user: ThinUser) { | ||||||
| 		return this.dbQueue.add('exportCustomEmojis', { | 		return this.dbQueue.add('exportCustomEmojis', { | ||||||
| 			user: user, | 			user: user, | ||||||
|  | @ -82,6 +87,7 @@ export class QueueService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public createExportNotesJob(user: ThinUser) { | 	public createExportNotesJob(user: ThinUser) { | ||||||
| 		return this.dbQueue.add('exportNotes', { | 		return this.dbQueue.add('exportNotes', { | ||||||
| 			user: user, | 			user: user, | ||||||
|  | @ -91,6 +97,7 @@ export class QueueService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public createExportFollowingJob(user: ThinUser, excludeMuting = false, excludeInactive = false) { | 	public createExportFollowingJob(user: ThinUser, excludeMuting = false, excludeInactive = false) { | ||||||
| 		return this.dbQueue.add('exportFollowing', { | 		return this.dbQueue.add('exportFollowing', { | ||||||
| 			user: user, | 			user: user, | ||||||
|  | @ -102,6 +109,7 @@ export class QueueService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public createExportMuteJob(user: ThinUser) { | 	public createExportMuteJob(user: ThinUser) { | ||||||
| 		return this.dbQueue.add('exportMuting', { | 		return this.dbQueue.add('exportMuting', { | ||||||
| 			user: user, | 			user: user, | ||||||
|  | @ -111,6 +119,7 @@ export class QueueService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public createExportBlockingJob(user: ThinUser) { | 	public createExportBlockingJob(user: ThinUser) { | ||||||
| 		return this.dbQueue.add('exportBlocking', { | 		return this.dbQueue.add('exportBlocking', { | ||||||
| 			user: user, | 			user: user, | ||||||
|  | @ -120,6 +129,7 @@ export class QueueService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public createExportUserListsJob(user: ThinUser) { | 	public createExportUserListsJob(user: ThinUser) { | ||||||
| 		return this.dbQueue.add('exportUserLists', { | 		return this.dbQueue.add('exportUserLists', { | ||||||
| 			user: user, | 			user: user, | ||||||
|  | @ -129,6 +139,7 @@ export class QueueService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public createImportFollowingJob(user: ThinUser, fileId: DriveFile['id']) { | 	public createImportFollowingJob(user: ThinUser, fileId: DriveFile['id']) { | ||||||
| 		return this.dbQueue.add('importFollowing', { | 		return this.dbQueue.add('importFollowing', { | ||||||
| 			user: user, | 			user: user, | ||||||
|  | @ -139,6 +150,7 @@ export class QueueService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public createImportMutingJob(user: ThinUser, fileId: DriveFile['id']) { | 	public createImportMutingJob(user: ThinUser, fileId: DriveFile['id']) { | ||||||
| 		return this.dbQueue.add('importMuting', { | 		return this.dbQueue.add('importMuting', { | ||||||
| 			user: user, | 			user: user, | ||||||
|  | @ -149,6 +161,7 @@ export class QueueService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public createImportBlockingJob(user: ThinUser, fileId: DriveFile['id']) { | 	public createImportBlockingJob(user: ThinUser, fileId: DriveFile['id']) { | ||||||
| 		return this.dbQueue.add('importBlocking', { | 		return this.dbQueue.add('importBlocking', { | ||||||
| 			user: user, | 			user: user, | ||||||
|  | @ -159,6 +172,7 @@ export class QueueService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public createImportUserListsJob(user: ThinUser, fileId: DriveFile['id']) { | 	public createImportUserListsJob(user: ThinUser, fileId: DriveFile['id']) { | ||||||
| 		return this.dbQueue.add('importUserLists', { | 		return this.dbQueue.add('importUserLists', { | ||||||
| 			user: user, | 			user: user, | ||||||
|  | @ -169,6 +183,7 @@ export class QueueService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public createImportCustomEmojisJob(user: ThinUser, fileId: DriveFile['id']) { | 	public createImportCustomEmojisJob(user: ThinUser, fileId: DriveFile['id']) { | ||||||
| 		return this.dbQueue.add('importCustomEmojis', { | 		return this.dbQueue.add('importCustomEmojis', { | ||||||
| 			user: user, | 			user: user, | ||||||
|  | @ -179,6 +194,7 @@ export class QueueService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public createDeleteAccountJob(user: ThinUser, opts: { soft?: boolean; } = {}) { | 	public createDeleteAccountJob(user: ThinUser, opts: { soft?: boolean; } = {}) { | ||||||
| 		return this.dbQueue.add('deleteAccount', { | 		return this.dbQueue.add('deleteAccount', { | ||||||
| 			user: user, | 			user: user, | ||||||
|  | @ -189,6 +205,7 @@ export class QueueService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public createDeleteObjectStorageFileJob(key: string) { | 	public createDeleteObjectStorageFileJob(key: string) { | ||||||
| 		return this.objectStorageQueue.add('deleteFile', { | 		return this.objectStorageQueue.add('deleteFile', { | ||||||
| 			key: key, | 			key: key, | ||||||
|  | @ -198,6 +215,7 @@ export class QueueService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public createCleanRemoteFilesJob() { | 	public createCleanRemoteFilesJob() { | ||||||
| 		return this.objectStorageQueue.add('cleanRemoteFiles', {}, { | 		return this.objectStorageQueue.add('cleanRemoteFiles', {}, { | ||||||
| 			removeOnComplete: true, | 			removeOnComplete: true, | ||||||
|  | @ -205,6 +223,7 @@ export class QueueService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public webhookDeliver(webhook: Webhook, type: typeof webhookEventTypes[number], content: unknown) { | 	public webhookDeliver(webhook: Webhook, type: typeof webhookEventTypes[number], content: unknown) { | ||||||
| 		const data = { | 		const data = { | ||||||
| 			type, | 			type, | ||||||
|  | @ -228,6 +247,7 @@ export class QueueService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public destroy() { | 	public destroy() { | ||||||
| 		this.deliverQueue.once('cleaned', (jobs, status) => { | 		this.deliverQueue.once('cleaned', (jobs, status) => { | ||||||
| 			//deliverLogger.succ(`Cleaned ${jobs.length} ${status} jobs`);
 | 			//deliverLogger.succ(`Cleaned ${jobs.length} ${status} jobs`);
 | ||||||
|  |  | ||||||
|  | @ -49,6 +49,7 @@ type DecodedReaction = { | ||||||
| 	 */ | 	 */ | ||||||
| 	host?: string | null; | 	host?: string | null; | ||||||
| }; | }; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class ReactionService { | 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) { | 	public async create(user: { id: User['id']; host: User['host']; }, note: Note, reaction?: string) { | ||||||
| 		// Check blocking
 | 		// Check blocking
 | ||||||
| 		if (note.userId !== user.id) { | 		if (note.userId !== user.id) { | ||||||
|  | @ -196,6 +198,7 @@ export class ReactionService { | ||||||
| 		//#endregion
 | 		//#endregion
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async delete(user: { id: User['id']; host: User['host']; }, note: Note) { | 	public async delete(user: { id: User['id']; host: User['host']; }, note: Note) { | ||||||
| 		// if already unreacted
 | 		// if already unreacted
 | ||||||
| 		const exist = await this.noteReactionsRepository.findOneBy({ | 		const exist = await this.noteReactionsRepository.findOneBy({ | ||||||
|  | @ -244,11 +247,13 @@ export class ReactionService { | ||||||
| 		//#endregion
 | 		//#endregion
 | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	@bindThis | ||||||
| 	public async getFallbackReaction(): Promise<string> { | 	public async getFallbackReaction(): Promise<string> { | ||||||
| 		const meta = await this.metaService.fetch(); | 		const meta = await this.metaService.fetch(); | ||||||
| 		return meta.useStarForReactionFallback ? '⭐' : '👍'; | 		return meta.useStarForReactionFallback ? '⭐' : '👍'; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public convertLegacyReactions(reactions: Record<string, number>) { | 	public convertLegacyReactions(reactions: Record<string, number>) { | ||||||
| 		const _reactions = {} as Record<string, number>; | 		const _reactions = {} as Record<string, number>; | ||||||
| 
 | 
 | ||||||
|  | @ -279,6 +284,7 @@ export class ReactionService { | ||||||
| 		return _reactions2; | 		return _reactions2; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async toDbReaction(reaction?: string | null, reacterHost?: string | null): Promise<string> { | 	public async toDbReaction(reaction?: string | null, reacterHost?: string | null): Promise<string> { | ||||||
| 		if (reaction == null) return await this.getFallbackReaction(); | 		if (reaction == null) return await this.getFallbackReaction(); | ||||||
| 
 | 
 | ||||||
|  | @ -311,6 +317,7 @@ export class ReactionService { | ||||||
| 		return await this.getFallbackReaction(); | 		return await this.getFallbackReaction(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public decodeReaction(str: string): DecodedReaction { | 	public decodeReaction(str: string): DecodedReaction { | ||||||
| 		const custom = str.match(/^:([\w+-]+)(?:@([\w.-]+))?:$/); | 		const custom = str.match(/^:([\w+-]+)(?:@([\w.-]+))?:$/); | ||||||
| 
 | 
 | ||||||
|  | @ -332,6 +339,7 @@ export class ReactionService { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public convertLegacyReaction(reaction: string): string { | 	public convertLegacyReaction(reaction: string): string { | ||||||
| 		reaction = this.decodeReaction(reaction).reaction; | 		reaction = this.decodeReaction(reaction).reaction; | ||||||
| 		if (Object.keys(legacies).includes(reaction)) return legacies[reaction]; | 		if (Object.keys(legacies).includes(reaction)) return legacies[reaction]; | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ import { DI } from '@/di-symbols.js'; | ||||||
| import { deepClone } from '@/misc/clone.js'; | import { deepClone } from '@/misc/clone.js'; | ||||||
| 
 | 
 | ||||||
| const ACTOR_USERNAME = 'relay.actor' as const; | const ACTOR_USERNAME = 'relay.actor' as const; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class RelayService { | export class RelayService { | ||||||
|  | @ -32,6 +33,7 @@ export class RelayService { | ||||||
| 		this.relaysCache = new Cache<Relay[]>(1000 * 60 * 10); | 		this.relaysCache = new Cache<Relay[]>(1000 * 60 * 10); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async getRelayActor(): Promise<ILocalUser> { | 	private async getRelayActor(): Promise<ILocalUser> { | ||||||
| 		const user = await this.usersRepository.findOneBy({ | 		const user = await this.usersRepository.findOneBy({ | ||||||
| 			host: IsNull(), | 			host: IsNull(), | ||||||
|  | @ -44,6 +46,7 @@ export class RelayService { | ||||||
| 		return created as ILocalUser; | 		return created as ILocalUser; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async addRelay(inbox: string): Promise<Relay> { | 	public async addRelay(inbox: string): Promise<Relay> { | ||||||
| 		const relay = await this.relaysRepository.insert({ | 		const relay = await this.relaysRepository.insert({ | ||||||
| 			id: this.idService.genId(), | 			id: this.idService.genId(), | ||||||
|  | @ -59,6 +62,7 @@ export class RelayService { | ||||||
| 		return relay; | 		return relay; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async removeRelay(inbox: string): Promise<void> { | 	public async removeRelay(inbox: string): Promise<void> { | ||||||
| 		const relay = await this.relaysRepository.findOneBy({ | 		const relay = await this.relaysRepository.findOneBy({ | ||||||
| 			inbox, | 			inbox, | ||||||
|  | @ -77,11 +81,13 @@ export class RelayService { | ||||||
| 		await this.relaysRepository.delete(relay.id); | 		await this.relaysRepository.delete(relay.id); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async listRelay(): Promise<Relay[]> { | 	public async listRelay(): Promise<Relay[]> { | ||||||
| 		const relays = await this.relaysRepository.find(); | 		const relays = await this.relaysRepository.find(); | ||||||
| 		return relays; | 		return relays; | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	@bindThis | ||||||
| 	public async relayAccepted(id: string): Promise<string> { | 	public async relayAccepted(id: string): Promise<string> { | ||||||
| 		const result = await this.relaysRepository.update(id, { | 		const result = await this.relaysRepository.update(id, { | ||||||
| 			status: 'accepted', | 			status: 'accepted', | ||||||
|  | @ -90,6 +96,7 @@ export class RelayService { | ||||||
| 		return JSON.stringify(result); | 		return JSON.stringify(result); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async relayRejected(id: string): Promise<string> { | 	public async relayRejected(id: string): Promise<string> { | ||||||
| 		const result = await this.relaysRepository.update(id, { | 		const result = await this.relaysRepository.update(id, { | ||||||
| 			status: 'rejected', | 			status: 'rejected', | ||||||
|  | @ -98,6 +105,7 @@ export class RelayService { | ||||||
| 		return JSON.stringify(result); | 		return JSON.stringify(result); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async deliverToRelays(user: { id: User['id']; host: null; }, activity: any): Promise<void> { | 	public async deliverToRelays(user: { id: User['id']; host: null; }, activity: any): Promise<void> { | ||||||
| 		if (activity == null) return; | 		if (activity == null) return; | ||||||
| 	 | 	 | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| import { Inject, Injectable } from '@nestjs/common'; | import { Inject, Injectable } from '@nestjs/common'; | ||||||
| import type Logger from '@/logger.js'; | import type Logger from '@/logger.js'; | ||||||
| import { LoggerService } from '@/core/LoggerService.js'; | import { LoggerService } from '@/core/LoggerService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class RemoteLoggerService { | export class RemoteLoggerService { | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ import { UtilityService } from '@/core/UtilityService.js'; | ||||||
| import { WebfingerService } from '@/core/WebfingerService.js'; | import { WebfingerService } from '@/core/WebfingerService.js'; | ||||||
| import { RemoteLoggerService } from '@/core/RemoteLoggerService.js'; | import { RemoteLoggerService } from '@/core/RemoteLoggerService.js'; | ||||||
| import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; | import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class RemoteUserResolveService { | export class RemoteUserResolveService { | ||||||
|  | @ -31,6 +32,7 @@ export class RemoteUserResolveService { | ||||||
| 		this.logger = this.remoteLoggerService.logger.createSubLogger('resolve-user'); | 		this.logger = this.remoteLoggerService.logger.createSubLogger('resolve-user'); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async resolveUser(username: string, host: string | null): Promise<User> { | 	public async resolveUser(username: string, host: string | null): Promise<User> { | ||||||
| 		const usernameLower = username.toLowerCase(); | 		const usernameLower = username.toLowerCase(); | ||||||
| 	 | 	 | ||||||
|  | @ -116,6 +118,7 @@ export class RemoteUserResolveService { | ||||||
| 		return user; | 		return user; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async resolveSelf(acctLower: string) { | 	private async resolveSelf(acctLower: string) { | ||||||
| 		this.logger.info(`WebFinger for ${chalk.yellow(acctLower)}`); | 		this.logger.info(`WebFinger for ${chalk.yellow(acctLower)}`); | ||||||
| 		const finger = await this.webfingerService.webfinger(acctLower).catch(err => { | 		const finger = await this.webfingerService.webfinger(acctLower).catch(err => { | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import { DI } from '@/di-symbols.js'; | ||||||
| import type { Config } from '@/config.js'; | import type { Config } from '@/config.js'; | ||||||
| import type { Meta } from '@/models/entities/Meta.js'; | import type { Meta } from '@/models/entities/Meta.js'; | ||||||
| import { HttpRequestService } from '@/core/HttpRequestService.js'; | import { HttpRequestService } from '@/core/HttpRequestService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class S3Service { | export class S3Service { | ||||||
|  | @ -16,6 +17,7 @@ export class S3Service { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public getS3(meta: Meta) { | 	public getS3(meta: Meta) { | ||||||
| 		const u = meta.objectStorageEndpoint != null | 		const u = meta.objectStorageEndpoint != null | ||||||
| 			? `${meta.objectStorageUseSSL ? 'https://' : 'http://'}${meta.objectStorageEndpoint}` | 			? `${meta.objectStorageUseSSL ? 'https://' : 'http://'}${meta.objectStorageEndpoint}` | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ import generateUserToken from '@/misc/generate-native-user-token.js'; | ||||||
| import UsersChart from './chart/charts/users.js'; | import UsersChart from './chart/charts/users.js'; | ||||||
| import { UserEntityService } from '@/core/entities/UserEntityService.js'; | import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||||
| import { UtilityService } from './UtilityService.js'; | import { UtilityService } from './UtilityService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class SignupService { | export class SignupService { | ||||||
|  | @ -37,6 +38,7 @@ export class SignupService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async signup(opts: { | 	public async signup(opts: { | ||||||
| 		username: User['username']; | 		username: User['username']; | ||||||
| 		password?: string | null; | 		password?: string | null; | ||||||
|  |  | ||||||
|  | @ -103,6 +103,7 @@ function PEMString(pemBuffer: Buffer, type = 'CERTIFICATE') { | ||||||
| 		`\n-----END ${type}-----\n` | 		`\n-----END ${type}-----\n` | ||||||
| 	); | 	); | ||||||
| } | } | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class TwoFactorAuthenticationService { | export class TwoFactorAuthenticationService { | ||||||
|  | @ -115,6 +116,7 @@ export class TwoFactorAuthenticationService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public hash(data: Buffer) { | 	public hash(data: Buffer) { | ||||||
| 		return crypto | 		return crypto | ||||||
| 			.createHash('sha256') | 			.createHash('sha256') | ||||||
|  | @ -122,6 +124,7 @@ export class TwoFactorAuthenticationService { | ||||||
| 			.digest(); | 			.digest(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public verifySignin({ | 	public verifySignin({ | ||||||
| 		publicKey, | 		publicKey, | ||||||
| 		authenticatorData, | 		authenticatorData, | ||||||
|  | @ -159,6 +162,7 @@ export class TwoFactorAuthenticationService { | ||||||
| 			.verify(PEMString(publicKey), signature); | 			.verify(PEMString(publicKey), signature); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public getProcedures() { | 	public getProcedures() { | ||||||
| 		return { | 		return { | ||||||
| 			none: { | 			none: { | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||||
| import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; | import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; | ||||||
| import { LoggerService } from '@/core/LoggerService.js'; | import { LoggerService } from '@/core/LoggerService.js'; | ||||||
| import { WebhookService } from '@/core/WebhookService.js'; | import { WebhookService } from '@/core/WebhookService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class UserBlockingService { | export class UserBlockingService { | ||||||
|  | @ -50,6 +51,7 @@ export class UserBlockingService { | ||||||
| 		this.logger = this.loggerService.getLogger('user-block'); | 		this.logger = this.loggerService.getLogger('user-block'); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async block(blocker: User, blockee: User) { | 	public async block(blocker: User, blockee: User) { | ||||||
| 		await Promise.all([ | 		await Promise.all([ | ||||||
| 			this.cancelRequest(blocker, blockee), | 			this.cancelRequest(blocker, blockee), | ||||||
|  | @ -76,6 +78,7 @@ export class UserBlockingService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async cancelRequest(follower: User, followee: User) { | 	private async cancelRequest(follower: User, followee: User) { | ||||||
| 		const request = await this.followRequestsRepository.findOneBy({ | 		const request = await this.followRequestsRepository.findOneBy({ | ||||||
| 			followeeId: followee.id, | 			followeeId: followee.id, | ||||||
|  | @ -126,6 +129,7 @@ export class UserBlockingService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async unFollow(follower: User, followee: User) { | 	private async unFollow(follower: User, followee: User) { | ||||||
| 		const following = await this.followingsRepository.findOneBy({ | 		const following = await this.followingsRepository.findOneBy({ | ||||||
| 			followerId: follower.id, | 			followerId: follower.id, | ||||||
|  | @ -167,6 +171,7 @@ export class UserBlockingService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async removeFromList(listOwner: User, user: User) { | 	private async removeFromList(listOwner: User, user: User) { | ||||||
| 		const userLists = await this.userListsRepository.findBy({ | 		const userLists = await this.userListsRepository.findBy({ | ||||||
| 			userId: listOwner.id, | 			userId: listOwner.id, | ||||||
|  | @ -180,6 +185,7 @@ export class UserBlockingService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async unblock(blocker: CacheableUser, blockee: CacheableUser) { | 	public async unblock(blocker: CacheableUser, blockee: CacheableUser) { | ||||||
| 		const blocking = await this.blockingsRepository.findOneBy({ | 		const blocking = await this.blockingsRepository.findOneBy({ | ||||||
| 			blockerId: blocker.id, | 			blockerId: blocker.id, | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ import type { CacheableLocalUser, CacheableUser, ILocalUser } from '@/models/ent | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import { UserEntityService } from '@/core/entities/UserEntityService.js'; | import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||||
| import type { OnApplicationShutdown } from '@nestjs/common'; | import type { OnApplicationShutdown } from '@nestjs/common'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class UserCacheService implements OnApplicationShutdown { | export class UserCacheService implements OnApplicationShutdown { | ||||||
|  | @ -23,7 +24,7 @@ export class UserCacheService implements OnApplicationShutdown { | ||||||
| 
 | 
 | ||||||
| 		private userEntityService: UserEntityService, | 		private userEntityService: UserEntityService, | ||||||
| 	) { | 	) { | ||||||
| 		this.onMessage = this.onMessage.bind(this); | 		//this.onMessage = this.onMessage.bind(this);
 | ||||||
| 
 | 
 | ||||||
| 		this.userByIdCache = new Cache<CacheableUser>(Infinity); | 		this.userByIdCache = new Cache<CacheableUser>(Infinity); | ||||||
| 		this.localUserByNativeTokenCache = new Cache<CacheableLocalUser | null>(Infinity); | 		this.localUserByNativeTokenCache = new Cache<CacheableLocalUser | null>(Infinity); | ||||||
|  | @ -33,6 +34,7 @@ export class UserCacheService implements OnApplicationShutdown { | ||||||
| 		this.redisSubscriber.on('message', this.onMessage); | 		this.redisSubscriber.on('message', this.onMessage); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async onMessage(_: string, data: string): Promise<void> { | 	private async onMessage(_: string, data: string): Promise<void> { | ||||||
| 		const obj = JSON.parse(data); | 		const obj = JSON.parse(data); | ||||||
| 
 | 
 | ||||||
|  | @ -68,6 +70,7 @@ export class UserCacheService implements OnApplicationShutdown { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public onApplicationShutdown(signal?: string | undefined) { | 	public onApplicationShutdown(signal?: string | undefined) { | ||||||
| 		this.redisSubscriber.off('message', this.onMessage); | 		this.redisSubscriber.off('message', this.onMessage); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -31,6 +31,7 @@ type Remote = IRemoteUser | { | ||||||
| 	inbox: IRemoteUser['inbox']; | 	inbox: IRemoteUser['inbox']; | ||||||
| }; | }; | ||||||
| type Both = Local | Remote; | type Both = Local | Remote; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class UserFollowingService { | 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<void> { | 	public async follow(_follower: { id: User['id'] }, _followee: { id: User['id'] }, requestId?: string): Promise<void> { | ||||||
| 		const [follower, followee] = await Promise.all([ | 		const [follower, followee] = await Promise.all([ | ||||||
| 			this.usersRepository.findOneByOrFail({ id: _follower.id }), | 			this.usersRepository.findOneByOrFail({ id: _follower.id }), | ||||||
|  | @ -140,6 +142,7 @@ export class UserFollowingService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async insertFollowingDoc( | 	private async insertFollowingDoc( | ||||||
| 		followee: { | 		followee: { | ||||||
| 			id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'] | 			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( | 	public async unfollow( | ||||||
| 		follower: { | 		follower: { | ||||||
| 			id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; | 			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( | 	private async decrementFollowing( | ||||||
| 		follower: {id: User['id']; host: User['host']; }, | 		follower: {id: User['id']; host: User['host']; }, | ||||||
| 		followee: { 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); | 		this.perUserFollowingChart.update(follower, followee, false); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async createFollowRequest( | 	public async createFollowRequest( | ||||||
| 		follower: { | 		follower: { | ||||||
| 			id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; | 			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( | 	public async cancelFollowRequest( | ||||||
| 		followee: { | 		followee: { | ||||||
| 			id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox'] | 			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)); | 		}).then(packed => this.globalEventServie.publishMainStream(followee.id, 'meUpdated', packed)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async acceptFollowRequest( | 	public async acceptFollowRequest( | ||||||
| 		followee: { | 		followee: { | ||||||
| 			id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; | 			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)); | 		}).then(packed => this.globalEventServie.publishMainStream(followee.id, 'meUpdated', packed)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async acceptAllFollowRequests( | 	public async acceptAllFollowRequests( | ||||||
| 		user: { | 		user: { | ||||||
| 			id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; | 			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 | 	 * API following/request/reject | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async rejectFollowRequest(user: Local, follower: Both): Promise<void> { | 	public async rejectFollowRequest(user: Local, follower: Both): Promise<void> { | ||||||
| 		if (this.userEntityService.isRemoteUser(follower)) { | 		if (this.userEntityService.isRemoteUser(follower)) { | ||||||
| 			this.deliverReject(user, follower); | 			this.deliverReject(user, follower); | ||||||
|  | @ -491,6 +501,7 @@ export class UserFollowingService { | ||||||
| 	/** | 	/** | ||||||
| 	 * API following/reject | 	 * API following/reject | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async rejectFollow(user: Local, follower: Both): Promise<void> { | 	public async rejectFollow(user: Local, follower: Both): Promise<void> { | ||||||
| 		if (this.userEntityService.isRemoteUser(follower)) { | 		if (this.userEntityService.isRemoteUser(follower)) { | ||||||
| 			this.deliverReject(user, follower); | 			this.deliverReject(user, follower); | ||||||
|  | @ -506,6 +517,7 @@ export class UserFollowingService { | ||||||
| 	/** | 	/** | ||||||
| 	 * AP Reject/Follow | 	 * AP Reject/Follow | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async remoteReject(actor: Remote, follower: Local): Promise<void> { | 	public async remoteReject(actor: Remote, follower: Local): Promise<void> { | ||||||
| 		await this.removeFollowRequest(actor, follower); | 		await this.removeFollowRequest(actor, follower); | ||||||
| 		await this.removeFollow(actor, follower); | 		await this.removeFollow(actor, follower); | ||||||
|  | @ -515,6 +527,7 @@ export class UserFollowingService { | ||||||
| 	/** | 	/** | ||||||
| 	 * Remove follow request record | 	 * Remove follow request record | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	private async removeFollowRequest(followee: Both, follower: Both): Promise<void> { | 	private async removeFollowRequest(followee: Both, follower: Both): Promise<void> { | ||||||
| 		const request = await this.followRequestsRepository.findOneBy({ | 		const request = await this.followRequestsRepository.findOneBy({ | ||||||
| 			followeeId: followee.id, | 			followeeId: followee.id, | ||||||
|  | @ -529,6 +542,7 @@ export class UserFollowingService { | ||||||
| 	/** | 	/** | ||||||
| 	 * Remove follow record | 	 * Remove follow record | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	private async removeFollow(followee: Both, follower: Both): Promise<void> { | 	private async removeFollow(followee: Both, follower: Both): Promise<void> { | ||||||
| 		const following = await this.followingsRepository.findOneBy({ | 		const following = await this.followingsRepository.findOneBy({ | ||||||
| 			followeeId: followee.id, | 			followeeId: followee.id, | ||||||
|  | @ -544,6 +558,7 @@ export class UserFollowingService { | ||||||
| 	/** | 	/** | ||||||
| 	 * Deliver Reject to remote | 	 * Deliver Reject to remote | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	private async deliverReject(followee: Local, follower: Remote): Promise<void> { | 	private async deliverReject(followee: Local, follower: Remote): Promise<void> { | ||||||
| 		const request = await this.followRequestsRepository.findOneBy({ | 		const request = await this.followRequestsRepository.findOneBy({ | ||||||
| 			followeeId: followee.id, | 			followeeId: followee.id, | ||||||
|  | @ -557,6 +572,7 @@ export class UserFollowingService { | ||||||
| 	/** | 	/** | ||||||
| 	 * Publish unfollow to local | 	 * Publish unfollow to local | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	private async publishUnfollow(followee: Both, follower: Local): Promise<void> { | 	private async publishUnfollow(followee: Both, follower: Local): Promise<void> { | ||||||
| 		const packedFollowee = await this.userEntityService.pack(followee.id, follower, { | 		const packedFollowee = await this.userEntityService.pack(followee.id, follower, { | ||||||
| 			detail: true, | 			detail: true, | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ import type { UserKeypairsRepository } from '@/models/index.js'; | ||||||
| import { Cache } from '@/misc/cache.js'; | import { Cache } from '@/misc/cache.js'; | ||||||
| import type { UserKeypair } from '@/models/entities/UserKeypair.js'; | import type { UserKeypair } from '@/models/entities/UserKeypair.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class UserKeypairStoreService { | export class UserKeypairStoreService { | ||||||
|  | @ -16,6 +17,7 @@ export class UserKeypairStoreService { | ||||||
| 		this.cache = new Cache<UserKeypair>(Infinity); | 		this.cache = new Cache<UserKeypair>(Infinity); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async getUserKeypair(userId: User['id']): Promise<UserKeypair> { | 	public async getUserKeypair(userId: User['id']): Promise<UserKeypair> { | ||||||
| 		return await this.cache.fetch(userId, () => this.userKeypairsRepository.findOneByOrFail({ userId: userId })); | 		return await this.cache.fetch(userId, () => this.userKeypairsRepository.findOneByOrFail({ userId: userId })); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ import { GlobalEventService } from '@/core/GlobalEventService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import { UserEntityService } from '@/core/entities/UserEntityService.js'; | import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||||
| import { ProxyAccountService } from '@/core/ProxyAccountService.js'; | import { ProxyAccountService } from '@/core/ProxyAccountService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class UserListService { | export class UserListService { | ||||||
|  | @ -27,6 +28,7 @@ export class UserListService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async push(target: User, list: UserList) { | 	public async push(target: User, list: UserList) { | ||||||
| 		await this.userListJoiningsRepository.insert({ | 		await this.userListJoiningsRepository.insert({ | ||||||
| 			id: this.idService.genId(), | 			id: this.idService.genId(), | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import { QueueService } from '@/core/QueueService.js'; | ||||||
| import { GlobalEventService } from '@/core/GlobalEventService.js'; | import { GlobalEventService } from '@/core/GlobalEventService.js'; | ||||||
| import type { User } from '@/models/entities/User.js'; | import type { User } from '@/models/entities/User.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class UserMutingService { | export class UserMutingService { | ||||||
|  | @ -21,6 +22,7 @@ export class UserMutingService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async mute(user: User, target: User): Promise<void> { | 	public async mute(user: User, target: User): Promise<void> { | ||||||
| 		await this.mutingsRepository.insert({ | 		await this.mutingsRepository.insert({ | ||||||
| 			id: this.idService.genId(), | 			id: this.idService.genId(), | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ import { DI } from '@/di-symbols.js'; | ||||||
| import type { Config } from '@/config.js'; | import type { Config } from '@/config.js'; | ||||||
| import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; | import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; | ||||||
| import { UserEntityService } from '@/core/entities/UserEntityService.js'; | import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class UserSuspendService { | export class UserSuspendService { | ||||||
|  | @ -28,6 +29,7 @@ export class UserSuspendService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async doPostSuspend(user: { id: User['id']; host: User['host'] }): Promise<void> { | 	public async doPostSuspend(user: { id: User['id']; host: User['host'] }): Promise<void> { | ||||||
| 		this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true }); | 		this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true }); | ||||||
| 	 | 	 | ||||||
|  | @ -57,6 +59,7 @@ export class UserSuspendService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async doPostUnsuspend(user: User): Promise<void> { | 	public async doPostUnsuspend(user: User): Promise<void> { | ||||||
| 		this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false }); | 		this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false }); | ||||||
| 	 | 	 | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ import { toASCII } from 'punycode'; | ||||||
| import { Inject, Injectable } from '@nestjs/common'; | import { Inject, Injectable } from '@nestjs/common'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import type { Config } from '@/config.js'; | import type { Config } from '@/config.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class UtilityService { | export class UtilityService { | ||||||
|  | @ -12,24 +13,29 @@ export class UtilityService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public getFullApAccount(username: string, host: string | null): string { | 	public getFullApAccount(username: string, host: string | null): string { | ||||||
| 		return host ? `${username}@${this.toPuny(host)}` : `${username}@${this.toPuny(this.config.host)}`; | 		return host ? `${username}@${this.toPuny(host)}` : `${username}@${this.toPuny(this.config.host)}`; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public isSelfHost(host: string | null): boolean { | 	public isSelfHost(host: string | null): boolean { | ||||||
| 		if (host == null) return true; | 		if (host == null) return true; | ||||||
| 		return this.toPuny(this.config.host) === this.toPuny(host); | 		return this.toPuny(this.config.host) === this.toPuny(host); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public extractDbHost(uri: string): string { | 	public extractDbHost(uri: string): string { | ||||||
| 		const url = new URL(uri); | 		const url = new URL(uri); | ||||||
| 		return this.toPuny(url.hostname); | 		return this.toPuny(url.hostname); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public toPuny(host: string): string { | 	public toPuny(host: string): string { | ||||||
| 		return toASCII(host.toLowerCase()); | 		return toASCII(host.toLowerCase()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public toPunyNullable(host: string | null | undefined): string | null { | 	public toPunyNullable(host: string | null | undefined): string | null { | ||||||
| 		if (host == null) return null; | 		if (host == null) return null; | ||||||
| 		return toASCII(host.toLowerCase()); | 		return toASCII(host.toLowerCase()); | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import type { Config } from '@/config.js'; | ||||||
| import { ImageProcessingService } from '@/core/ImageProcessingService.js'; | import { ImageProcessingService } from '@/core/ImageProcessingService.js'; | ||||||
| import type { IImage } from '@/core/ImageProcessingService.js'; | import type { IImage } from '@/core/ImageProcessingService.js'; | ||||||
| import { createTempDir } from '@/misc/create-temp.js'; | import { createTempDir } from '@/misc/create-temp.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class VideoProcessingService { | export class VideoProcessingService { | ||||||
|  | @ -16,6 +17,7 @@ export class VideoProcessingService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async generateVideoThumbnail(source: string): Promise<IImage> { | 	public async generateVideoThumbnail(source: string): Promise<IImage> { | ||||||
| 		const [dir, cleanup] = await createTempDir(); | 		const [dir, cleanup] = await createTempDir(); | ||||||
| 	 | 	 | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ type IWebFinger = { | ||||||
| 	links: ILink[]; | 	links: ILink[]; | ||||||
| 	subject: string; | 	subject: string; | ||||||
| }; | }; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class WebfingerService { | export class WebfingerService { | ||||||
|  | @ -25,12 +26,14 @@ export class WebfingerService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async webfinger(query: string): Promise<IWebFinger> { | 	public async webfinger(query: string): Promise<IWebFinger> { | ||||||
| 		const url = this.genUrl(query); | 		const url = this.genUrl(query); | ||||||
| 
 | 
 | ||||||
| 		return await this.httpRequestService.getJson(url, 'application/jrd+json, application/json') as IWebFinger; | 		return await this.httpRequestService.getJson(url, 'application/jrd+json, application/json') as IWebFinger; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private genUrl(query: string): string { | 	private genUrl(query: string): string { | ||||||
| 		if (query.match(/^https?:\/\//)) { | 		if (query.match(/^https?:\/\//)) { | ||||||
| 			const u = new URL(query); | 			const u = new URL(query); | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ import type { WebhooksRepository } from '@/models/index.js'; | ||||||
| import type { Webhook } from '@/models/entities/Webhook.js'; | import type { Webhook } from '@/models/entities/Webhook.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import type { OnApplicationShutdown } from '@nestjs/common'; | import type { OnApplicationShutdown } from '@nestjs/common'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class WebhookService implements OnApplicationShutdown { | export class WebhookService implements OnApplicationShutdown { | ||||||
|  | @ -17,10 +18,11 @@ export class WebhookService implements OnApplicationShutdown { | ||||||
| 		@Inject(DI.webhooksRepository) | 		@Inject(DI.webhooksRepository) | ||||||
| 		private webhooksRepository: WebhooksRepository, | 		private webhooksRepository: WebhooksRepository, | ||||||
| 	) { | 	) { | ||||||
| 		this.onMessage = this.onMessage.bind(this); | 		//this.onMessage = this.onMessage.bind(this);
 | ||||||
| 		this.redisSubscriber.on('message', this.onMessage); | 		this.redisSubscriber.on('message', this.onMessage); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async getActiveWebhooks() { | 	public async getActiveWebhooks() { | ||||||
| 		if (!this.webhooksFetched) { | 		if (!this.webhooksFetched) { | ||||||
| 			this.webhooks = await this.webhooksRepository.findBy({ | 			this.webhooks = await this.webhooksRepository.findBy({ | ||||||
|  | @ -32,6 +34,7 @@ export class WebhookService implements OnApplicationShutdown { | ||||||
| 		return this.webhooks; | 		return this.webhooks; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async onMessage(_: string, data: string): Promise<void> { | 	private async onMessage(_: string, data: string): Promise<void> { | ||||||
| 		const obj = JSON.parse(data); | 		const obj = JSON.parse(data); | ||||||
| 
 | 
 | ||||||
|  | @ -64,6 +67,7 @@ export class WebhookService implements OnApplicationShutdown { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public onApplicationShutdown(signal?: string | undefined) { | 	public onApplicationShutdown(signal?: string | undefined) { | ||||||
| 		this.redisSubscriber.off('message', this.onMessage); | 		this.redisSubscriber.off('message', this.onMessage); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ import promiseLimit from 'promise-limit'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import type { CacheableRemoteUser, CacheableUser } from '@/models/entities/User.js'; | import type { CacheableRemoteUser, CacheableUser } from '@/models/entities/User.js'; | ||||||
| import { concat, toArray, toSingle, unique } from '@/misc/prelude/array.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 { 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 { ApPersonService } from './models/ApPersonService.js'; | ||||||
| import type { ApObject } from './type.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<AudienceInfo> { | 	public async parseAudience(actor: CacheableRemoteUser, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise<AudienceInfo> { | ||||||
| 		const toGroups = this.groupingAudience(getApIds(to), actor); | 		const toGroups = this.groupingAudience(getApIds(to), actor); | ||||||
| 		const ccGroups = this.groupingAudience(getApIds(cc), actor); | 		const ccGroups = this.groupingAudience(getApIds(cc), actor); | ||||||
|  | @ -66,6 +68,7 @@ export class ApAudienceService { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	@bindThis | ||||||
| 	private groupingAudience(ids: string[], actor: CacheableRemoteUser) { | 	private groupingAudience(ids: string[], actor: CacheableRemoteUser) { | ||||||
| 		const groups = { | 		const groups = { | ||||||
| 			public: [] as string[], | 			public: [] as string[], | ||||||
|  | @ -88,6 +91,7 @@ export class ApAudienceService { | ||||||
| 		return groups; | 		return groups; | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	@bindThis | ||||||
| 	private isPublic(id: string) { | 	private isPublic(id: string) { | ||||||
| 		return [ | 		return [ | ||||||
| 			'https://www.w3.org/ns/activitystreams#Public', | 			'https://www.w3.org/ns/activitystreams#Public', | ||||||
|  | @ -96,6 +100,7 @@ export class ApAudienceService { | ||||||
| 		].includes(id); | 		].includes(id); | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	@bindThis | ||||||
| 	private isFollowers(id: string, actor: CacheableRemoteUser) { | 	private isFollowers(id: string, actor: CacheableRemoteUser) { | ||||||
| 		return ( | 		return ( | ||||||
| 			id === (actor.followersUri ?? `${actor.uri}/followers`) | 			id === (actor.followersUri ?? `${actor.uri}/followers`) | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ import type { UserPublickey } from '@/models/entities/UserPublickey.js'; | ||||||
| import { UserCacheService } from '@/core/UserCacheService.js'; | import { UserCacheService } from '@/core/UserCacheService.js'; | ||||||
| import type { Note } from '@/models/entities/Note.js'; | import type { Note } from '@/models/entities/Note.js'; | ||||||
| import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; | import type { MessagingMessage } from '@/models/entities/MessagingMessage.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import { getApId } from './type.js'; | import { getApId } from './type.js'; | ||||||
| import { ApPersonService } from './models/ApPersonService.js'; | import { ApPersonService } from './models/ApPersonService.js'; | ||||||
| import type { IObject } from './type.js'; | import type { IObject } from './type.js'; | ||||||
|  | @ -57,6 +58,7 @@ export class ApDbResolverService { | ||||||
| 		this.publicKeyByUserIdCache = new Cache<UserPublickey | null>(Infinity); | 		this.publicKeyByUserIdCache = new Cache<UserPublickey | null>(Infinity); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public parseUri(value: string | IObject): UriParseResult { | 	public parseUri(value: string | IObject): UriParseResult { | ||||||
| 		const uri = getApId(value); | 		const uri = getApId(value); | ||||||
| 	 | 	 | ||||||
|  | @ -82,6 +84,7 @@ export class ApDbResolverService { | ||||||
| 	/** | 	/** | ||||||
| 	 * AP Note => Misskey Note in DB | 	 * AP Note => Misskey Note in DB | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async getNoteFromApId(value: string | IObject): Promise<Note | null> { | 	public async getNoteFromApId(value: string | IObject): Promise<Note | null> { | ||||||
| 		const parsed = this.parseUri(value); | 		const parsed = this.parseUri(value); | ||||||
| 
 | 
 | ||||||
|  | @ -98,6 +101,7 @@ export class ApDbResolverService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async getMessageFromApId(value: string | IObject): Promise<MessagingMessage | null> { | 	public async getMessageFromApId(value: string | IObject): Promise<MessagingMessage | null> { | ||||||
| 		const parsed = this.parseUri(value); | 		const parsed = this.parseUri(value); | ||||||
| 
 | 
 | ||||||
|  | @ -117,6 +121,7 @@ export class ApDbResolverService { | ||||||
| 	/** | 	/** | ||||||
| 	 * AP Person => Misskey User in DB | 	 * AP Person => Misskey User in DB | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async getUserFromApId(value: string | IObject): Promise<CacheableUser | null> { | 	public async getUserFromApId(value: string | IObject): Promise<CacheableUser | null> { | ||||||
| 		const parsed = this.parseUri(value); | 		const parsed = this.parseUri(value); | ||||||
| 
 | 
 | ||||||
|  | @ -136,6 +141,7 @@ export class ApDbResolverService { | ||||||
| 	/** | 	/** | ||||||
| 	 * AP KeyId => Misskey User and Key | 	 * AP KeyId => Misskey User and Key | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async getAuthUserFromKeyId(keyId: string): Promise<{ | 	public async getAuthUserFromKeyId(keyId: string): Promise<{ | ||||||
| 		user: CacheableRemoteUser; | 		user: CacheableRemoteUser; | ||||||
| 		key: UserPublickey; | 		key: UserPublickey; | ||||||
|  | @ -161,6 +167,7 @@ export class ApDbResolverService { | ||||||
| 	/** | 	/** | ||||||
| 	 * AP Actor id => Misskey User and Key | 	 * AP Actor id => Misskey User and Key | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async getAuthUserFromApId(uri: string): Promise<{ | 	public async getAuthUserFromApId(uri: string): Promise<{ | ||||||
| 		user: CacheableRemoteUser; | 		user: CacheableRemoteUser; | ||||||
| 		key: UserPublickey | null; | 		key: UserPublickey | null; | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ import type { Config } from '@/config.js'; | ||||||
| import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; | import type { ILocalUser, IRemoteUser, User } from '@/models/entities/User.js'; | ||||||
| import { QueueService } from '@/core/QueueService.js'; | import { QueueService } from '@/core/QueueService.js'; | ||||||
| import { UserEntityService } from '@/core/entities/UserEntityService.js'; | import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| interface IRecipe { | interface IRecipe { | ||||||
| 	type: string; | 	type: string; | ||||||
|  | @ -48,6 +49,7 @@ export class ApDeliverManagerService { | ||||||
| 	 * @param activity Activity | 	 * @param activity Activity | ||||||
| 	 * @param from Followee | 	 * @param from Followee | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async deliverToFollowers(actor: { id: ILocalUser['id']; host: null; }, activity: any) { | 	public async deliverToFollowers(actor: { id: ILocalUser['id']; host: null; }, activity: any) { | ||||||
| 		const manager = new DeliverManager( | 		const manager = new DeliverManager( | ||||||
| 			this.userEntityService, | 			this.userEntityService, | ||||||
|  | @ -65,6 +67,7 @@ export class ApDeliverManagerService { | ||||||
| 	 * @param activity Activity | 	 * @param activity Activity | ||||||
| 	 * @param to Target user | 	 * @param to Target user | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async deliverToUser(actor: { id: ILocalUser['id']; host: null; }, activity: any, to: IRemoteUser) { | 	public async deliverToUser(actor: { id: ILocalUser['id']; host: null; }, activity: any, to: IRemoteUser) { | ||||||
| 		const manager = new DeliverManager( | 		const manager = new DeliverManager( | ||||||
| 			this.userEntityService, | 			this.userEntityService, | ||||||
|  | @ -77,6 +80,7 @@ export class ApDeliverManagerService { | ||||||
| 		await manager.execute(); | 		await manager.execute(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public createDeliverManager(actor: { id: User['id']; host: null; }, activity: any) { | 	public createDeliverManager(actor: { id: User['id']; host: null; }, activity: any) { | ||||||
| 		return new DeliverManager( | 		return new DeliverManager( | ||||||
| 			this.userEntityService, | 			this.userEntityService, | ||||||
|  | @ -114,6 +118,7 @@ class DeliverManager { | ||||||
| 	/** | 	/** | ||||||
| 	 * Add recipe for followers deliver | 	 * Add recipe for followers deliver | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public addFollowersRecipe() { | 	public addFollowersRecipe() { | ||||||
| 		const deliver = { | 		const deliver = { | ||||||
| 			type: 'Followers', | 			type: 'Followers', | ||||||
|  | @ -126,6 +131,7 @@ class DeliverManager { | ||||||
| 	 * Add recipe for direct deliver | 	 * Add recipe for direct deliver | ||||||
| 	 * @param to To | 	 * @param to To | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public addDirectRecipe(to: IRemoteUser) { | 	public addDirectRecipe(to: IRemoteUser) { | ||||||
| 		const recipe = { | 		const recipe = { | ||||||
| 			type: 'Direct', | 			type: 'Direct', | ||||||
|  | @ -139,6 +145,7 @@ class DeliverManager { | ||||||
| 	 * Add recipe | 	 * Add recipe | ||||||
| 	 * @param recipe Recipe | 	 * @param recipe Recipe | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public addRecipe(recipe: IRecipe) { | 	public addRecipe(recipe: IRecipe) { | ||||||
| 		this.recipes.push(recipe); | 		this.recipes.push(recipe); | ||||||
| 	} | 	} | ||||||
|  | @ -146,6 +153,7 @@ class DeliverManager { | ||||||
| 	/** | 	/** | ||||||
| 	 * Execute delivers | 	 * Execute delivers | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async execute() { | 	public async execute() { | ||||||
| 		if (!this.userEntityService.isLocalUser(this.actor)) return; | 		if (!this.userEntityService.isLocalUser(this.actor)) return; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -32,6 +32,7 @@ import { ApPersonService } from './models/ApPersonService.js'; | ||||||
| import { ApQuestionService } from './models/ApQuestionService.js'; | import { ApQuestionService } from './models/ApQuestionService.js'; | ||||||
| import type { Resolver } from './ApResolverService.js'; | import type { Resolver } from './ApResolverService.js'; | ||||||
| import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IRead, IReject, IRemove, IUndo, IUpdate } from './type.js'; | 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() | @Injectable() | ||||||
| export class ApInboxService { | export class ApInboxService { | ||||||
|  | @ -85,6 +86,7 @@ export class ApInboxService { | ||||||
| 		this.logger = this.apLoggerService.logger; | 		this.logger = this.apLoggerService.logger; | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	@bindThis | ||||||
| 	public async performActivity(actor: CacheableRemoteUser, activity: IObject) { | 	public async performActivity(actor: CacheableRemoteUser, activity: IObject) { | ||||||
| 		if (isCollectionOrOrderedCollection(activity)) { | 		if (isCollectionOrOrderedCollection(activity)) { | ||||||
| 			const resolver = this.apResolverService.createResolver(); | 			const resolver = this.apResolverService.createResolver(); | ||||||
|  | @ -112,6 +114,7 @@ export class ApInboxService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async performOneActivity(actor: CacheableRemoteUser, activity: IObject): Promise<void> { | 	public async performOneActivity(actor: CacheableRemoteUser, activity: IObject): Promise<void> { | ||||||
| 		if (actor.isSuspended) return; | 		if (actor.isSuspended) return; | ||||||
| 
 | 
 | ||||||
|  | @ -148,6 +151,7 @@ export class ApInboxService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async follow(actor: CacheableRemoteUser, activity: IFollow): Promise<string> { | 	private async follow(actor: CacheableRemoteUser, activity: IFollow): Promise<string> { | ||||||
| 		const followee = await this.apDbResolverService.getUserFromApId(activity.object); | 		const followee = await this.apDbResolverService.getUserFromApId(activity.object); | ||||||
| 	 | 	 | ||||||
|  | @ -163,6 +167,7 @@ export class ApInboxService { | ||||||
| 		return 'ok'; | 		return 'ok'; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async like(actor: CacheableRemoteUser, activity: ILike): Promise<string> { | 	private async like(actor: CacheableRemoteUser, activity: ILike): Promise<string> { | ||||||
| 		const targetUri = getApId(activity.object); | 		const targetUri = getApId(activity.object); | ||||||
| 
 | 
 | ||||||
|  | @ -180,6 +185,7 @@ export class ApInboxService { | ||||||
| 		}).then(() => 'ok'); | 		}).then(() => 'ok'); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async read(actor: CacheableRemoteUser, activity: IRead): Promise<string> { | 	private async read(actor: CacheableRemoteUser, activity: IRead): Promise<string> { | ||||||
| 		const id = await getApId(activity.object); | 		const id = await getApId(activity.object); | ||||||
| 
 | 
 | ||||||
|  | @ -202,6 +208,7 @@ export class ApInboxService { | ||||||
| 		return `ok: mark as read (${message.userId} => ${message.recipientId} ${message.id})`; | 		return `ok: mark as read (${message.userId} => ${message.recipientId} ${message.id})`; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async accept(actor: CacheableRemoteUser, activity: IAccept): Promise<string> { | 	private async accept(actor: CacheableRemoteUser, activity: IAccept): Promise<string> { | ||||||
| 		const uri = activity.id ?? activity; | 		const uri = activity.id ?? activity; | ||||||
| 
 | 
 | ||||||
|  | @ -219,6 +226,7 @@ export class ApInboxService { | ||||||
| 		return `skip: Unknown Accept type: ${getApType(object)}`; | 		return `skip: Unknown Accept type: ${getApType(object)}`; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async acceptFollow(actor: CacheableRemoteUser, activity: IFollow): Promise<string> { | 	private async acceptFollow(actor: CacheableRemoteUser, activity: IFollow): Promise<string> { | ||||||
| 		// ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある
 | 		// ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある
 | ||||||
| 
 | 
 | ||||||
|  | @ -242,6 +250,7 @@ export class ApInboxService { | ||||||
| 		return 'ok'; | 		return 'ok'; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async add(actor: CacheableRemoteUser, activity: IAdd): Promise<void> { | 	private async add(actor: CacheableRemoteUser, activity: IAdd): Promise<void> { | ||||||
| 		if ('actor' in activity && actor.uri !== activity.actor) { | 		if ('actor' in activity && actor.uri !== activity.actor) { | ||||||
| 			throw new Error('invalid actor'); | 			throw new Error('invalid actor'); | ||||||
|  | @ -261,6 +270,7 @@ export class ApInboxService { | ||||||
| 		throw new Error(`unknown target: ${activity.target}`); | 		throw new Error(`unknown target: ${activity.target}`); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async announce(actor: CacheableRemoteUser, activity: IAnnounce): Promise<void> { | 	private async announce(actor: CacheableRemoteUser, activity: IAnnounce): Promise<void> { | ||||||
| 		const uri = getApId(activity); | 		const uri = getApId(activity); | ||||||
| 
 | 
 | ||||||
|  | @ -271,6 +281,7 @@ export class ApInboxService { | ||||||
| 		this.announceNote(actor, activity, targetUri); | 		this.announceNote(actor, activity, targetUri); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async announceNote(actor: CacheableRemoteUser, activity: IAnnounce, targetUri: string): Promise<void> { | 	private async announceNote(actor: CacheableRemoteUser, activity: IAnnounce, targetUri: string): Promise<void> { | ||||||
| 		const uri = getApId(activity); | 		const uri = getApId(activity); | ||||||
| 
 | 
 | ||||||
|  | @ -330,6 +341,7 @@ export class ApInboxService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async block(actor: CacheableRemoteUser, activity: IBlock): Promise<string> { | 	private async block(actor: CacheableRemoteUser, activity: IBlock): Promise<string> { | ||||||
| 		// ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず
 | 		// ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず
 | ||||||
| 
 | 
 | ||||||
|  | @ -347,6 +359,7 @@ export class ApInboxService { | ||||||
| 		return 'ok'; | 		return 'ok'; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async create(actor: CacheableRemoteUser, activity: ICreate): Promise<void> { | 	private async create(actor: CacheableRemoteUser, activity: ICreate): Promise<void> { | ||||||
| 		const uri = getApId(activity); | 		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<string> { | 	private async createNote(resolver: Resolver, actor: CacheableRemoteUser, note: IObject, silent = false, activity?: ICreate): Promise<string> { | ||||||
| 		const uri = getApId(note); | 		const uri = getApId(note); | ||||||
| 
 | 
 | ||||||
|  | @ -416,6 +430,7 @@ export class ApInboxService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async delete(actor: CacheableRemoteUser, activity: IDelete): Promise<string> { | 	private async delete(actor: CacheableRemoteUser, activity: IDelete): Promise<string> { | ||||||
| 		if ('actor' in activity && actor.uri !== activity.actor) { | 		if ('actor' in activity && actor.uri !== activity.actor) { | ||||||
| 			throw new Error('invalid actor'); | 			throw new Error('invalid actor'); | ||||||
|  | @ -457,6 +472,7 @@ export class ApInboxService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async deleteActor(actor: CacheableRemoteUser, uri: string): Promise<string> { | 	private async deleteActor(actor: CacheableRemoteUser, uri: string): Promise<string> { | ||||||
| 		this.logger.info(`Deleting the Actor: ${uri}`); | 		this.logger.info(`Deleting the Actor: ${uri}`); | ||||||
| 	 | 	 | ||||||
|  | @ -478,6 +494,7 @@ export class ApInboxService { | ||||||
| 		return `ok: queued ${job.name} ${job.id}`; | 		return `ok: queued ${job.name} ${job.id}`; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async deleteNote(actor: CacheableRemoteUser, uri: string): Promise<string> { | 	private async deleteNote(actor: CacheableRemoteUser, uri: string): Promise<string> { | ||||||
| 		this.logger.info(`Deleting the Note: ${uri}`); | 		this.logger.info(`Deleting the Note: ${uri}`); | ||||||
| 	 | 	 | ||||||
|  | @ -510,6 +527,7 @@ export class ApInboxService { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async flag(actor: CacheableRemoteUser, activity: IFlag): Promise<string> { | 	private async flag(actor: CacheableRemoteUser, activity: IFlag): Promise<string> { | ||||||
| 		// objectは `(User|Note) | (User|Note)[]` だけど、全パターンDBスキーマと対応させられないので
 | 		// objectは `(User|Note) | (User|Note)[]` だけど、全パターンDBスキーマと対応させられないので
 | ||||||
| 		// 対象ユーザーは一番最初のユーザー として あとはコメントとして格納する
 | 		// 対象ユーザーは一番最初のユーザー として あとはコメントとして格納する
 | ||||||
|  | @ -534,6 +552,7 @@ export class ApInboxService { | ||||||
| 		return 'ok'; | 		return 'ok'; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async reject(actor: CacheableRemoteUser, activity: IReject): Promise<string> { | 	private async reject(actor: CacheableRemoteUser, activity: IReject): Promise<string> { | ||||||
| 		const uri = activity.id ?? activity; | 		const uri = activity.id ?? activity; | ||||||
| 
 | 
 | ||||||
|  | @ -551,6 +570,7 @@ export class ApInboxService { | ||||||
| 		return `skip: Unknown Reject type: ${getApType(object)}`; | 		return `skip: Unknown Reject type: ${getApType(object)}`; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async rejectFollow(actor: CacheableRemoteUser, activity: IFollow): Promise<string> { | 	private async rejectFollow(actor: CacheableRemoteUser, activity: IFollow): Promise<string> { | ||||||
| 		// ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある
 | 		// ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある
 | ||||||
| 	 | 	 | ||||||
|  | @ -574,6 +594,7 @@ export class ApInboxService { | ||||||
| 		return 'ok'; | 		return 'ok'; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async remove(actor: CacheableRemoteUser, activity: IRemove): Promise<void> { | 	private async remove(actor: CacheableRemoteUser, activity: IRemove): Promise<void> { | ||||||
| 		if ('actor' in activity && actor.uri !== activity.actor) { | 		if ('actor' in activity && actor.uri !== activity.actor) { | ||||||
| 			throw new Error('invalid actor'); | 			throw new Error('invalid actor'); | ||||||
|  | @ -593,6 +614,7 @@ export class ApInboxService { | ||||||
| 		throw new Error(`unknown target: ${activity.target}`); | 		throw new Error(`unknown target: ${activity.target}`); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async undo(actor: CacheableRemoteUser, activity: IUndo): Promise<string> { | 	private async undo(actor: CacheableRemoteUser, activity: IUndo): Promise<string> { | ||||||
| 		if ('actor' in activity && actor.uri !== activity.actor) { | 		if ('actor' in activity && actor.uri !== activity.actor) { | ||||||
| 			throw new Error('invalid actor'); | 			throw new Error('invalid actor'); | ||||||
|  | @ -618,6 +640,7 @@ export class ApInboxService { | ||||||
| 		return `skip: unknown object type ${getApType(object)}`; | 		return `skip: unknown object type ${getApType(object)}`; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async undoAccept(actor: CacheableRemoteUser, activity: IAccept): Promise<string> { | 	private async undoAccept(actor: CacheableRemoteUser, activity: IAccept): Promise<string> { | ||||||
| 		const follower = await this.apDbResolverService.getUserFromApId(activity.object); | 		const follower = await this.apDbResolverService.getUserFromApId(activity.object); | ||||||
| 		if (follower == null) { | 		if (follower == null) { | ||||||
|  | @ -637,6 +660,7 @@ export class ApInboxService { | ||||||
| 		return 'skip: フォローされていない'; | 		return 'skip: フォローされていない'; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async undoAnnounce(actor: CacheableRemoteUser, activity: IAnnounce): Promise<string> { | 	private async undoAnnounce(actor: CacheableRemoteUser, activity: IAnnounce): Promise<string> { | ||||||
| 		const uri = getApId(activity); | 		const uri = getApId(activity); | ||||||
| 
 | 
 | ||||||
|  | @ -651,6 +675,7 @@ export class ApInboxService { | ||||||
| 		return 'ok: deleted'; | 		return 'ok: deleted'; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async undoBlock(actor: CacheableRemoteUser, activity: IBlock): Promise<string> { | 	private async undoBlock(actor: CacheableRemoteUser, activity: IBlock): Promise<string> { | ||||||
| 		const blockee = await this.apDbResolverService.getUserFromApId(activity.object); | 		const blockee = await this.apDbResolverService.getUserFromApId(activity.object); | ||||||
| 
 | 
 | ||||||
|  | @ -666,6 +691,7 @@ export class ApInboxService { | ||||||
| 		return 'ok'; | 		return 'ok'; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async undoFollow(actor: CacheableRemoteUser, activity: IFollow): Promise<string> { | 	private async undoFollow(actor: CacheableRemoteUser, activity: IFollow): Promise<string> { | ||||||
| 		const followee = await this.apDbResolverService.getUserFromApId(activity.object); | 		const followee = await this.apDbResolverService.getUserFromApId(activity.object); | ||||||
| 		if (followee == null) { | 		if (followee == null) { | ||||||
|  | @ -699,6 +725,7 @@ export class ApInboxService { | ||||||
| 		return 'skip: リクエストもフォローもされていない'; | 		return 'skip: リクエストもフォローもされていない'; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async undoLike(actor: CacheableRemoteUser, activity: ILike): Promise<string> { | 	private async undoLike(actor: CacheableRemoteUser, activity: ILike): Promise<string> { | ||||||
| 		const targetUri = getApId(activity.object); | 		const targetUri = getApId(activity.object); | ||||||
| 
 | 
 | ||||||
|  | @ -713,6 +740,7 @@ export class ApInboxService { | ||||||
| 		return 'ok'; | 		return 'ok'; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async update(actor: CacheableRemoteUser, activity: IUpdate): Promise<string> { | 	private async update(actor: CacheableRemoteUser, activity: IUpdate): Promise<string> { | ||||||
| 		if ('actor' in activity && actor.uri !== activity.actor) { | 		if ('actor' in activity && actor.uri !== activity.actor) { | ||||||
| 			return 'skip: invalid actor'; | 			return 'skip: invalid actor'; | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| import { Inject, Injectable } from '@nestjs/common'; | import { Inject, Injectable } from '@nestjs/common'; | ||||||
| import type Logger from '@/logger.js'; | import type Logger from '@/logger.js'; | ||||||
| import { RemoteLoggerService } from '@/core/RemoteLoggerService.js'; | import { RemoteLoggerService } from '@/core/RemoteLoggerService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class ApLoggerService { | export class ApLoggerService { | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ import { MfmService } from '@/core/MfmService.js'; | ||||||
| import type { Note } from '@/models/entities/Note.js'; | import type { Note } from '@/models/entities/Note.js'; | ||||||
| import { extractApHashtagObjects } from './models/tag.js'; | import { extractApHashtagObjects } from './models/tag.js'; | ||||||
| import type { IObject } from './type.js'; | import type { IObject } from './type.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class ApMfmService { | export class ApMfmService { | ||||||
|  | @ -17,12 +18,14 @@ export class ApMfmService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public htmlToMfm(html: string, tag?: IObject | IObject[]) { | 	public htmlToMfm(html: string, tag?: IObject | IObject[]) { | ||||||
| 		const hashtagNames = extractApHashtagObjects(tag).map(x => x.name).filter((x): x is string => x != null); | 		const hashtagNames = extractApHashtagObjects(tag).map(x => x.name).filter((x): x is string => x != null); | ||||||
| 	 | 	 | ||||||
| 		return this.mfmService.fromHtml(html, hashtagNames); | 		return this.mfmService.fromHtml(html, hashtagNames); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public getNoteHtml(note: Note) { | 	public getNoteHtml(note: Note) { | ||||||
| 		if (!note.text) return ''; | 		if (!note.text) return ''; | ||||||
| 		return this.mfmService.toHtml(mfm.parse(note.text), JSON.parse(note.mentionedRemoteUsers)); | 		return this.mfmService.toHtml(mfm.parse(note.text), JSON.parse(note.mentionedRemoteUsers)); | ||||||
|  |  | ||||||
|  | @ -25,6 +25,7 @@ import { LdSignatureService } from './LdSignatureService.js'; | ||||||
| import { ApMfmService } from './ApMfmService.js'; | import { ApMfmService } from './ApMfmService.js'; | ||||||
| import type { IActivity, IObject } from './type.js'; | import type { IActivity, IObject } from './type.js'; | ||||||
| import type { IIdentifier } from './models/identifier.js'; | import type { IIdentifier } from './models/identifier.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class ApRendererService { | export class ApRendererService { | ||||||
|  | @ -59,6 +60,7 @@ export class ApRendererService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public renderAccept(object: any, user: { id: User['id']; host: null }) { | 	public renderAccept(object: any, user: { id: User['id']; host: null }) { | ||||||
| 		return { | 		return { | ||||||
| 			type: 'Accept', | 			type: 'Accept', | ||||||
|  | @ -67,6 +69,7 @@ export class ApRendererService { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public renderAdd(user: ILocalUser, target: any, object: any) { | 	public renderAdd(user: ILocalUser, target: any, object: any) { | ||||||
| 		return { | 		return { | ||||||
| 			type: 'Add', | 			type: 'Add', | ||||||
|  | @ -76,6 +79,7 @@ export class ApRendererService { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public renderAnnounce(object: any, note: Note) { | 	public renderAnnounce(object: any, note: Note) { | ||||||
| 		const attributedTo = `${this.config.url}/users/${note.userId}`; | 		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. | 	 * @param block The block to be rendered. The blockee relation must be loaded. | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public renderBlock(block: Blocking) { | 	public renderBlock(block: Blocking) { | ||||||
| 		if (block.blockee?.uri == null) { | 		if (block.blockee?.uri == null) { | ||||||
| 			throw new Error('renderBlock: missing blockee uri'); | 			throw new Error('renderBlock: missing blockee uri'); | ||||||
|  | @ -121,6 +126,7 @@ export class ApRendererService { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public renderCreate(object: any, note: Note) { | 	public renderCreate(object: any, note: Note) { | ||||||
| 		const activity = { | 		const activity = { | ||||||
| 			id: `${this.config.url}/notes/${note.id}/activity`, | 			id: `${this.config.url}/notes/${note.id}/activity`, | ||||||
|  | @ -136,6 +142,7 @@ export class ApRendererService { | ||||||
| 		return activity; | 		return activity; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public renderDelete(object: any, user: { id: User['id']; host: null }) { | 	public renderDelete(object: any, user: { id: User['id']; host: null }) { | ||||||
| 		return { | 		return { | ||||||
| 			type: 'Delete', | 			type: 'Delete', | ||||||
|  | @ -145,6 +152,7 @@ export class ApRendererService { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public renderDocument(file: DriveFile) { | 	public renderDocument(file: DriveFile) { | ||||||
| 		return { | 		return { | ||||||
| 			type: 'Document', | 			type: 'Document', | ||||||
|  | @ -154,6 +162,7 @@ export class ApRendererService { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public renderEmoji(emoji: Emoji) { | 	public renderEmoji(emoji: Emoji) { | ||||||
| 		return { | 		return { | ||||||
| 			id: `${this.config.url}/emojis/${emoji.name}`, | 			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
 | 	// to anonymise reporters, the reporting actor must be a system user
 | ||||||
| 	// object has to be a uri or array of uris
 | 	// object has to be a uri or array of uris
 | ||||||
|  | 	@bindThis | ||||||
| 	public renderFlag(user: ILocalUser, object: [string], content: string) { | 	public renderFlag(user: ILocalUser, object: [string], content: string) { | ||||||
| 		return { | 		return { | ||||||
| 			type: 'Flag', | 			type: 'Flag', | ||||||
|  | @ -179,6 +189,7 @@ export class ApRendererService { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public renderFollowRelay(relay: Relay, relayActor: ILocalUser) { | 	public renderFollowRelay(relay: Relay, relayActor: ILocalUser) { | ||||||
| 		const follow = { | 		const follow = { | ||||||
| 			id: `${this.config.url}/activities/follow-relay/${relay.id}`, | 			id: `${this.config.url}/activities/follow-relay/${relay.id}`, | ||||||
|  | @ -194,11 +205,13 @@ export class ApRendererService { | ||||||
| 	 * Convert (local|remote)(Follower|Followee)ID to URL | 	 * Convert (local|remote)(Follower|Followee)ID to URL | ||||||
| 	 * @param id Follower|Followee ID | 	 * @param id Follower|Followee ID | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async renderFollowUser(id: User['id']) { | 	public async renderFollowUser(id: User['id']) { | ||||||
| 		const user = await this.usersRepository.findOneByOrFail({ id: id }); | 		const user = await this.usersRepository.findOneByOrFail({ id: id }); | ||||||
| 		return this.userEntityService.isLocalUser(user) ? `${this.config.url}/users/${user.id}` : user.uri; | 		return this.userEntityService.isLocalUser(user) ? `${this.config.url}/users/${user.id}` : user.uri; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public renderFollow( | 	public renderFollow( | ||||||
| 		follower: { id: User['id']; host: User['host']; uri: User['host'] }, | 		follower: { id: User['id']; host: User['host']; uri: User['host'] }, | ||||||
| 		followee: { 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; | 		return follow; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public renderHashtag(tag: string) { | 	public renderHashtag(tag: string) { | ||||||
| 		return { | 		return { | ||||||
| 			type: 'Hashtag', | 			type: 'Hashtag', | ||||||
|  | @ -222,6 +236,7 @@ export class ApRendererService { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public renderImage(file: DriveFile) { | 	public renderImage(file: DriveFile) { | ||||||
| 		return { | 		return { | ||||||
| 			type: 'Image', | 			type: 'Image', | ||||||
|  | @ -231,6 +246,7 @@ export class ApRendererService { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public renderKey(user: ILocalUser, key: UserKeypair, postfix?: string) { | 	public renderKey(user: ILocalUser, key: UserKeypair, postfix?: string) { | ||||||
| 		return { | 		return { | ||||||
| 			id: `${this.config.url}/users/${user.id}${postfix ?? '/publickey'}`, | 			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 }) { | 	public async renderLike(noteReaction: NoteReaction, note: { uri: string | null }) { | ||||||
| 		const reaction = noteReaction.reaction; | 		const reaction = noteReaction.reaction; | ||||||
| 
 | 
 | ||||||
|  | @ -268,6 +285,7 @@ export class ApRendererService { | ||||||
| 		return object; | 		return object; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public renderMention(mention: User) { | 	public renderMention(mention: User) { | ||||||
| 		return { | 		return { | ||||||
| 			type: 'Mention', | 			type: 'Mention', | ||||||
|  | @ -276,6 +294,7 @@ export class ApRendererService { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async renderNote(note: Note, dive = true, isTalk = false): Promise<IObject> { | 	public async renderNote(note: Note, dive = true, isTalk = false): Promise<IObject> { | ||||||
| 		const getPromisedFiles = async (ids: string[]) => { | 		const getPromisedFiles = async (ids: string[]) => { | ||||||
| 			if (!ids || ids.length === 0) return []; | 			if (!ids || ids.length === 0) return []; | ||||||
|  | @ -420,6 +439,7 @@ export class ApRendererService { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async renderPerson(user: ILocalUser) { | 	public async renderPerson(user: ILocalUser) { | ||||||
| 		const id = `${this.config.url}/users/${user.id}`; | 		const id = `${this.config.url}/users/${user.id}`; | ||||||
| 		const isSystem = !!user.username.match(/\./); | 		const isSystem = !!user.username.match(/\./); | ||||||
|  | @ -496,6 +516,7 @@ export class ApRendererService { | ||||||
| 		return person; | 		return person; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async renderQuestion(user: { id: User['id'] }, note: Note, poll: Poll) { | 	public async renderQuestion(user: { id: User['id'] }, note: Note, poll: Poll) { | ||||||
| 		const question = { | 		const question = { | ||||||
| 			type: 'Question', | 			type: 'Question', | ||||||
|  | @ -515,6 +536,7 @@ export class ApRendererService { | ||||||
| 		return question; | 		return question; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public renderRead(user: { id: User['id'] }, message: MessagingMessage) { | 	public renderRead(user: { id: User['id'] }, message: MessagingMessage) { | ||||||
| 		return { | 		return { | ||||||
| 			type: 'Read', | 			type: 'Read', | ||||||
|  | @ -523,6 +545,7 @@ export class ApRendererService { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public renderReject(object: any, user: { id: User['id'] }) { | 	public renderReject(object: any, user: { id: User['id'] }) { | ||||||
| 		return { | 		return { | ||||||
| 			type: 'Reject', | 			type: 'Reject', | ||||||
|  | @ -531,6 +554,7 @@ export class ApRendererService { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public renderRemove(user: { id: User['id'] }, target: any, object: any) { | 	public renderRemove(user: { id: User['id'] }, target: any, object: any) { | ||||||
| 		return { | 		return { | ||||||
| 			type: 'Remove', | 			type: 'Remove', | ||||||
|  | @ -540,6 +564,7 @@ export class ApRendererService { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public renderTombstone(id: string) { | 	public renderTombstone(id: string) { | ||||||
| 		return { | 		return { | ||||||
| 			id, | 			id, | ||||||
|  | @ -547,6 +572,7 @@ export class ApRendererService { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public renderUndo(object: any, user: { id: User['id'] }) { | 	public renderUndo(object: any, user: { id: User['id'] }) { | ||||||
| 		if (object == null) return null; | 		if (object == null) return null; | ||||||
| 		const id = typeof object.id === 'string' && object.id.startsWith(this.config.url) ? `${object.id}/undo` : undefined; | 		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'] }) { | 	public renderUpdate(object: any, user: { id: User['id'] }) { | ||||||
| 		const activity = { | 		const activity = { | ||||||
| 			id: `${this.config.url}/users/${user.id}#updates/${new Date().getTime()}`, | 			id: `${this.config.url}/users/${user.id}#updates/${new Date().getTime()}`, | ||||||
|  | @ -573,6 +600,7 @@ export class ApRendererService { | ||||||
| 		return activity; | 		return activity; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public renderVote(user: { id: User['id'] }, vote: PollVote, note: Note, poll: Poll, pollOwner: IRemoteUser) { | 	public renderVote(user: { id: User['id'] }, vote: PollVote, note: Note, poll: Poll, pollOwner: IRemoteUser) { | ||||||
| 		return { | 		return { | ||||||
| 			id: `${this.config.url}/users/${user.id}#votes/${vote.id}/activity`, | 			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 { | 	public renderActivity(x: any): IActivity | null { | ||||||
| 		if (x == null) return null; | 		if (x == null) return null; | ||||||
| 	 | 	 | ||||||
|  | @ -632,6 +661,7 @@ export class ApRendererService { | ||||||
| 		}, x); | 		}, x); | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	@bindThis | ||||||
| 	public async attachLdSignature(activity: any, user: { id: User['id']; host: null; }): Promise<IActivity> { | 	public async attachLdSignature(activity: any, user: { id: User['id']; host: null; }): Promise<IActivity> { | ||||||
| 		const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); | 		const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); | ||||||
| 	 | 	 | ||||||
|  | @ -651,6 +681,7 @@ export class ApRendererService { | ||||||
| 	 * @param prev URL of prev page (optional) | 	 * @param prev URL of prev page (optional) | ||||||
| 	 * @param next URL of next page (optional) | 	 * @param next URL of next page (optional) | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public renderOrderedCollectionPage(id: string, totalItems: any, orderedItems: any, partOf: string, prev?: string, next?: string) { | 	public renderOrderedCollectionPage(id: string, totalItems: any, orderedItems: any, partOf: string, prev?: string, next?: string) { | ||||||
| 		const page = { | 		const page = { | ||||||
| 			id, | 			id, | ||||||
|  | @ -674,6 +705,7 @@ export class ApRendererService { | ||||||
| 	 * @param last URL of last page (optional) | 	 * @param last URL of last page (optional) | ||||||
| 	 * @param orderedItems attached objects (optional) | 	 * @param orderedItems attached objects (optional) | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public renderOrderedCollection(id: string | null, totalItems: any, first?: string, last?: string, orderedItems?: IObject[]) { | 	public renderOrderedCollection(id: string | null, totalItems: any, first?: string, last?: string, orderedItems?: IObject[]) { | ||||||
| 		const page: any = { | 		const page: any = { | ||||||
| 			id, | 			id, | ||||||
|  | @ -688,6 +720,7 @@ export class ApRendererService { | ||||||
| 		return page; | 		return page; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async getEmojis(names: string[]): Promise<Emoji[]> { | 	private async getEmojis(names: string[]): Promise<Emoji[]> { | ||||||
| 		if (names == null || names.length === 0) return []; | 		if (names == null || names.length === 0) return []; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ import type { Config } from '@/config.js'; | ||||||
| import type { User } from '@/models/entities/User.js'; | import type { User } from '@/models/entities/User.js'; | ||||||
| import { UserKeypairStoreService } from '@/core/UserKeypairStoreService.js'; | import { UserKeypairStoreService } from '@/core/UserKeypairStoreService.js'; | ||||||
| import { HttpRequestService } from '@/core/HttpRequestService.js'; | import { HttpRequestService } from '@/core/HttpRequestService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| type Request = { | type Request = { | ||||||
| 	url: string; | 	url: string; | ||||||
|  | @ -36,6 +37,7 @@ export class ApRequestService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private createSignedPost(args: { key: PrivateKey, url: string, body: string, additionalHeaders: Record<string, string> }): Signed { | 	private createSignedPost(args: { key: PrivateKey, url: string, body: string, additionalHeaders: Record<string, string> }): Signed { | ||||||
| 		const u = new URL(args.url); | 		const u = new URL(args.url); | ||||||
| 		const digestHeader = `SHA-256=${crypto.createHash('sha256').update(args.body).digest('base64')}`; | 		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<string, string> }): Signed { | 	private createSignedGet(args: { key: PrivateKey, url: string, additionalHeaders: Record<string, string> }): Signed { | ||||||
| 		const u = new URL(args.url); | 		const u = new URL(args.url); | ||||||
| 
 | 
 | ||||||
|  | @ -84,6 +87,7 @@ export class ApRequestService { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private signToRequest(request: Request, key: PrivateKey, includeHeaders: string[]): Signed { | 	private signToRequest(request: Request, key: PrivateKey, includeHeaders: string[]): Signed { | ||||||
| 		const signingString = this.genSigningString(request, includeHeaders); | 		const signingString = this.genSigningString(request, includeHeaders); | ||||||
| 		const signature = crypto.sign('sha256', Buffer.from(signingString), key.privateKeyPem).toString('base64'); | 		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 { | 	private genSigningString(request: Request, includeHeaders: string[]): string { | ||||||
| 		request.headers = this.lcObjectKey(request.headers); | 		request.headers = this.lcObjectKey(request.headers); | ||||||
| 
 | 
 | ||||||
|  | @ -117,16 +122,19 @@ export class ApRequestService { | ||||||
| 		return results.join('\n'); | 		return results.join('\n'); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private lcObjectKey(src: Record<string, string>): Record<string, string> { | 	private lcObjectKey(src: Record<string, string>): Record<string, string> { | ||||||
| 		const dst: Record<string, string> = {}; | 		const dst: Record<string, string> = {}; | ||||||
| 		for (const key of Object.keys(src).filter(x => x !== '__proto__' && typeof src[x] === 'string')) dst[key.toLowerCase()] = src[key]; | 		for (const key of Object.keys(src).filter(x => x !== '__proto__' && typeof src[x] === 'string')) dst[key.toLowerCase()] = src[key]; | ||||||
| 		return dst; | 		return dst; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private objectAssignWithLcKey(a: Record<string, string>, b: Record<string, string>): Record<string, string> { | 	private objectAssignWithLcKey(a: Record<string, string>, b: Record<string, string>): Record<string, string> { | ||||||
| 		return Object.assign(this.lcObjectKey(a), this.lcObjectKey(b)); | 		return Object.assign(this.lcObjectKey(a), this.lcObjectKey(b)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async signedPost(user: { id: User['id'] }, url: string, object: any) { | 	public async signedPost(user: { id: User['id'] }, url: string, object: any) { | ||||||
| 		const body = JSON.stringify(object); | 		const body = JSON.stringify(object); | ||||||
| 
 | 
 | ||||||
|  | @ -157,6 +165,7 @@ export class ApRequestService { | ||||||
| 	 * @param user http-signature user | 	 * @param user http-signature user | ||||||
| 	 * @param url URL to fetch | 	 * @param url URL to fetch | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async signedGet(url: string, user: { id: User['id'] }) { | 	public async signedGet(url: string, user: { id: User['id'] }) { | ||||||
| 		const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); | 		const keypair = await this.userKeypairStoreService.getUserKeypair(user.id); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,58 +7,13 @@ import { MetaService } from '@/core/MetaService.js'; | ||||||
| import { HttpRequestService } from '@/core/HttpRequestService.js'; | import { HttpRequestService } from '@/core/HttpRequestService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import { UtilityService } from '@/core/UtilityService.js'; | import { UtilityService } from '@/core/UtilityService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import { isCollectionOrOrderedCollection } from './type.js'; | import { isCollectionOrOrderedCollection } from './type.js'; | ||||||
| import { ApDbResolverService } from './ApDbResolverService.js'; | import { ApDbResolverService } from './ApDbResolverService.js'; | ||||||
| import { ApRendererService } from './ApRendererService.js'; | import { ApRendererService } from './ApRendererService.js'; | ||||||
| import { ApRequestService } from './ApRequestService.js'; | import { ApRequestService } from './ApRequestService.js'; | ||||||
| import type { IObject, ICollection, IOrderedCollection } from './type.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 { | export class Resolver { | ||||||
| 	private history: Set<string>; | 	private history: Set<string>; | ||||||
| 	private user?: ILocalUser; | 	private user?: ILocalUser; | ||||||
|  | @ -76,15 +31,17 @@ export class Resolver { | ||||||
| 		private httpRequestService: HttpRequestService, | 		private httpRequestService: HttpRequestService, | ||||||
| 		private apRendererService: ApRendererService, | 		private apRendererService: ApRendererService, | ||||||
| 		private apDbResolverService: ApDbResolverService, | 		private apDbResolverService: ApDbResolverService, | ||||||
| 		private recursionLimit = 100 | 		private recursionLimit = 100, | ||||||
| 	) { | 	) { | ||||||
| 		this.history = new Set(); | 		this.history = new Set(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public getHistory(): string[] { | 	public getHistory(): string[] { | ||||||
| 		return Array.from(this.history); | 		return Array.from(this.history); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async resolveCollection(value: string | IObject): Promise<ICollection | IOrderedCollection> { | 	public async resolveCollection(value: string | IObject): Promise<ICollection | IOrderedCollection> { | ||||||
| 		const collection = typeof value === 'string' | 		const collection = typeof value === 'string' | ||||||
| 			? await this.resolve(value) | 			? await this.resolve(value) | ||||||
|  | @ -97,6 +54,7 @@ export class Resolver { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async resolve(value: string | IObject): Promise<IObject> { | 	public async resolve(value: string | IObject): Promise<IObject> { | ||||||
| 		if (value == null) { | 		if (value == null) { | ||||||
| 			throw new Error('resolvee is null (or undefined)'); | 			throw new Error('resolvee is null (or undefined)'); | ||||||
|  | @ -152,6 +110,7 @@ export class Resolver { | ||||||
| 		return object; | 		return object; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private resolveLocal(url: string): Promise<IObject> { | 	private resolveLocal(url: string): Promise<IObject> { | ||||||
| 		const parsed = this.apDbResolverService.parseUri(url); | 		const parsed = this.apDbResolverService.parseUri(url); | ||||||
| 		if (!parsed.local) throw new Error('resolveLocal: not local'); | 		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, | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -2,22 +2,11 @@ import * as crypto from 'node:crypto'; | ||||||
| import { Inject, Injectable } from '@nestjs/common'; | import { Inject, Injectable } from '@nestjs/common'; | ||||||
| import fetch from 'node-fetch'; | import fetch from 'node-fetch'; | ||||||
| import { HttpRequestService } from '@/core/HttpRequestService.js'; | import { HttpRequestService } from '@/core/HttpRequestService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import { CONTEXTS } from './misc/contexts.js'; | import { CONTEXTS } from './misc/contexts.js'; | ||||||
| 
 | 
 | ||||||
| // RsaSignature2017 based from https://github.com/transmute-industries/RsaSignature2017
 | // 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 { | class LdSignature { | ||||||
| 	public debug = false; | 	public debug = false; | ||||||
| 	public preLoad = true; | 	public preLoad = true; | ||||||
|  | @ -28,6 +17,7 @@ class LdSignature { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async signRsaSignature2017(data: any, privateKey: string, creator: string, domain?: string, created?: Date): Promise<any> { | 	public async signRsaSignature2017(data: any, privateKey: string, creator: string, domain?: string, created?: Date): Promise<any> { | ||||||
| 		const options = { | 		const options = { | ||||||
| 			type: 'RsaSignature2017', | 			type: 'RsaSignature2017', | ||||||
|  | @ -64,6 +54,7 @@ class LdSignature { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async verifyRsaSignature2017(data: any, publicKey: string): Promise<boolean> { | 	public async verifyRsaSignature2017(data: any, publicKey: string): Promise<boolean> { | ||||||
| 		const toBeSigned = await this.createVerifyData(data, data.signature); | 		const toBeSigned = await this.createVerifyData(data, data.signature); | ||||||
| 		const verifier = crypto.createVerify('sha256'); | 		const verifier = crypto.createVerify('sha256'); | ||||||
|  | @ -71,6 +62,7 @@ class LdSignature { | ||||||
| 		return verifier.verify(publicKey, data.signature.signatureValue, 'base64'); | 		return verifier.verify(publicKey, data.signature.signatureValue, 'base64'); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async createVerifyData(data: any, options: any) { | 	public async createVerifyData(data: any, options: any) { | ||||||
| 		const transformedOptions = { | 		const transformedOptions = { | ||||||
| 			...options, | 			...options, | ||||||
|  | @ -90,11 +82,13 @@ class LdSignature { | ||||||
| 		return verifyData; | 		return verifyData; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async normalize(data: any) { | 	public async normalize(data: any) { | ||||||
| 		const customLoader = this.getLoader(); | 		const customLoader = this.getLoader(); | ||||||
| 		return 42; | 		return 42; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private getLoader() { | 	private getLoader() { | ||||||
| 		return async (url: string): Promise<any> => { | 		return async (url: string): Promise<any> => { | ||||||
| 			if (!url.match('^https?\:\/\/')) throw `Invalid URL ${url}`; | 			if (!url.match('^https?\:\/\/')) throw `Invalid URL ${url}`; | ||||||
|  | @ -120,6 +114,7 @@ class LdSignature { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private async fetchDocument(url: string) { | 	private async fetchDocument(url: string) { | ||||||
| 		const json = await fetch(url, { | 		const json = await fetch(url, { | ||||||
| 			headers: { | 			headers: { | ||||||
|  | @ -139,9 +134,23 @@ class LdSignature { | ||||||
| 		return json; | 		return json; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public sha256(data: string): string { | 	public sha256(data: string): string { | ||||||
| 		const hash = crypto.createHash('sha256'); | 		const hash = crypto.createHash('sha256'); | ||||||
| 		hash.update(data); | 		hash.update(data); | ||||||
| 		return hash.digest('hex'); | 		return hash.digest('hex'); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | @Injectable() | ||||||
|  | export class LdSignatureService { | ||||||
|  | 	constructor( | ||||||
|  | 		private httpRequestService: HttpRequestService, | ||||||
|  | 	) { | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@bindThis | ||||||
|  | 	public use(): LdSignature { | ||||||
|  | 		return new LdSignature(this.httpRequestService); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ import { DriveService } from '@/core/DriveService.js'; | ||||||
| import type Logger from '@/logger.js'; | import type Logger from '@/logger.js'; | ||||||
| import { ApResolverService } from '../ApResolverService.js'; | import { ApResolverService } from '../ApResolverService.js'; | ||||||
| import { ApLoggerService } from '../ApLoggerService.js'; | import { ApLoggerService } from '../ApLoggerService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class ApImageService { | export class ApImageService { | ||||||
|  | @ -34,6 +35,7 @@ export class ApImageService { | ||||||
| 	/** | 	/** | ||||||
| 	 * Imageを作成します。 | 	 * Imageを作成します。 | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async createImage(actor: CacheableRemoteUser, value: any): Promise<DriveFile> { | 	public async createImage(actor: CacheableRemoteUser, value: any): Promise<DriveFile> { | ||||||
| 		// 投稿者が凍結されていたらスキップ
 | 		// 投稿者が凍結されていたらスキップ
 | ||||||
| 		if (actor.isSuspended) { | 		if (actor.isSuspended) { | ||||||
|  | @ -81,6 +83,7 @@ export class ApImageService { | ||||||
| 	 * Misskeyに対象のImageが登録されていればそれを返し、そうでなければ | 	 * Misskeyに対象のImageが登録されていればそれを返し、そうでなければ | ||||||
| 	 * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 | 	 * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async resolveImage(actor: CacheableRemoteUser, value: any): Promise<DriveFile> { | 	public async resolveImage(actor: CacheableRemoteUser, value: any): Promise<DriveFile> { | ||||||
| 		// TODO
 | 		// TODO
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ import { isMention } from '../type.js'; | ||||||
| import { ApResolverService, Resolver } from '../ApResolverService.js'; | import { ApResolverService, Resolver } from '../ApResolverService.js'; | ||||||
| import { ApPersonService } from './ApPersonService.js'; | import { ApPersonService } from './ApPersonService.js'; | ||||||
| import type { IObject, IApMention } from '../type.js'; | import type { IObject, IApMention } from '../type.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class ApMentionService { | export class ApMentionService { | ||||||
|  | @ -21,6 +22,7 @@ export class ApMentionService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async extractApMentions(tags: IObject | IObject[] | null | undefined, resolver: Resolver) { | 	public async extractApMentions(tags: IObject | IObject[] | null | undefined, resolver: Resolver) { | ||||||
| 		const hrefs = unique(this.extractApMentionObjects(tags).map(x => x.href as string)); | 		const hrefs = unique(this.extractApMentionObjects(tags).map(x => x.href as string)); | ||||||
| 
 | 
 | ||||||
|  | @ -32,6 +34,7 @@ export class ApMentionService { | ||||||
| 		return mentionedUsers; | 		return mentionedUsers; | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	@bindThis | ||||||
| 	public extractApMentionObjects(tags: IObject | IObject[] | null | undefined): IApMention[] { | 	public extractApMentionObjects(tags: IObject | IObject[] | null | undefined): IApMention[] { | ||||||
| 		if (tags == null) return []; | 		if (tags == null) return []; | ||||||
| 		return toArray(tags).filter(isMention); | 		return toArray(tags).filter(isMention); | ||||||
|  |  | ||||||
|  | @ -32,6 +32,7 @@ import { ApQuestionService } from './ApQuestionService.js'; | ||||||
| import { ApImageService } from './ApImageService.js'; | import { ApImageService } from './ApImageService.js'; | ||||||
| import type { Resolver } from '../ApResolverService.js'; | import type { Resolver } from '../ApResolverService.js'; | ||||||
| import type { IObject, IPost } from '../type.js'; | import type { IObject, IPost } from '../type.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class ApNoteService { | export class ApNoteService { | ||||||
|  | @ -74,6 +75,7 @@ export class ApNoteService { | ||||||
| 		this.logger = this.apLoggerService.logger; | 		this.logger = this.apLoggerService.logger; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public validateNote(object: any, uri: string) { | 	public validateNote(object: any, uri: string) { | ||||||
| 		const expectHost = this.utilityService.extractDbHost(uri); | 		const expectHost = this.utilityService.extractDbHost(uri); | ||||||
| 	 | 	 | ||||||
|  | @ -101,6 +103,7 @@ export class ApNoteService { | ||||||
| 	 * | 	 * | ||||||
| 	 * Misskeyに対象のNoteが登録されていればそれを返します。 | 	 * Misskeyに対象のNoteが登録されていればそれを返します。 | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async fetchNote(object: string | IObject): Promise<Note | null> { | 	public async fetchNote(object: string | IObject): Promise<Note | null> { | ||||||
| 		return await this.apDbResolverService.getNoteFromApId(object); | 		return await this.apDbResolverService.getNoteFromApId(object); | ||||||
| 	} | 	} | ||||||
|  | @ -108,6 +111,7 @@ export class ApNoteService { | ||||||
| 	/** | 	/** | ||||||
| 	 * Noteを作成します。 | 	 * Noteを作成します。 | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<Note | null> { | 	public async createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<Note | null> { | ||||||
| 		if (resolver == null) resolver = this.apResolverService.createResolver(); | 		if (resolver == null) resolver = this.apResolverService.createResolver(); | ||||||
| 	 | 	 | ||||||
|  | @ -313,6 +317,7 @@ export class ApNoteService { | ||||||
| 	 * Misskeyに対象のNoteが登録されていればそれを返し、そうでなければ | 	 * Misskeyに対象のNoteが登録されていればそれを返し、そうでなければ | ||||||
| 	 * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 | 	 * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async resolveNote(value: string | IObject, resolver?: Resolver): Promise<Note | null> { | 	public async resolveNote(value: string | IObject, resolver?: Resolver): Promise<Note | null> { | ||||||
| 		const uri = typeof value === 'string' ? value : value.id; | 		const uri = typeof value === 'string' ? value : value.id; | ||||||
| 		if (uri == null) throw new Error('missing uri'); | 		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<Emoji[]> { | 	public async extractEmojis(tags: IObject | IObject[], host: string): Promise<Emoji[]> { | ||||||
| 		host = this.utilityService.toPuny(host); | 		host = this.utilityService.toPuny(host); | ||||||
| 	 | 	 | ||||||
|  |  | ||||||
|  | @ -72,6 +72,7 @@ function addService(target: { [x: string]: any }, source: IApPropertyValue) { | ||||||
| 		target[source.name.split(':')[2]] = service(id, username); | 		target[source.name.split(':')[2]] = service(id, username); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class ApPersonService implements OnModuleInit { | export class ApPersonService implements OnModuleInit { | ||||||
|  | @ -161,6 +162,7 @@ export class ApPersonService implements OnModuleInit { | ||||||
| 	 * @param x Fetched object | 	 * @param x Fetched object | ||||||
| 	 * @param uri Fetch target URI | 	 * @param uri Fetch target URI | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	private validateActor(x: IObject, uri: string): IActor { | 	private validateActor(x: IObject, uri: string): IActor { | ||||||
| 		const expectHost = this.utilityService.toPuny(new URL(uri).hostname); | 		const expectHost = this.utilityService.toPuny(new URL(uri).hostname); | ||||||
| 
 | 
 | ||||||
|  | @ -224,6 +226,7 @@ export class ApPersonService implements OnModuleInit { | ||||||
| 	 * | 	 * | ||||||
| 	 * Misskeyに対象のPersonが登録されていればそれを返します。 | 	 * Misskeyに対象のPersonが登録されていればそれを返します。 | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async fetchPerson(uri: string, resolver?: Resolver): Promise<CacheableUser | null> { | 	public async fetchPerson(uri: string, resolver?: Resolver): Promise<CacheableUser | null> { | ||||||
| 		if (typeof uri !== 'string') throw new Error('uri is not string'); | 		if (typeof uri !== 'string') throw new Error('uri is not string'); | ||||||
| 
 | 
 | ||||||
|  | @ -253,6 +256,7 @@ export class ApPersonService implements OnModuleInit { | ||||||
| 	/** | 	/** | ||||||
| 	 * Personを作成します。 | 	 * Personを作成します。 | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async createPerson(uri: string, resolver?: Resolver): Promise<User> { | 	public async createPerson(uri: string, resolver?: Resolver): Promise<User> { | ||||||
| 		if (typeof uri !== 'string') throw new Error('uri is not string'); | 		if (typeof uri !== 'string') throw new Error('uri is not string'); | ||||||
| 
 | 
 | ||||||
|  | @ -402,6 +406,7 @@ export class ApPersonService implements OnModuleInit { | ||||||
| 	 * @param resolver Resolver | 	 * @param resolver Resolver | ||||||
| 	 * @param hint Hint of Person object (この値が正当なPersonの場合、Remote resolveをせずに更新に利用します) | 	 * @param hint Hint of Person object (この値が正当なPersonの場合、Remote resolveをせずに更新に利用します) | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async updatePerson(uri: string, resolver?: Resolver | null, hint?: IObject): Promise<void> { | 	public async updatePerson(uri: string, resolver?: Resolver | null, hint?: IObject): Promise<void> { | ||||||
| 		if (typeof uri !== 'string') throw new Error('uri is not string'); | 		if (typeof uri !== 'string') throw new Error('uri is not string'); | ||||||
| 
 | 
 | ||||||
|  | @ -512,6 +517,7 @@ export class ApPersonService implements OnModuleInit { | ||||||
| 	 * Misskeyに対象のPersonが登録されていればそれを返し、そうでなければ | 	 * Misskeyに対象のPersonが登録されていればそれを返し、そうでなければ | ||||||
| 	 * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 | 	 * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async resolvePerson(uri: string, resolver?: Resolver): Promise<CacheableUser> { | 	public async resolvePerson(uri: string, resolver?: Resolver): Promise<CacheableUser> { | ||||||
| 		if (typeof uri !== 'string') throw new Error('uri is not string'); | 		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); | 		return await this.createPerson(uri, resolver); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public analyzeAttachments(attachments: IObject | IObject[] | undefined) { | 	public analyzeAttachments(attachments: IObject | IObject[] | undefined) { | ||||||
| 		const fields: { | 		const fields: { | ||||||
| 		name: string, | 		name: string, | ||||||
|  | @ -551,6 +558,7 @@ export class ApPersonService implements OnModuleInit { | ||||||
| 		return { fields, services }; | 		return { fields, services }; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async updateFeatured(userId: User['id'], resolver?: Resolver) { | 	public async updateFeatured(userId: User['id'], resolver?: Resolver) { | ||||||
| 		const user = await this.usersRepository.findOneByOrFail({ id: userId }); | 		const user = await this.usersRepository.findOneByOrFail({ id: userId }); | ||||||
| 		if (!this.userEntityService.isRemoteUser(user)) return; | 		if (!this.userEntityService.isRemoteUser(user)) return; | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ import { ApLoggerService } from '../ApLoggerService.js'; | ||||||
| import { ApResolverService } from '../ApResolverService.js'; | import { ApResolverService } from '../ApResolverService.js'; | ||||||
| import type { Resolver } from '../ApResolverService.js'; | import type { Resolver } from '../ApResolverService.js'; | ||||||
| import type { IObject, IQuestion } from '../type.js'; | import type { IObject, IQuestion } from '../type.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class ApQuestionService { | export class ApQuestionService { | ||||||
|  | @ -30,6 +31,7 @@ export class ApQuestionService { | ||||||
| 		this.logger = this.apLoggerService.logger; | 		this.logger = this.apLoggerService.logger; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async extractPollFromQuestion(source: string | IObject, resolver?: Resolver): Promise<IPoll> { | 	public async extractPollFromQuestion(source: string | IObject, resolver?: Resolver): Promise<IPoll> { | ||||||
| 		if (resolver == null) resolver = this.apResolverService.createResolver(); | 		if (resolver == null) resolver = this.apResolverService.createResolver(); | ||||||
| 
 | 
 | ||||||
|  | @ -65,6 +67,7 @@ export class ApQuestionService { | ||||||
| 	 * @param uri URI of AP Question object | 	 * @param uri URI of AP Question object | ||||||
| 	 * @returns true if updated | 	 * @returns true if updated | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	public async updateQuestion(value: any, resolver?: Resolver) { | 	public async updateQuestion(value: any, resolver?: Resolver) { | ||||||
| 		const uri = typeof value === 'string' ? value : value.id; | 		const uri = typeof value === 'string' ? value : value.id; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| import { Inject, Injectable } from '@nestjs/common'; | import { Inject, Injectable } from '@nestjs/common'; | ||||||
| import type Logger from '@/logger.js'; | import type Logger from '@/logger.js'; | ||||||
| import { LoggerService } from '@/core/LoggerService.js'; | import { LoggerService } from '@/core/LoggerService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class ChartLoggerService { | export class ChartLoggerService { | ||||||
|  |  | ||||||
|  | @ -13,6 +13,7 @@ import PerUserFollowingChart from './charts/per-user-following.js'; | ||||||
| import PerUserDriveChart from './charts/per-user-drive.js'; | import PerUserDriveChart from './charts/per-user-drive.js'; | ||||||
| import ApRequestChart from './charts/ap-request.js'; | import ApRequestChart from './charts/ap-request.js'; | ||||||
| import type { OnApplicationShutdown } from '@nestjs/common'; | import type { OnApplicationShutdown } from '@nestjs/common'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class ChartManagementService implements OnApplicationShutdown { | export class ChartManagementService implements OnApplicationShutdown { | ||||||
|  | @ -49,6 +50,7 @@ export class ChartManagementService implements OnApplicationShutdown { | ||||||
| 		]; | 		]; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async run() { | 	public async run() { | ||||||
| 		// 20分おきにメモリ情報をDBに書き込み
 | 		// 20分おきにメモリ情報をDBに書き込み
 | ||||||
| 		this.saveIntervalId = setInterval(() => { | 		this.saveIntervalId = setInterval(() => { | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ import { DataSource } from 'typeorm'; | ||||||
| import { AppLockService } from '@/core/AppLockService.js'; | import { AppLockService } from '@/core/AppLockService.js'; | ||||||
| import type { User } from '@/models/entities/User.js'; | import type { User } from '@/models/entities/User.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import Chart from '../core.js'; | import Chart from '../core.js'; | ||||||
| import { ChartLoggerService } from '../ChartLoggerService.js'; | import { ChartLoggerService } from '../ChartLoggerService.js'; | ||||||
| import { name, schema } from './entities/active-users.js'; | import { name, schema } from './entities/active-users.js'; | ||||||
|  | @ -36,6 +37,7 @@ export default class ActiveUsersChart extends Chart<typeof schema> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async read(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise<void> { | 	public async read(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise<void> { | ||||||
| 		await this.commit({ | 		await this.commit({ | ||||||
| 			'read': [user.id], | 			'read': [user.id], | ||||||
|  | @ -48,6 +50,7 @@ export default class ActiveUsersChart extends Chart<typeof schema> { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async write(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise<void> { | 	public async write(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise<void> { | ||||||
| 		await this.commit({ | 		await this.commit({ | ||||||
| 			'write': [user.id], | 			'write': [user.id], | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ import { Injectable, Inject } from '@nestjs/common'; | ||||||
| import { DataSource } from 'typeorm'; | import { DataSource } from 'typeorm'; | ||||||
| import { AppLockService } from '@/core/AppLockService.js'; | import { AppLockService } from '@/core/AppLockService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import Chart from '../core.js'; | import Chart from '../core.js'; | ||||||
| import { ChartLoggerService } from '../ChartLoggerService.js'; | import { ChartLoggerService } from '../ChartLoggerService.js'; | ||||||
| import { name, schema } from './entities/ap-request.js'; | import { name, schema } from './entities/ap-request.js'; | ||||||
|  | @ -31,18 +32,21 @@ export default class ApRequestChart extends Chart<typeof schema> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async deliverSucc(): Promise<void> { | 	public async deliverSucc(): Promise<void> { | ||||||
| 		await this.commit({ | 		await this.commit({ | ||||||
| 			'deliverSucceeded': 1, | 			'deliverSucceeded': 1, | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async deliverFail(): Promise<void> { | 	public async deliverFail(): Promise<void> { | ||||||
| 		await this.commit({ | 		await this.commit({ | ||||||
| 			'deliverFailed': 1, | 			'deliverFailed': 1, | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async inbox(): Promise<void> { | 	public async inbox(): Promise<void> { | ||||||
| 		await this.commit({ | 		await this.commit({ | ||||||
| 			'inboxReceived': 1, | 			'inboxReceived': 1, | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ import { Not, IsNull, DataSource } from 'typeorm'; | ||||||
| import type { DriveFile } from '@/models/entities/DriveFile.js'; | import type { DriveFile } from '@/models/entities/DriveFile.js'; | ||||||
| import { AppLockService } from '@/core/AppLockService.js'; | import { AppLockService } from '@/core/AppLockService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import Chart from '../core.js'; | import Chart from '../core.js'; | ||||||
| import { ChartLoggerService } from '../ChartLoggerService.js'; | import { ChartLoggerService } from '../ChartLoggerService.js'; | ||||||
| import { name, schema } from './entities/drive.js'; | import { name, schema } from './entities/drive.js'; | ||||||
|  | @ -32,6 +33,7 @@ export default class DriveChart extends Chart<typeof schema> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async update(file: DriveFile, isAdditional: boolean): Promise<void> { | 	public async update(file: DriveFile, isAdditional: boolean): Promise<void> { | ||||||
| 		const fileSizeKb = file.size / 1000; | 		const fileSizeKb = file.size / 1000; | ||||||
| 		await this.commit(file.userHost === null ? { | 		await this.commit(file.userHost === null ? { | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ import type { FollowingsRepository, InstancesRepository } from '@/models/index.j | ||||||
| import { AppLockService } from '@/core/AppLockService.js'; | import { AppLockService } from '@/core/AppLockService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import { MetaService } from '@/core/MetaService.js'; | import { MetaService } from '@/core/MetaService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import Chart from '../core.js'; | import Chart from '../core.js'; | ||||||
| import { ChartLoggerService } from '../ChartLoggerService.js'; | import { ChartLoggerService } from '../ChartLoggerService.js'; | ||||||
| import { name, schema } from './entities/federation.js'; | import { name, schema } from './entities/federation.js'; | ||||||
|  | @ -107,6 +108,7 @@ export default class FederationChart extends Chart<typeof schema> { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async deliverd(host: string, succeeded: boolean): Promise<void> { | 	public async deliverd(host: string, succeeded: boolean): Promise<void> { | ||||||
| 		await this.commit(succeeded ? { | 		await this.commit(succeeded ? { | ||||||
| 			'deliveredInstances': [host], | 			'deliveredInstances': [host], | ||||||
|  | @ -115,6 +117,7 @@ export default class FederationChart extends Chart<typeof schema> { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async inbox(host: string): Promise<void> { | 	public async inbox(host: string): Promise<void> { | ||||||
| 		await this.commit({ | 		await this.commit({ | ||||||
| 			'inboxInstances': [host], | 			'inboxInstances': [host], | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ import type { User } from '@/models/entities/User.js'; | ||||||
| import { AppLockService } from '@/core/AppLockService.js'; | import { AppLockService } from '@/core/AppLockService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import { UserEntityService } from '@/core/entities/UserEntityService.js'; | import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import Chart from '../core.js'; | import Chart from '../core.js'; | ||||||
| import { ChartLoggerService } from '../ChartLoggerService.js'; | import { ChartLoggerService } from '../ChartLoggerService.js'; | ||||||
| import { name, schema } from './entities/hashtag.js'; | import { name, schema } from './entities/hashtag.js'; | ||||||
|  | @ -34,6 +35,7 @@ export default class HashtagChart extends Chart<typeof schema> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async update(hashtag: string, user: { id: User['id'], host: User['host'] }): Promise<void> { | 	public async update(hashtag: string, user: { id: User['id'], host: User['host'] }): Promise<void> { | ||||||
| 		await this.commit({ | 		await this.commit({ | ||||||
| 			'local.users': this.userEntityService.isLocalUser(user) ? [user.id] : [], | 			'local.users': this.userEntityService.isLocalUser(user) ? [user.id] : [], | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ import type { Note } from '@/models/entities/Note.js'; | ||||||
| import { AppLockService } from '@/core/AppLockService.js'; | import { AppLockService } from '@/core/AppLockService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import { UtilityService } from '@/core/UtilityService.js'; | import { UtilityService } from '@/core/UtilityService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import Chart from '../core.js'; | import Chart from '../core.js'; | ||||||
| import { ChartLoggerService } from '../ChartLoggerService.js'; | import { ChartLoggerService } from '../ChartLoggerService.js'; | ||||||
| import { name, schema } from './entities/instance.js'; | import { name, schema } from './entities/instance.js'; | ||||||
|  | @ -68,12 +69,14 @@ export default class InstanceChart extends Chart<typeof schema> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async requestReceived(host: string): Promise<void> { | 	public async requestReceived(host: string): Promise<void> { | ||||||
| 		await this.commit({ | 		await this.commit({ | ||||||
| 			'requests.received': 1, | 			'requests.received': 1, | ||||||
| 		}, this.utilityService.toPuny(host)); | 		}, this.utilityService.toPuny(host)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async requestSent(host: string, isSucceeded: boolean): Promise<void> { | 	public async requestSent(host: string, isSucceeded: boolean): Promise<void> { | ||||||
| 		await this.commit({ | 		await this.commit({ | ||||||
| 			'requests.succeeded': isSucceeded ? 1 : 0, | 			'requests.succeeded': isSucceeded ? 1 : 0, | ||||||
|  | @ -81,6 +84,7 @@ export default class InstanceChart extends Chart<typeof schema> { | ||||||
| 		}, this.utilityService.toPuny(host)); | 		}, this.utilityService.toPuny(host)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async newUser(host: string): Promise<void> { | 	public async newUser(host: string): Promise<void> { | ||||||
| 		await this.commit({ | 		await this.commit({ | ||||||
| 			'users.total': 1, | 			'users.total': 1, | ||||||
|  | @ -88,6 +92,7 @@ export default class InstanceChart extends Chart<typeof schema> { | ||||||
| 		}, this.utilityService.toPuny(host)); | 		}, this.utilityService.toPuny(host)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async updateNote(host: string, note: Note, isAdditional: boolean): Promise<void> { | 	public async updateNote(host: string, note: Note, isAdditional: boolean): Promise<void> { | ||||||
| 		await this.commit({ | 		await this.commit({ | ||||||
| 			'notes.total': isAdditional ? 1 : -1, | 			'notes.total': isAdditional ? 1 : -1, | ||||||
|  | @ -100,6 +105,7 @@ export default class InstanceChart extends Chart<typeof schema> { | ||||||
| 		}, this.utilityService.toPuny(host)); | 		}, this.utilityService.toPuny(host)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async updateFollowing(host: string, isAdditional: boolean): Promise<void> { | 	public async updateFollowing(host: string, isAdditional: boolean): Promise<void> { | ||||||
| 		await this.commit({ | 		await this.commit({ | ||||||
| 			'following.total': isAdditional ? 1 : -1, | 			'following.total': isAdditional ? 1 : -1, | ||||||
|  | @ -108,6 +114,7 @@ export default class InstanceChart extends Chart<typeof schema> { | ||||||
| 		}, this.utilityService.toPuny(host)); | 		}, this.utilityService.toPuny(host)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async updateFollowers(host: string, isAdditional: boolean): Promise<void> { | 	public async updateFollowers(host: string, isAdditional: boolean): Promise<void> { | ||||||
| 		await this.commit({ | 		await this.commit({ | ||||||
| 			'followers.total': isAdditional ? 1 : -1, | 			'followers.total': isAdditional ? 1 : -1, | ||||||
|  | @ -116,6 +123,7 @@ export default class InstanceChart extends Chart<typeof schema> { | ||||||
| 		}, this.utilityService.toPuny(host)); | 		}, this.utilityService.toPuny(host)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async updateDrive(file: DriveFile, isAdditional: boolean): Promise<void> { | 	public async updateDrive(file: DriveFile, isAdditional: boolean): Promise<void> { | ||||||
| 		const fileSizeKb = file.size / 1000; | 		const fileSizeKb = file.size / 1000; | ||||||
| 		await this.commit({ | 		await this.commit({ | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ import type { NotesRepository } from '@/models/index.js'; | ||||||
| import type { Note } from '@/models/entities/Note.js'; | import type { Note } from '@/models/entities/Note.js'; | ||||||
| import { AppLockService } from '@/core/AppLockService.js'; | import { AppLockService } from '@/core/AppLockService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import Chart from '../core.js'; | import Chart from '../core.js'; | ||||||
| import { ChartLoggerService } from '../ChartLoggerService.js'; | import { ChartLoggerService } from '../ChartLoggerService.js'; | ||||||
| import { name, schema } from './entities/notes.js'; | import { name, schema } from './entities/notes.js'; | ||||||
|  | @ -44,6 +45,7 @@ export default class NotesChart extends Chart<typeof schema> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async update(note: Note, isAdditional: boolean): Promise<void> { | 	public async update(note: Note, isAdditional: boolean): Promise<void> { | ||||||
| 		const prefix = note.userHost === null ? 'local' : 'remote'; | 		const prefix = note.userHost === null ? 'local' : 'remote'; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import type { DriveFile } from '@/models/entities/DriveFile.js'; | ||||||
| import { AppLockService } from '@/core/AppLockService.js'; | import { AppLockService } from '@/core/AppLockService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; | import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import Chart from '../core.js'; | import Chart from '../core.js'; | ||||||
| import { ChartLoggerService } from '../ChartLoggerService.js'; | import { ChartLoggerService } from '../ChartLoggerService.js'; | ||||||
| import { name, schema } from './entities/per-user-drive.js'; | import { name, schema } from './entities/per-user-drive.js'; | ||||||
|  | @ -46,6 +47,7 @@ export default class PerUserDriveChart extends Chart<typeof schema> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async update(file: DriveFile, isAdditional: boolean): Promise<void> { | 	public async update(file: DriveFile, isAdditional: boolean): Promise<void> { | ||||||
| 		const fileSizeKb = file.size / 1000; | 		const fileSizeKb = file.size / 1000; | ||||||
| 		await this.commit({ | 		await this.commit({ | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import { AppLockService } from '@/core/AppLockService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import { UserEntityService } from '@/core/entities/UserEntityService.js'; | import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||||
| import type { FollowingsRepository } from '@/models/index.js'; | import type { FollowingsRepository } from '@/models/index.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import Chart from '../core.js'; | import Chart from '../core.js'; | ||||||
| import { ChartLoggerService } from '../ChartLoggerService.js'; | import { ChartLoggerService } from '../ChartLoggerService.js'; | ||||||
| import { name, schema } from './entities/per-user-following.js'; | import { name, schema } from './entities/per-user-following.js'; | ||||||
|  | @ -55,6 +56,7 @@ export default class PerUserFollowingChart extends Chart<typeof schema> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async update(follower: { id: User['id']; host: User['host']; }, followee: { id: User['id']; host: User['host']; }, isFollow: boolean): Promise<void> { | 	public async update(follower: { id: User['id']; host: User['host']; }, followee: { id: User['id']; host: User['host']; }, isFollow: boolean): Promise<void> { | ||||||
| 		const prefixFollower = this.userEntityService.isLocalUser(follower) ? 'local' : 'remote'; | 		const prefixFollower = this.userEntityService.isLocalUser(follower) ? 'local' : 'remote'; | ||||||
| 		const prefixFollowee = this.userEntityService.isLocalUser(followee) ? 'local' : 'remote'; | 		const prefixFollowee = this.userEntityService.isLocalUser(followee) ? 'local' : 'remote'; | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import type { Note } from '@/models/entities/Note.js'; | ||||||
| import { AppLockService } from '@/core/AppLockService.js'; | import { AppLockService } from '@/core/AppLockService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import type { NotesRepository } from '@/models/index.js'; | import type { NotesRepository } from '@/models/index.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import Chart from '../core.js'; | import Chart from '../core.js'; | ||||||
| import { ChartLoggerService } from '../ChartLoggerService.js'; | import { ChartLoggerService } from '../ChartLoggerService.js'; | ||||||
| import { name, schema } from './entities/per-user-notes.js'; | import { name, schema } from './entities/per-user-notes.js'; | ||||||
|  | @ -43,6 +44,7 @@ export default class PerUserNotesChart extends Chart<typeof schema> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async update(user: { id: User['id'] }, note: Note, isAdditional: boolean): Promise<void> { | 	public async update(user: { id: User['id'] }, note: Note, isAdditional: boolean): Promise<void> { | ||||||
| 		await this.commit({ | 		await this.commit({ | ||||||
| 			'total': isAdditional ? 1 : -1, | 			'total': isAdditional ? 1 : -1, | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import type { Note } from '@/models/entities/Note.js'; | ||||||
| import { AppLockService } from '@/core/AppLockService.js'; | import { AppLockService } from '@/core/AppLockService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import { UserEntityService } from '@/core/entities/UserEntityService.js'; | import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import Chart from '../core.js'; | import Chart from '../core.js'; | ||||||
| import { ChartLoggerService } from '../ChartLoggerService.js'; | import { ChartLoggerService } from '../ChartLoggerService.js'; | ||||||
| import { name, schema } from './entities/per-user-reactions.js'; | import { name, schema } from './entities/per-user-reactions.js'; | ||||||
|  | @ -35,6 +36,7 @@ export default class PerUserReactionsChart extends Chart<typeof schema> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async update(user: { id: User['id'], host: User['host'] }, note: Note): Promise<void> { | 	public async update(user: { id: User['id'], host: User['host'] }, note: Note): Promise<void> { | ||||||
| 		const prefix = this.userEntityService.isLocalUser(user) ? 'local' : 'remote'; | 		const prefix = this.userEntityService.isLocalUser(user) ? 'local' : 'remote'; | ||||||
| 		this.commit({ | 		this.commit({ | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ import { DataSource } from 'typeorm'; | ||||||
| import { AppLockService } from '@/core/AppLockService.js'; | import { AppLockService } from '@/core/AppLockService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import Logger from '@/logger.js'; | import Logger from '@/logger.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import Chart from '../core.js'; | import Chart from '../core.js'; | ||||||
| import { name, schema } from './entities/test-grouped.js'; | import { name, schema } from './entities/test-grouped.js'; | ||||||
| import type { KVs } from '../core.js'; | import type { KVs } from '../core.js'; | ||||||
|  | @ -35,6 +36,7 @@ export default class TestGroupedChart extends Chart<typeof schema> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async increment(group: string): Promise<void> { | 	public async increment(group: string): Promise<void> { | ||||||
| 		if (this.total[group] == null) this.total[group] = 0; | 		if (this.total[group] == null) this.total[group] = 0; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ import { DataSource } from 'typeorm'; | ||||||
| import { AppLockService } from '@/core/AppLockService.js'; | import { AppLockService } from '@/core/AppLockService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import Logger from '@/logger.js'; | import Logger from '@/logger.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import Chart from '../core.js'; | import Chart from '../core.js'; | ||||||
| import { name, schema } from './entities/test-intersection.js'; | import { name, schema } from './entities/test-intersection.js'; | ||||||
| import type { KVs } from '../core.js'; | import type { KVs } from '../core.js'; | ||||||
|  | @ -31,12 +32,14 @@ export default class TestIntersectionChart extends Chart<typeof schema> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async addA(key: string): Promise<void> { | 	public async addA(key: string): Promise<void> { | ||||||
| 		await this.commit({ | 		await this.commit({ | ||||||
| 			a: [key], | 			a: [key], | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async addB(key: string): Promise<void> { | 	public async addB(key: string): Promise<void> { | ||||||
| 		await this.commit({ | 		await this.commit({ | ||||||
| 			b: [key], | 			b: [key], | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ import { DataSource } from 'typeorm'; | ||||||
| import { AppLockService } from '@/core/AppLockService.js'; | import { AppLockService } from '@/core/AppLockService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import Logger from '@/logger.js'; | import Logger from '@/logger.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import Chart from '../core.js'; | import Chart from '../core.js'; | ||||||
| import { name, schema } from './entities/test-unique.js'; | import { name, schema } from './entities/test-unique.js'; | ||||||
| import type { KVs } from '../core.js'; | import type { KVs } from '../core.js'; | ||||||
|  | @ -31,6 +32,7 @@ export default class TestUniqueChart extends Chart<typeof schema> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async uniqueIncrement(key: string): Promise<void> { | 	public async uniqueIncrement(key: string): Promise<void> { | ||||||
| 		await this.commit({ | 		await this.commit({ | ||||||
| 			foo: [key], | 			foo: [key], | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ import { DataSource } from 'typeorm'; | ||||||
| import { AppLockService } from '@/core/AppLockService.js'; | import { AppLockService } from '@/core/AppLockService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import Logger from '@/logger.js'; | import Logger from '@/logger.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import Chart from '../core.js'; | import Chart from '../core.js'; | ||||||
| import { name, schema } from './entities/test.js'; | import { name, schema } from './entities/test.js'; | ||||||
| import type { KVs } from '../core.js'; | import type { KVs } from '../core.js'; | ||||||
|  | @ -35,6 +36,7 @@ export default class TestChart extends Chart<typeof schema> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async increment(): Promise<void> { | 	public async increment(): Promise<void> { | ||||||
| 		this.total++; | 		this.total++; | ||||||
| 
 | 
 | ||||||
|  | @ -44,6 +46,7 @@ export default class TestChart extends Chart<typeof schema> { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async decrement(): Promise<void> { | 	public async decrement(): Promise<void> { | ||||||
| 		this.total--; | 		this.total--; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import { AppLockService } from '@/core/AppLockService.js'; | ||||||
| import { DI } from '@/di-symbols.js'; | import { DI } from '@/di-symbols.js'; | ||||||
| import { UserEntityService } from '@/core/entities/UserEntityService.js'; | import { UserEntityService } from '@/core/entities/UserEntityService.js'; | ||||||
| import type { UsersRepository } from '@/models/index.js'; | import type { UsersRepository } from '@/models/index.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import Chart from '../core.js'; | import Chart from '../core.js'; | ||||||
| import { ChartLoggerService } from '../ChartLoggerService.js'; | import { ChartLoggerService } from '../ChartLoggerService.js'; | ||||||
| import { name, schema } from './entities/users.js'; | import { name, schema } from './entities/users.js'; | ||||||
|  | @ -46,6 +47,7 @@ export default class UsersChart extends Chart<typeof schema> { | ||||||
| 		return {}; | 		return {}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async update(user: { id: User['id'], host: User['host'] }, isAdditional: boolean): Promise<void> { | 	public async update(user: { id: User['id'], host: User['host'] }, isAdditional: boolean): Promise<void> { | ||||||
| 		const prefix = this.userEntityService.isLocalUser(user) ? 'local' : 'remote'; | 		const prefix = this.userEntityService.isLocalUser(user) ? 'local' : 'remote'; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ import * as nestedProperty from 'nested-property'; | ||||||
| import { EntitySchema, LessThan, Between } from 'typeorm'; | import { EntitySchema, LessThan, Between } from 'typeorm'; | ||||||
| import { dateUTC, isTimeSame, isTimeBefore, subtractTime, addTime } from '@/misc/prelude/time.js'; | import { dateUTC, isTimeSame, isTimeBefore, subtractTime, addTime } from '@/misc/prelude/time.js'; | ||||||
| import type Logger from '@/logger.js'; | import type Logger from '@/logger.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| import type { Repository, DataSource } from 'typeorm'; | import type { Repository, DataSource } from 'typeorm'; | ||||||
| 
 | 
 | ||||||
| const columnPrefix = '___' as const; | const columnPrefix = '___' as const; | ||||||
|  | @ -249,6 +250,7 @@ export default abstract class Chart<T extends Schema> { | ||||||
| 		this.repositoryForDay = db.getRepository<{ id: number; group?: string | null; date: number; }>(day); | 		this.repositoryForDay = db.getRepository<{ id: number; group?: string | null; date: number; }>(day); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private convertRawRecord(x: RawRecord<T>): KVs<T> { | 	private convertRawRecord(x: RawRecord<T>): KVs<T> { | ||||||
| 		const kvs = {} as Record<string, number>; | 		const kvs = {} as Record<string, number>; | ||||||
| 		for (const k of Object.keys(x).filter((k) => k.startsWith(columnPrefix)) as (keyof Columns<T>)[]) { | 		for (const k of Object.keys(x).filter((k) => k.startsWith(columnPrefix)) as (keyof Columns<T>)[]) { | ||||||
|  | @ -257,6 +259,7 @@ export default abstract class Chart<T extends Schema> { | ||||||
| 		return kvs as KVs<T>; | 		return kvs as KVs<T>; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private getNewLog(latest: KVs<T> | null): KVs<T> { | 	private getNewLog(latest: KVs<T> | null): KVs<T> { | ||||||
| 		const log = {} as Record<keyof T, number>; | 		const log = {} as Record<keyof T, number>; | ||||||
| 		for (const [k, v] of Object.entries(this.schema) as ([keyof typeof this['schema'], this['schema'][string]])[]) { | 		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<T extends Schema> { | ||||||
| 		return log as KVs<T>; | 		return log as KVs<T>; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	private getLatestLog(group: string | null, span: 'hour' | 'day'): Promise<RawRecord<T> | null> { | 	private getLatestLog(group: string | null, span: 'hour' | 'day'): Promise<RawRecord<T> | null> { | ||||||
| 		const repository = | 		const repository = | ||||||
| 			span === 'hour' ? this.repositoryForHour : | 			span === 'hour' ? this.repositoryForHour : | ||||||
|  | @ -288,6 +292,7 @@ export default abstract class Chart<T extends Schema> { | ||||||
| 	/** | 	/** | ||||||
| 	 * 現在(=今のHour or Day)のログをデータベースから探して、あればそれを返し、なければ作成して返します。 | 	 * 現在(=今のHour or Day)のログをデータベースから探して、あればそれを返し、なければ作成して返します。 | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@bindThis | ||||||
| 	private async claimCurrentLog(group: string | null, span: 'hour' | 'day'): Promise<RawRecord<T>> { | 	private async claimCurrentLog(group: string | null, span: 'hour' | 'day'): Promise<RawRecord<T>> { | ||||||
| 		const [y, m, d, h] = Chart.getCurrentDate(); | 		const [y, m, d, h] = Chart.getCurrentDate(); | ||||||
| 
 | 
 | ||||||
|  | @ -380,6 +385,7 @@ export default abstract class Chart<T extends Schema> { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async save(): Promise<void> { | 	public async save(): Promise<void> { | ||||||
| 		if (this.buffer.length === 0) { | 		if (this.buffer.length === 0) { | ||||||
| 			this.logger.info(`${this.name}: Write skipped`); | 			this.logger.info(`${this.name}: Write skipped`); | ||||||
|  | @ -498,6 +504,7 @@ export default abstract class Chart<T extends Schema> { | ||||||
| 					update(logHour, logDay)))); | 					update(logHour, logDay)))); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async tick(major: boolean, group: string | null = null): Promise<void> { | 	public async tick(major: boolean, group: string | null = null): Promise<void> { | ||||||
| 		const data = major ? await this.tickMajor(group) : await this.tickMinor(group); | 		const data = major ? await this.tickMajor(group) : await this.tickMinor(group); | ||||||
| 
 | 
 | ||||||
|  | @ -533,10 +540,12 @@ export default abstract class Chart<T extends Schema> { | ||||||
| 			update(logHour, logDay)); | 			update(logHour, logDay)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public resync(group: string | null = null): Promise<void> { | 	public resync(group: string | null = null): Promise<void> { | ||||||
| 		return this.tick(true, group); | 		return this.tick(true, group); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async clean(): Promise<void> { | 	public async clean(): Promise<void> { | ||||||
| 		const current = dateUTC(Chart.getCurrentDate()); | 		const current = dateUTC(Chart.getCurrentDate()); | ||||||
| 
 | 
 | ||||||
|  | @ -572,6 +581,7 @@ export default abstract class Chart<T extends Schema> { | ||||||
| 		]); | 		]); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async getChartRaw(span: 'hour' | 'day', amount: number, cursor: Date | null, group: string | null = null): Promise<ChartResult<T>> { | 	public async getChartRaw(span: 'hour' | 'day', amount: number, cursor: Date | null, group: string | null = null): Promise<ChartResult<T>> { | ||||||
| 		const [y, m, d, h, _m, _s, _ms] = cursor ? Chart.parseDate(subtractTime(addTime(cursor, 1, span), 1)) : Chart.getCurrentDate(); | 		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; | 		const [y2, m2, d2, h2] = cursor ? Chart.parseDate(addTime(cursor, 1, span)) : [] as never; | ||||||
|  | @ -676,6 +686,7 @@ export default abstract class Chart<T extends Schema> { | ||||||
| 		return res; | 		return res; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async getChart(span: 'hour' | 'day', amount: number, cursor: Date | null, group: string | null = null): Promise<Unflatten<ChartResult<T>>> { | 	public async getChart(span: 'hour' | 'day', amount: number, cursor: Date | null, group: string | null = null): Promise<Unflatten<ChartResult<T>>> { | ||||||
| 		const result = await this.getChartRaw(span, amount, cursor, group); | 		const result = await this.getChartRaw(span, amount, cursor, group); | ||||||
| 		const object = {}; | 		const object = {}; | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ import type { AbuseUserReportsRepository } from '@/models/index.js'; | ||||||
| import { awaitAll } from '@/misc/prelude/await-all.js'; | import { awaitAll } from '@/misc/prelude/await-all.js'; | ||||||
| import type { AbuseUserReport } from '@/models/entities/AbuseUserReport.js'; | import type { AbuseUserReport } from '@/models/entities/AbuseUserReport.js'; | ||||||
| import { UserEntityService } from './UserEntityService.js'; | import { UserEntityService } from './UserEntityService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class AbuseUserReportEntityService { | export class AbuseUserReportEntityService { | ||||||
|  | @ -15,6 +16,7 @@ export class AbuseUserReportEntityService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async pack( | 	public async pack( | ||||||
| 		src: AbuseUserReport['id'] | AbuseUserReport, | 		src: AbuseUserReport['id'] | AbuseUserReport, | ||||||
| 	) { | 	) { | ||||||
|  | @ -41,6 +43,7 @@ export class AbuseUserReportEntityService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public packMany( | 	public packMany( | ||||||
| 		reports: any[], | 		reports: any[], | ||||||
| 	) { | 	) { | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ import type { AntennaNotesRepository, AntennasRepository, UserGroupJoiningsRepos | ||||||
| import { awaitAll } from '@/misc/prelude/await-all.js'; | import { awaitAll } from '@/misc/prelude/await-all.js'; | ||||||
| import type { Packed } from '@/misc/schema.js'; | import type { Packed } from '@/misc/schema.js'; | ||||||
| import type { Antenna } from '@/models/entities/Antenna.js'; | import type { Antenna } from '@/models/entities/Antenna.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class AntennaEntityService { | export class AntennaEntityService { | ||||||
|  | @ -19,6 +20,7 @@ export class AntennaEntityService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async pack( | 	public async pack( | ||||||
| 		src: Antenna['id'] | Antenna, | 		src: Antenna['id'] | Antenna, | ||||||
| 	): Promise<Packed<'Antenna'>> { | 	): Promise<Packed<'Antenna'>> { | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ import type { Packed } from '@/misc/schema.js'; | ||||||
| import type { App } from '@/models/entities/App.js'; | import type { App } from '@/models/entities/App.js'; | ||||||
| import type { User } from '@/models/entities/User.js'; | import type { User } from '@/models/entities/User.js'; | ||||||
| import { UserEntityService } from './UserEntityService.js'; | import { UserEntityService } from './UserEntityService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class AppEntityService { | export class AppEntityService { | ||||||
|  | @ -18,6 +19,7 @@ export class AppEntityService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async pack( | 	public async pack( | ||||||
| 		src: App['id'] | App, | 		src: App['id'] | App, | ||||||
| 		me?: { id: User['id'] } | null | undefined, | 		me?: { id: User['id'] } | null | undefined, | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ import type { AuthSession } from '@/models/entities/AuthSession.js'; | ||||||
| import type { User } from '@/models/entities/User.js'; | import type { User } from '@/models/entities/User.js'; | ||||||
| import { UserEntityService } from './UserEntityService.js'; | import { UserEntityService } from './UserEntityService.js'; | ||||||
| import { AppEntityService } from './AppEntityService.js'; | import { AppEntityService } from './AppEntityService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class AuthSessionEntityService { | export class AuthSessionEntityService { | ||||||
|  | @ -18,6 +19,7 @@ export class AuthSessionEntityService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async pack( | 	public async pack( | ||||||
| 		src: AuthSession['id'] | AuthSession, | 		src: AuthSession['id'] | AuthSession, | ||||||
| 		me?: { id: User['id'] } | null | undefined, | 		me?: { id: User['id'] } | null | undefined, | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ import type { Packed } from '@/misc/schema.js'; | ||||||
| import type { Blocking } from '@/models/entities/Blocking.js'; | import type { Blocking } from '@/models/entities/Blocking.js'; | ||||||
| import type { User } from '@/models/entities/User.js'; | import type { User } from '@/models/entities/User.js'; | ||||||
| import { UserEntityService } from './UserEntityService.js'; | import { UserEntityService } from './UserEntityService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class BlockingEntityService { | export class BlockingEntityService { | ||||||
|  | @ -17,6 +18,7 @@ export class BlockingEntityService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async pack( | 	public async pack( | ||||||
| 		src: Blocking['id'] | Blocking, | 		src: Blocking['id'] | Blocking, | ||||||
| 		me?: { id: User['id'] } | null | undefined, | 		me?: { id: User['id'] } | null | undefined, | ||||||
|  | @ -33,6 +35,7 @@ export class BlockingEntityService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public packMany( | 	public packMany( | ||||||
| 		blockings: any[], | 		blockings: any[], | ||||||
| 		me: { id: User['id'] }, | 		me: { id: User['id'] }, | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ import type { User } from '@/models/entities/User.js'; | ||||||
| import type { Channel } from '@/models/entities/Channel.js'; | import type { Channel } from '@/models/entities/Channel.js'; | ||||||
| import { UserEntityService } from './UserEntityService.js'; | import { UserEntityService } from './UserEntityService.js'; | ||||||
| import { DriveFileEntityService } from './DriveFileEntityService.js'; | import { DriveFileEntityService } from './DriveFileEntityService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class ChannelEntityService { | export class ChannelEntityService { | ||||||
|  | @ -29,6 +30,7 @@ export class ChannelEntityService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async pack( | 	public async pack( | ||||||
| 		src: Channel['id'] | Channel, | 		src: Channel['id'] | Channel, | ||||||
| 		me?: { id: User['id'] } | null | undefined, | 		me?: { id: User['id'] } | null | undefined, | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ import type { } from '@/models/entities/Blocking.js'; | ||||||
| import type { User } from '@/models/entities/User.js'; | import type { User } from '@/models/entities/User.js'; | ||||||
| import type { Clip } from '@/models/entities/Clip.js'; | import type { Clip } from '@/models/entities/Clip.js'; | ||||||
| import { UserEntityService } from './UserEntityService.js'; | import { UserEntityService } from './UserEntityService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class ClipEntityService { | export class ClipEntityService { | ||||||
|  | @ -18,6 +19,7 @@ export class ClipEntityService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async pack( | 	public async pack( | ||||||
| 		src: Clip['id'] | Clip, | 		src: Clip['id'] | Clip, | ||||||
| 	): Promise<Packed<'Clip'>> { | 	): Promise<Packed<'Clip'>> { | ||||||
|  | @ -34,6 +36,7 @@ export class ClipEntityService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public packMany( | 	public packMany( | ||||||
| 		clips: Clip[], | 		clips: Clip[], | ||||||
| 	) { | 	) { | ||||||
|  |  | ||||||
|  | @ -19,6 +19,7 @@ type PackOptions = { | ||||||
| 	self?: boolean, | 	self?: boolean, | ||||||
| 	withUser?: boolean, | 	withUser?: boolean, | ||||||
| }; | }; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class DriveFileEntityService { | export class DriveFileEntityService { | ||||||
|  | @ -44,6 +45,7 @@ export class DriveFileEntityService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	@bindThis | ||||||
| 	public validateFileName(name: string): boolean { | 	public validateFileName(name: string): boolean { | ||||||
| 		return ( | 		return ( | ||||||
| 			(name.trim().length > 0) && | 			(name.trim().length > 0) && | ||||||
|  | @ -54,6 +56,7 @@ export class DriveFileEntityService { | ||||||
| 		); | 		); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public getPublicProperties(file: DriveFile): DriveFile['properties'] { | 	public getPublicProperties(file: DriveFile): DriveFile['properties'] { | ||||||
| 		if (file.properties.orientation != null) { | 		if (file.properties.orientation != null) { | ||||||
| 			const properties = deepClone(file.properties); | 			const properties = deepClone(file.properties); | ||||||
|  | @ -67,6 +70,7 @@ export class DriveFileEntityService { | ||||||
| 		return file.properties; | 		return file.properties; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public getPublicUrl(file: DriveFile, thumbnail = false): string | null { | 	public getPublicUrl(file: DriveFile, thumbnail = false): string | null { | ||||||
| 		// リモートかつメディアプロキシ
 | 		// リモートかつメディアプロキシ
 | ||||||
| 		if (file.uri != null && file.userHost != null && this.config.mediaProxy != 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); | 		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<number> { | 	public async calcDriveUsageOf(user: User['id'] | { id: User['id'] }): Promise<number> { | ||||||
| 		const id = typeof user === 'object' ? user.id : user; | 		const id = typeof user === 'object' ? user.id : user; | ||||||
| 
 | 
 | ||||||
|  | @ -103,6 +108,7 @@ export class DriveFileEntityService { | ||||||
| 		return parseInt(sum, 10) ?? 0; | 		return parseInt(sum, 10) ?? 0; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async calcDriveUsageOfHost(host: string): Promise<number> { | 	public async calcDriveUsageOfHost(host: string): Promise<number> { | ||||||
| 		const { sum } = await this.driveFilesRepository | 		const { sum } = await this.driveFilesRepository | ||||||
| 			.createQueryBuilder('file') | 			.createQueryBuilder('file') | ||||||
|  | @ -114,6 +120,7 @@ export class DriveFileEntityService { | ||||||
| 		return parseInt(sum, 10) ?? 0; | 		return parseInt(sum, 10) ?? 0; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async calcDriveUsageOfLocal(): Promise<number> { | 	public async calcDriveUsageOfLocal(): Promise<number> { | ||||||
| 		const { sum } = await this.driveFilesRepository | 		const { sum } = await this.driveFilesRepository | ||||||
| 			.createQueryBuilder('file') | 			.createQueryBuilder('file') | ||||||
|  | @ -125,6 +132,7 @@ export class DriveFileEntityService { | ||||||
| 		return parseInt(sum, 10) ?? 0; | 		return parseInt(sum, 10) ?? 0; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async calcDriveUsageOfRemote(): Promise<number> { | 	public async calcDriveUsageOfRemote(): Promise<number> { | ||||||
| 		const { sum } = await this.driveFilesRepository | 		const { sum } = await this.driveFilesRepository | ||||||
| 			.createQueryBuilder('file') | 			.createQueryBuilder('file') | ||||||
|  | @ -136,6 +144,7 @@ export class DriveFileEntityService { | ||||||
| 		return parseInt(sum, 10) ?? 0; | 		return parseInt(sum, 10) ?? 0; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async pack( | 	public async pack( | ||||||
| 		src: DriveFile['id'] | DriveFile, | 		src: DriveFile['id'] | DriveFile, | ||||||
| 		options?: PackOptions, | 		options?: PackOptions, | ||||||
|  | @ -169,6 +178,7 @@ export class DriveFileEntityService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async packNullable( | 	public async packNullable( | ||||||
| 		src: DriveFile['id'] | DriveFile, | 		src: DriveFile['id'] | DriveFile, | ||||||
| 		options?: PackOptions, | 		options?: PackOptions, | ||||||
|  | @ -203,6 +213,7 @@ export class DriveFileEntityService { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async packMany( | 	public async packMany( | ||||||
| 		files: (DriveFile['id'] | DriveFile)[], | 		files: (DriveFile['id'] | DriveFile)[], | ||||||
| 		options?: PackOptions, | 		options?: PackOptions, | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ import type { } from '@/models/entities/Blocking.js'; | ||||||
| import type { User } from '@/models/entities/User.js'; | import type { User } from '@/models/entities/User.js'; | ||||||
| import type { DriveFolder } from '@/models/entities/DriveFolder.js'; | import type { DriveFolder } from '@/models/entities/DriveFolder.js'; | ||||||
| import { UserEntityService } from './UserEntityService.js'; | import { UserEntityService } from './UserEntityService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class DriveFolderEntityService { | export class DriveFolderEntityService { | ||||||
|  | @ -19,6 +20,7 @@ export class DriveFolderEntityService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async pack( | 	public async pack( | ||||||
| 		src: DriveFolder['id'] | DriveFolder, | 		src: DriveFolder['id'] | DriveFolder, | ||||||
| 		options?: { | 		options?: { | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ import type { } from '@/models/entities/Blocking.js'; | ||||||
| import type { User } from '@/models/entities/User.js'; | import type { User } from '@/models/entities/User.js'; | ||||||
| import type { Emoji } from '@/models/entities/Emoji.js'; | import type { Emoji } from '@/models/entities/Emoji.js'; | ||||||
| import { UserEntityService } from './UserEntityService.js'; | import { UserEntityService } from './UserEntityService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class EmojiEntityService { | export class EmojiEntityService { | ||||||
|  | @ -18,6 +19,7 @@ export class EmojiEntityService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async pack( | 	public async pack( | ||||||
| 		src: Emoji['id'] | Emoji, | 		src: Emoji['id'] | Emoji, | ||||||
| 	): Promise<Packed<'Emoji'>> { | 	): Promise<Packed<'Emoji'>> { | ||||||
|  | @ -34,6 +36,7 @@ export class EmojiEntityService { | ||||||
| 		}; | 		}; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public packMany( | 	public packMany( | ||||||
| 		emojis: any[], | 		emojis: any[], | ||||||
| 	) { | 	) { | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ import type { } from '@/models/entities/Blocking.js'; | ||||||
| import type { User } from '@/models/entities/User.js'; | import type { User } from '@/models/entities/User.js'; | ||||||
| import type { FollowRequest } from '@/models/entities/FollowRequest.js'; | import type { FollowRequest } from '@/models/entities/FollowRequest.js'; | ||||||
| import { UserEntityService } from './UserEntityService.js'; | import { UserEntityService } from './UserEntityService.js'; | ||||||
|  | import { bindThis } from '@/decorators.js'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class FollowRequestEntityService { | export class FollowRequestEntityService { | ||||||
|  | @ -18,6 +19,7 @@ export class FollowRequestEntityService { | ||||||
| 	) { | 	) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@bindThis | ||||||
| 	public async pack( | 	public async pack( | ||||||
| 		src: FollowRequest['id'] | FollowRequest, | 		src: FollowRequest['id'] | FollowRequest, | ||||||
| 		me?: { id: User['id'] } | null | undefined, | 		me?: { id: User['id'] } | null | undefined, | ||||||
|  |  | ||||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue