diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts index 046a82149..138929c4b 100644 --- a/packages/backend/src/models/repositories/user.ts +++ b/packages/backend/src/models/repositories/user.ts @@ -60,12 +60,24 @@ const birthdaySchema = { type: 'string', pattern: /^([0-9]{4})-([0-9]{2})-([0-9] function isLocalUser(user: User): user is ILocalUser; function isLocalUser(user: T): user is T & { host: null; }; +/** + * Returns true if the user is local. + * + * @param user The user to check. + * @returns True if the user is local. + */ function isLocalUser(user: User | { host: User['host'] }): boolean { return user.host == null; } function isRemoteUser(user: User): user is IRemoteUser; function isRemoteUser(user: T): user is T & { host: string; }; +/** + * Returns true if the user is remote. + * + * @param user The user to check. + * @returns True if the user is remote. + */ function isRemoteUser(user: User | { host: User['host'] }): boolean { return !isLocalUser(user); } diff --git a/packages/backend/src/remote/activitypub/kernel/move/index.ts b/packages/backend/src/remote/activitypub/kernel/move/index.ts index 501ee694b..1ca97e5c0 100644 --- a/packages/backend/src/remote/activitypub/kernel/move/index.ts +++ b/packages/backend/src/remote/activitypub/kernel/move/index.ts @@ -12,27 +12,44 @@ import { ApiError } from '@/server/api/error.js'; import { meta } from '@/server/api/endpoints/following/create.js'; import { IObject, IActor } from '../../type.js'; import type { IMove } from '../../type.js'; +import Resolver from '@/remote/activitypub/resolver.js'; export default async (actor: CacheableRemoteUser, activity: IMove): Promise => { // ※ There is a block target in activity.object, which should be a local user that exists. const dbResolver = new DbResolver(); + const resolver = new Resolver(); let new_acc = await dbResolver.getUserFromApId(activity.target); - if (!new_acc) new_acc = await getRemoteUser(activity.target); + let actor_new; + let actor_old; + if (!new_acc) actor_new = await resolver.resolve(activity.target) as IActor; - let old_acc = await dbResolver.getUserFromApId(activity.actor); - if (!old_acc) new_acc = await getRemoteUser(activity.actor); + let old_acc = actor; + if (!old_acc) actor_old = await resolver.resolve(activity.actor) as IActor; - if (!new_acc || new_acc.uri === null) { + if ((!new_acc || new_acc.uri === null) && (!actor_new || actor_new.id === null)) { return 'move: new acc not found'; } - if (!old_acc || old_acc.uri === null) { + if ((!old_acc || old_acc.uri === null) && (!actor_old || actor_old.id === null)) { return 'move: old acc not found'; } - await updatePerson(new_acc.uri); - await updatePerson(old_acc.uri); - new_acc = await getRemoteUser(new_acc.uri); - old_acc = await getRemoteUser(old_acc.uri); + + let newUri: string | null | undefined + let oldUri: string | null | undefined + newUri = new_acc ? new_acc.uri : + actor_new?.url?.toString(); + + oldUri = old_acc ? old_acc.uri : + actor_old?.url?.toString(); + + if(newUri === null || newUri === undefined) return 'move: new acc not found #2'; + if(oldUri === null || oldUri === undefined) return 'move: old acc not found #2'; + + await updatePerson(newUri); + await updatePerson(oldUri); + + new_acc = await getRemoteUser(newUri); + old_acc = await getRemoteUser(oldUri); if (old_acc === null || old_acc.uri === null || !new_acc.alsoKnownAs?.includes(old_acc.uri)) return 'move: accounts invalid'; @@ -45,18 +62,18 @@ export default async (actor: CacheableRemoteUser, activity: IMove): Promise { - if (!following.follower?.host) { + followings.forEach(async following => { + if (following.follower?.host) { const follower = following.follower; - deleteFollowing(follower!, old_acc!); + await deleteFollowing(follower!, old_acc!); try { - create(follower!, new_acc!); + await create(follower!, new_acc!); } catch (e) { if (e instanceof IdentifiableError) { - if (e.id === '710e8fb0-b8c3-4922-be49-d5d93d8e6a6e') throw new ApiError(meta.errors.blocking); - if (e.id === '3338392a-f764-498d-8855-db939dcf8c48') throw new ApiError(meta.errors.blocked); + if (e.id === '710e8fb0-b8c3-4922-be49-d5d93d8e6a6e') return meta.errors.blocking; + if (e.id === '3338392a-f764-498d-8855-db939dcf8c48') return meta.errors.blocked; } - throw e; + return e; } } }); diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index 3566410ed..21ef83af7 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -279,21 +279,21 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise { if (typeof uri !== 'string') throw new Error('uri is not string'); - // URIがこのサーバーを指しているならスキップ + // Skip if the URI points to this server if (uri.startsWith(config.url + '/')) { return; } - //#region このサーバーに既に登録されているか + //#region Already registered on this server? const exist = await Users.findOneBy({ uri }) as IRemoteUser; if (exist == null) { @@ -309,7 +309,7 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint logger.info(`Updating the Person: ${person.id}`); - // アバターとヘッダー画像をフェッチ + // Fetch avatar and header image const [avatar, banner] = await Promise.all([ person.icon, person.image, @@ -319,7 +319,7 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint : resolveImage(exist, img).catch(() => null), )); - // カスタム絵文字取得 + // Custom pictogram acquisition const emojis = await extractEmojis(person.tag || [], exist.host).catch(e => { logger.info(`extractEmojis: ${e}`); return [] as Emoji[]; @@ -378,10 +378,10 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint publishInternalEvent('remoteUserUpdated', { id: exist.id }); - // ハッシュタグ更新 + // Hashtag Update updateUsertags(exist, tags); - // 該当ユーザーが既にフォロワーになっていた場合はFollowingもアップデートする + // If the user in question is a follower, followers will also be updated. await Followings.update({ followerId: exist.id, }, { @@ -392,15 +392,15 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint } /** - * Personを解決します。 + * Resolve Person. * - * Misskeyに対象のPersonが登録されていればそれを返し、そうでなければ - * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。 + * If the target person is registered in Calckey, it returns it; + * otherwise, it fetches it from the remote server, registers it in Calckey, and returns it. */ export async function resolvePerson(uri: string, resolver?: Resolver): Promise { if (typeof uri !== 'string') throw new Error('uri is not string'); - //#region このサーバーに既に登録されていたらそれを返す + //#region If already registered on this server, return it. const exist = await fetchPerson(uri); if (exist) { @@ -408,7 +408,7 @@ export async function resolvePerson(uri: string, resolver?: Resolver): Promise(2); const featuredNotes = await Promise.all(items - .filter(item => getApType(item) === 'Note') // TODO: Noteでなくてもいいかも + .filter(item => getApType(item) === 'Note') // TODO: Maybe it doesn't have to be a Note. .slice(0, 5) .map(item => limit(() => resolveNote(item, resolver)))); await db.transaction(async transactionalEntityManager => { await transactionalEntityManager.delete(UserNotePining, { userId: user.id }); - // とりあえずidを別の時間で生成して順番を維持 + // For now, generate the id at a different time and maintain the order. let td = 0; for (const note of featuredNotes.filter(note => note != null)) { td -= 1000; diff --git a/packages/backend/src/server/activitypub/following.ts b/packages/backend/src/server/activitypub/following.ts index f843d79f0..26cee819b 100644 --- a/packages/backend/src/server/activitypub/following.ts +++ b/packages/backend/src/server/activitypub/following.ts @@ -61,7 +61,7 @@ export default async (ctx: Router.RouterContext) => { followerId: user.id, } as FindOptionsWhere; - // カーソルが指定されている場合 + // If a cursor is specified if (cursor) { query.id = LessThan(cursor); } @@ -73,7 +73,7 @@ export default async (ctx: Router.RouterContext) => { order: { id: -1 }, }); - // 「次のページ」があるかどうか + // Whether there is a "next page" or not const inStock = followings.length === limit + 1; if (inStock) followings.pop();