@rmdes/indiekit-endpoint-activitypub 1.0.26 → 1.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,6 +12,9 @@ export function dashboardController(mountPath) {
12
12
  const followingCollection = application?.collections?.get("ap_following");
13
13
  const activitiesCollection =
14
14
  application?.collections?.get("ap_activities");
15
+ const featuredCollection = application?.collections?.get("ap_featured");
16
+ const featuredTagsCollection =
17
+ application?.collections?.get("ap_featured_tags");
15
18
 
16
19
  const followerCount = followersCollection
17
20
  ? await followersCollection.countDocuments()
@@ -19,6 +22,12 @@ export function dashboardController(mountPath) {
19
22
  const followingCount = followingCollection
20
23
  ? await followingCollection.countDocuments()
21
24
  : 0;
25
+ const pinnedCount = featuredCollection
26
+ ? await featuredCollection.countDocuments()
27
+ : 0;
28
+ const tagCount = featuredTagsCollection
29
+ ? await featuredTagsCollection.countDocuments()
30
+ : 0;
22
31
 
23
32
  const recentActivities = activitiesCollection
24
33
  ? await activitiesCollection
@@ -38,6 +47,8 @@ export function dashboardController(mountPath) {
38
47
  title: response.locals.__("activitypub.title"),
39
48
  followerCount,
40
49
  followingCount,
50
+ pinnedCount,
51
+ tagCount,
41
52
  recentActivities,
42
53
  refollowStatus,
43
54
  mountPath,
@@ -85,9 +85,14 @@ export function registerInboxListeners(inboxChain, options) {
85
85
  });
86
86
  })
87
87
  .on(Undo, async (ctx, undo) => {
88
- const actorObj = await undo.getActor();
89
- const actorUrl = actorObj?.id?.href || "";
90
- const inner = await undo.getObject();
88
+ const actorUrl = undo.actorId?.href || "";
89
+ let inner;
90
+ try {
91
+ inner = await undo.getObject();
92
+ } catch {
93
+ // Inner activity not dereferenceable — can't determine what was undone
94
+ return;
95
+ }
91
96
 
92
97
  if (inner instanceof Follow) {
93
98
  await collections.ap_followers.deleteOne({ actorUrl });
@@ -98,14 +103,14 @@ export function registerInboxListeners(inboxChain, options) {
98
103
  summary: `${actorUrl} unfollowed you`,
99
104
  });
100
105
  } else if (inner instanceof Like) {
101
- const objectId = (await inner.getObject())?.id?.href || "";
106
+ const objectId = inner.objectId?.href || "";
102
107
  await collections.ap_activities.deleteOne({
103
108
  type: "Like",
104
109
  actorUrl,
105
110
  objectUrl: objectId,
106
111
  });
107
112
  } else if (inner instanceof Announce) {
108
- const objectId = (await inner.getObject())?.id?.href || "";
113
+ const objectId = inner.objectId?.href || "";
109
114
  await collections.ap_activities.deleteOne({
110
115
  type: "Announce",
111
116
  actorUrl,
@@ -194,18 +199,27 @@ export function registerInboxListeners(inboxChain, options) {
194
199
  }
195
200
  })
196
201
  .on(Like, async (ctx, like) => {
197
- const objectId = (await like.getObject())?.id?.href || "";
202
+ // Use .objectId to get the URL without dereferencing the remote object.
203
+ // Calling .getObject() would trigger an HTTP fetch to the remote server,
204
+ // which fails with 404 when the server has Authorized Fetch (Secure Mode)
205
+ // enabled — causing pointless retries and log spam.
206
+ const objectId = like.objectId?.href || "";
198
207
 
199
208
  // Only log likes of our own content
200
209
  const pubUrl = collections._publicationUrl;
201
210
  if (!objectId || (pubUrl && !objectId.startsWith(pubUrl))) return;
202
211
 
203
- const actorObj = await like.getActor();
204
- const actorUrl = actorObj?.id?.href || "";
205
- const actorName =
206
- actorObj?.name?.toString() ||
207
- actorObj?.preferredUsername?.toString() ||
208
- actorUrl;
212
+ const actorUrl = like.actorId?.href || "";
213
+ let actorName = actorUrl;
214
+ try {
215
+ const actorObj = await like.getActor();
216
+ actorName =
217
+ actorObj?.name?.toString() ||
218
+ actorObj?.preferredUsername?.toString() ||
219
+ actorUrl;
220
+ } catch {
221
+ /* actor not dereferenceable — use URL */
222
+ }
209
223
 
210
224
  await logActivity(collections, storeRawActivities, {
211
225
  direction: "inbound",
@@ -217,18 +231,24 @@ export function registerInboxListeners(inboxChain, options) {
217
231
  });
218
232
  })
219
233
  .on(Announce, async (ctx, announce) => {
220
- const objectId = (await announce.getObject())?.id?.href || "";
234
+ // Use .objectId no remote fetch needed (see Like handler comment)
235
+ const objectId = announce.objectId?.href || "";
221
236
 
222
237
  // Only log boosts of our own content
223
238
  const pubUrl = collections._publicationUrl;
224
239
  if (!objectId || (pubUrl && !objectId.startsWith(pubUrl))) return;
225
240
 
226
- const actorObj = await announce.getActor();
227
- const actorUrl = actorObj?.id?.href || "";
228
- const actorName =
229
- actorObj?.name?.toString() ||
230
- actorObj?.preferredUsername?.toString() ||
231
- actorUrl;
241
+ const actorUrl = announce.actorId?.href || "";
242
+ let actorName = actorUrl;
243
+ try {
244
+ const actorObj = await announce.getActor();
245
+ actorName =
246
+ actorObj?.name?.toString() ||
247
+ actorObj?.preferredUsername?.toString() ||
248
+ actorUrl;
249
+ } catch {
250
+ /* actor not dereferenceable — use URL */
251
+ }
232
252
 
233
253
  await logActivity(collections, storeRawActivities, {
234
254
  direction: "inbound",
@@ -240,11 +260,23 @@ export function registerInboxListeners(inboxChain, options) {
240
260
  });
241
261
  })
242
262
  .on(Create, async (ctx, create) => {
243
- const object = await create.getObject();
263
+ let object;
264
+ try {
265
+ object = await create.getObject();
266
+ } catch {
267
+ // Remote object not dereferenceable (Authorized Fetch, deleted, etc.)
268
+ return;
269
+ }
244
270
  if (!object) return;
245
271
 
246
- const actorObj = await create.getActor();
247
- const actorUrl = actorObj?.id?.href || "";
272
+ const actorUrl = create.actorId?.href || "";
273
+ let actorObj;
274
+ try {
275
+ actorObj = await create.getActor();
276
+ } catch {
277
+ // Actor not dereferenceable — use URL as fallback
278
+ actorObj = null;
279
+ }
248
280
  const actorName =
249
281
  actorObj?.name?.toString() ||
250
282
  actorObj?.preferredUsername?.toString() ||
@@ -284,7 +316,7 @@ export function registerInboxListeners(inboxChain, options) {
284
316
  });
285
317
  })
286
318
  .on(Delete, async (ctx, del) => {
287
- const objectId = (await del.getObject())?.id?.href || "";
319
+ const objectId = del.objectId?.href || "";
288
320
  if (objectId) {
289
321
  await collections.ap_activities.deleteMany({ objectUrl: objectId });
290
322
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rmdes/indiekit-endpoint-activitypub",
3
- "version": "1.0.26",
3
+ "version": "1.0.27",
4
4
  "description": "ActivityPub federation endpoint for Indiekit via Fedify. Adds full fediverse support: actor, inbox, outbox, followers, following, syndication, and Mastodon migration.",
5
5
  "keywords": [
6
6
  "indiekit",
@@ -22,6 +22,14 @@
22
22
  title: __("activitypub.activities"),
23
23
  url: mountPath + "/admin/activities"
24
24
  },
25
+ {
26
+ title: pinnedCount + " " + __("activitypub.featured"),
27
+ url: mountPath + "/admin/featured"
28
+ },
29
+ {
30
+ title: tagCount + " " + __("activitypub.featuredTags"),
31
+ url: mountPath + "/admin/tags"
32
+ },
25
33
  {
26
34
  title: __("activitypub.profile.title"),
27
35
  url: mountPath + "/admin/profile"