fix remote move queue
This commit is contained in:
parent
2978dad2be
commit
2694baf85d
|
@ -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: User): user is ILocalUser;
|
||||||
function isLocalUser<T extends { host: User['host'] }>(user: T): user is T & { host: null; };
|
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 {
|
function isLocalUser(user: User | { host: User['host'] }): boolean {
|
||||||
return user.host == null;
|
return user.host == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isRemoteUser(user: User): user is IRemoteUser;
|
function isRemoteUser(user: User): user is IRemoteUser;
|
||||||
function isRemoteUser<T extends { host: User['host'] }>(user: T): user is T & { host: string; };
|
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 {
|
function isRemoteUser(user: User | { host: User['host'] }): boolean {
|
||||||
return !isLocalUser(user);
|
return !isLocalUser(user);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,27 +12,44 @@ import { ApiError } from '@/server/api/error.js';
|
||||||
import { meta } from '@/server/api/endpoints/following/create.js';
|
import { meta } from '@/server/api/endpoints/following/create.js';
|
||||||
import { IObject, IActor } from '../../type.js';
|
import { IObject, IActor } from '../../type.js';
|
||||||
import type { IMove } 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> => {
|
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.
|
// ※ There is a block target in activity.object, which should be a local user that exists.
|
||||||
|
|
||||||
const dbResolver = new DbResolver();
|
const dbResolver = new DbResolver();
|
||||||
|
const resolver = new Resolver();
|
||||||
let new_acc = await dbResolver.getUserFromApId(activity.target);
|
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);
|
let old_acc = actor;
|
||||||
if (!old_acc) new_acc = await getRemoteUser(<string>activity.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';
|
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';
|
return 'move: old acc not found';
|
||||||
}
|
}
|
||||||
await updatePerson(new_acc.uri);
|
|
||||||
await updatePerson(old_acc.uri);
|
let newUri: string | null | undefined
|
||||||
new_acc = await getRemoteUser(new_acc.uri);
|
let oldUri: string | null | undefined
|
||||||
old_acc = await getRemoteUser(old_acc.uri);
|
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';
|
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
|
const followings = await query
|
||||||
.getMany();
|
.getMany();
|
||||||
|
|
||||||
followings.forEach(following => {
|
followings.forEach(async following => {
|
||||||
if (!following.follower?.host) {
|
if (following.follower?.host) {
|
||||||
const follower = following.follower;
|
const follower = following.follower;
|
||||||
deleteFollowing(follower!, old_acc!);
|
await deleteFollowing(follower!, old_acc!);
|
||||||
try {
|
try {
|
||||||
create(follower!, new_acc!);
|
await create(follower!, new_acc!);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof IdentifiableError) {
|
if (e instanceof IdentifiableError) {
|
||||||
if (e.id === '710e8fb0-b8c3-4922-be49-d5d93d8e6a6e') throw new ApiError(meta.errors.blocking);
|
if (e.id === '710e8fb0-b8c3-4922-be49-d5d93d8e6a6e') return meta.errors.blocking;
|
||||||
if (e.id === '3338392a-f764-498d-8855-db939dcf8c48') throw new ApiError(meta.errors.blocked);
|
if (e.id === '3338392a-f764-498d-8855-db939dcf8c48') return meta.errors.blocked;
|
||||||
}
|
}
|
||||||
throw e;
|
return e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -279,21 +279,21 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Personの情報を更新します。
|
* Update Person data from remote.
|
||||||
* Misskeyに対象のPersonが登録されていなければ無視します。
|
* If the target Person is not registered in Calckey, it is ignored.
|
||||||
* @param uri URI of Person
|
* @param uri URI of Person
|
||||||
* @param resolver Resolver
|
* @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> {
|
export async function updatePerson(uri: string, resolver?: Resolver | null, hint?: IObject): Promise<void> {
|
||||||
if (typeof uri !== 'string') throw new Error('uri is not string');
|
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 + '/')) {
|
if (uri.startsWith(config.url + '/')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//#region このサーバーに既に登録されているか
|
//#region Already registered on this server?
|
||||||
const exist = await Users.findOneBy({ uri }) as IRemoteUser;
|
const exist = await Users.findOneBy({ uri }) as IRemoteUser;
|
||||||
|
|
||||||
if (exist == null) {
|
if (exist == null) {
|
||||||
|
@ -309,7 +309,7 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint
|
||||||
|
|
||||||
logger.info(`Updating the Person: ${person.id}`);
|
logger.info(`Updating the Person: ${person.id}`);
|
||||||
|
|
||||||
// アバターとヘッダー画像をフェッチ
|
// Fetch avatar and header image
|
||||||
const [avatar, banner] = await Promise.all([
|
const [avatar, banner] = await Promise.all([
|
||||||
person.icon,
|
person.icon,
|
||||||
person.image,
|
person.image,
|
||||||
|
@ -319,7 +319,7 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint
|
||||||
: resolveImage(exist, img).catch(() => null),
|
: resolveImage(exist, img).catch(() => null),
|
||||||
));
|
));
|
||||||
|
|
||||||
// カスタム絵文字取得
|
// Custom pictogram acquisition
|
||||||
const emojis = await extractEmojis(person.tag || [], exist.host).catch(e => {
|
const emojis = await extractEmojis(person.tag || [], exist.host).catch(e => {
|
||||||
logger.info(`extractEmojis: ${e}`);
|
logger.info(`extractEmojis: ${e}`);
|
||||||
return [] as Emoji[];
|
return [] as Emoji[];
|
||||||
|
@ -378,10 +378,10 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint
|
||||||
|
|
||||||
publishInternalEvent('remoteUserUpdated', { id: exist.id });
|
publishInternalEvent('remoteUserUpdated', { id: exist.id });
|
||||||
|
|
||||||
// ハッシュタグ更新
|
// Hashtag Update
|
||||||
updateUsertags(exist, tags);
|
updateUsertags(exist, tags);
|
||||||
|
|
||||||
// 該当ユーザーが既にフォロワーになっていた場合はFollowingもアップデートする
|
// If the user in question is a follower, followers will also be updated.
|
||||||
await Followings.update({
|
await Followings.update({
|
||||||
followerId: exist.id,
|
followerId: exist.id,
|
||||||
}, {
|
}, {
|
||||||
|
@ -392,15 +392,15 @@ export async function updatePerson(uri: string, resolver?: Resolver | null, hint
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Personを解決します。
|
* Resolve Person.
|
||||||
*
|
*
|
||||||
* Misskeyに対象のPersonが登録されていればそれを返し、そうでなければ
|
* If the target person is registered in Calckey, it returns it;
|
||||||
* リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
|
* 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> {
|
export async function resolvePerson(uri: string, resolver?: Resolver): Promise<CacheableUser> {
|
||||||
if (typeof uri !== 'string') throw new Error('uri is not string');
|
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);
|
const exist = await fetchPerson(uri);
|
||||||
|
|
||||||
if (exist) {
|
if (exist) {
|
||||||
|
@ -408,7 +408,7 @@ export async function resolvePerson(uri: string, resolver?: Resolver): Promise<C
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
// リモートサーバーからフェッチしてきて登録
|
// Fetched from remote server and registered
|
||||||
if (resolver == null) resolver = new Resolver();
|
if (resolver == null) resolver = new Resolver();
|
||||||
return await createPerson(uri, resolver);
|
return await createPerson(uri, resolver);
|
||||||
}
|
}
|
||||||
|
@ -486,14 +486,14 @@ export async function updateFeatured(userId: User['id'], resolver?: Resolver) {
|
||||||
// Resolve and regist Notes
|
// Resolve and regist Notes
|
||||||
const limit = promiseLimit<Note | null>(2);
|
const limit = promiseLimit<Note | null>(2);
|
||||||
const featuredNotes = await Promise.all(items
|
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)
|
.slice(0, 5)
|
||||||
.map(item => limit(() => resolveNote(item, resolver))));
|
.map(item => limit(() => resolveNote(item, resolver))));
|
||||||
|
|
||||||
await db.transaction(async transactionalEntityManager => {
|
await db.transaction(async transactionalEntityManager => {
|
||||||
await transactionalEntityManager.delete(UserNotePining, { userId: user.id });
|
await transactionalEntityManager.delete(UserNotePining, { userId: user.id });
|
||||||
|
|
||||||
// とりあえずidを別の時間で生成して順番を維持
|
// For now, generate the id at a different time and maintain the order.
|
||||||
let td = 0;
|
let td = 0;
|
||||||
for (const note of featuredNotes.filter(note => note != null)) {
|
for (const note of featuredNotes.filter(note => note != null)) {
|
||||||
td -= 1000;
|
td -= 1000;
|
||||||
|
|
|
@ -61,7 +61,7 @@ export default async (ctx: Router.RouterContext) => {
|
||||||
followerId: user.id,
|
followerId: user.id,
|
||||||
} as FindOptionsWhere<Following>;
|
} as FindOptionsWhere<Following>;
|
||||||
|
|
||||||
// カーソルが指定されている場合
|
// If a cursor is specified
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
query.id = LessThan(cursor);
|
query.id = LessThan(cursor);
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ export default async (ctx: Router.RouterContext) => {
|
||||||
order: { id: -1 },
|
order: { id: -1 },
|
||||||
});
|
});
|
||||||
|
|
||||||
// 「次のページ」があるかどうか
|
// Whether there is a "next page" or not
|
||||||
const inStock = followings.length === limit + 1;
|
const inStock = followings.length === limit + 1;
|
||||||
if (inStock) followings.pop();
|
if (inStock) followings.pop();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue