@rmdes/indiekit-endpoint-conversations 2.1.4 → 2.1.6

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.
@@ -120,20 +120,16 @@ async function backfillMissingAvatars(indiekit, stateCollection) {
120
120
  const state = await stateCollection.findOne({ _id: "poll_cursors" });
121
121
  if (state?.avatar_backfill_complete) return;
122
122
 
123
- // Find conversation_items with empty author.photo
124
- const itemsWithoutPhoto = await itemsCollection
125
- .find({
126
- $or: [
127
- { "author.photo": "" },
128
- { "author.photo": null },
129
- { "author.photo": { $exists: false } },
130
- ],
131
- })
132
- .limit(200)
133
- .toArray();
134
-
135
- if (itemsWithoutPhoto.length === 0) {
136
- // Mark backfill as complete so we don't query every cycle
123
+ // Get unique actor URLs with empty photos (deduplicate)
124
+ const actorUrls = await itemsCollection.distinct("author.url", {
125
+ $or: [
126
+ { "author.photo": "" },
127
+ { "author.photo": null },
128
+ { "author.photo": { $exists: false } },
129
+ ],
130
+ });
131
+
132
+ if (actorUrls.length === 0) {
137
133
  await stateCollection.findOneAndUpdate(
138
134
  { _id: "poll_cursors" },
139
135
  { $set: { avatar_backfill_complete: true } },
@@ -144,13 +140,12 @@ async function backfillMissingAvatars(indiekit, stateCollection) {
144
140
 
145
141
  let updated = 0;
146
142
 
147
- for (const item of itemsWithoutPhoto) {
148
- const actorUrl = item.author?.url;
143
+ for (const actorUrl of actorUrls) {
149
144
  if (!actorUrl) continue;
150
145
 
151
146
  let photo = "";
152
147
 
153
- // Strategy 1: Check ap_notifications (most reliable — has actorPhoto for all interaction types)
148
+ // Strategy 1: Check ap_notifications
154
149
  if (!photo && notificationsCollection) {
155
150
  try {
156
151
  const notification = await notificationsCollection.findOne({
@@ -161,7 +156,7 @@ async function backfillMissingAvatars(indiekit, stateCollection) {
161
156
  } catch { /* ignore */ }
162
157
  }
163
158
 
164
- // Strategy 2: Check ap_activities for actorAvatar (new field from inbox handler fix)
159
+ // Strategy 2: Check ap_activities for actorAvatar
165
160
  if (!photo && activitiesCollection) {
166
161
  try {
167
162
  const activity = await activitiesCollection.findOne({
@@ -180,8 +175,20 @@ async function backfillMissingAvatars(indiekit, stateCollection) {
180
175
  } catch { /* ignore */ }
181
176
  }
182
177
 
183
- // Strategy 4: Fetch actor profile from the fediverse (last resort)
184
- if (!photo && actorUrl) {
178
+ // Strategy 4a: Use AP endpoint's signed fetch (handles Authorized Fetch servers)
179
+ if (!photo) {
180
+ const resolveAvatar =
181
+ indiekit.config?.application?.resolveActorAvatar ||
182
+ indiekit.resolveActorAvatar;
183
+ if (resolveAvatar) {
184
+ try {
185
+ photo = await resolveAvatar(actorUrl);
186
+ } catch { /* ignore */ }
187
+ }
188
+ }
189
+
190
+ // Strategy 4b: Plain fetch fallback (AP endpoint not installed)
191
+ if (!photo) {
185
192
  try {
186
193
  const resp = await fetch(actorUrl, {
187
194
  headers: { Accept: "application/activity+json, application/ld+json" },
@@ -210,18 +217,16 @@ async function backfillMissingAvatars(indiekit, stateCollection) {
210
217
 
211
218
  if (updated > 0) {
212
219
  console.info(
213
- `[Conversations] Avatar backfill: updated ${updated} actors with photos`,
220
+ `[Conversations] Avatar backfill: updated ${updated}/${actorUrls.length} actors with photos`,
214
221
  );
215
222
  }
216
223
 
217
- // If fewer than 200 items found, backfill is complete
218
- if (itemsWithoutPhoto.length < 200) {
219
- await stateCollection.findOneAndUpdate(
220
- { _id: "poll_cursors" },
221
- { $set: { avatar_backfill_complete: true } },
222
- { upsert: true },
223
- );
224
- }
224
+ // Mark complete all actors have been attempted
225
+ await stateCollection.findOneAndUpdate(
226
+ { _id: "poll_cursors" },
227
+ { $set: { avatar_backfill_complete: true } },
228
+ { upsert: true },
229
+ );
225
230
  } catch (error) {
226
231
  // Non-critical — log and continue
227
232
  console.warn("[Conversations] Avatar backfill error:", error.message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rmdes/indiekit-endpoint-conversations",
3
- "version": "2.1.4",
3
+ "version": "2.1.6",
4
4
  "description": "Conversation aggregation endpoint for Indiekit. Backend enrichment service that polls Mastodon/Bluesky notifications and serves JF2-compatible data for the interactions page.",
5
5
  "keywords": [
6
6
  "indiekit",