Implement recieve moveTo 🚀 🎉💩 (#9171)

This commit is contained in:
Kainoa Kanter 2022-12-03 00:26:45 +00:00
commit 71a259c949
7 changed files with 100 additions and 12 deletions

View file

@ -7,7 +7,7 @@ export async function fetchMeta(noCache = false): Promise<Meta> {
if (!noCache && cache) return cache; if (!noCache && cache) return cache;
return await db.transaction(async transactionalEntityManager => { return await db.transaction(async transactionalEntityManager => {
// 過去のバグでレコードが複数出来てしまっている可能性があるので新しいIDを優先する // New IDs are prioritized because multiple records may have been created due to past bugs.
const metas = await transactionalEntityManager.find(Meta, { const metas = await transactionalEntityManager.find(Meta, {
order: { order: {
id: 'DESC', id: 'DESC',
@ -20,7 +20,7 @@ export async function fetchMeta(noCache = false): Promise<Meta> {
cache = meta; cache = meta;
return meta; return meta;
} else { } else {
// metaが空のときfetchMetaが同時に呼ばれるとここが同時に呼ばれてしまうことがあるのでフェイルセーフなupsertを使う // If fetchMeta is called at the same time when meta is empty, this part may be called at the same time, so use fail-safe upsert.
const saved = await transactionalEntityManager const saved = await transactionalEntityManager
.upsert( .upsert(
Meta, Meta,

View file

@ -5,7 +5,7 @@ import DbResolver from '../../db-resolver.js';
import { Users } from '@/models/index.js'; import { Users } from '@/models/index.js';
export default async (actor: CacheableRemoteUser, activity: IBlock): Promise<string> => { export default async (actor: CacheableRemoteUser, activity: IBlock): Promise<string> => {
// ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず // ※ 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 blockee = await dbResolver.getUserFromApId(activity.object); const blockee = await dbResolver.getUserFromApId(activity.object);
@ -15,7 +15,7 @@ export default async (actor: CacheableRemoteUser, activity: IBlock): Promise<str
} }
if (blockee.host != null) { if (blockee.host != null) {
return `skip: ブロックしようとしているユーザーはローカルユーザーではありません`; return `skip: The user you are trying to block is not a local user`;
} }
await block(await Users.findOneByOrFail({ id: actor.id }), await Users.findOneByOrFail({ id: blockee.id })); await block(await Users.findOneByOrFail({ id: actor.id }), await Users.findOneByOrFail({ id: blockee.id }));

View file

@ -1,4 +1,23 @@
import { IObject, isCreate, isDelete, isUpdate, isRead, isFollow, isAccept, isReject, isAdd, isRemove, isAnnounce, isLike, isUndo, isBlock, isCollectionOrOrderedCollection, isCollection, isFlag } from '../type.js'; import {
IObject,
isCreate,
isDelete,
isUpdate,
isRead,
isFollow,
isAccept,
isReject,
isAdd,
isRemove,
isAnnounce,
isLike,
isUndo,
isBlock,
isCollectionOrOrderedCollection,
isCollection,
isFlag,
isMove
} from '../type.js';
import { CacheableRemoteUser } from '@/models/entities/user.js'; import { CacheableRemoteUser } from '@/models/entities/user.js';
import create from './create/index.js'; import create from './create/index.js';
import performDeleteActivity from './delete/index.js'; import performDeleteActivity from './delete/index.js';
@ -17,7 +36,7 @@ import flag from './flag/index.js';
import { apLogger } from '../logger.js'; import { apLogger } from '../logger.js';
import Resolver from '../resolver.js'; import Resolver from '../resolver.js';
import { toArray } from '@/prelude/array.js'; import { toArray } from '@/prelude/array.js';
import { Users } from '@/models/index.js'; import move from './move/index.js';
export async function performActivity(actor: CacheableRemoteUser, activity: IObject) { export async function performActivity(actor: CacheableRemoteUser, activity: IObject) {
if (isCollectionOrOrderedCollection(activity)) { if (isCollectionOrOrderedCollection(activity)) {
@ -68,6 +87,8 @@ async function performOneActivity(actor: CacheableRemoteUser, activity: IObject)
await block(actor, activity); await block(actor, activity);
} else if (isFlag(activity)) { } else if (isFlag(activity)) {
await flag(actor, activity); await flag(actor, activity);
} else if (isMove(activity)) {
await move(actor,activity);
} else { } else {
apLogger.warn(`unrecognized activity type: ${(activity as any).type}`); apLogger.warn(`unrecognized activity type: ${(activity as any).type}`);
} }

View file

@ -0,0 +1,63 @@
import {CacheableRemoteUser, IRemoteUser, User} from '@/models/entities/user.js';
import {IMove, IObject, IActor} from '../../type.js';
import DbResolver from "@/remote/activitypub/db-resolver";
import {getRemoteUser} from '@/server/api/common/getters.js';
import { updatePerson } from '@/remote/activitypub/models/person.js';
import {Followings, Users} from "@/models";
import {makePaginationQuery} from "@/server/api/common/make-pagination-query";
import deleteFollowing from '@/services/following/delete.js';
import create from '@/services/following/create.js';
import {IdentifiableError} from "@/misc/identifiable-error";
import {ApiError} from "@/server/api/error";
import {meta} from "@/server/api/endpoints/following/create";
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();
let new_acc = await dbResolver.getUserFromApId(activity.target);
if (!new_acc) new_acc = await getRemoteUser(<string>activity.target);
let old_acc = await dbResolver.getUserFromApId(activity.actor);
if (!old_acc) new_acc = await getRemoteUser(<string>activity.actor);
if (!new_acc || new_acc.uri === null) {
return `move: new acc not found`;
}
if (!old_acc || old_acc.uri === 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);
if(old_acc === null || old_acc.uri === null || !new_acc?.alsoKnownAs?.includes(old_acc.uri)) return `move: accounts invalid`;
old_acc.movedToUri = new_acc?.uri
const query = makePaginationQuery(Followings.createQueryBuilder('following'))
.andWhere('following.followeeId = :userId', { userId: old_acc.id })
.innerJoinAndSelect('following.follower', 'follower');
const followings = await query
.getMany();
followings.forEach(following => {
if(!following.follower?.host) {
let follower = following.follower;
deleteFollowing(follower!, old_acc!);
try {
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);
}
throw e;
}
}
})
return `ok`;
};

View file

@ -160,7 +160,7 @@ export interface IActor extends IObject {
alsoKnownAs?: string[]; alsoKnownAs?: string[];
discoverable?: boolean; discoverable?: boolean;
inbox: string; inbox: string;
sharedInbox?: string; // 後方互換性のため sharedInbox?: string; // backward compatibility.. ig
publicKey?: { publicKey?: {
id: string; id: string;
publicKeyPem: string; publicKeyPem: string;
@ -283,6 +283,7 @@ export interface IFlag extends IActivity {
export interface IMove extends IActivity { export interface IMove extends IActivity {
type: 'Move'; type: 'Move';
target: IObject | string;
} }
export const isCreate = (object: IObject): object is ICreate => getApType(object) === 'Create'; export const isCreate = (object: IObject): object is ICreate => getApType(object) === 'Create';
@ -299,3 +300,4 @@ export const isLike = (object: IObject): object is ILike => getApType(object) ==
export const isAnnounce = (object: IObject): object is IAnnounce => getApType(object) === 'Announce'; export const isAnnounce = (object: IObject): object is IAnnounce => getApType(object) === 'Announce';
export const isBlock = (object: IObject): object is IBlock => getApType(object) === 'Block'; export const isBlock = (object: IObject): object is IBlock => getApType(object) === 'Block';
export const isFlag = (object: IObject): object is IFlag => getApType(object) === 'Flag'; export const isFlag = (object: IObject): object is IFlag => getApType(object) === 'Flag';
export const isMove = (object: IObject): object is IMove => getApType(object) === 'Move';

View file

@ -19,7 +19,7 @@ import { genIdenticon } from '@/misc/gen-identicon.js';
import { createTemp } from '@/misc/create-temp.js'; import { createTemp } from '@/misc/create-temp.js';
import { publishMainStream } from '@/services/stream.js'; import { publishMainStream } from '@/services/stream.js';
import * as Acct from '@/misc/acct.js'; import * as Acct from '@/misc/acct.js';
import { envOption } from '../env.js'; import { envOption } from '@/env';
import activityPub from './activitypub.js'; import activityPub from './activitypub.js';
import nodeinfo from './nodeinfo.js'; import nodeinfo from './nodeinfo.js';
import wellKnown from './well-known.js'; import wellKnown from './well-known.js';
@ -164,5 +164,6 @@ export default () => new Promise(resolve => {
} }
}); });
// @ts-ignore
server.listen(config.port, resolve); server.listen(config.port, resolve);
}); });

View file

@ -11,11 +11,11 @@ const router = new Router();
const nodeinfo2_1path = '/nodeinfo/2.1'; const nodeinfo2_1path = '/nodeinfo/2.1';
const nodeinfo2_0path = '/nodeinfo/2.0'; const nodeinfo2_0path = '/nodeinfo/2.0';
export const links = [/* (awaiting release) { export const links = [{
rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1', rel: 'https://nodeinfo.diaspora.software/ns/schema/2.1',
href: config.url + nodeinfo2_1path href: config.url + nodeinfo2_1path
}, */{ }, {
rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0', rel: 'https://nodeinfo.diaspora.software/ns/schema/2.0',
href: config.url + nodeinfo2_0path, href: config.url + nodeinfo2_0path,
}]; }];
@ -96,6 +96,7 @@ router.get(nodeinfo2_1path, async ctx => {
router.get(nodeinfo2_0path, async ctx => { router.get(nodeinfo2_0path, async ctx => {
const base = await cache.fetch(null, () => nodeinfo2()); const base = await cache.fetch(null, () => nodeinfo2());
// @ts-ignore
delete base.software.repository; delete base.software.repository;
ctx.body = { version: '2.0', ...base }; ctx.body = { version: '2.0', ...base };