[object Object]

← back to build-pages

api/recent: fleet-wide most-recent commits, date-sorted + 3 smoke

86268fdf3fce9ee8dc617e998dc08c8d687f76a5 · 2026-05-13 14:32:46 -0700 · SteveStudio2

Files touched

Diff

commit 86268fdf3fce9ee8dc617e998dc08c8d687f76a5
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Wed May 13 14:32:46 2026 -0700

    api/recent: fleet-wide most-recent commits, date-sorted + 3 smoke
---
 server.js     | 26 ++++++++++++++++++++++++++
 test/smoke.js |  6 ++++++
 2 files changed, 32 insertions(+)

diff --git a/server.js b/server.js
index ae6d0f0..76b53dd 100644
--- a/server.js
+++ b/server.js
@@ -516,6 +516,31 @@ app.get('/p/:slug/c/:hash', async (req, res, next) => {
   } catch (e) { next(e); }
 });
 
+// /api/recent — most recent N commits across the entire fleet, merged + sorted
+// by date desc. Powers a "today across the fleet" rail. Shares the bundle
+// cache so it's free after the first warm.
+app.get('/api/recent', async (req, res, next) => {
+  const limit = Math.min(Math.max(parseInt(req.query.limit, 10) || 20, 1), 200);
+  try {
+    const all = [];
+    for (const [slug, p] of Object.entries(PROJECTS)) {
+      const bundle = await getProjectBundle(slug, p).catch(() => null);
+      if (!bundle) continue;
+      // Each project's commits are already date-desc; take the leading slice
+      // up to `limit`. Final merge will sort the union back into date order.
+      for (const c of bundle.commits.slice(0, limit)) {
+        all.push({
+          slug, project: p.name,
+          hash: c.shortHash, date: c.dateISO, subject: c.subject,
+        });
+      }
+    }
+    all.sort((a, b) => (b.date < a.date ? -1 : b.date > a.date ? 1 : 0));
+    res.setHeader('Cache-Control', 'public, max-age=30, stale-while-revalidate=60');
+    res.json({ total: all.length, limit, results: all.slice(0, limit) });
+  } catch (e) { next(e); }
+});
+
 // Cross-project search — case-insensitive substring match on commit
 // subject + body across every registered project. Used to power a "search
 // the whole fleet" experience on the home page. ?q= required (min 2 chars),
@@ -655,6 +680,7 @@ app.get('/api', (_req, res) => {
       'GET /api/projects/:slug':               'Per-project stats — head SHA, commit_count, top agents/skills, first/last commit',
       'GET /api/projects/:slug/commits':       'Paginated raw commits — ?limit=N&offset=N (1-500 default 100)',
       'GET /api/search':                       'Cross-project commit search — ?q=<term>&limit=N, round-robin + by_project breakdown',
+      'GET /api/recent':                       'Most recent N commits across the whole fleet — date-sorted, ?limit=20',
       'GET /p/:slug':                          'Project journal HTML page (?q= deep-link supported)',
       'GET /p/:slug/c/:hash':                  'Commit detail page — full diff + body',
       'GET /p/:slug/feed.atom':                'Atom 1.0 feed — up to 50 recent commits, autodiscovered on project page',
diff --git a/test/smoke.js b/test/smoke.js
index 1873fc3..1ac3377 100644
--- a/test/smoke.js
+++ b/test/smoke.js
@@ -179,6 +179,12 @@ function check(name, cond, hint = '') {
   check('404: branded HTML',       /build-pages/.test(r.body));
   check('404: back-link',          /back to all projects/.test(r.body));
 
+  // 13b. /api/recent — fleet-wide recent commits
+  r = await fetch('/api/recent?limit=5');
+  check('recent: 200',             r.status === 200);
+  check('recent: results array',   /"results":\[/.test(r.body));
+  check('recent: hash field',      /"hash":"[a-f0-9]{4,}/.test(r.body));
+
   // 14. cross-project search (round-robin merge + per-project breakdown)
   r = await fetch('/api/search?q=whisper&limit=10');
   check('search: 200',             r.status === 200);

← d1c2c50 commit page: diff +/- syntax highlighting (green add, red re  ·  back to build-pages  ·  home: 'Recent across the fleet' rail — server-rendered top 1 afcaaa3 →