AP featured collectionの修正 / Collection Activityの対応 / typeの修正など (#5460)
* resolver type / fix updateFeatured * type ApObject * fix strange type * AP Activity * Collection Activityが失敗したらとりあえず無視
This commit is contained in:
		
							parent
							
								
									3a093f8bd7
								
							
						
					
					
						commit
						e14509574d
					
				
					 10 changed files with 85 additions and 106 deletions
				
			
		| 
						 | 
				
			
			@ -120,3 +120,11 @@ export function cumulativeSum(xs: number[]): number[] {
 | 
			
		|||
export function fromEntries(xs: [string, any][]): { [x: string]: any; } {
 | 
			
		||||
	return xs.reduce((obj, [k, v]) => Object.assign(obj, { [k]: v }), {} as { [x: string]: any; });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function toArray<T>(x: T | T[] | undefined): T[] {
 | 
			
		||||
	return Array.isArray(x) ? x : x != null ? [x] : [];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function toSingle<T>(x: T | T[] | undefined): T | undefined {
 | 
			
		||||
	return Array.isArray(x) ? x[0] : x;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
import Resolver from '../../resolver';
 | 
			
		||||
import { IRemoteUser } from '../../../../models/entities/user';
 | 
			
		||||
import announceNote from './note';
 | 
			
		||||
import { IAnnounce, INote, validPost, getApId } from '../../type';
 | 
			
		||||
import { IAnnounce, validPost, getApId } from '../../type';
 | 
			
		||||
import { apLogger } from '../../logger';
 | 
			
		||||
 | 
			
		||||
const logger = apLogger;
 | 
			
		||||
| 
						 | 
				
			
			@ -23,7 +23,7 @@ export default async (actor: IRemoteUser, activity: IAnnounce): Promise<void> =>
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	if (validPost.includes(object.type)) {
 | 
			
		||||
		announceNote(resolver, actor, activity, object as INote);
 | 
			
		||||
		announceNote(resolver, actor, activity, object);
 | 
			
		||||
	} else {
 | 
			
		||||
		logger.warn(`Unknown announce type: ${object.type}`);
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
import Resolver from '../../resolver';
 | 
			
		||||
import post from '../../../../services/note/create';
 | 
			
		||||
import { IRemoteUser, User } from '../../../../models/entities/user';
 | 
			
		||||
import { IAnnounce, INote, getApId, getApIds } from '../../type';
 | 
			
		||||
import { IAnnounce, IObject, getApId, getApIds } from '../../type';
 | 
			
		||||
import { fetchNote, resolveNote } from '../../models/note';
 | 
			
		||||
import { resolvePerson } from '../../models/person';
 | 
			
		||||
import { apLogger } from '../../logger';
 | 
			
		||||
| 
						 | 
				
			
			@ -14,7 +14,7 @@ const logger = apLogger;
 | 
			
		|||
/**
 | 
			
		||||
 * アナウンスアクティビティを捌きます
 | 
			
		||||
 */
 | 
			
		||||
export default async function(resolver: Resolver, actor: IRemoteUser, activity: IAnnounce, note: INote): Promise<void> {
 | 
			
		||||
export default async function(resolver: Resolver, actor: IRemoteUser, activity: IAnnounce, note: IObject): Promise<void> {
 | 
			
		||||
	const uri = getApId(activity);
 | 
			
		||||
 | 
			
		||||
	// アナウンサーが凍結されていたらスキップ
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
import config from '../../../../config';
 | 
			
		||||
import { IBlock } from '../../type';
 | 
			
		||||
import { IBlock, getApId } from '../../type';
 | 
			
		||||
import block from '../../../../services/blocking/create';
 | 
			
		||||
import { apLogger } from '../../logger';
 | 
			
		||||
import { Users } from '../../../../models';
 | 
			
		||||
| 
						 | 
				
			
			@ -8,10 +8,9 @@ import { IRemoteUser } from '../../../../models/entities/user';
 | 
			
		|||
const logger = apLogger;
 | 
			
		||||
 | 
			
		||||
export default async (actor: IRemoteUser, activity: IBlock): Promise<void> => {
 | 
			
		||||
	const id = typeof activity.object == 'string' ? activity.object : activity.object.id;
 | 
			
		||||
	if (id == null) throw new Error('missing id');
 | 
			
		||||
	const id = getApId(activity.object);
 | 
			
		||||
 | 
			
		||||
	const uri = activity.id || activity;
 | 
			
		||||
	const uri = getApId(activity);
 | 
			
		||||
 | 
			
		||||
	logger.info(`Block: ${uri}`);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,13 +1,13 @@
 | 
			
		|||
import Resolver from '../../resolver';
 | 
			
		||||
import { IRemoteUser } from '../../../../models/entities/user';
 | 
			
		||||
import { createNote, fetchNote } from '../../models/note';
 | 
			
		||||
import { getApId } from '../../type';
 | 
			
		||||
import { getApId, IObject } from '../../type';
 | 
			
		||||
import { getApLock } from '../../../../misc/app-lock';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 投稿作成アクティビティを捌きます
 | 
			
		||||
 */
 | 
			
		||||
export default async function(resolver: Resolver, actor: IRemoteUser, note: any, silent = false): Promise<void> {
 | 
			
		||||
export default async function(resolver: Resolver, actor: IRemoteUser, note: IObject, silent = false): Promise<void> {
 | 
			
		||||
	const uri = getApId(note);
 | 
			
		||||
 | 
			
		||||
	const unlock = await getApLock(uri);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
import { Object } from '../type';
 | 
			
		||||
import { IObject, isCreate, isDelete, isUpdate, isFollow, isAccept, isReject, isAdd, isRemove, isAnnounce, isLike, isUndo, isBlock, isCollectionOrOrderedCollection, isCollection  } from '../type';
 | 
			
		||||
import { IRemoteUser } from '../../../models/entities/user';
 | 
			
		||||
import create from './create';
 | 
			
		||||
import performDeleteActivity from './delete';
 | 
			
		||||
| 
						 | 
				
			
			@ -13,68 +13,53 @@ import add from './add';
 | 
			
		|||
import remove from './remove';
 | 
			
		||||
import block from './block';
 | 
			
		||||
import { apLogger } from '../logger';
 | 
			
		||||
import Resolver from '../resolver';
 | 
			
		||||
import { toArray } from '../../../prelude/array';
 | 
			
		||||
 | 
			
		||||
const self = async (actor: IRemoteUser, activity: Object): Promise<void> => {
 | 
			
		||||
export async function performActivity(actor: IRemoteUser, activity: IObject) {
 | 
			
		||||
	if (isCollectionOrOrderedCollection(activity)) {
 | 
			
		||||
		const resolver = new Resolver();
 | 
			
		||||
		for (const item of toArray(isCollection(activity) ? activity.items : activity.orderedItems)) {
 | 
			
		||||
			const act = await resolver.resolve(item);
 | 
			
		||||
			try {
 | 
			
		||||
				await performOneActivity(actor, act);
 | 
			
		||||
			} catch (e) {
 | 
			
		||||
				apLogger.error(e);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		await performOneActivity(actor, activity);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function performOneActivity(actor: IRemoteUser, activity: IObject): Promise<void> {
 | 
			
		||||
	if (actor.isSuspended) return;
 | 
			
		||||
 | 
			
		||||
	switch (activity.type) {
 | 
			
		||||
	case 'Create':
 | 
			
		||||
	if (isCreate(activity)) {
 | 
			
		||||
		await create(actor, activity);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 'Delete':
 | 
			
		||||
	} else if (isDelete(activity)) {
 | 
			
		||||
		await performDeleteActivity(actor, activity);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 'Update':
 | 
			
		||||
	} else if (isUpdate(activity)) {
 | 
			
		||||
		await performUpdateActivity(actor, activity);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 'Follow':
 | 
			
		||||
	} else if (isFollow(activity)) {
 | 
			
		||||
		await follow(actor, activity);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 'Accept':
 | 
			
		||||
	} else if (isAccept(activity)) {
 | 
			
		||||
		await accept(actor, activity);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 'Reject':
 | 
			
		||||
	} else if (isReject(activity)) {
 | 
			
		||||
		await reject(actor, activity);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 'Add':
 | 
			
		||||
	} else if (isAdd(activity)) {
 | 
			
		||||
		await add(actor, activity).catch(err => apLogger.error(err));
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 'Remove':
 | 
			
		||||
	} else if (isRemove(activity)) {
 | 
			
		||||
		await remove(actor, activity).catch(err => apLogger.error(err));
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 'Announce':
 | 
			
		||||
	} else if (isAnnounce(activity)) {
 | 
			
		||||
		await announce(actor, activity);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 'Like':
 | 
			
		||||
	} else if (isLike(activity)) {
 | 
			
		||||
		await like(actor, activity);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 'Undo':
 | 
			
		||||
	} else if (isUndo(activity)) {
 | 
			
		||||
		await undo(actor, activity);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 'Block':
 | 
			
		||||
	} else if (isBlock(activity)) {
 | 
			
		||||
		await block(actor, activity);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 'Collection':
 | 
			
		||||
	case 'OrderedCollection':
 | 
			
		||||
		// TODO
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
	} else {
 | 
			
		||||
		apLogger.warn(`unknown activity type: ${(activity as any).type}`);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default self;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,8 @@ import { UserProfile } from '../../../models/entities/user-profile';
 | 
			
		|||
import { validActor } from '../../../remote/activitypub/type';
 | 
			
		||||
import { getConnection } from 'typeorm';
 | 
			
		||||
import { ensure } from '../../../prelude/ensure';
 | 
			
		||||
import { toArray } from '../../../prelude/array';
 | 
			
		||||
 | 
			
		||||
const logger = apLogger;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -463,8 +465,7 @@ export async function updateFeatured(userId: User['id']) {
 | 
			
		|||
 | 
			
		||||
	// Resolve to Object(may be Note) arrays
 | 
			
		||||
	const unresolvedItems = isCollection(collection) ? collection.items : collection.orderedItems;
 | 
			
		||||
	const items = await resolver.resolve(unresolvedItems);
 | 
			
		||||
	if (!Array.isArray(items)) throw new Error(`Collection items is not an array`);
 | 
			
		||||
	const items = await Promise.all(toArray(unresolvedItems).map(x => resolver.resolve(x)));
 | 
			
		||||
 | 
			
		||||
	// Resolve and regist Notes
 | 
			
		||||
	const limit = promiseLimit<Note | null>(2);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
import { Object } from './type';
 | 
			
		||||
import { IObject } from './type';
 | 
			
		||||
import { IRemoteUser } from '../../models/entities/user';
 | 
			
		||||
import kernel from './kernel';
 | 
			
		||||
import { performActivity } from './kernel';
 | 
			
		||||
 | 
			
		||||
export default async (actor: IRemoteUser, activity: Object): Promise<void> => {
 | 
			
		||||
	await kernel(actor, activity);
 | 
			
		||||
export default async (actor: IRemoteUser, activity: IObject): Promise<void> => {
 | 
			
		||||
	await performActivity(actor, activity);
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
import * as request from 'request-promise-native';
 | 
			
		||||
import { IObject } from './type';
 | 
			
		||||
import { IObject, isCollectionOrOrderedCollection, ICollection, IOrderedCollection } from './type';
 | 
			
		||||
import config from '../../config';
 | 
			
		||||
 | 
			
		||||
export default class Resolver {
 | 
			
		||||
| 
						 | 
				
			
			@ -14,31 +14,19 @@ export default class Resolver {
 | 
			
		|||
		return Array.from(this.history);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public async resolveCollection(value: any) {
 | 
			
		||||
	public async resolveCollection(value: string | IObject): Promise<ICollection | IOrderedCollection> {
 | 
			
		||||
		const collection = typeof value === 'string'
 | 
			
		||||
			? await this.resolve(value)
 | 
			
		||||
			: value;
 | 
			
		||||
 | 
			
		||||
		switch (collection.type) {
 | 
			
		||||
			case 'Collection': {
 | 
			
		||||
				collection.objects = collection.items;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			case 'OrderedCollection': {
 | 
			
		||||
				collection.objects = collection.orderedItems;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			default: {
 | 
			
		||||
				throw new Error(`unknown collection type: ${collection.type}`);
 | 
			
		||||
			}
 | 
			
		||||
		if (isCollectionOrOrderedCollection(collection)) {
 | 
			
		||||
			return collection;
 | 
			
		||||
		} else {
 | 
			
		||||
			throw new Error(`unknown collection type: ${collection.type}`);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return collection;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public async resolve(value: any): Promise<IObject> {
 | 
			
		||||
	public async resolve(value: string | IObject): Promise<IObject> {
 | 
			
		||||
		if (value == null) {
 | 
			
		||||
			throw new Error('resolvee is null (or undefined)');
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,5 @@
 | 
			
		|||
export type obj = { [x: string]: any };
 | 
			
		||||
export type ApObject = IObject | string | (IObject | string)[];
 | 
			
		||||
 | 
			
		||||
export interface IObject {
 | 
			
		||||
	'@context': string | obj | obj[];
 | 
			
		||||
| 
						 | 
				
			
			@ -6,9 +7,9 @@ export interface IObject {
 | 
			
		|||
	id?: string;
 | 
			
		||||
	summary?: string;
 | 
			
		||||
	published?: string;
 | 
			
		||||
	cc?: IObject | string | (IObject | string)[];
 | 
			
		||||
	to?: IObject | string | (IObject | string)[];
 | 
			
		||||
	attributedTo: IObject | string | (IObject | string)[];
 | 
			
		||||
	cc?: ApObject;
 | 
			
		||||
	to?: ApObject;
 | 
			
		||||
	attributedTo: ApObject;
 | 
			
		||||
	attachment?: any[];
 | 
			
		||||
	inReplyTo?: any;
 | 
			
		||||
	replies?: ICollection;
 | 
			
		||||
| 
						 | 
				
			
			@ -26,7 +27,7 @@ export interface IObject {
 | 
			
		|||
/**
 | 
			
		||||
 * Get array of ActivityStreams Objects id
 | 
			
		||||
 */
 | 
			
		||||
export function getApIds(value: IObject | string | (IObject | string)[] | undefined): string[] {
 | 
			
		||||
export function getApIds(value: ApObject | undefined): string[] {
 | 
			
		||||
	if (value == null) return [];
 | 
			
		||||
	const array = Array.isArray(value) ? value : [value];
 | 
			
		||||
	return array.map(x => getApId(x));
 | 
			
		||||
| 
						 | 
				
			
			@ -35,7 +36,7 @@ export function getApIds(value: IObject | string | (IObject | string)[] | undefi
 | 
			
		|||
/**
 | 
			
		||||
 * Get first ActivityStreams Object id
 | 
			
		||||
 */
 | 
			
		||||
export function getOneApId(value: IObject | string | (IObject | string)[]): string {
 | 
			
		||||
export function getOneApId(value: ApObject): string {
 | 
			
		||||
	const firstOne = Array.isArray(value) ? value[0] : value;
 | 
			
		||||
	return getApId(firstOne);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -59,13 +60,13 @@ export interface IActivity extends IObject {
 | 
			
		|||
export interface ICollection extends IObject {
 | 
			
		||||
	type: 'Collection';
 | 
			
		||||
	totalItems: number;
 | 
			
		||||
	items: IObject | string | IObject[] | string[];
 | 
			
		||||
	items: ApObject;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IOrderedCollection extends IObject {
 | 
			
		||||
	type: 'OrderedCollection';
 | 
			
		||||
	totalItems: number;
 | 
			
		||||
	orderedItems: IObject | string | IObject[] | string[];
 | 
			
		||||
	orderedItems: ApObject;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const validPost = ['Note', 'Question', 'Article', 'Audio', 'Document', 'Image', 'Page', 'Video'];
 | 
			
		||||
| 
						 | 
				
			
			@ -170,18 +171,15 @@ export interface IBlock extends IActivity {
 | 
			
		|||
	type: 'Block';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type Object =
 | 
			
		||||
	ICollection |
 | 
			
		||||
	IOrderedCollection |
 | 
			
		||||
	ICreate |
 | 
			
		||||
	IDelete |
 | 
			
		||||
	IUpdate |
 | 
			
		||||
	IUndo |
 | 
			
		||||
	IFollow |
 | 
			
		||||
	IAccept |
 | 
			
		||||
	IReject |
 | 
			
		||||
	IAdd |
 | 
			
		||||
	IRemove |
 | 
			
		||||
	ILike |
 | 
			
		||||
	IAnnounce |
 | 
			
		||||
	IBlock;
 | 
			
		||||
export const isCreate = (object: IObject): object is ICreate => object.type === 'Create';
 | 
			
		||||
export const isDelete = (object: IObject): object is IDelete => object.type === 'Delete';
 | 
			
		||||
export const isUpdate = (object: IObject): object is IUpdate => object.type === 'Update';
 | 
			
		||||
export const isUndo = (object: IObject): object is IUndo => object.type === 'Undo';
 | 
			
		||||
export const isFollow = (object: IObject): object is IFollow => object.type === 'Follow';
 | 
			
		||||
export const isAccept = (object: IObject): object is IAccept => object.type === 'Accept';
 | 
			
		||||
export const isReject = (object: IObject): object is IReject => object.type === 'Reject';
 | 
			
		||||
export const isAdd = (object: IObject): object is IAdd => object.type === 'Add';
 | 
			
		||||
export const isRemove = (object: IObject): object is IRemove => object.type === 'Remove';
 | 
			
		||||
export const isLike = (object: IObject): object is ILike => object.type === 'Like';
 | 
			
		||||
export const isAnnounce = (object: IObject): object is IAnnounce => object.type === 'Announce';
 | 
			
		||||
export const isBlock = (object: IObject): object is IBlock => object.type === 'Block';
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue