cleanup(backend): refactor UtilityService (MisskeyIO#858)

This commit is contained in:
あわわわとーにゅ 2024-12-28 11:39:48 +09:00 committed by GitHub
parent cb73368c83
commit ff195d4f8d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 106 additions and 126 deletions

View file

@ -311,7 +311,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
: this.utilityService.isSelfHost(src) ? null // 自ホスト指定 : this.utilityService.isSelfHost(src) ? null // 自ホスト指定
: (src || noteUserHost); // 指定されたホスト || ノートなどの所有者のホスト (こっちがリアクションにマッチすることはない) : (src || noteUserHost); // 指定されたホスト || ノートなどの所有者のホスト (こっちがリアクションにマッチすることはない)
host = this.utilityService.toPunyNullable(host); host = host ? this.utilityService.normalizeHost(host) : null;
return host; return host;
} }
@ -324,7 +324,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
const name = match[1]; const name = match[1];
// ホスト正規化 // ホスト正規化
const host = this.utilityService.toPunyNullable(this.normalizeHost(match[2], noteUserHost)); const host = this.normalizeHost(match[2], noteUserHost);
return { name, host }; return { name, host };
} }

View file

@ -236,7 +236,7 @@ export class EmailService {
} }
const emailDomain: string = emailAddress.split('@')[1]; const emailDomain: string = emailAddress.split('@')[1];
const isBanned = this.utilityService.isBlockedHost(meta.bannedEmailDomains, emailDomain); const isBanned = this.utilityService.isItemListedIn(emailDomain, meta.bannedEmailDomains);
if (isBanned) { if (isBanned) {
return { return {
@ -304,7 +304,7 @@ export class EmailService {
reason: 'mx', reason: 'mx',
}; };
} }
if (json.mx_host?.some(host => this.utilityService.isBlockedHost(meta.bannedEmailDomains, host))) { if (json.mx_host?.some(host => this.utilityService.isItemListedIn(host, meta.bannedEmailDomains))) {
return { return {
valid: false, valid: false,
reason: 'mx', reason: 'mx',

View file

@ -47,7 +47,7 @@ export class FederatedInstanceService implements OnApplicationShutdown {
@bindThis @bindThis
public async fetch(host: string): Promise<MiInstance> { public async fetch(host: string): Promise<MiInstance> {
host = this.utilityService.toPuny(host); host = this.utilityService.normalizeHost(host);
const cached = await this.federatedInstanceCache.get(host); const cached = await this.federatedInstanceCache.get(host);
if (cached) return cached; if (cached) return cached;

View file

@ -285,7 +285,7 @@ export class NoteCreateService implements OnApplicationShutdown {
throw new IdentifiableError('689ee33f-f97c-479a-ac49-1b9f8140af99', 'Notes including prohibited words are not allowed.'); throw new IdentifiableError('689ee33f-f97c-479a-ac49-1b9f8140af99', 'Notes including prohibited words are not allowed.');
} }
const inSilencedInstance = this.utilityService.isSilencedHost(meta.silencedHosts, user.host); const inSilencedInstance = this.utilityService.isItemListedIn(user.host, meta.silencedHosts);
if (data.visibility === 'public' && inSilencedInstance && user.host !== null) { if (data.visibility === 'public' && inSilencedInstance && user.host !== null) {
data.visibility = 'home'; data.visibility = 'home';

View file

@ -129,7 +129,7 @@ export class ReactionService {
} else if (_reaction) { } else if (_reaction) {
const custom = reaction.match(isCustomEmojiRegexp); const custom = reaction.match(isCustomEmojiRegexp);
if (custom) { if (custom) {
const reacterHost = this.utilityService.toPunyNullable(user.host); const reacterHost = user.host ? this.utilityService.normalizeHost(user.host) : null;
const name = custom[1]; const name = custom[1];
const emoji = reacterHost == null const emoji = reacterHost == null

View file

@ -54,9 +54,7 @@ export class RemoteUserResolveService {
}) as MiLocalUser; }) as MiLocalUser;
} }
host = this.utilityService.toPuny(host); if (this.utilityService.isSelfHost(host)) {
if (host === this.utilityService.toPuny(this.config.host)) {
this.logger.info(`return local user: ${usernameLower}`); this.logger.info(`return local user: ${usernameLower}`);
return await this.usersRepository.findOneBy({ usernameLower, host: IsNull() }).then(u => { return await this.usersRepository.findOneBy({ usernameLower, host: IsNull() }).then(u => {
if (u == null) { if (u == null) {
@ -67,6 +65,8 @@ export class RemoteUserResolveService {
}) as MiLocalUser; }) as MiLocalUser;
} }
host = this.utilityService.normalizeHost(host);
const user = await this.usersRepository.findOneBy({ usernameLower, host }) as MiRemoteUser | null; const user = await this.usersRepository.findOneBy({ usernameLower, host }) as MiRemoteUser | null;
const acctLower = `${usernameLower}@${host}`; const acctLower = `${usernameLower}@${host}`;

View file

@ -141,7 +141,7 @@ export class SignupService {
id: this.idService.gen(), id: this.idService.gen(),
username: username, username: username,
usernameLower: username.toLowerCase(), usernameLower: username.toLowerCase(),
host: this.utilityService.toPunyNullable(host), host: host ? this.utilityService.normalizeHost(host) : null,
token: secret, token: secret,
isRoot: isTheFirstUser, isRoot: isTheFirstUser,
})); }));

View file

@ -173,7 +173,7 @@ export class UserFollowingService implements OnModuleInit {
followee.isLocked || followee.isLocked ||
(followeeProfile.carefulBot && follower.isBot) || (followeeProfile.carefulBot && follower.isBot) ||
(this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee) && process.env.FORCE_FOLLOW_REMOTE_USER_FOR_TESTING !== 'true') || (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee) && process.env.FORCE_FOLLOW_REMOTE_USER_FOR_TESTING !== 'true') ||
(this.userEntityService.isLocalUser(followee) && this.userEntityService.isRemoteUser(follower) && this.utilityService.isSilencedHost((await this.metaService.fetch()).silencedHosts, follower.host)) (this.userEntityService.isLocalUser(followee) && this.userEntityService.isRemoteUser(follower) && this.utilityService.isItemListedIn(follower.host, (await this.metaService.fetch()).silencedHosts))
) { ) {
let autoAccept = false; let autoAccept = false;

View file

@ -5,8 +5,8 @@
import { URL } from 'node:url'; import { URL } from 'node:url';
import punycode from 'punycode.js'; import punycode from 'punycode.js';
import { Inject, Injectable } from '@nestjs/common';
import RE2 from 're2'; import RE2 from 're2';
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'; import { bindThis } from '@/decorators.js';
@ -21,36 +21,26 @@ export class UtilityService {
@bindThis @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.normalizeHost(host)}` : `${username}@${this.normalizeHost(this.config.host)}`;
} }
@bindThis @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.normalizeHost(this.config.host) === this.normalizeHost(host);
} }
@bindThis @bindThis
public isUriLocal(uri: string): boolean { public isUriLocal(uri: string): boolean {
return this.punyHost(uri) === this.toPuny(this.config.host); return this.normalizeHost(this.config.hostname) === this.extractHost(uri);
} }
@bindThis @bindThis
public isBlockedHost(blockedHosts: string[], host: string | null): boolean { public isItemListedIn(item: string | null, list: string[] | undefined): boolean {
if (host == null) return false; if (!list || !item) return false;
return blockedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`)); list = list.map(x => '.' + this.normalizeHost(x).split(':')[0]);
} item = '.' + this.normalizeHost(item).split(':')[0];
return list.some(x => item.endsWith(x));
@bindThis
public isSilencedHost(silencedHosts: string[] | undefined, host: string | null): boolean {
if (!silencedHosts || host == null) return false;
return silencedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`));
}
@bindThis
public isSensitiveMediaHost(sensitiveMediaHosts: string[] | undefined, host: string | null): boolean {
if (!sensitiveMediaHosts || host == null) return false;
return sensitiveMediaHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`));
} }
@bindThis @bindThis
@ -93,26 +83,14 @@ export class UtilityService {
} }
@bindThis @bindThis
public extractDbHost(uri: string): string { public normalizeHost(host: string): string {
const url = new URL(uri);
return this.toPuny(url.host);
}
@bindThis
public toPuny(host: string): string {
return punycode.toASCII(host.toLowerCase()); return punycode.toASCII(host.toLowerCase());
} }
@bindThis @bindThis
public toPunyNullable(host: string | null | undefined): string | null { public extractHost(uri: string): string {
if (host == null) return null; // ASCII String で返されるので punycode 化はいらない
return punycode.toASCII(host.toLowerCase()); // ref: https://url.spec.whatwg.org/#host-serializing
} return new URL(uri).host;
@bindThis
public punyHost(url: string): string {
const urlObj = new URL(url);
const host = `${this.toPuny(urlObj.hostname)}${urlObj.port.length > 0 ? ':' + urlObj.port : ''}`;
return host;
} }
} }

View file

@ -64,8 +64,9 @@ export class ApDbResolverService implements OnApplicationShutdown {
public parseUri(value: string | IObject): UriParseResult { public parseUri(value: string | IObject): UriParseResult {
const separator = '/'; const separator = '/';
const uri = new URL(getApId(value)); const apId = getApId(value);
if (this.utilityService.toPuny(uri.host) !== this.utilityService.toPuny(this.config.host)) { const uri = new URL(apId);
if (!this.utilityService.isUriLocal(apId)) {
return { local: false, uri: uri.href }; return { local: false, uri: uri.href };
} }

View file

@ -100,12 +100,12 @@ export class ApInboxService {
const items = toArray(isCollection(activity) ? activity.items : activity.orderedItems); const items = toArray(isCollection(activity) ? activity.items : activity.orderedItems);
if (items.length >= resolver.getRecursionLimit()) { if (items.length >= resolver.getRecursionLimit()) {
throw new Error(`skipping activity: collection would surpass recursion limit: ${this.utilityService.extractDbHost(actor.uri)}`); throw new Error(`skipping activity: collection would surpass recursion limit: ${this.utilityService.extractHost(actor.uri)}`);
} }
for (const item of items) { for (const item of items) {
const act = await resolver.resolve(item); const act = await resolver.resolve(item);
if (act.id == null || this.utilityService.extractDbHost(act.id) !== this.utilityService.extractDbHost(actor.uri)) { if (act.id == null || this.utilityService.extractHost(act.id) !== this.utilityService.extractHost(actor.uri)) {
this.logger.warn('skipping activity: activity id is null or mismatching'); this.logger.warn('skipping activity: activity id is null or mismatching');
continue; continue;
} }
@ -310,7 +310,7 @@ export class ApInboxService {
// アナウンス先をブロックしてたら中断 // アナウンス先をブロックしてたら中断
const meta = await this.metaService.fetch(); const meta = await this.metaService.fetch();
if (this.utilityService.isBlockedHost(meta.blockedHosts, this.utilityService.extractDbHost(uri))) return 'skip: blocked host'; if (this.utilityService.isItemListedIn(this.utilityService.extractHost(uri), meta.blockedHosts)) return 'skip: blocked host';
const unlock = await this.appLockService.getApLock(uri); const unlock = await this.appLockService.getApLock(uri);
@ -432,7 +432,7 @@ export class ApInboxService {
} }
if (typeof note.id === 'string') { if (typeof note.id === 'string') {
if (this.utilityService.extractDbHost(actor.uri) !== this.utilityService.extractDbHost(note.id)) { if (this.utilityService.extractHost(actor.uri) !== this.utilityService.extractHost(note.id)) {
return 'skip: host in actor.uri !== note.id'; return 'skip: host in actor.uri !== note.id';
} }
} else { } else {

View file

@ -220,7 +220,7 @@ export class ApRequestService {
const alternate = fragment.querySelector('head > link[rel="alternate"][type="application/activity+json"]'); const alternate = fragment.querySelector('head > link[rel="alternate"][type="application/activity+json"]');
if (alternate) { if (alternate) {
const href = alternate.getAttribute('href'); const href = alternate.getAttribute('href');
if (href && this.utilityService.punyHost(url) === this.utilityService.punyHost(href)) { if (href && this.utilityService.extractHost(url) === this.utilityService.extractHost(href)) {
return await this.signedGet(href, user, false); return await this.signedGet(href, user, false);
} }
} }

View file

@ -89,18 +89,18 @@ export class Resolver {
} }
if (this.history.size > this.recursionLimit) { if (this.history.size > this.recursionLimit) {
throw new Error(`hit recursion limit: ${this.utilityService.extractDbHost(value)}`); throw new Error(`hit recursion limit: ${this.utilityService.extractHost(value)}`);
} }
this.history.add(value); this.history.add(value);
const host = this.utilityService.extractDbHost(value); const host = this.utilityService.extractHost(value);
if (this.utilityService.isSelfHost(host)) { if (this.utilityService.isSelfHost(host)) {
return await this.resolveLocal(value); return await this.resolveLocal(value);
} }
const meta = await this.metaService.fetch(); const meta = await this.metaService.fetch();
if (this.utilityService.isBlockedHost(meta.blockedHosts, host)) { if (this.utilityService.isItemListedIn(host, meta.blockedHosts)) {
throw new Error('Instance is blocked'); throw new Error('Instance is blocked');
} }
@ -128,7 +128,7 @@ export class Resolver {
throw new Error('invalid AP object: missing id'); throw new Error('invalid AP object: missing id');
} }
if (this.utilityService.punyHost(object.id) !== this.utilityService.punyHost(value)) { if (this.utilityService.extractHost(object.id) !== this.utilityService.extractHost(value)) {
throw new Error(`invalid AP object ${value}: id ${object.id} has different host`); throw new Error(`invalid AP object ${value}: id ${object.id} has different host`);
} }

View file

@ -81,20 +81,21 @@ export class ApNoteService {
@bindThis @bindThis
public validateNote(object: IObject, uri: string, actor?: MiRemoteUser): Error | null { public validateNote(object: IObject, uri: string, actor?: MiRemoteUser): Error | null {
const expectHost = this.utilityService.extractDbHost(uri); const expectedHost = this.utilityService.extractHost(uri);
const apType = getApType(object); const apType = getApType(object);
if (apType == null || !validPost.includes(apType)) { if (apType == null || !validPost.includes(apType)) {
return new IdentifiableError('d450b8a9-48e4-4dab-ae36-f4db763fda7c', `invalid Note: invalid object type ${apType ?? 'undefined'}`); return new IdentifiableError('d450b8a9-48e4-4dab-ae36-f4db763fda7c', `invalid Note: invalid object type ${apType ?? 'undefined'}`);
} }
if (object.id && this.utilityService.extractDbHost(object.id) !== expectHost) { let actualHost = object.id && this.utilityService.extractHost(object.id);
return new IdentifiableError('d450b8a9-48e4-4dab-ae36-f4db763fda7c', `invalid Note: id has different host. expected: ${expectHost}, actual: ${this.utilityService.extractDbHost(object.id)}`); if (actualHost && expectedHost !== actualHost) {
return new IdentifiableError('d450b8a9-48e4-4dab-ae36-f4db763fda7c', `invalid Note: id has different host. expected: ${expectedHost}, actual: ${actualHost}`);
} }
const actualHost = object.attributedTo && this.utilityService.extractDbHost(getOneApId(object.attributedTo)); actualHost = object.attributedTo && this.utilityService.extractHost(getOneApId(object.attributedTo));
if (object.attributedTo && actualHost !== expectHost) { if (actualHost && expectedHost !== actualHost) {
return new IdentifiableError('d450b8a9-48e4-4dab-ae36-f4db763fda7c', `invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${actualHost}`); return new IdentifiableError('d450b8a9-48e4-4dab-ae36-f4db763fda7c', `invalid Note: attributedTo has different host. expected: ${expectedHost}, actual: ${actualHost}`);
} }
if (object.published && !this.idService.isSafeT(new Date(object.published).valueOf())) { if (object.published && !this.idService.isSafeT(new Date(object.published).valueOf())) {
@ -165,8 +166,8 @@ export class ApNoteService {
throw new Error('unexpected schema of note url: ' + url); throw new Error('unexpected schema of note url: ' + url);
} }
if (this.utilityService.punyHost(url) !== this.utilityService.punyHost(note.id)) { if (this.utilityService.extractHost(note.id) !== this.utilityService.extractHost(url)) {
throw new Error(`note url & uri host mismatch: note url: ${url}, note uri: ${note.id}`); throw new Error(`note id and url have different host: ${note.id} - ${url}`);
} }
} }
@ -234,7 +235,7 @@ export class ApNoteService {
} }
} }
const isSensitiveMediaHost = this.utilityService.isSensitiveMediaHost(meta.sensitiveMediaHosts, this.utilityService.extractDbHost(note.id ?? entryUri)); const isSensitiveMediaHost = this.utilityService.isItemListedIn(this.utilityService.extractHost(note.id ?? entryUri), meta.sensitiveMediaHosts);
// 添付ファイル // 添付ファイル
const files: MiDriveFile[] = []; const files: MiDriveFile[] = [];
@ -349,7 +350,7 @@ export class ApNoteService {
this.logger.info('The note is already inserted while creating itself, reading again'); this.logger.info('The note is already inserted while creating itself, reading again');
const duplicate = await this.fetchNote(value); const duplicate = await this.fetchNote(value);
if (!duplicate) { if (!duplicate) {
throw new Error('The note creation failed with duplication error even when there is no duplication'); throw new Error(`The note creation failed with duplication error even when there is no duplication: ${entryUri}`);
} }
return duplicate; return duplicate;
} }
@ -367,7 +368,7 @@ export class ApNoteService {
// ブロックしていたら中断 // ブロックしていたら中断
const meta = await this.metaService.fetch(); const meta = await this.metaService.fetch();
if (this.utilityService.isBlockedHost(meta.blockedHosts, this.utilityService.extractDbHost(uri))) { if (this.utilityService.isItemListedIn(this.utilityService.extractHost(uri), meta.blockedHosts)) {
throw new StatusError('blocked host', 451); throw new StatusError('blocked host', 451);
} }
@ -396,7 +397,7 @@ export class ApNoteService {
@bindThis @bindThis
public async extractEmojis(tags: IObject | IObject[], host: string): Promise<MiEmoji[]> { public async extractEmojis(tags: IObject | IObject[], host: string): Promise<MiEmoji[]> {
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
host = this.utilityService.toPuny(host); host = this.utilityService.normalizeHost(host);
const eomjiTags = toArray(tags).filter(isEmoji); const eomjiTags = toArray(tags).filter(isEmoji);

View file

@ -137,7 +137,7 @@ export class ApPersonService implements OnModuleInit {
*/ */
@bindThis @bindThis
private validateActor(x: IObject, uri: string): IActor { private validateActor(x: IObject, uri: string): IActor {
const expectHost = this.utilityService.punyHost(uri); const expectedHost = this.utilityService.extractHost(uri);
if (!isActor(x)) { if (!isActor(x)) {
throw new Error(`invalid Actor type '${x.type}'`); throw new Error(`invalid Actor type '${x.type}'`);
@ -151,15 +151,18 @@ export class ApPersonService implements OnModuleInit {
throw new Error('invalid Actor: wrong inbox'); throw new Error('invalid Actor: wrong inbox');
} }
if (this.utilityService.punyHost(x.inbox) !== expectHost) { let actualHost = this.utilityService.extractHost(x.inbox);
throw new Error('invalid Actor: inbox has different host'); if (expectedHost !== actualHost) {
throw new Error(`invalid Actor: inbox has different host. expected: ${expectedHost}, actual: ${actualHost}`);
} }
const sharedInboxObject = x.sharedInbox ?? (x.endpoints ? x.endpoints.sharedInbox : undefined); const sharedInboxObject = x.sharedInbox ?? (x.endpoints ? x.endpoints.sharedInbox : undefined);
if (sharedInboxObject != null) { if (sharedInboxObject != null) {
const sharedInbox = getApId(sharedInboxObject); const sharedInbox = getApId(sharedInboxObject);
if (!(typeof sharedInbox === 'string' && sharedInbox.length > 0 && this.utilityService.punyHost(sharedInbox) === expectHost)) { if (!sharedInbox) throw new Error('invalid Actor: wrong shared inbox');
throw new Error('invalid Actor: wrong shared inbox'); actualHost = this.utilityService.extractHost(sharedInbox);
if (expectedHost !== actualHost) {
throw new Error(`invalid Actor: shared inbox has different host. expected: ${expectedHost}, actual: ${actualHost}`);
} }
} }
@ -167,12 +170,10 @@ export class ApPersonService implements OnModuleInit {
const xCollection = (x as IActor)[collection]; const xCollection = (x as IActor)[collection];
if (xCollection != null) { if (xCollection != null) {
const collectionUri = getApId(xCollection); const collectionUri = getApId(xCollection);
if (typeof collectionUri === 'string' && collectionUri.length > 0) { if (!collectionUri) throw new Error(`invalid Actor: wrong ${collection}`);
if (this.utilityService.punyHost(collectionUri) !== expectHost) { actualHost = this.utilityService.extractHost(collectionUri);
throw new Error(`invalid Actor: ${collection} has different host`); if (expectedHost !== actualHost) {
} throw new Error(`invalid Actor: ${collection} has different host. expected: ${expectedHost}, actual: ${actualHost}`);
} else if (collectionUri != null) {
throw new Error(`invalid Actor: wrong ${collection}`);
} }
} }
} }
@ -200,9 +201,9 @@ export class ApPersonService implements OnModuleInit {
x.summary = truncate(x.summary, summaryLength); x.summary = truncate(x.summary, summaryLength);
} }
const idHost = this.utilityService.punyHost(x.id); actualHost = this.utilityService.extractHost(x.id);
if (idHost !== expectHost) { if (expectedHost !== actualHost) {
throw new Error('invalid Actor: id has different host'); throw new Error(`invalid Actor: id has different host. expected: ${expectedHost}, actual: ${actualHost}`);
} }
if (x.publicKey) { if (x.publicKey) {
@ -210,9 +211,9 @@ export class ApPersonService implements OnModuleInit {
throw new Error('invalid Actor: publicKey.id is not a string'); throw new Error('invalid Actor: publicKey.id is not a string');
} }
const publicKeyIdHost = this.utilityService.punyHost(x.publicKey.id); actualHost = this.utilityService.extractHost(x.publicKey.id);
if (publicKeyIdHost !== expectHost) { if (expectedHost !== actualHost) {
throw new Error('invalid Actor: publicKey.id has different host'); throw new Error(`invalid Actor: publicKey.id has different host. expected: ${expectedHost}, actual: ${actualHost}`);
} }
} }
@ -258,7 +259,7 @@ export class ApPersonService implements OnModuleInit {
if (Array.isArray(img)) { if (Array.isArray(img)) {
img = img.find(item => item && item.url) ?? null; img = img.find(item => item && item.url) ?? null;
} }
// if we have an explicitly missing image, return an // if we have an explicitly missing image, return an
// explicitly-null set of values // explicitly-null set of values
if ((img == null) || (typeof img === 'object' && img.url == null)) { if ((img == null) || (typeof img === 'object' && img.url == null)) {
@ -296,8 +297,7 @@ export class ApPersonService implements OnModuleInit {
public async createPerson(uri: string, resolver?: Resolver): Promise<MiRemoteUser> { public async createPerson(uri: string, resolver?: Resolver): Promise<MiRemoteUser> {
if (typeof uri !== 'string') throw new Error('uri is not string'); if (typeof uri !== 'string') throw new Error('uri is not string');
const host = this.utilityService.punyHost(uri); if (this.utilityService.isUriLocal(uri)) {
if (host === this.utilityService.toPuny(this.config.host)) {
throw new StatusError('cannot resolve local user', 400, 'cannot resolve local user'); throw new StatusError('cannot resolve local user', 400, 'cannot resolve local user');
} }
@ -345,13 +345,14 @@ export class ApPersonService implements OnModuleInit {
throw new Error('unexpected schema of person url: ' + url); throw new Error('unexpected schema of person url: ' + url);
} }
if (this.utilityService.punyHost(url) !== this.utilityService.punyHost(person.id)) { if (this.utilityService.extractHost(person.id) !== this.utilityService.extractHost(url)) {
throw new Error(`person url <> uri host mismatch: ${url} <> ${person.id}`); throw new Error(`person id and url have different host: ${person.id} - ${url}`);
} }
} }
// Create user // Create user
let user: MiRemoteUser | null = null; let user: MiRemoteUser | null = null;
const host = this.utilityService.extractHost(uri);
//#region カスタム絵文字取得 //#region カスタム絵文字取得
const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host) const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host)
@ -542,8 +543,8 @@ export class ApPersonService implements OnModuleInit {
throw new Error('unexpected schema of person url: ' + url); throw new Error('unexpected schema of person url: ' + url);
} }
if (this.utilityService.punyHost(url) !== this.utilityService.punyHost(person.id)) { if (this.utilityService.extractHost(person.id) !== this.utilityService.extractHost(url)) {
throw new Error(`person url <> uri host mismatch: ${url} <> ${person.id}`); throw new Error(`person id and url have different host: ${person.id} - ${url}`);
} }
} }

View file

@ -77,7 +77,7 @@ export default class InstanceChart extends Chart<typeof schema> { // eslint-disa
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.normalizeHost(host));
} }
@bindThis @bindThis
@ -85,7 +85,7 @@ export default class InstanceChart extends Chart<typeof schema> { // eslint-disa
await this.commit({ await this.commit({
'requests.succeeded': isSucceeded ? 1 : 0, 'requests.succeeded': isSucceeded ? 1 : 0,
'requests.failed': isSucceeded ? 0 : 1, 'requests.failed': isSucceeded ? 0 : 1,
}, this.utilityService.toPuny(host)); }, this.utilityService.normalizeHost(host));
} }
@bindThis @bindThis
@ -93,7 +93,7 @@ export default class InstanceChart extends Chart<typeof schema> { // eslint-disa
await this.commit({ await this.commit({
'users.total': 1, 'users.total': 1,
'users.inc': 1, 'users.inc': 1,
}, this.utilityService.toPuny(host)); }, this.utilityService.normalizeHost(host));
} }
@bindThis @bindThis
@ -106,7 +106,7 @@ export default class InstanceChart extends Chart<typeof schema> { // eslint-disa
'notes.diffs.renote': note.renoteId != null ? (isAdditional ? 1 : -1) : 0, 'notes.diffs.renote': note.renoteId != null ? (isAdditional ? 1 : -1) : 0,
'notes.diffs.reply': note.replyId != null ? (isAdditional ? 1 : -1) : 0, 'notes.diffs.reply': note.replyId != null ? (isAdditional ? 1 : -1) : 0,
'notes.diffs.withFile': note.fileIds.length > 0 ? (isAdditional ? 1 : -1) : 0, 'notes.diffs.withFile': note.fileIds.length > 0 ? (isAdditional ? 1 : -1) : 0,
}, this.utilityService.toPuny(host)); }, this.utilityService.normalizeHost(host));
} }
@bindThis @bindThis
@ -115,7 +115,7 @@ export default class InstanceChart extends Chart<typeof schema> { // eslint-disa
'following.total': isAdditional ? 1 : -1, 'following.total': isAdditional ? 1 : -1,
'following.inc': isAdditional ? 1 : 0, 'following.inc': isAdditional ? 1 : 0,
'following.dec': isAdditional ? 0 : 1, 'following.dec': isAdditional ? 0 : 1,
}, this.utilityService.toPuny(host)); }, this.utilityService.normalizeHost(host));
} }
@bindThis @bindThis
@ -124,7 +124,7 @@ export default class InstanceChart extends Chart<typeof schema> { // eslint-disa
'followers.total': isAdditional ? 1 : -1, 'followers.total': isAdditional ? 1 : -1,
'followers.inc': isAdditional ? 1 : 0, 'followers.inc': isAdditional ? 1 : 0,
'followers.dec': isAdditional ? 0 : 1, 'followers.dec': isAdditional ? 0 : 1,
}, this.utilityService.toPuny(host)); }, this.utilityService.normalizeHost(host));
} }
@bindThis @bindThis

View file

@ -158,7 +158,7 @@ export class DriveFileEntityService {
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')
.where('file.userHost = :host', { host: this.utilityService.toPuny(host) }) .where('file.userHost = :host', { host: this.utilityService.normalizeHost(host) })
.andWhere('file.isLink = FALSE') .andWhere('file.isLink = FALSE')
.select('SUM(file.size)', 'sum') .select('SUM(file.size)', 'sum')
.getRawOne(); .getRawOne();

View file

@ -40,7 +40,7 @@ export class InstanceEntityService {
followersCount: instance.followersCount, followersCount: instance.followersCount,
isNotResponding: instance.isNotResponding, isNotResponding: instance.isNotResponding,
isSuspended: instance.isSuspended, isSuspended: instance.isSuspended,
isBlocked: this.utilityService.isBlockedHost(meta.blockedHosts, instance.host), isBlocked: this.utilityService.isItemListedIn(instance.host, meta.blockedHosts),
softwareName: instance.softwareName, softwareName: instance.softwareName,
softwareVersion: instance.softwareVersion, softwareVersion: instance.softwareVersion,
openRegistrations: instance.openRegistrations, openRegistrations: instance.openRegistrations,
@ -48,8 +48,8 @@ export class InstanceEntityService {
description: instance.description, description: instance.description,
maintainerName: instance.maintainerName, maintainerName: instance.maintainerName,
maintainerEmail: instance.maintainerEmail, maintainerEmail: instance.maintainerEmail,
isSilenced: this.utilityService.isSilencedHost(meta.silencedHosts, instance.host), isSilenced: this.utilityService.isItemListedIn(instance.host, meta.silencedHosts),
isSensitiveMedia: this.utilityService.isSensitiveMediaHost(meta.sensitiveMediaHosts, instance.host), isSensitiveMedia: this.utilityService.isItemListedIn(instance.host, meta.sensitiveMediaHosts),
iconUrl: instance.iconUrl, iconUrl: instance.iconUrl,
faviconUrl: instance.faviconUrl, faviconUrl: instance.faviconUrl,
themeColor: instance.themeColor, themeColor: instance.themeColor,

View file

@ -53,7 +53,7 @@ export class DeliverProcessorService {
// ブロックしてたら中断 // ブロックしてたら中断
const meta = await this.metaService.fetch(); const meta = await this.metaService.fetch();
if (this.utilityService.isBlockedHost(meta.blockedHosts, this.utilityService.toPuny(host))) { if (this.utilityService.isItemListedIn(host, meta.blockedHosts)) {
return 'skip (blocked)'; return 'skip (blocked)';
} }
@ -67,7 +67,7 @@ export class DeliverProcessorService {
}); });
this.suspendedHostsCache.set(suspendedHosts); this.suspendedHostsCache.set(suspendedHosts);
} }
if (suspendedHosts.map(x => x.host).includes(this.utilityService.toPuny(host))) { if (suspendedHosts.map(x => x.host).includes(this.utilityService.normalizeHost(host))) {
return 'skip (suspended)'; return 'skip (suspended)';
} }

View file

@ -76,7 +76,7 @@ export class ImportBlockingProcessorService {
host: IsNull(), host: IsNull(),
usernameLower: username.toLowerCase(), usernameLower: username.toLowerCase(),
}) : await this.usersRepository.findOneBy({ }) : await this.usersRepository.findOneBy({
host: this.utilityService.toPuny(host), host: this.utilityService.normalizeHost(host),
usernameLower: username.toLowerCase(), usernameLower: username.toLowerCase(),
}); });

View file

@ -76,7 +76,7 @@ export class ImportFollowingProcessorService {
host: IsNull(), host: IsNull(),
usernameLower: username.toLowerCase(), usernameLower: username.toLowerCase(),
}) : await this.usersRepository.findOneBy({ }) : await this.usersRepository.findOneBy({
host: this.utilityService.toPuny(host), host: this.utilityService.normalizeHost(host),
usernameLower: username.toLowerCase(), usernameLower: username.toLowerCase(),
}); });

View file

@ -71,7 +71,7 @@ export class ImportMutingProcessorService {
host: IsNull(), host: IsNull(),
usernameLower: username.toLowerCase(), usernameLower: username.toLowerCase(),
}) : await this.usersRepository.findOneBy({ }) : await this.usersRepository.findOneBy({
host: this.utilityService.toPuny(host), host: this.utilityService.normalizeHost(host),
usernameLower: username.toLowerCase(), usernameLower: username.toLowerCase(),
}); });

View file

@ -90,7 +90,7 @@ export class ImportUserListsProcessorService {
host: IsNull(), host: IsNull(),
usernameLower: username.toLowerCase(), usernameLower: username.toLowerCase(),
}) : await this.usersRepository.findOneBy({ }) : await this.usersRepository.findOneBy({
host: this.utilityService.toPuny(host!), host: this.utilityService.normalizeHost(host!),
usernameLower: username.toLowerCase(), usernameLower: username.toLowerCase(),
}); });

View file

@ -3,7 +3,6 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { URL } from 'node:url';
import { Injectable, OnApplicationShutdown } from '@nestjs/common'; import { Injectable, OnApplicationShutdown } from '@nestjs/common';
import httpSignature from '@peertube/http-signature'; import httpSignature from '@peertube/http-signature';
import * as Bull from 'bullmq'; import * as Bull from 'bullmq';
@ -65,11 +64,11 @@ export class InboxProcessorService implements OnApplicationShutdown {
this.logger.debug(JSON.stringify(info, null, 2)); this.logger.debug(JSON.stringify(info, null, 2));
//#endregion //#endregion
const host = this.utilityService.toPuny(new URL(signature.keyId).hostname); const host = this.utilityService.extractHost(signature.keyId);
// ブロックしてたら中断 // ブロックしてたら中断
const meta = await this.metaService.fetch(); const meta = await this.metaService.fetch();
if (this.utilityService.isBlockedHost(meta.blockedHosts, host)) { if (this.utilityService.isItemListedIn(host, meta.blockedHosts)) {
return `Blocked request: ${host}`; return `Blocked request: ${host}`;
} }
@ -164,8 +163,8 @@ export class InboxProcessorService implements OnApplicationShutdown {
} }
// ブロックしてたら中断 // ブロックしてたら中断
const ldHost = this.utilityService.extractDbHost(authUser.user.uri); const ldHost = this.utilityService.extractHost(authUser.user.uri);
if (this.utilityService.isBlockedHost(meta.blockedHosts, ldHost)) { if (this.utilityService.isItemListedIn(ldHost, meta.blockedHosts)) {
throw new Bull.UnrecoverableError(`Blocked request: ${ldHost}`); throw new Bull.UnrecoverableError(`Blocked request: ${ldHost}`);
} }
} else { } else {
@ -175,8 +174,8 @@ export class InboxProcessorService implements OnApplicationShutdown {
// activity.idがあればホストが署名者のホストであることを確認する // activity.idがあればホストが署名者のホストであることを確認する
if (typeof activity.id === 'string') { if (typeof activity.id === 'string') {
const signerHost = this.utilityService.extractDbHost(authUser.user.uri!); const signerHost = this.utilityService.extractHost(authUser.user.uri!);
const activityIdHost = this.utilityService.extractDbHost(activity.id); const activityIdHost = this.utilityService.extractHost(activity.id);
if (signerHost !== activityIdHost) { if (signerHost !== activityIdHost) {
throw new Bull.UnrecoverableError(`skip: signerHost(${signerHost}) !== activity.id host(${activityIdHost}`); throw new Bull.UnrecoverableError(`skip: signerHost(${signerHost}) !== activity.id host(${activityIdHost}`);
} }

View file

@ -94,7 +94,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (ps.host == null) { if (ps.host == null) {
q.andWhere('emoji.host IS NOT NULL'); q.andWhere('emoji.host IS NOT NULL');
} else { } else {
q.andWhere('emoji.host = :host', { host: this.utilityService.toPuny(ps.host) }); q.andWhere('emoji.host = :host', { host: this.utilityService.normalizeHost(ps.host) });
} }
if (ps.query) { if (ps.query) {

View file

@ -36,7 +36,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private fetchInstanceMetadataService: FetchInstanceMetadataService, private fetchInstanceMetadataService: FetchInstanceMetadataService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async (ps, me) => {
const instance = await this.instancesRepository.findOneBy({ host: this.utilityService.toPuny(ps.host) }); const instance = await this.instancesRepository.findOneBy({ host: this.utilityService.normalizeHost(ps.host) });
if (instance == null) { if (instance == null) {
throw new Error('instance not found'); throw new Error('instance not found');

View file

@ -40,7 +40,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private moderationLogService: ModerationLogService, private moderationLogService: ModerationLogService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async (ps, me) => {
const instance = await this.instancesRepository.findOneBy({ host: this.utilityService.toPuny(ps.host) }); const instance = await this.instancesRepository.findOneBy({ host: this.utilityService.normalizeHost(ps.host) });
if (instance == null) { if (instance == null) {
throw new Error('instance not found'); throw new Error('instance not found');

View file

@ -114,7 +114,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private async fetchAny(uri: string, me: MiLocalUser | null | undefined): Promise<SchemaType<typeof meta['res']> | null> { private async fetchAny(uri: string, me: MiLocalUser | null | undefined): Promise<SchemaType<typeof meta['res']> | null> {
// ブロックしてたら中断 // ブロックしてたら中断
const fetchedMeta = await this.metaService.fetch(); const fetchedMeta = await this.metaService.fetch();
if (this.utilityService.isBlockedHost(fetchedMeta.blockedHosts, this.utilityService.extractDbHost(uri))) return null; if (this.utilityService.isItemListedIn(this.utilityService.extractHost(uri), fetchedMeta.blockedHosts)) return null;
let local = await this.mergePack(me, ...await Promise.all([ let local = await this.mergePack(me, ...await Promise.all([
this.apDbResolverService.getUserFromApId(uri), this.apDbResolverService.getUserFromApId(uri),
@ -122,7 +122,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
])); ]));
if (local != null) return local; if (local != null) return local;
const host = this.utilityService.extractDbHost(uri); const host = this.utilityService.extractHost(uri);
// local object, not found in db? fail // local object, not found in db? fail
if (this.utilityService.isSelfHost(host)) return null; if (this.utilityService.isSelfHost(host)) return null;

View file

@ -41,7 +41,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
) { ) {
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async (ps, me) => {
const instance = await this.instancesRepository const instance = await this.instancesRepository
.findOneBy({ host: this.utilityService.toPuny(ps.host) }); .findOneBy({ host: this.utilityService.normalizeHost(ps.host) });
return instance ? await this.instanceEntityService.pack(instance, me) : null; return instance ? await this.instanceEntityService.pack(instance, me) : null;
}); });

View file

@ -87,7 +87,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async (ps, me) => {
const user = await this.usersRepository.findOneBy(ps.userId != null const user = await this.usersRepository.findOneBy(ps.userId != null
? { id: ps.userId } ? { id: ps.userId }
: { usernameLower: ps.username!.toLowerCase(), host: this.utilityService.toPunyNullable(ps.host) ?? IsNull() }); : { usernameLower: ps.username!.toLowerCase(), host: ps.host ? this.utilityService.normalizeHost(ps.host) : IsNull() });
if (user == null) { if (user == null) {
throw new ApiError(meta.errors.noSuchUser); throw new ApiError(meta.errors.noSuchUser);

View file

@ -99,7 +99,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async (ps, me) => {
const user = await this.usersRepository.findOneBy(ps.userId != null const user = await this.usersRepository.findOneBy(ps.userId != null
? { id: ps.userId } ? { id: ps.userId }
: { usernameLower: ps.username!.toLowerCase(), host: this.utilityService.toPunyNullable(ps.host) ?? IsNull() }); : { usernameLower: ps.username!.toLowerCase(), host: ps.host ? this.utilityService.normalizeHost(ps.host) : IsNull() });
if (user == null) { if (user == null) {
throw new ApiError(meta.errors.noSuchUser); throw new ApiError(meta.errors.noSuchUser);