This commit is contained in:
tamaina 2025-08-30 18:43:02 +09:00
parent 020400cfe7
commit efe63de853
3 changed files with 74 additions and 25 deletions

View File

@ -75,7 +75,7 @@ export class RemoteUserResolveService {
const self = await this.resolveSelf(acctLower);
if (this.utilityService.isUriLocal(self.href)) {
const local = this.apDbResolverService.parseUri(self.href);
const local = this.apDbResolverService.parseLocalUri(self.href);
if (local.local && local.type === 'users') {
// the LR points to local
return (await this.apDbResolverService

View File

@ -4,6 +4,8 @@
*/
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
import * as misskey from 'misskey-js';
import { IsNull } from 'typeorm';
import { DI } from '@/di-symbols.js';
import type { NotesRepository, UserPublickeysRepository, UsersRepository } from '@/models/_.js';
import type { Config } from '@/config.js';
@ -32,6 +34,12 @@ export type UriParseResult = {
local: false;
/** uri in DB */
uri: string;
} | {
/** wether the URI was generated by us and acct.host points to us */
local: boolean;
type: 'acct';
/** Acct */
acct: misskey.acct.Acct;
};
@Injectable()
@ -60,15 +68,33 @@ export class ApDbResolverService implements OnApplicationShutdown {
this.publicKeyByUserIdCache = new MemoryKVCache<MiUserPublickey | null>(1000 * 60 * 60 * 12); // 12h
}
/**
* If provided uri's hostname points to this server, parse it. If not, return the uri as-is.
* @param value
* @returns
*/
@bindThis
public parseUri(value: string | IObject): UriParseResult {
public parseLocalUri(value: string | IObject): UriParseResult {
const separator = '/';
const uri = new URL(getApId(value));
if (this.utilityService.toPuny(uri.host) !== this.utilityService.toPuny(this.config.host)) {
if (!this.utilityService.isSelfHost(uri.hostname)) {
return { local: false, uri: uri.href };
}
try {
const acct = misskey.acct.parseUrl(uri.toString());
if (acct) {
return {
local: this.utilityService.isSelfHost(acct.host),
type: 'acct',
acct,
};
}
} finally {
// no-op
}
const [, type, id, ...rest] = uri.pathname.split(separator);
return {
local: true,
@ -83,19 +109,21 @@ export class ApDbResolverService implements OnApplicationShutdown {
*/
@bindThis
public async getNoteFromApId(value: string | IObject): Promise<MiNote | null> {
const parsed = this.parseUri(value);
const parsed = this.parseLocalUri(value);
if (parsed.local) {
if (parsed.type !== 'notes') return null;
return await this.notesRepository.findOneBy({
id: parsed.id,
});
} else {
if ('uri' in parsed) {
return await this.notesRepository.findOneBy({
uri: parsed.uri,
});
}
if (parsed.type === 'notes') {
return await this.notesRepository.findOneBy({
id: parsed.id,
});
}
return null;
}
/**
@ -103,21 +131,30 @@ export class ApDbResolverService implements OnApplicationShutdown {
*/
@bindThis
public async getUserFromApId(value: string | IObject): Promise<MiLocalUser | MiRemoteUser | null> {
const parsed = this.parseUri(value);
const parsed = this.parseLocalUri(value);
if (parsed.local) {
if (parsed.type !== 'users') return null;
if ('acct' in parsed) {
return await this.usersRepository.findOneBy({
usernameLower: parsed.acct.username.toLowerCase(),
host: (parsed.acct.host == null || parsed.local) ? IsNull() : this.utilityService.toPuny(parsed.acct.host),
}) as MiLocalUser | MiRemoteUser | null;
}
return await this.cacheService.userByIdCache.fetchMaybe(
parsed.id,
() => this.usersRepository.findOneBy({ id: parsed.id, isDeleted: false }).then(x => x ?? undefined),
) as MiLocalUser | undefined ?? null;
} else {
if ('uri' in parsed) {
return await this.cacheService.uriPersonCache.fetch(
parsed.uri,
() => this.usersRepository.findOneBy({ uri: parsed.uri, isDeleted: false }),
) as MiRemoteUser | null;
}
if (parsed.type === 'users') {
return await this.cacheService.userByIdCache.fetchMaybe(
parsed.id,
() => this.usersRepository.findOneBy({ id: parsed.id, isDeleted: false }).then(x => x ?? undefined),
) as MiLocalUser | undefined ?? null;
}
return null;
}
/**

View File

@ -15,9 +15,10 @@ import { bindThis } from '@/decorators.js';
import { LoggerService } from '@/core/LoggerService.js';
import type Logger from '@/logger.js';
import { SystemAccountService } from '@/core/SystemAccountService.js';
import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { isCollectionOrOrderedCollection } from './type.js';
import { ApDbResolverService } from './ApDbResolverService.js';
import { ApDbResolverService, type UriParseResult } from './ApDbResolverService.js';
import { ApRendererService } from './ApRendererService.js';
import { ApRequestService } from './ApRequestService.js';
import { FetchAllowSoftFailMask } from './misc/check-against-url.js';
@ -42,6 +43,7 @@ export class Resolver {
private httpRequestService: HttpRequestService,
private apRendererService: ApRendererService,
private apDbResolverService: ApDbResolverService,
private remoteUserResolveService: RemoteUserResolveService,
private loggerService: LoggerService,
private recursionLimit = 256,
) {
@ -96,8 +98,9 @@ export class Resolver {
this.history.add(value);
const host = this.utilityService.extractDbHost(value);
if (this.utilityService.isSelfHost(host)) {
return await this.resolveLocal(value);
const parsed = this.apDbResolverService.parseLocalUri(value);
if (parsed.local === true) {
return await this.resolveLocal(parsed);
}
if (!this.utilityService.isFederationAllowedHost(host)) {
@ -124,9 +127,18 @@ export class Resolver {
}
@bindThis
private resolveLocal(url: string): Promise<IObject> {
const parsed = this.apDbResolverService.parseUri(url);
if (!parsed.local) throw new IdentifiableError('02b40cd0-fa92-4b0c-acc9-fb2ada952ab8', 'resolveLocal: not local');
private resolveLocal(parsed: UriParseResult): Promise<IObject> {
if (parsed.local === false || 'uri' in parsed) {
throw new IdentifiableError('02b40cd0-fa92-4b0c-acc9-fb2ada952ab8', 'resolveLocal: not local');
}
if ('acct' in parsed) {
return this.usersRepository.findOneByOrFail({
usernameLower: parsed.acct.username.toLowerCase(),
host: IsNull(),
})
.then(user => this.apRendererService.renderPerson(user as MiLocalUser));
}
switch (parsed.type) {
case 'notes':