summaryrefslogtreecommitdiff
path: root/packages/backend/src/core/RemoteUserResolveService.ts
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2022-12-04 10:16:03 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2022-12-04 10:16:03 +0900
commit22ccb0fa716a84560c8599781647baaaeb8e80bd (patch)
tree2c89d24ad7fd3ee46c4edc434fa54f189eba2ae9 /packages/backend/src/core/RemoteUserResolveService.ts
parentFix forkbomb 2 (diff)
downloadsharkey-22ccb0fa716a84560c8599781647baaaeb8e80bd.tar.gz
sharkey-22ccb0fa716a84560c8599781647baaaeb8e80bd.tar.bz2
sharkey-22ccb0fa716a84560c8599781647baaaeb8e80bd.zip
refactor
Diffstat (limited to 'packages/backend/src/core/RemoteUserResolveService.ts')
-rw-r--r--packages/backend/src/core/RemoteUserResolveService.ts132
1 files changed, 132 insertions, 0 deletions
diff --git a/packages/backend/src/core/RemoteUserResolveService.ts b/packages/backend/src/core/RemoteUserResolveService.ts
new file mode 100644
index 0000000000..809b50f6e9
--- /dev/null
+++ b/packages/backend/src/core/RemoteUserResolveService.ts
@@ -0,0 +1,132 @@
+import { URL } from 'node:url';
+import { Inject, Injectable } from '@nestjs/common';
+import chalk from 'chalk';
+import { IsNull } from 'typeorm';
+import { DI } from '@/di-symbols.js';
+import type { UsersRepository } from '@/models/index.js';
+import type { IRemoteUser, User } from '@/models/entities/User.js';
+import type { Config } from '@/config.js';
+import type Logger from '@/logger.js';
+import { UtilityService } from '@/core/UtilityService.js';
+import { WebfingerService } from '@/core/WebfingerService.js';
+import { RemoteLoggerService } from '@/core/RemoteLoggerService.js';
+import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
+
+@Injectable()
+export class RemoteUserResolveService {
+ private logger: Logger;
+
+ constructor(
+ @Inject(DI.config)
+ private config: Config,
+
+ @Inject(DI.usersRepository)
+ private usersRepository: UsersRepository,
+
+ private utilityService: UtilityService,
+ private webfingerService: WebfingerService,
+ private remoteLoggerService: RemoteLoggerService,
+ private apPersonService: ApPersonService,
+ ) {
+ this.logger = this.remoteLoggerService.logger.createSubLogger('resolve-user');
+ }
+
+ public async resolveUser(username: string, host: string | null): Promise<User> {
+ const usernameLower = username.toLowerCase();
+
+ if (host == null) {
+ this.logger.info(`return local user: ${usernameLower}`);
+ return await this.usersRepository.findOneBy({ usernameLower, host: IsNull() }).then(u => {
+ if (u == null) {
+ throw new Error('user not found');
+ } else {
+ return u;
+ }
+ });
+ }
+
+ host = this.utilityService.toPuny(host);
+
+ if (this.config.host === host) {
+ this.logger.info(`return local user: ${usernameLower}`);
+ return await this.usersRepository.findOneBy({ usernameLower, host: IsNull() }).then(u => {
+ if (u == null) {
+ throw new Error('user not found');
+ } else {
+ return u;
+ }
+ });
+ }
+
+ const user = await this.usersRepository.findOneBy({ usernameLower, host }) as IRemoteUser | null;
+
+ const acctLower = `${usernameLower}@${host}`;
+
+ if (user == null) {
+ const self = await this.resolveSelf(acctLower);
+
+ this.logger.succ(`return new remote user: ${chalk.magenta(acctLower)}`);
+ return await this.apPersonService.createPerson(self.href);
+ }
+
+ // ユーザー情報が古い場合は、WebFilgerからやりなおして返す
+ if (user.lastFetchedAt == null || Date.now() - user.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) {
+ // 繋がらないインスタンスに何回も試行するのを防ぐ, 後続の同様処理の連続試行を防ぐ ため 試行前にも更新する
+ await this.usersRepository.update(user.id, {
+ lastFetchedAt: new Date(),
+ });
+
+ this.logger.info(`try resync: ${acctLower}`);
+ const self = await this.resolveSelf(acctLower);
+
+ if (user.uri !== self.href) {
+ // if uri mismatch, Fix (user@host <=> AP's Person id(IRemoteUser.uri)) mapping.
+ this.logger.info(`uri missmatch: ${acctLower}`);
+ this.logger.info(`recovery missmatch uri for (username=${username}, host=${host}) from ${user.uri} to ${self.href}`);
+
+ // validate uri
+ const uri = new URL(self.href);
+ if (uri.hostname !== host) {
+ throw new Error('Invalid uri');
+ }
+
+ await this.usersRepository.update({
+ usernameLower,
+ host: host,
+ }, {
+ uri: self.href,
+ });
+ } else {
+ this.logger.info(`uri is fine: ${acctLower}`);
+ }
+
+ await this.apPersonService.updatePerson(self.href);
+
+ this.logger.info(`return resynced remote user: ${acctLower}`);
+ return await this.usersRepository.findOneBy({ uri: self.href }).then(u => {
+ if (u == null) {
+ throw new Error('user not found');
+ } else {
+ return u;
+ }
+ });
+ }
+
+ this.logger.info(`return existing remote user: ${acctLower}`);
+ return user;
+ }
+
+ private async resolveSelf(acctLower: string) {
+ this.logger.info(`WebFinger for ${chalk.yellow(acctLower)}`);
+ const finger = await this.webfingerService.webfinger(acctLower).catch(err => {
+ this.logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: ${ err.statusCode ?? err.message }`);
+ throw new Error(`Failed to WebFinger for ${acctLower}: ${ err.statusCode ?? err.message }`);
+ });
+ const self = finger.links.find(link => link.rel != null && link.rel.toLowerCase() === 'self');
+ if (!self) {
+ this.logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: self link not found`);
+ throw new Error('self link not found');
+ }
+ return self;
+ }
+}