@rmdes/indiekit-endpoint-blogroll 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.
@@ -18,16 +18,18 @@ import { handleMicrosubWebhook, isMicrosubAvailable } from "../sync/microsub.js"
18
18
  async function listBlogs(request, response) {
19
19
  const { application } = request.app.locals;
20
20
 
21
- const { category, limit = 100, offset = 0 } = request.query;
21
+ const { category, source, sort, limit = 100, offset = 0 } = request.query;
22
22
 
23
23
  try {
24
24
  const blogs = await getBlogs(application, {
25
25
  category,
26
+ source,
27
+ sort,
26
28
  limit: Number(limit),
27
29
  offset: Number(offset),
28
30
  });
29
31
 
30
- const total = await countBlogs(application, { category });
32
+ const total = await countBlogs(application, { category, source });
31
33
 
32
34
  response.json({
33
35
  items: blogs.map(sanitizeBlog),
@@ -232,11 +234,11 @@ function sanitizeBlog(blog) {
232
234
  itemCount: blog.itemCount,
233
235
  pinned: blog.pinned,
234
236
  lastFetchAt: blog.lastFetchAt,
237
+ source: blog.source || null,
235
238
  };
236
239
 
237
240
  // Include Microsub metadata if applicable
238
241
  if (blog.source === "microsub") {
239
- sanitized.source = "microsub";
240
242
  sanitized.microsubChannel = blog.microsubChannelName;
241
243
  }
242
244
 
@@ -77,13 +77,13 @@ async function sync(request, response) {
77
77
 
78
78
  if (result.skipped) {
79
79
  request.session.messages = [
80
- { type: "warning", content: request.__("blogroll.sync.already_running") },
80
+ { type: "warning", content: request.__("blogroll.syncResult.already_running") },
81
81
  ];
82
82
  } else if (result.success) {
83
83
  request.session.messages = [
84
84
  {
85
85
  type: "success",
86
- content: request.__("blogroll.sync.success", {
86
+ content: request.__("blogroll.syncResult.success", {
87
87
  blogs: result.blogs.success,
88
88
  items: result.items.added,
89
89
  }),
@@ -91,13 +91,13 @@ async function sync(request, response) {
91
91
  ];
92
92
  } else {
93
93
  request.session.messages = [
94
- { type: "error", content: request.__("blogroll.sync.error", { error: result.error }) },
94
+ { type: "error", content: request.__("blogroll.syncResult.error", { error: result.error }) },
95
95
  ];
96
96
  }
97
97
  } catch (error) {
98
98
  console.error("[Blogroll] Manual sync error:", error);
99
99
  request.session.messages = [
100
- { type: "error", content: request.__("blogroll.sync.error", { error: error.message }) },
100
+ { type: "error", content: request.__("blogroll.syncResult.error", { error: error.message }) },
101
101
  ];
102
102
  }
103
103
 
@@ -118,7 +118,7 @@ async function clearResync(request, response) {
118
118
  request.session.messages = [
119
119
  {
120
120
  type: "success",
121
- content: request.__("blogroll.sync.cleared_success", {
121
+ content: request.__("blogroll.syncResult.cleared_success", {
122
122
  blogs: result.blogs.success,
123
123
  items: result.items.added,
124
124
  }),
@@ -126,13 +126,13 @@ async function clearResync(request, response) {
126
126
  ];
127
127
  } else {
128
128
  request.session.messages = [
129
- { type: "error", content: request.__("blogroll.sync.error", { error: result.error }) },
129
+ { type: "error", content: request.__("blogroll.syncResult.error", { error: result.error }) },
130
130
  ];
131
131
  }
132
132
  } catch (error) {
133
133
  console.error("[Blogroll] Clear resync error:", error);
134
134
  request.session.messages = [
135
- { type: "error", content: request.__("blogroll.sync.error", { error: error.message }) },
135
+ { type: "error", content: request.__("blogroll.syncResult.error", { error: error.message }) },
136
136
  ];
137
137
  }
138
138
 
@@ -29,10 +29,18 @@ export async function getBlogs(application, options = {}) {
29
29
  if (!includeHidden) query.hidden = { $ne: true };
30
30
  if (category) query.category = category;
31
31
  if (sourceId) query.sourceId = new ObjectId(sourceId);
32
+ if (options.source) query.source = options.source;
33
+
34
+ // Default sort: pinned first, then alphabetical
35
+ // "recent" sort: pinned first, then by last fetch time (newest first)
36
+ const sortOrder =
37
+ options.sort === "recent"
38
+ ? { pinned: -1, lastFetchAt: -1, title: 1 }
39
+ : { pinned: -1, title: 1 };
32
40
 
33
41
  return collection
34
42
  .find(query)
35
- .sort({ pinned: -1, title: 1 })
43
+ .sort(sortOrder)
36
44
  .skip(offset)
37
45
  .limit(limit)
38
46
  .toArray();
@@ -46,11 +54,12 @@ export async function getBlogs(application, options = {}) {
46
54
  */
47
55
  export async function countBlogs(application, options = {}) {
48
56
  const collection = getCollection(application);
49
- const { category, includeHidden = false } = options;
57
+ const { category, source, includeHidden = false } = options;
50
58
 
51
59
  const query = { status: { $ne: "deleted" } };
52
60
  if (!includeHidden) query.hidden = { $ne: true };
53
61
  if (category) query.category = category;
62
+ if (source) query.source = source;
54
63
 
55
64
  return collection.countDocuments(query);
56
65
  }
@@ -108,6 +108,7 @@ export async function syncFeedlandSource(application, source) {
108
108
  const result = await upsertBlog(application, {
109
109
  ...blog,
110
110
  category,
111
+ source: "feedland",
111
112
  sourceId: source._id,
112
113
  });
113
114
 
package/locales/de.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "clearConfirm": "Dadurch werden alle zwischengespeicherten Einträge gelöscht und alles neu abgerufen. Fortfahren?"
27
27
  },
28
28
 
29
- "sync": {
29
+ "syncResult": {
30
30
  "success": "Synced {{blogs}} blogs, added {{items}} items.",
31
31
  "error": "Sync failed: {{error}}",
32
32
  "already_running": "A sync is already in progress.",
package/locales/en.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "clearConfirm": "This will delete all cached items and re-fetch everything. Continue?"
27
27
  },
28
28
 
29
- "sync": {
29
+ "syncResult": {
30
30
  "success": "Synced {{blogs}} blogs, added {{items}} items.",
31
31
  "error": "Sync failed: {{error}}",
32
32
  "already_running": "A sync is already in progress.",
@@ -26,7 +26,7 @@
26
26
  "clearConfirm": "Esto eliminará todas las entradas almacenadas en caché y volverá a descargar todo. ¿Continuar?"
27
27
  },
28
28
 
29
- "sync": {
29
+ "syncResult": {
30
30
  "success": "Synced {{blogs}} blogs, added {{items}} items.",
31
31
  "error": "Sync failed: {{error}}",
32
32
  "already_running": "A sync is already in progress.",
package/locales/es.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "clearConfirm": "Esto eliminará todas las entradas almacenadas en caché y volverá a obtenerlo todo. ¿Continuar?"
27
27
  },
28
28
 
29
- "sync": {
29
+ "syncResult": {
30
30
  "success": "Synced {{blogs}} blogs, added {{items}} items.",
31
31
  "error": "Sync failed: {{error}}",
32
32
  "already_running": "A sync is already in progress.",
package/locales/fr.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "clearConfirm": "Cela supprimera toutes les entrées mises en cache et récupérera tout à nouveau. Continuer ?"
27
27
  },
28
28
 
29
- "sync": {
29
+ "syncResult": {
30
30
  "success": "Synced {{blogs}} blogs, added {{items}} items.",
31
31
  "error": "Sync failed: {{error}}",
32
32
  "already_running": "A sync is already in progress.",
package/locales/hi.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "clearConfirm": "इससे सभी कैश किए गए आइटम हटा दिए जाएंगे और सब कुछ फिर से प्राप्त किया जाएगा। जारी रखें?"
27
27
  },
28
28
 
29
- "sync": {
29
+ "syncResult": {
30
30
  "success": "Synced {{blogs}} blogs, added {{items}} items.",
31
31
  "error": "Sync failed: {{error}}",
32
32
  "already_running": "A sync is already in progress.",
package/locales/id.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "clearConfirm": "Ini akan menghapus semua item yang di-cache dan mengambil semuanya lagi. Lanjutkan?"
27
27
  },
28
28
 
29
- "sync": {
29
+ "syncResult": {
30
30
  "success": "Synced {{blogs}} blogs, added {{items}} items.",
31
31
  "error": "Sync failed: {{error}}",
32
32
  "already_running": "A sync is already in progress.",
package/locales/it.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "clearConfirm": "Questo cancellerà tutti gli elementi memorizzati e recupererà tutto nuovamente. Continuare?"
27
27
  },
28
28
 
29
- "sync": {
29
+ "syncResult": {
30
30
  "success": "Synced {{blogs}} blogs, added {{items}} items.",
31
31
  "error": "Sync failed: {{error}}",
32
32
  "already_running": "A sync is already in progress.",
package/locales/nl.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "clearConfirm": "Dit verwijdert alle gecachte items en haalt alles opnieuw op. Doorgaan?"
27
27
  },
28
28
 
29
- "sync": {
29
+ "syncResult": {
30
30
  "success": "Synced {{blogs}} blogs, added {{items}} items.",
31
31
  "error": "Sync failed: {{error}}",
32
32
  "already_running": "A sync is already in progress.",
package/locales/pl.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "clearConfirm": "Spowoduje to usunięcie wszystkich elementów w pamięci podręcznej i ponowne pobranie wszystkiego. Kontynuować?"
27
27
  },
28
28
 
29
- "sync": {
29
+ "syncResult": {
30
30
  "success": "Synced {{blogs}} blogs, added {{items}} items.",
31
31
  "error": "Sync failed: {{error}}",
32
32
  "already_running": "A sync is already in progress.",
@@ -26,7 +26,7 @@
26
26
  "clearConfirm": "Isso excluirá todos os itens em cache e buscará tudo novamente. Continuar?"
27
27
  },
28
28
 
29
- "sync": {
29
+ "syncResult": {
30
30
  "success": "Synced {{blogs}} blogs, added {{items}} items.",
31
31
  "error": "Sync failed: {{error}}",
32
32
  "already_running": "A sync is already in progress.",
package/locales/pt.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "clearConfirm": "Isto eliminará todos os itens em cache e voltará a obter tudo. Continuar?"
27
27
  },
28
28
 
29
- "sync": {
29
+ "syncResult": {
30
30
  "success": "Synced {{blogs}} blogs, added {{items}} items.",
31
31
  "error": "Sync failed: {{error}}",
32
32
  "already_running": "A sync is already in progress.",
package/locales/sr.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "clearConfirm": "Ово ће обрисати све кеширане ставке и поново преузети све. Наставити?"
27
27
  },
28
28
 
29
- "sync": {
29
+ "syncResult": {
30
30
  "success": "Synced {{blogs}} blogs, added {{items}} items.",
31
31
  "error": "Sync failed: {{error}}",
32
32
  "already_running": "A sync is already in progress.",
package/locales/sv.json CHANGED
@@ -26,7 +26,7 @@
26
26
  "clearConfirm": "Detta kommer att ta bort alla cachade poster och hämta allt igen. Fortsätta?"
27
27
  },
28
28
 
29
- "sync": {
29
+ "syncResult": {
30
30
  "success": "Synced {{blogs}} blogs, added {{items}} items.",
31
31
  "error": "Sync failed: {{error}}",
32
32
  "already_running": "A sync is already in progress.",
@@ -26,7 +26,7 @@
26
26
  "clearConfirm": "这将删除所有缓存的条目并重新获取所有内容。继续吗?"
27
27
  },
28
28
 
29
- "sync": {
29
+ "syncResult": {
30
30
  "success": "Synced {{blogs}} blogs, added {{items}} items.",
31
31
  "error": "Sync failed: {{error}}",
32
32
  "already_running": "A sync is already in progress.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rmdes/indiekit-endpoint-blogroll",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
4
4
  "description": "Blogroll endpoint for Indiekit. Aggregates blog feeds from OPML, JSON feeds, or manual entry.",
5
5
  "keywords": [
6
6
  "indiekit",