@rmdes/indiekit-endpoint-microsub 1.0.21 → 1.0.22

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.
package/index.js CHANGED
@@ -6,7 +6,7 @@ import { microsubController } from "./lib/controllers/microsub.js";
6
6
  import { readerController } from "./lib/controllers/reader.js";
7
7
  import { handleMediaProxy } from "./lib/media/proxy.js";
8
8
  import { startScheduler, stopScheduler } from "./lib/polling/scheduler.js";
9
- import { createIndexes } from "./lib/storage/items.js";
9
+ import { cleanupAllReadItems, createIndexes } from "./lib/storage/items.js";
10
10
  import { webmentionReceiver } from "./lib/webmention/receiver.js";
11
11
  import { websubHandler } from "./lib/websub/handler.js";
12
12
 
@@ -157,6 +157,11 @@ export default class MicrosubEndpoint {
157
157
  createIndexes(indiekit).catch((error) => {
158
158
  console.warn("[Microsub] Index creation failed:", error.message);
159
159
  });
160
+
161
+ // Cleanup old read items on startup
162
+ cleanupAllReadItems(indiekit).catch((error) => {
163
+ console.warn("[Microsub] Startup cleanup failed:", error.message);
164
+ });
160
165
  } else {
161
166
  console.warn(
162
167
  "[Microsub] Database not available at init, scheduler not started",
@@ -328,6 +328,68 @@ async function cleanupOldReadItems(collection, channelObjectId, userId) {
328
328
  }
329
329
  }
330
330
 
331
+ /**
332
+ * Cleanup all read items across all channels (startup cleanup)
333
+ * @param {object} application - Indiekit application
334
+ * @returns {Promise<number>} Total number of items deleted
335
+ */
336
+ export async function cleanupAllReadItems(application) {
337
+ const collection = getCollection(application);
338
+ const channelsCollection = application.collections.get("microsub_channels");
339
+
340
+ // Get all channels
341
+ const channels = await channelsCollection.find({}).toArray();
342
+ let totalDeleted = 0;
343
+
344
+ for (const channel of channels) {
345
+ // Get unique userIds who have read items in this channel
346
+ const readByUsers = await collection.distinct("readBy", {
347
+ channelId: channel._id,
348
+ readBy: { $exists: true, $ne: [] },
349
+ });
350
+
351
+ for (const userId of readByUsers) {
352
+ if (!userId) continue;
353
+
354
+ const readCount = await collection.countDocuments({
355
+ channelId: channel._id,
356
+ readBy: userId,
357
+ });
358
+
359
+ if (readCount > MAX_READ_ITEMS) {
360
+ const itemsToDelete = await collection
361
+ .find({
362
+ channelId: channel._id,
363
+ readBy: userId,
364
+ })
365
+ .sort({ published: -1, _id: -1 })
366
+ .skip(MAX_READ_ITEMS)
367
+ .project({ _id: 1 })
368
+ .toArray();
369
+
370
+ if (itemsToDelete.length > 0) {
371
+ const idsToDelete = itemsToDelete.map((item) => item._id);
372
+ const deleteResult = await collection.deleteMany({
373
+ _id: { $in: idsToDelete },
374
+ });
375
+ totalDeleted += deleteResult.deletedCount;
376
+ console.info(
377
+ `[Microsub] Startup cleanup: deleted ${deleteResult.deletedCount} old items from channel "${channel.name}"`,
378
+ );
379
+ }
380
+ }
381
+ }
382
+ }
383
+
384
+ if (totalDeleted > 0) {
385
+ console.info(
386
+ `[Microsub] Startup cleanup complete: ${totalDeleted} total items deleted`,
387
+ );
388
+ }
389
+
390
+ return totalDeleted;
391
+ }
392
+
331
393
  export async function markItemsRead(application, channelId, entryIds, userId) {
332
394
  const collection = getCollection(application);
333
395
  const channelObjectId =
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rmdes/indiekit-endpoint-microsub",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
4
4
  "description": "Microsub endpoint for Indiekit. Enables subscribing to feeds and reading content using the Microsub protocol.",
5
5
  "keywords": [
6
6
  "indiekit",