@rmdes/indiekit-endpoint-activitypub 2.0.18 → 2.0.19

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.
@@ -153,7 +153,15 @@ export function postDetailController(mountPath, plugin) {
153
153
 
154
154
  let object = null;
155
155
 
156
- if (!timelineItem) {
156
+ // If stored item has no media, re-fetch from Fedify to pick up
157
+ // attachments that were missed before the async iteration fix.
158
+ const storedHasNoMedia =
159
+ timelineItem &&
160
+ (!timelineItem.photo || timelineItem.photo.length === 0) &&
161
+ (!timelineItem.video || timelineItem.video.length === 0) &&
162
+ (!timelineItem.audio || timelineItem.audio.length === 0);
163
+
164
+ if (!timelineItem || storedHasNoMedia) {
157
165
  // Not in local timeline — fetch via lookupObject
158
166
  const handle = plugin.options.actor.handle;
159
167
  const ctx = plugin._federation.createContext(
@@ -185,7 +193,8 @@ export function postDetailController(mountPath, plugin) {
185
193
  }
186
194
  }
187
195
 
188
- if (!object) {
196
+ if (!object && !storedHasNoMedia) {
197
+ // Truly not found (no local item either)
189
198
  return response.status(404).render("activitypub-post-detail", {
190
199
  title: response.locals.__("activitypub.reader.post.title"),
191
200
  notFound: true, objectUrl, mountPath,
@@ -194,34 +203,57 @@ export function postDetailController(mountPath, plugin) {
194
203
  });
195
204
  }
196
205
 
197
- // If it's an actor (Person, Service, Application), redirect to profile
198
- if (
199
- object instanceof Person ||
200
- object instanceof Service ||
201
- object instanceof Application
202
- ) {
203
- return response.redirect(
204
- `${mountPath}/admin/reader/profile?url=${encodeURIComponent(objectUrl)}`,
205
- );
206
- }
206
+ if (object) {
207
+ // If it's an actor (Person, Service, Application), redirect to profile
208
+ if (
209
+ object instanceof Person ||
210
+ object instanceof Service ||
211
+ object instanceof Application
212
+ ) {
213
+ return response.redirect(
214
+ `${mountPath}/admin/reader/profile?url=${encodeURIComponent(objectUrl)}`,
215
+ );
216
+ }
207
217
 
208
- // Extract timeline item data from the Fedify object
209
- if (object instanceof Note || object instanceof Article) {
210
- try {
211
- timelineItem = await extractObjectData(object);
212
- } catch (error) {
213
- console.error(`[post-detail] extractObjectData failed for ${objectUrl}:`, error.message);
214
- return response.status(500).render("error", {
218
+ // Extract timeline item data from the Fedify object
219
+ if (object instanceof Note || object instanceof Article) {
220
+ try {
221
+ const freshItem = await extractObjectData(object);
222
+
223
+ // If re-fetch found media that the stored item was missing, update MongoDB
224
+ if (storedHasNoMedia && timelineCol) {
225
+ const hasMedia =
226
+ (freshItem.photo && freshItem.photo.length > 0) ||
227
+ (freshItem.video && freshItem.video.length > 0) ||
228
+ (freshItem.audio && freshItem.audio.length > 0);
229
+ if (hasMedia) {
230
+ await timelineCol.updateOne(
231
+ { $or: [{ uid: objectUrl }, { url: objectUrl }] },
232
+ { $set: { photo: freshItem.photo, video: freshItem.video, audio: freshItem.audio } },
233
+ ).catch(() => {});
234
+ }
235
+ }
236
+
237
+ timelineItem = freshItem;
238
+ } catch (error) {
239
+ // If re-extraction fails but we have a stored item, use it
240
+ if (!storedHasNoMedia) {
241
+ console.error(`[post-detail] extractObjectData failed for ${objectUrl}:`, error.message);
242
+ return response.status(500).render("error", {
243
+ title: "Error",
244
+ content: "Failed to extract post data",
245
+ });
246
+ }
247
+ // storedHasNoMedia=true means timelineItem still has the stored data
248
+ }
249
+ } else if (!storedHasNoMedia) {
250
+ return response.status(400).render("error", {
215
251
  title: "Error",
216
- content: "Failed to extract post data",
252
+ content: "Object is not a viewable post (must be Note or Article)",
217
253
  });
218
254
  }
219
- } else {
220
- return response.status(400).render("error", {
221
- title: "Error",
222
- content: "Object is not a viewable post (must be Note or Article)",
223
- });
224
255
  }
256
+ // If object is null and storedHasNoMedia, we fall through with the stored timelineItem
225
257
  }
226
258
 
227
259
  // Build interaction state for this post
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rmdes/indiekit-endpoint-activitypub",
3
- "version": "2.0.18",
3
+ "version": "2.0.19",
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",