mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-10-31 05:24:13 +00:00 
			
		
		
		
	View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/690 Closes #641 Approved-by: dakkar <dakkar@thenautilus.net> Approved-by: Marie <github@yuugi.dev>
This commit is contained in:
		
						commit
						16847ba491
					
				
					 7 changed files with 65 additions and 28 deletions
				
			
		|  | @ -59,7 +59,7 @@ export class ApDbResolverService implements OnApplicationShutdown { | |||
| 	} | ||||
| 
 | ||||
| 	@bindThis | ||||
| 	public parseUri(value: string | IObject): UriParseResult { | ||||
| 	public parseUri(value: string | IObject | [string | IObject]): UriParseResult { | ||||
| 		const separator = '/'; | ||||
| 
 | ||||
| 		const uri = new URL(getApId(value)); | ||||
|  | @ -78,7 +78,7 @@ export class ApDbResolverService implements OnApplicationShutdown { | |||
| 	 * AP Note => Misskey Note in DB | ||||
| 	 */ | ||||
| 	@bindThis | ||||
| 	public async getNoteFromApId(value: string | IObject): Promise<MiNote | null> { | ||||
| 	public async getNoteFromApId(value: string | IObject | [string | IObject]): Promise<MiNote | null> { | ||||
| 		const parsed = this.parseUri(value); | ||||
| 
 | ||||
| 		if (parsed.local) { | ||||
|  | @ -98,7 +98,7 @@ export class ApDbResolverService implements OnApplicationShutdown { | |||
| 	 * AP Person => Misskey User in DB | ||||
| 	 */ | ||||
| 	@bindThis | ||||
| 	public async getUserFromApId(value: string | IObject): Promise<MiLocalUser | MiRemoteUser | null> { | ||||
| 	public async getUserFromApId(value: string | IObject | [string | IObject]): Promise<MiLocalUser | MiRemoteUser | null> { | ||||
| 		const parsed = this.parseUri(value); | ||||
| 
 | ||||
| 		if (parsed.local) { | ||||
|  |  | |||
|  | @ -41,6 +41,7 @@ import { ApPersonService } from './models/ApPersonService.js'; | |||
| import { ApQuestionService } from './models/ApQuestionService.js'; | ||||
| import type { Resolver } from './ApResolverService.js'; | ||||
| import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IReject, IRemove, IUndo, IUpdate, IMove, IPost } from './type.js'; | ||||
| import { fromTuple } from '@/misc/from-tuple.js'; | ||||
| 
 | ||||
| @Injectable() | ||||
| export class ApInboxService { | ||||
|  | @ -253,7 +254,8 @@ export class ApInboxService { | |||
| 		} | ||||
| 
 | ||||
| 		if (activity.target === actor.featured) { | ||||
| 			const note = await this.apNoteService.resolveNote(activity.object); | ||||
| 			const object = fromTuple(activity.object); | ||||
| 			const note = await this.apNoteService.resolveNote(object); | ||||
| 			if (note == null) return 'note not found'; | ||||
| 			await this.notePiningService.addPinned(actor, note.id); | ||||
| 			return; | ||||
|  | @ -270,11 +272,12 @@ export class ApInboxService { | |||
| 
 | ||||
| 		const resolver = this.apResolverService.createResolver(); | ||||
| 
 | ||||
| 		if (!activity.object) return 'skip: activity has no object property'; | ||||
| 		const targetUri = getApId(activity.object); | ||||
| 		const activityObject = fromTuple(activity.object); | ||||
| 		if (!activityObject) return 'skip: activity has no object property'; | ||||
| 		const targetUri = getApId(activityObject); | ||||
| 		if (targetUri.startsWith('bear:')) return 'skip: bearcaps url not supported.'; | ||||
| 
 | ||||
| 		const target = await resolver.resolve(activity.object).catch(e => { | ||||
| 		const target = await resolver.resolve(activityObject).catch(e => { | ||||
| 			this.logger.error(`Resolution failed: ${e}`); | ||||
| 			return e; | ||||
| 		}); | ||||
|  | @ -370,29 +373,30 @@ export class ApInboxService { | |||
| 
 | ||||
| 		this.logger.info(`Create: ${uri}`); | ||||
| 
 | ||||
| 		if (!activity.object) return 'skip: activity has no object property'; | ||||
| 		const targetUri = getApId(activity.object); | ||||
| 		const activityObject = fromTuple(activity.object); | ||||
| 		if (!activityObject) return 'skip: activity has no object property'; | ||||
| 		const targetUri = getApId(activityObject); | ||||
| 		if (targetUri.startsWith('bear:')) return 'skip: bearcaps url not supported.'; | ||||
| 
 | ||||
| 		// copy audiences between activity <=> object.
 | ||||
| 		if (typeof activity.object === 'object') { | ||||
| 			const to = unique(concat([toArray(activity.to), toArray(activity.object.to)])); | ||||
| 			const cc = unique(concat([toArray(activity.cc), toArray(activity.object.cc)])); | ||||
| 		if (typeof activityObject === 'object') { | ||||
| 			const to = unique(concat([toArray(activity.to), toArray(activityObject.to)])); | ||||
| 			const cc = unique(concat([toArray(activity.cc), toArray(activityObject.cc)])); | ||||
| 
 | ||||
| 			activity.to = to; | ||||
| 			activity.cc = cc; | ||||
| 			activity.object.to = to; | ||||
| 			activity.object.cc = cc; | ||||
| 			activityObject.to = to; | ||||
| 			activityObject.cc = cc; | ||||
| 		} | ||||
| 
 | ||||
| 		// If there is no attributedTo, use Activity actor.
 | ||||
| 		if (typeof activity.object === 'object' && !activity.object.attributedTo) { | ||||
| 			activity.object.attributedTo = activity.actor; | ||||
| 		if (typeof activityObject === 'object' && !activityObject.attributedTo) { | ||||
| 			activityObject.attributedTo = activity.actor; | ||||
| 		} | ||||
| 
 | ||||
| 		const resolver = this.apResolverService.createResolver(); | ||||
| 
 | ||||
| 		const object = await resolver.resolve(activity.object).catch(e => { | ||||
| 		const object = await resolver.resolve(activityObject).catch(e => { | ||||
| 			this.logger.error(`Resolution failed: ${e}`); | ||||
| 			throw e; | ||||
| 		}); | ||||
|  | @ -448,15 +452,15 @@ export class ApInboxService { | |||
| 		// 削除対象objectのtype
 | ||||
| 		let formerType: string | undefined; | ||||
| 
 | ||||
| 		if (typeof activity.object === 'string') { | ||||
| 		const activityObject = fromTuple(activity.object); | ||||
| 		if (typeof activityObject === 'string') { | ||||
| 			// typeが不明だけど、どうせ消えてるのでremote resolveしない
 | ||||
| 			formerType = undefined; | ||||
| 		} else { | ||||
| 			const object = activity.object; | ||||
| 			if (isTombstone(object)) { | ||||
| 				formerType = toSingle(object.formerType); | ||||
| 			if (isTombstone(activityObject)) { | ||||
| 				formerType = toSingle(activityObject.formerType); | ||||
| 			} else { | ||||
| 				formerType = toSingle(object.type); | ||||
| 				formerType = toSingle(activityObject.type); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
|  | @ -616,7 +620,8 @@ export class ApInboxService { | |||
| 		} | ||||
| 
 | ||||
| 		if (activity.target === actor.featured) { | ||||
| 			const note = await this.apNoteService.resolveNote(activity.object); | ||||
| 			const activityObject = fromTuple(activity.object); | ||||
| 			const note = await this.apNoteService.resolveNote(activityObject); | ||||
| 			if (note == null) return 'note not found'; | ||||
| 			await this.notePiningService.removePinned(actor, note.id); | ||||
| 			return; | ||||
|  |  | |||
|  | @ -199,7 +199,8 @@ export class ApRendererService { | |||
| 			type: 'Flag', | ||||
| 			actor: this.userEntityService.genLocalUserUri(user.id), | ||||
| 			content, | ||||
| 			object, | ||||
| 			// This MUST be an array for Pleroma compatibility: https://activitypub.software/TransFem-org/Sharkey/-/issues/641#note_7301
 | ||||
| 			object: [object], | ||||
| 		}; | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ import { ApDbResolverService } from './ApDbResolverService.js'; | |||
| import { ApRendererService } from './ApRendererService.js'; | ||||
| import { ApRequestService } from './ApRequestService.js'; | ||||
| import type { IObject, ICollection, IOrderedCollection } from './type.js'; | ||||
| import { fromTuple } from '@/misc/from-tuple.js'; | ||||
| 
 | ||||
| export class Resolver { | ||||
| 	private history: Set<string>; | ||||
|  | @ -67,7 +68,10 @@ export class Resolver { | |||
| 	} | ||||
| 
 | ||||
| 	@bindThis | ||||
| 	public async resolve(value: string | IObject): Promise<IObject> { | ||||
| 	public async resolve(value: string | IObject | [string | IObject]): Promise<IObject> { | ||||
| 		// eslint-disable-next-line no-param-reassign
 | ||||
| 		value = fromTuple(value); | ||||
| 
 | ||||
| 		if (typeof value !== 'string') { | ||||
| 			return value; | ||||
| 		} | ||||
|  |  | |||
|  | @ -3,6 +3,8 @@ | |||
|  * SPDX-License-Identifier: AGPL-3.0-only | ||||
|  */ | ||||
| 
 | ||||
| import { fromTuple } from '@/misc/from-tuple.js'; | ||||
| 
 | ||||
| export type Obj = { [x: string]: any }; | ||||
| export type ApObject = IObject | string | (IObject | string)[]; | ||||
| 
 | ||||
|  | @ -52,10 +54,13 @@ export function getOneApId(value: ApObject): string { | |||
| /** | ||||
|  * Get ActivityStreams Object id | ||||
|  */ | ||||
| export function getApId(value: string | IObject): string { | ||||
| export function getApId(value: string | IObject | [string | IObject]): string { | ||||
| 	// eslint-disable-next-line no-param-reassign
 | ||||
| 	value = fromTuple(value); | ||||
| 
 | ||||
| 	if (typeof value === 'string') return value; | ||||
| 	if (typeof value.id === 'string') return value.id; | ||||
| 	throw new Error('cannot detemine id'); | ||||
| 	throw new Error('cannot determine id'); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  | @ -84,7 +89,9 @@ export function getApHrefNullable(value: string | IObject | undefined): string | | |||
| export interface IActivity extends IObject { | ||||
| 	//type: 'Activity';
 | ||||
| 	actor: IObject | string; | ||||
| 	object: IObject | string; | ||||
| 	// ActivityPub spec allows for arrays: https://www.w3.org/TR/activitystreams-vocabulary/#properties
 | ||||
| 	// Misskey can only handle one value, so we use a tuple for that case.
 | ||||
| 	object: IObject | string | [IObject | string] ; | ||||
| 	target?: IObject | string; | ||||
| 	/** LD-Signature */ | ||||
| 	signature?: { | ||||
|  |  | |||
							
								
								
									
										7
									
								
								packages/backend/src/misc/from-tuple.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								packages/backend/src/misc/from-tuple.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| export function fromTuple<T>(value: T | [T]): T { | ||||
| 	if (Array.isArray(value)) { | ||||
| 		return value[0]; | ||||
| 	} | ||||
| 
 | ||||
| 	return value; | ||||
| } | ||||
							
								
								
									
										13
									
								
								packages/backend/test/unit/misc/from-tuple.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								packages/backend/test/unit/misc/from-tuple.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,13 @@ | |||
| import { fromTuple } from '@/misc/from-tuple.js'; | ||||
| 
 | ||||
| describe(fromTuple, () => { | ||||
| 	it('should return value when value is not an array', () => { | ||||
| 		const value = fromTuple('abc'); | ||||
| 		expect(value).toBe('abc'); | ||||
| 	}); | ||||
| 
 | ||||
| 	it('should return first element when value is an array', () => { | ||||
| 		const value = fromTuple(['abc']); | ||||
| 		expect(value).toBe('abc'); | ||||
| 	}); | ||||
| }); | ||||
		Loading…
	
	Add table
		
		Reference in a new issue