@rmdes/indiekit-endpoint-blogroll 1.0.12 → 1.0.14
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/lib/storage/blogs.js +45 -22
- package/lib/sync/microsub.js +30 -2
- package/lib/sync/scheduler.js +2 -2
- package/package.json +1 -1
package/lib/storage/blogs.js
CHANGED
|
@@ -262,31 +262,54 @@ export async function upsertBlog(application, data) {
|
|
|
262
262
|
filter.sourceId = new ObjectId(data.sourceId);
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
+
// Build $set with base fields
|
|
266
|
+
const setFields = {
|
|
267
|
+
title: data.title,
|
|
268
|
+
siteUrl: data.siteUrl,
|
|
269
|
+
feedType: data.feedType,
|
|
270
|
+
category: data.category,
|
|
271
|
+
sourceId: data.sourceId ? new ObjectId(data.sourceId) : null,
|
|
272
|
+
updatedAt: now,
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
// Conditionally add microsub/optional fields to $set when provided
|
|
276
|
+
if (data.source !== undefined) setFields.source = data.source;
|
|
277
|
+
if (data.microsubFeedId !== undefined) setFields.microsubFeedId = data.microsubFeedId;
|
|
278
|
+
if (data.microsubChannelId !== undefined) setFields.microsubChannelId = data.microsubChannelId;
|
|
279
|
+
if (data.microsubChannelName !== undefined) setFields.microsubChannelName = data.microsubChannelName;
|
|
280
|
+
if (data.skipItemFetch !== undefined) setFields.skipItemFetch = data.skipItemFetch;
|
|
281
|
+
if (data.photo !== undefined) setFields.photo = data.photo;
|
|
282
|
+
if (data.lastFetchAt !== undefined) setFields.lastFetchAt = data.lastFetchAt;
|
|
283
|
+
if (data.status !== undefined) setFields.status = data.status;
|
|
284
|
+
|
|
285
|
+
// $setOnInsert only for fields NOT already in $set (avoids MongoDB path conflicts)
|
|
286
|
+
const insertDefaults = {
|
|
287
|
+
description: null,
|
|
288
|
+
tags: [],
|
|
289
|
+
author: null,
|
|
290
|
+
lastError: null,
|
|
291
|
+
itemCount: 0,
|
|
292
|
+
pinned: false,
|
|
293
|
+
hidden: false,
|
|
294
|
+
notes: null,
|
|
295
|
+
createdAt: now,
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
// Add defaults for optional fields only when they're NOT in $set
|
|
299
|
+
if (!("source" in setFields)) insertDefaults.source = null;
|
|
300
|
+
if (!("microsubFeedId" in setFields)) insertDefaults.microsubFeedId = null;
|
|
301
|
+
if (!("microsubChannelId" in setFields)) insertDefaults.microsubChannelId = null;
|
|
302
|
+
if (!("microsubChannelName" in setFields)) insertDefaults.microsubChannelName = null;
|
|
303
|
+
if (!("skipItemFetch" in setFields)) insertDefaults.skipItemFetch = false;
|
|
304
|
+
if (!("photo" in setFields)) insertDefaults.photo = null;
|
|
305
|
+
if (!("lastFetchAt" in setFields)) insertDefaults.lastFetchAt = null;
|
|
306
|
+
if (!("status" in setFields)) insertDefaults.status = "active";
|
|
307
|
+
|
|
265
308
|
const result = await collection.updateOne(
|
|
266
309
|
filter,
|
|
267
310
|
{
|
|
268
|
-
$set:
|
|
269
|
-
|
|
270
|
-
siteUrl: data.siteUrl,
|
|
271
|
-
feedType: data.feedType,
|
|
272
|
-
category: data.category,
|
|
273
|
-
sourceId: data.sourceId ? new ObjectId(data.sourceId) : null,
|
|
274
|
-
updatedAt: now,
|
|
275
|
-
},
|
|
276
|
-
$setOnInsert: {
|
|
277
|
-
description: null,
|
|
278
|
-
tags: [],
|
|
279
|
-
photo: null,
|
|
280
|
-
author: null,
|
|
281
|
-
status: "active",
|
|
282
|
-
lastFetchAt: null,
|
|
283
|
-
lastError: null,
|
|
284
|
-
itemCount: 0,
|
|
285
|
-
pinned: false,
|
|
286
|
-
hidden: false,
|
|
287
|
-
notes: null,
|
|
288
|
-
createdAt: now,
|
|
289
|
-
},
|
|
311
|
+
$set: setFields,
|
|
312
|
+
$setOnInsert: insertDefaults,
|
|
290
313
|
},
|
|
291
314
|
{ upsert: true }
|
|
292
315
|
);
|
package/lib/sync/microsub.js
CHANGED
|
@@ -43,6 +43,7 @@ export async function syncMicrosubSource(application, source) {
|
|
|
43
43
|
let added = 0;
|
|
44
44
|
let updated = 0;
|
|
45
45
|
let total = 0;
|
|
46
|
+
const currentMicrosubFeedIds = [];
|
|
46
47
|
|
|
47
48
|
for (const channel of channels) {
|
|
48
49
|
// Get all feeds subscribed in this channel
|
|
@@ -50,6 +51,7 @@ export async function syncMicrosubSource(application, source) {
|
|
|
50
51
|
|
|
51
52
|
for (const feed of feeds) {
|
|
52
53
|
total++;
|
|
54
|
+
currentMicrosubFeedIds.push(feed._id.toString());
|
|
53
55
|
|
|
54
56
|
// Store REFERENCE to Microsub feed, not a copy
|
|
55
57
|
// Items will be queried from microsub_items directly
|
|
@@ -83,14 +85,40 @@ export async function syncMicrosubSource(application, source) {
|
|
|
83
85
|
}
|
|
84
86
|
}
|
|
85
87
|
|
|
88
|
+
// Orphan detection: soft-delete blogs whose microsub feed no longer exists
|
|
89
|
+
let orphaned = 0;
|
|
90
|
+
if (currentMicrosubFeedIds.length > 0) {
|
|
91
|
+
const db = application.getBlogrollDb();
|
|
92
|
+
const orphanResult = await db.collection("blogrollBlogs").updateMany(
|
|
93
|
+
{
|
|
94
|
+
source: "microsub",
|
|
95
|
+
sourceId: source._id,
|
|
96
|
+
microsubFeedId: { $nin: currentMicrosubFeedIds },
|
|
97
|
+
status: { $ne: "deleted" },
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
$set: {
|
|
101
|
+
status: "deleted",
|
|
102
|
+
hidden: true,
|
|
103
|
+
deletedAt: new Date(),
|
|
104
|
+
updatedAt: new Date(),
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
orphaned = orphanResult.modifiedCount;
|
|
109
|
+
if (orphaned > 0) {
|
|
110
|
+
console.log(`[Blogroll] Cleaned up ${orphaned} orphaned Microsub blog(s) no longer subscribed`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
86
114
|
// Update source sync status
|
|
87
115
|
await updateSourceSyncStatus(application, source._id, { success: true });
|
|
88
116
|
|
|
89
117
|
console.log(
|
|
90
|
-
`[Blogroll] Synced Microsub source "${source.name}": ${added} added, ${updated} updated, ${total} total from ${channels.length} channels (items served from Microsub)`
|
|
118
|
+
`[Blogroll] Synced Microsub source "${source.name}": ${added} added, ${updated} updated, ${orphaned} orphaned, ${total} total from ${channels.length} channels (items served from Microsub)`
|
|
91
119
|
);
|
|
92
120
|
|
|
93
|
-
return { success: true, added, updated, total };
|
|
121
|
+
return { success: true, added, updated, orphaned, total };
|
|
94
122
|
} catch (error) {
|
|
95
123
|
// Update source with error status
|
|
96
124
|
await updateSourceSyncStatus(application, source._id, {
|
package/lib/sync/scheduler.js
CHANGED
|
@@ -198,9 +198,9 @@ export async function clearAndResync(application, options = {}) {
|
|
|
198
198
|
// Clear all items (but keep blogs and sources)
|
|
199
199
|
await db.collection("blogrollItems").deleteMany({});
|
|
200
200
|
|
|
201
|
-
// Reset blog item counts and status
|
|
201
|
+
// Reset blog item counts and status (skip soft-deleted blogs)
|
|
202
202
|
await db.collection("blogrollBlogs").updateMany(
|
|
203
|
-
{},
|
|
203
|
+
{ status: { $ne: "deleted" } },
|
|
204
204
|
{
|
|
205
205
|
$set: {
|
|
206
206
|
itemCount: 0,
|