forked from FoundKeyGang/FoundKey
server: implement receiving Move activities
For now only creates notifications.
This commit is contained in:
parent
3c2092935c
commit
910976a55b
4 changed files with 84 additions and 2 deletions
|
@ -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';
|
||||
|
||||
|
@ -230,6 +230,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;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { Resolver } from '@/remote/activitypub/resolver.js';
|
|||
import { extractDbHost } from '@/misc/convert-host.js';
|
||||
import { shouldBlockInstance } from '@/misc/should-block-instance.js';
|
||||
import { apLogger } from '../logger.js';
|
||||
import { IObject, isCreate, isDelete, isUpdate, isRead, isFollow, isAccept, isReject, isAdd, isRemove, isAnnounce, isLike, isUndo, isBlock, isCollectionOrOrderedCollection, isCollection, isFlag, getApId } from '../type.js';
|
||||
import { IObject, isCreate, isDelete, isUpdate, isRead, isFollow, isAccept, isReject, isAdd, isRemove, isAnnounce, isLike, isUndo, isBlock, isCollectionOrOrderedCollection, isCollection, isFlag, isMove, getApId } from '../type.js';
|
||||
import create from './create/index.js';
|
||||
import performDeleteActivity from './delete/index.js';
|
||||
import performUpdateActivity from './update/index.js';
|
||||
|
@ -19,6 +19,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, resolver: Resolver): Promise<void> {
|
||||
if (isCollectionOrOrderedCollection(activity)) {
|
||||
|
@ -73,6 +74,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, resolver);
|
||||
} else {
|
||||
apLogger.warn(`unrecognized activity type: ${(activity as any).type}`);
|
||||
}
|
||||
|
|
62
packages/backend/src/remote/activitypub/kernel/move/index.ts
Normal file
62
packages/backend/src/remote/activitypub/kernel/move/index.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
import { IsNull } from 'typeorm';
|
||||
import { CacheableRemoteUser } from '@/models/entities/user.js';
|
||||
import { resolvePerson } 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, isActor, getApId } from '../../type.js';
|
||||
|
||||
export async function move(actor: CacheableRemoteUser, activity: IMove, resolver: Resolver): Promise<void> {
|
||||
// actor is not move origin
|
||||
if (activity.object == null || getApId(activity.object) !== actor.uri) return;
|
||||
|
||||
// actor already moved
|
||||
if (actor.movedTo != null) return;
|
||||
|
||||
// no move target
|
||||
if (activity.target == null) return;
|
||||
|
||||
/* 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.
|
||||
* This also checks whether the move target is blocked
|
||||
*/
|
||||
const movedToAp = await resolver.resolve(getApId(activity.target));
|
||||
|
||||
// move target is not an actor
|
||||
if (!isActor(movedToAp)) return;
|
||||
|
||||
// move destination has not accepted
|
||||
if (!Array.isArray(movedToAp.alsoKnownAs) || !movedToAp.alsoKnownAs.includes(actor.id)) return;
|
||||
|
||||
// ensure the user exists
|
||||
const movedTo = await resolvePerson(getApId(activity.target), resolver, movedToAp);
|
||||
// move target is already suspended
|
||||
if (movedTo.isSuspended) return;
|
||||
|
||||
// process move for local followers
|
||||
const followings = Followings.find({
|
||||
select: {
|
||||
followerId: true,
|
||||
},
|
||||
where: {
|
||||
followeeId: actor.id,
|
||||
followerHost: IsNull(),
|
||||
},
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
Users.update(actor.id, {
|
||||
movedToId: movedTo.id,
|
||||
}),
|
||||
...followings.map(async (following) => {
|
||||
// TODO: autoAcceptMove?
|
||||
|
||||
await createNotification(following.followerId, 'move', {
|
||||
notifierId: actor.id,
|
||||
moveTargetId: movedTo.id,
|
||||
});
|
||||
}),
|
||||
]);
|
||||
}
|
|
@ -296,6 +296,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';
|
||||
|
@ -310,6 +314,7 @@ 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';
|
||||
|
||||
export interface ILink {
|
||||
href: string;
|
||||
|
|
Loading…
Reference in a new issue