fix remote move queue

This commit is contained in:
cutestnekoaqua 2022-12-07 18:16:23 +01:00
parent 2978dad2be
commit 2694baf85d
4 changed files with 63 additions and 34 deletions

View file

@ -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<T extends { host: User['host'] }>(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<T extends { host: User['host'] }>(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);
}

View file

@ -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<string> => {
// ※ 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(<string>activity.target);
let actor_new;
let actor_old;
if (!new_acc) actor_new = await resolver.resolve(<string>activity.target) as IActor;
let old_acc = await dbResolver.getUserFromApId(activity.actor);
if (!old_acc) new_acc = await getRemoteUser(<string>activity.actor);
let old_acc = actor;
if (!old_acc) actor_old = await resolver.resolve(<string>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<stri
const followings = await query
.getMany();
followings.forEach(following => {
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;
}
}
});

View file

@ -279,21 +279,21 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
}
/**
* Personの情報を更新します
* Misskeyに対象のPersonが登録されていなければ無視します
* Update Person data from remote.
* If the target Person is not registered in Calckey, it is ignored.
* @param uri URI of Person
* @param resolver Resolver
* @param hint Hint of Person object (Personの場合Remote resolveをせずに更新に利用します)
* @param hint Hint of Person object (If this value is a valid Person, it is used for updating without Remote resolve)
*/
export async function updatePerson(uri: string, resolver?: Resolver | null, hint?: IObject): Promise<void> {
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<CacheableUser> {
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<C
}
//#endregion
// リモートサーバーからフェッチしてきて登録
// Fetched from remote server and registered
if (resolver == null) resolver = new Resolver();
return await createPerson(uri, resolver);
}
@ -486,14 +486,14 @@ export async function updateFeatured(userId: User['id'], resolver?: Resolver) {
// Resolve and regist Notes
const limit = promiseLimit<Note | null>(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;

View file

@ -61,7 +61,7 @@ export default async (ctx: Router.RouterContext) => {
followerId: user.id,
} as FindOptionsWhere<Following>;
// カーソルが指定されている場合
// 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();