From f02d46e76e0f5afdb97e4d77254dc382aa3c81db Mon Sep 17 00:00:00 2001 From: Ricardo Date: Tue, 17 Feb 2026 14:20:10 +0100 Subject: [PATCH] fix: resolve [Object Object] bug and add sort/source API params Rename duplicate "sync" locale key to "syncResult" to fix the sources list page showing [Object Object] instead of the Sync button label. Add sort=recent and source= query params to the blogs API for the sidebar widget tabs feature. Tag FeedLand blogs with source: "feedland" and expose source field for all blogs in API responses. Bump version to 1.0.22. --- lib/controllers/api.js | 8 +++++--- lib/controllers/dashboard.js | 14 +++++++------- lib/storage/blogs.js | 13 +++++++++++-- lib/sync/feedland.js | 1 + locales/de.json | 2 +- locales/en.json | 2 +- locales/es-419.json | 2 +- locales/es.json | 2 +- locales/fr.json | 2 +- locales/hi.json | 2 +- locales/id.json | 2 +- locales/it.json | 2 +- locales/nl.json | 2 +- locales/pl.json | 2 +- locales/pt-BR.json | 2 +- locales/pt.json | 2 +- locales/sr.json | 2 +- locales/sv.json | 2 +- locales/zh-Hans-CN.json | 2 +- package.json | 2 +- 20 files changed, 40 insertions(+), 28 deletions(-) diff --git a/lib/controllers/api.js b/lib/controllers/api.js index 984bb22..4a64e38 100644 --- a/lib/controllers/api.js +++ b/lib/controllers/api.js @@ -18,16 +18,18 @@ import { handleMicrosubWebhook, isMicrosubAvailable } from "../sync/microsub.js" async function listBlogs(request, response) { const { application } = request.app.locals; - const { category, limit = 100, offset = 0 } = request.query; + const { category, source, sort, limit = 100, offset = 0 } = request.query; try { const blogs = await getBlogs(application, { category, + source, + sort, limit: Number(limit), offset: Number(offset), }); - const total = await countBlogs(application, { category }); + const total = await countBlogs(application, { category, source }); response.json({ items: blogs.map(sanitizeBlog), @@ -232,11 +234,11 @@ function sanitizeBlog(blog) { itemCount: blog.itemCount, pinned: blog.pinned, lastFetchAt: blog.lastFetchAt, + source: blog.source || null, }; // Include Microsub metadata if applicable if (blog.source === "microsub") { - sanitized.source = "microsub"; sanitized.microsubChannel = blog.microsubChannelName; } diff --git a/lib/controllers/dashboard.js b/lib/controllers/dashboard.js index 8c4eab7..97e4051 100644 --- a/lib/controllers/dashboard.js +++ b/lib/controllers/dashboard.js @@ -77,13 +77,13 @@ async function sync(request, response) { if (result.skipped) { request.session.messages = [ - { type: "warning", content: request.__("blogroll.sync.already_running") }, + { type: "warning", content: request.__("blogroll.syncResult.already_running") }, ]; } else if (result.success) { request.session.messages = [ { type: "success", - content: request.__("blogroll.sync.success", { + content: request.__("blogroll.syncResult.success", { blogs: result.blogs.success, items: result.items.added, }), @@ -91,13 +91,13 @@ async function sync(request, response) { ]; } else { request.session.messages = [ - { type: "error", content: request.__("blogroll.sync.error", { error: result.error }) }, + { type: "error", content: request.__("blogroll.syncResult.error", { error: result.error }) }, ]; } } catch (error) { console.error("[Blogroll] Manual sync error:", error); request.session.messages = [ - { type: "error", content: request.__("blogroll.sync.error", { error: error.message }) }, + { type: "error", content: request.__("blogroll.syncResult.error", { error: error.message }) }, ]; } @@ -118,7 +118,7 @@ async function clearResync(request, response) { request.session.messages = [ { type: "success", - content: request.__("blogroll.sync.cleared_success", { + content: request.__("blogroll.syncResult.cleared_success", { blogs: result.blogs.success, items: result.items.added, }), @@ -126,13 +126,13 @@ async function clearResync(request, response) { ]; } else { request.session.messages = [ - { type: "error", content: request.__("blogroll.sync.error", { error: result.error }) }, + { type: "error", content: request.__("blogroll.syncResult.error", { error: result.error }) }, ]; } } catch (error) { console.error("[Blogroll] Clear resync error:", error); request.session.messages = [ - { type: "error", content: request.__("blogroll.sync.error", { error: error.message }) }, + { type: "error", content: request.__("blogroll.syncResult.error", { error: error.message }) }, ]; } diff --git a/lib/storage/blogs.js b/lib/storage/blogs.js index e5ebac4..7a2a597 100644 --- a/lib/storage/blogs.js +++ b/lib/storage/blogs.js @@ -29,10 +29,18 @@ export async function getBlogs(application, options = {}) { if (!includeHidden) query.hidden = { $ne: true }; if (category) query.category = category; if (sourceId) query.sourceId = new ObjectId(sourceId); + if (options.source) query.source = options.source; + + // Default sort: pinned first, then alphabetical + // "recent" sort: pinned first, then by last fetch time (newest first) + const sortOrder = + options.sort === "recent" + ? { pinned: -1, lastFetchAt: -1, title: 1 } + : { pinned: -1, title: 1 }; return collection .find(query) - .sort({ pinned: -1, title: 1 }) + .sort(sortOrder) .skip(offset) .limit(limit) .toArray(); @@ -46,11 +54,12 @@ export async function getBlogs(application, options = {}) { */ export async function countBlogs(application, options = {}) { const collection = getCollection(application); - const { category, includeHidden = false } = options; + const { category, source, includeHidden = false } = options; const query = { status: { $ne: "deleted" } }; if (!includeHidden) query.hidden = { $ne: true }; if (category) query.category = category; + if (source) query.source = source; return collection.countDocuments(query); } diff --git a/lib/sync/feedland.js b/lib/sync/feedland.js index a69fb2d..8099479 100644 --- a/lib/sync/feedland.js +++ b/lib/sync/feedland.js @@ -108,6 +108,7 @@ export async function syncFeedlandSource(application, source) { const result = await upsertBlog(application, { ...blog, category, + source: "feedland", sourceId: source._id, }); diff --git a/locales/de.json b/locales/de.json index 63c8d09..6335bc2 100644 --- a/locales/de.json +++ b/locales/de.json @@ -26,7 +26,7 @@ "clearConfirm": "Dadurch werden alle zwischengespeicherten Einträge gelöscht und alles neu abgerufen. Fortfahren?" }, - "sync": { + "syncResult": { "success": "Synced {{blogs}} blogs, added {{items}} items.", "error": "Sync failed: {{error}}", "already_running": "A sync is already in progress.", diff --git a/locales/en.json b/locales/en.json index 9553fd5..26ea6b2 100644 --- a/locales/en.json +++ b/locales/en.json @@ -26,7 +26,7 @@ "clearConfirm": "This will delete all cached items and re-fetch everything. Continue?" }, - "sync": { + "syncResult": { "success": "Synced {{blogs}} blogs, added {{items}} items.", "error": "Sync failed: {{error}}", "already_running": "A sync is already in progress.", diff --git a/locales/es-419.json b/locales/es-419.json index ba6a8bc..5edbc3c 100644 --- a/locales/es-419.json +++ b/locales/es-419.json @@ -26,7 +26,7 @@ "clearConfirm": "Esto eliminará todas las entradas almacenadas en caché y volverá a descargar todo. ¿Continuar?" }, - "sync": { + "syncResult": { "success": "Synced {{blogs}} blogs, added {{items}} items.", "error": "Sync failed: {{error}}", "already_running": "A sync is already in progress.", diff --git a/locales/es.json b/locales/es.json index d4b841c..f2d52f5 100644 --- a/locales/es.json +++ b/locales/es.json @@ -26,7 +26,7 @@ "clearConfirm": "Esto eliminará todas las entradas almacenadas en caché y volverá a obtenerlo todo. ¿Continuar?" }, - "sync": { + "syncResult": { "success": "Synced {{blogs}} blogs, added {{items}} items.", "error": "Sync failed: {{error}}", "already_running": "A sync is already in progress.", diff --git a/locales/fr.json b/locales/fr.json index e30782d..2ab8faa 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -26,7 +26,7 @@ "clearConfirm": "Cela supprimera toutes les entrées mises en cache et récupérera tout à nouveau. Continuer ?" }, - "sync": { + "syncResult": { "success": "Synced {{blogs}} blogs, added {{items}} items.", "error": "Sync failed: {{error}}", "already_running": "A sync is already in progress.", diff --git a/locales/hi.json b/locales/hi.json index c3ce4ba..1d96721 100644 --- a/locales/hi.json +++ b/locales/hi.json @@ -26,7 +26,7 @@ "clearConfirm": "इससे सभी कैश किए गए आइटम हटा दिए जाएंगे और सब कुछ फिर से प्राप्त किया जाएगा। जारी रखें?" }, - "sync": { + "syncResult": { "success": "Synced {{blogs}} blogs, added {{items}} items.", "error": "Sync failed: {{error}}", "already_running": "A sync is already in progress.", diff --git a/locales/id.json b/locales/id.json index 26fe9f5..f88c48d 100644 --- a/locales/id.json +++ b/locales/id.json @@ -26,7 +26,7 @@ "clearConfirm": "Ini akan menghapus semua item yang di-cache dan mengambil semuanya lagi. Lanjutkan?" }, - "sync": { + "syncResult": { "success": "Synced {{blogs}} blogs, added {{items}} items.", "error": "Sync failed: {{error}}", "already_running": "A sync is already in progress.", diff --git a/locales/it.json b/locales/it.json index dcdaa0b..4babe43 100644 --- a/locales/it.json +++ b/locales/it.json @@ -26,7 +26,7 @@ "clearConfirm": "Questo cancellerà tutti gli elementi memorizzati e recupererà tutto nuovamente. Continuare?" }, - "sync": { + "syncResult": { "success": "Synced {{blogs}} blogs, added {{items}} items.", "error": "Sync failed: {{error}}", "already_running": "A sync is already in progress.", diff --git a/locales/nl.json b/locales/nl.json index 89522cf..6725509 100644 --- a/locales/nl.json +++ b/locales/nl.json @@ -26,7 +26,7 @@ "clearConfirm": "Dit verwijdert alle gecachte items en haalt alles opnieuw op. Doorgaan?" }, - "sync": { + "syncResult": { "success": "Synced {{blogs}} blogs, added {{items}} items.", "error": "Sync failed: {{error}}", "already_running": "A sync is already in progress.", diff --git a/locales/pl.json b/locales/pl.json index c2aae5c..a20f3f2 100644 --- a/locales/pl.json +++ b/locales/pl.json @@ -26,7 +26,7 @@ "clearConfirm": "Spowoduje to usunięcie wszystkich elementów w pamięci podręcznej i ponowne pobranie wszystkiego. Kontynuować?" }, - "sync": { + "syncResult": { "success": "Synced {{blogs}} blogs, added {{items}} items.", "error": "Sync failed: {{error}}", "already_running": "A sync is already in progress.", diff --git a/locales/pt-BR.json b/locales/pt-BR.json index 3ff4e91..3497bf5 100644 --- a/locales/pt-BR.json +++ b/locales/pt-BR.json @@ -26,7 +26,7 @@ "clearConfirm": "Isso excluirá todos os itens em cache e buscará tudo novamente. Continuar?" }, - "sync": { + "syncResult": { "success": "Synced {{blogs}} blogs, added {{items}} items.", "error": "Sync failed: {{error}}", "already_running": "A sync is already in progress.", diff --git a/locales/pt.json b/locales/pt.json index e795794..ebfd06a 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -26,7 +26,7 @@ "clearConfirm": "Isto eliminará todos os itens em cache e voltará a obter tudo. Continuar?" }, - "sync": { + "syncResult": { "success": "Synced {{blogs}} blogs, added {{items}} items.", "error": "Sync failed: {{error}}", "already_running": "A sync is already in progress.", diff --git a/locales/sr.json b/locales/sr.json index ba725b5..5f55348 100644 --- a/locales/sr.json +++ b/locales/sr.json @@ -26,7 +26,7 @@ "clearConfirm": "Ово ће обрисати све кеширане ставке и поново преузети све. Наставити?" }, - "sync": { + "syncResult": { "success": "Synced {{blogs}} blogs, added {{items}} items.", "error": "Sync failed: {{error}}", "already_running": "A sync is already in progress.", diff --git a/locales/sv.json b/locales/sv.json index 2a101aa..7a96cb4 100644 --- a/locales/sv.json +++ b/locales/sv.json @@ -26,7 +26,7 @@ "clearConfirm": "Detta kommer att ta bort alla cachade poster och hämta allt igen. Fortsätta?" }, - "sync": { + "syncResult": { "success": "Synced {{blogs}} blogs, added {{items}} items.", "error": "Sync failed: {{error}}", "already_running": "A sync is already in progress.", diff --git a/locales/zh-Hans-CN.json b/locales/zh-Hans-CN.json index 823ab53..fb39aed 100644 --- a/locales/zh-Hans-CN.json +++ b/locales/zh-Hans-CN.json @@ -26,7 +26,7 @@ "clearConfirm": "这将删除所有缓存的条目并重新获取所有内容。继续吗?" }, - "sync": { + "syncResult": { "success": "Synced {{blogs}} blogs, added {{items}} items.", "error": "Sync failed: {{error}}", "already_running": "A sync is already in progress.", diff --git a/package.json b/package.json index a1df4b3..39daaa7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rmdes/indiekit-endpoint-blogroll", - "version": "1.0.21", + "version": "1.0.22", "description": "Blogroll endpoint for Indiekit. Aggregates blog feeds from OPML, JSON feeds, or manual entry.", "keywords": [ "indiekit",