feat: add lastItemAt field — tracks newest item publish date per blog

Stores the most recent item's published date on the blog document
during feed sync. Exposed in API response alongside lastFetchAt.
Enables sorting/displaying blogs by content freshness rather than
last fetch time.
This commit is contained in:
Ricardo
2026-02-17 17:43:44 +01:00
parent f02d46e76e
commit 4d56d58703
4 changed files with 15 additions and 1 deletions

View File

@@ -234,6 +234,7 @@ function sanitizeBlog(blog) {
itemCount: blog.itemCount,
pinned: blog.pinned,
lastFetchAt: blog.lastFetchAt,
lastItemAt: blog.lastItemAt || null,
source: blog.source || null,
};

View File

@@ -110,6 +110,7 @@ export async function createBlog(application, data) {
author: data.author || null,
status: "active",
lastFetchAt: null,
lastItemAt: null,
lastError: null,
itemCount: 0,
pinned: data.pinned || false,
@@ -200,6 +201,7 @@ export async function updateBlogStatus(application, id, status) {
if (status.itemCount !== undefined) {
update.itemCount = status.itemCount;
}
if (status.lastItemAt) update.lastItemAt = status.lastItemAt;
if (status.title) update.title = status.title;
if (status.photo) update.photo = status.photo;
if (status.siteUrl) update.siteUrl = status.siteUrl;
@@ -289,6 +291,7 @@ export async function upsertBlog(application, data) {
if (data.skipItemFetch !== undefined) setFields.skipItemFetch = data.skipItemFetch;
if (data.photo !== undefined) setFields.photo = data.photo;
if (data.lastFetchAt !== undefined) setFields.lastFetchAt = data.lastFetchAt;
if (data.lastItemAt !== undefined) setFields.lastItemAt = data.lastItemAt;
if (data.status !== undefined) setFields.status = data.status;
// $setOnInsert only for fields NOT already in $set (avoids MongoDB path conflicts)
@@ -312,6 +315,7 @@ export async function upsertBlog(application, data) {
if (!("skipItemFetch" in setFields)) insertDefaults.skipItemFetch = false;
if (!("photo" in setFields)) insertDefaults.photo = null;
if (!("lastFetchAt" in setFields)) insertDefaults.lastFetchAt = null;
if (!("lastItemAt" in setFields)) insertDefaults.lastItemAt = null;
if (!("status" in setFields)) insertDefaults.status = "active";
const result = await collection.updateOne(

View File

@@ -307,10 +307,19 @@ export async function syncBlogItems(application, blog, options = {}) {
if (result.upserted) added++;
}
// Compute newest item publish date
let newestDate = null;
for (const item of feed.items) {
if (item.published && (!newestDate || item.published > newestDate)) {
newestDate = item.published;
}
}
// Update blog metadata
const updateData = {
success: true,
itemCount: feed.items.length,
lastItemAt: newestDate,
};
// Update title if not manually set (still has feedUrl as title)

View File

@@ -1,6 +1,6 @@
{
"name": "@rmdes/indiekit-endpoint-blogroll",
"version": "1.0.22",
"version": "1.0.23",
"description": "Blogroll endpoint for Indiekit. Aggregates blog feeds from OPML, JSON feeds, or manual entry.",
"keywords": [
"indiekit",