summaryrefslogtreecommitdiff
path: root/packages/backend
diff options
context:
space:
mode:
authorHazelnoot <acomputerdog@gmail.com>2024-10-15 21:24:57 +0000
committerHazelnoot <acomputerdog@gmail.com>2024-10-15 21:24:57 +0000
commit16847ba491ef4dffd0daa6b83e8da2c2bc1c1e76 (patch)
tree1f7e80163528a0d4d088bfc966ed9f0f72e6b567 /packages/backend
parentmerge: feat: Allow users to view pending follow requests they sent (!663) (diff)
parentfactor out tuple logic into from-tuple.ts (diff)
downloadsharkey-16847ba491ef4dffd0daa6b83e8da2c2bc1c1e76.tar.gz
sharkey-16847ba491ef4dffd0daa6b83e8da2c2bc1c1e76.tar.bz2
sharkey-16847ba491ef4dffd0daa6b83e8da2c2bc1c1e76.zip
merge: Restore report forwarding to Pleroma (resolves #641) (!690)
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>
Diffstat (limited to 'packages/backend')
-rw-r--r--packages/backend/src/core/activitypub/ApDbResolverService.ts6
-rw-r--r--packages/backend/src/core/activitypub/ApInboxService.ts45
-rw-r--r--packages/backend/src/core/activitypub/ApRendererService.ts3
-rw-r--r--packages/backend/src/core/activitypub/ApResolverService.ts6
-rw-r--r--packages/backend/src/core/activitypub/type.ts13
-rw-r--r--packages/backend/src/misc/from-tuple.ts7
-rw-r--r--packages/backend/test/unit/misc/from-tuple.ts13
7 files changed, 65 insertions, 28 deletions
diff --git a/packages/backend/src/core/activitypub/ApDbResolverService.ts b/packages/backend/src/core/activitypub/ApDbResolverService.ts
index 062af39732..2cb558dbff 100644
--- a/packages/backend/src/core/activitypub/ApDbResolverService.ts
+++ b/packages/backend/src/core/activitypub/ApDbResolverService.ts
@@ -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) {
diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts
index bce67a458f..fd4b3d8d6f 100644
--- a/packages/backend/src/core/activitypub/ApInboxService.ts
+++ b/packages/backend/src/core/activitypub/ApInboxService.ts
@@ -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;
diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts
index 499a163d6c..ff6f462b4c 100644
--- a/packages/backend/src/core/activitypub/ApRendererService.ts
+++ b/packages/backend/src/core/activitypub/ApRendererService.ts
@@ -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],
};
}
diff --git a/packages/backend/src/core/activitypub/ApResolverService.ts b/packages/backend/src/core/activitypub/ApResolverService.ts
index b047a6c59b..fd69c7269b 100644
--- a/packages/backend/src/core/activitypub/ApResolverService.ts
+++ b/packages/backend/src/core/activitypub/ApResolverService.ts
@@ -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;
}
diff --git a/packages/backend/src/core/activitypub/type.ts b/packages/backend/src/core/activitypub/type.ts
index 2f58825de1..144793c214 100644
--- a/packages/backend/src/core/activitypub/type.ts
+++ b/packages/backend/src/core/activitypub/type.ts
@@ -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?: {
diff --git a/packages/backend/src/misc/from-tuple.ts b/packages/backend/src/misc/from-tuple.ts
new file mode 100644
index 0000000000..366b1e310f
--- /dev/null
+++ b/packages/backend/src/misc/from-tuple.ts
@@ -0,0 +1,7 @@
+export function fromTuple<T>(value: T | [T]): T {
+ if (Array.isArray(value)) {
+ return value[0];
+ }
+
+ return value;
+}
diff --git a/packages/backend/test/unit/misc/from-tuple.ts b/packages/backend/test/unit/misc/from-tuple.ts
new file mode 100644
index 0000000000..b523cb5782
--- /dev/null
+++ b/packages/backend/test/unit/misc/from-tuple.ts
@@ -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');
+ });
+});