server: implement receiving Move activities

For now only creates notifications.
This commit is contained in:
Johann150 2022-05-20 10:38:29 +02:00
parent 1f7f978bbd
commit ac3e807717
Signed by untrusted user: Johann150
GPG key ID: 9EE6577A2A06F8F1
4 changed files with 79 additions and 2 deletions

View file

@ -1,4 +1,4 @@
import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm';
import { Entity, Column, Index, OneToOne, ManyToOne, JoinColumn, PrimaryColumn } from 'typeorm';
import { id } from '../id.js';
import { DriveFile } from './drive-file.js';
@ -223,6 +223,18 @@ export class User {
})
public federateBlocks: boolean;
@Column({
...id(),
nullable: true,
})
public movedToId: User['id'] | null;
@ManyToOne(() => User, {
onDelete: 'SET NULL'
})
@JoinColumn()
public movedTo: User | null;
constructor(data: Partial<User>) {
if (data == null) return;

View file

@ -2,7 +2,7 @@ import { CacheableRemoteUser } from '@/models/entities/user.js';
import { toArray } from '@/prelude/array.js';
import { apLogger } from '../logger.js';
import Resolver from '../resolver.js';
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 create from './create/index.js';
import performDeleteActivity from './delete/index.js';
import performUpdateActivity from './update/index.js';
@ -17,6 +17,7 @@ import add from './add/index.js';
import remove from './remove/index.js';
import block from './block/index.js';
import flag from './flag/index.js';
import { move } from './move/index.js';
export async function performActivity(actor: CacheableRemoteUser, activity: IObject) {
if (isCollectionOrOrderedCollection(activity)) {
@ -67,6 +68,8 @@ async function performOneActivity(actor: CacheableRemoteUser, activity: IObject)
await block(actor, activity);
} else if (isFlag(activity)) {
await flag(actor, activity);
} else if (isMove(activity)) {
await move(actor, activity);
} else {
apLogger.warn(`unrecognized activity type: ${(activity as any).type}`);
}

View file

@ -0,0 +1,57 @@
import { CacheableRemoteUser } from '@/models/entities/user.js';
import { updatePerson } from '@/remote/activitypub/models/person.js';
import { Followings, Users } from '@/models/index.js';
import { createNotification } from '@/services/create-notification.js';
import Resolver from '../../resolver.js';
import { IMove, getApId } from '../../type.js';
export async function move(actor: CacheableRemoteUser, activity: IMove): Promise<void> {
const objectUri = getApId(activity);
// actor is not move origin
if (objectUri != actor.uri) return;
// actor already moved
if (actor.movedTo != null) return;
// no move target
if (activity.target == null) return;
const resolver = new Resolver();
/* the database resolver can not be used here, because:
* 1. It must be ensured that the latest data is used.
* 2. The AP representation is needed, because `alsoKnownAs`
* is not stored in the database.
*/
const movedToAp = resolver.resolve(activity.target);
// ensure the user exists
const movedTo = await resolvePerson(getApId(activity.target), resolver, movedToAp);
// move destination has not accepted
if (!Array.isArray(movedToAp.alsoKnownAs) || !moved_to.alsoKnownAs.includes(actor.id)) return;
// process move for local followers
const followingsQuery = Followings.createQueryBuilder('f')
.select('f.followerId')
.where('f.followeeId = :actorId', { actorId: actor.id })
.andWhere('f.followerHost IS NULL');
const followers = await UserProfiles.createQueryBuilder('profiles')
.where(`id IN (${ followingsQuery.getQuery() })`)
.getMany();
await Promise.all([
Users.update(actor.id, {
moved: movedTo.id,
}),
...followers.map(async (follower) => {
// TODO: autoAcceptMove?
await createNotification(follower.id, 'move', {
notifierId: actor.id,
});
}),
]);
}

View file

@ -279,6 +279,10 @@ export interface IFlag extends IActivity {
type: 'Flag';
}
export interface IMove extends IActivity {
type: 'Move';
}
export const isCreate = (object: IObject): object is ICreate => getApType(object) === 'Create';
export const isDelete = (object: IObject): object is IDelete => getApType(object) === 'Delete';
export const isUpdate = (object: IObject): object is IUpdate => getApType(object) === 'Update';
@ -293,3 +297,4 @@ export const isLike = (object: IObject): object is ILike => getApType(object) ==
export const isAnnounce = (object: IObject): object is IAnnounce => getApType(object) === 'Announce';
export const isBlock = (object: IObject): object is IBlock => getApType(object) === 'Block';
export const isFlag = (object: IObject): object is IFlag => getApType(object) === 'Flag';
export const isMove = (object: IObject): object is IMove => getApType(object) === 'Move';