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); const self = await this.resolveSelf(acctLower);
if (this.utilityService.isUriLocal(self.href)) { 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') { if (local.local && local.type === 'users') {
// the LR points to local // the LR points to local
return (await this.apDbResolverService return (await this.apDbResolverService

View File

@ -4,6 +4,8 @@
*/ */
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
import * as misskey from 'misskey-js';
import { IsNull } from 'typeorm';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { NotesRepository, UserPublickeysRepository, UsersRepository } from '@/models/_.js'; import type { NotesRepository, UserPublickeysRepository, UsersRepository } from '@/models/_.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
@ -32,6 +34,12 @@ export type UriParseResult = {
local: false; local: false;
/** uri in DB */ /** uri in DB */
uri: string; 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() @Injectable()
@ -60,15 +68,33 @@ export class ApDbResolverService implements OnApplicationShutdown {
this.publicKeyByUserIdCache = new MemoryKVCache<MiUserPublickey | null>(1000 * 60 * 60 * 12); // 12h 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 @bindThis
public parseUri(value: string | IObject): UriParseResult { public parseLocalUri(value: string | IObject): UriParseResult {
const separator = '/'; const separator = '/';
const uri = new URL(getApId(value)); 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 }; 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); const [, type, id, ...rest] = uri.pathname.split(separator);
return { return {
local: true, local: true,
@ -83,19 +109,21 @@ export class ApDbResolverService implements OnApplicationShutdown {
*/ */
@bindThis @bindThis
public async getNoteFromApId(value: string | IObject): Promise<MiNote | null> { public async getNoteFromApId(value: string | IObject): Promise<MiNote | null> {
const parsed = this.parseUri(value); const parsed = this.parseLocalUri(value);
if (parsed.local) { if ('uri' in parsed) {
if (parsed.type !== 'notes') return null;
return await this.notesRepository.findOneBy({
id: parsed.id,
});
} else {
return await this.notesRepository.findOneBy({ return await this.notesRepository.findOneBy({
uri: parsed.uri, 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 @bindThis
public async getUserFromApId(value: string | IObject): Promise<MiLocalUser | MiRemoteUser | null> { public async getUserFromApId(value: string | IObject): Promise<MiLocalUser | MiRemoteUser | null> {
const parsed = this.parseUri(value); const parsed = this.parseLocalUri(value);
if (parsed.local) { if ('acct' in parsed) {
if (parsed.type !== 'users') return null; 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( if ('uri' in parsed) {
parsed.id,
() => this.usersRepository.findOneBy({ id: parsed.id, isDeleted: false }).then(x => x ?? undefined),
) as MiLocalUser | undefined ?? null;
} else {
return await this.cacheService.uriPersonCache.fetch( return await this.cacheService.uriPersonCache.fetch(
parsed.uri, parsed.uri,
() => this.usersRepository.findOneBy({ uri: parsed.uri, isDeleted: false }), () => this.usersRepository.findOneBy({ uri: parsed.uri, isDeleted: false }),
) as MiRemoteUser | null; ) 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 { LoggerService } from '@/core/LoggerService.js';
import type Logger from '@/logger.js'; import type Logger from '@/logger.js';
import { SystemAccountService } from '@/core/SystemAccountService.js'; import { SystemAccountService } from '@/core/SystemAccountService.js';
import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js';
import { IdentifiableError } from '@/misc/identifiable-error.js'; import { IdentifiableError } from '@/misc/identifiable-error.js';
import { isCollectionOrOrderedCollection } from './type.js'; import { isCollectionOrOrderedCollection } from './type.js';
import { ApDbResolverService } from './ApDbResolverService.js'; import { ApDbResolverService, type UriParseResult } from './ApDbResolverService.js';
import { ApRendererService } from './ApRendererService.js'; import { ApRendererService } from './ApRendererService.js';
import { ApRequestService } from './ApRequestService.js'; import { ApRequestService } from './ApRequestService.js';
import { FetchAllowSoftFailMask } from './misc/check-against-url.js'; import { FetchAllowSoftFailMask } from './misc/check-against-url.js';
@ -42,6 +43,7 @@ export class Resolver {
private httpRequestService: HttpRequestService, private httpRequestService: HttpRequestService,
private apRendererService: ApRendererService, private apRendererService: ApRendererService,
private apDbResolverService: ApDbResolverService, private apDbResolverService: ApDbResolverService,
private remoteUserResolveService: RemoteUserResolveService,
private loggerService: LoggerService, private loggerService: LoggerService,
private recursionLimit = 256, private recursionLimit = 256,
) { ) {
@ -96,8 +98,9 @@ export class Resolver {
this.history.add(value); this.history.add(value);
const host = this.utilityService.extractDbHost(value); const host = this.utilityService.extractDbHost(value);
if (this.utilityService.isSelfHost(host)) { const parsed = this.apDbResolverService.parseLocalUri(value);
return await this.resolveLocal(value); if (parsed.local === true) {
return await this.resolveLocal(parsed);
} }
if (!this.utilityService.isFederationAllowedHost(host)) { if (!this.utilityService.isFederationAllowedHost(host)) {
@ -124,9 +127,18 @@ export class Resolver {
} }
@bindThis @bindThis
private resolveLocal(url: string): Promise<IObject> { private resolveLocal(parsed: UriParseResult): Promise<IObject> {
const parsed = this.apDbResolverService.parseUri(url); if (parsed.local === false || 'uri' in parsed) {
if (!parsed.local) throw new IdentifiableError('02b40cd0-fa92-4b0c-acc9-fb2ada952ab8', 'resolveLocal: not local'); 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) { switch (parsed.type) {
case 'notes': case 'notes':