[object Object]

← back to build-pages

api: /api/projects/:slug/commits — paginated raw JSON (limit + offset) + 4 smoke

254d1d066ce39da5f45496dc48a9f0c3d8b590b2 · 2026-05-13 13:34:44 -0700 · SteveStudio2

Files touched

Diff

commit 254d1d066ce39da5f45496dc48a9f0c3d8b590b2
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Wed May 13 13:34:44 2026 -0700

    api: /api/projects/:slug/commits — paginated raw JSON (limit + offset) + 4 smoke
---
 server.js     | 21 +++++++++++++++++++++
 test/smoke.js |  7 +++++++
 2 files changed, 28 insertions(+)

diff --git a/server.js b/server.js
index 9b68cf1..412d46a 100644
--- a/server.js
+++ b/server.js
@@ -434,6 +434,27 @@ app.get('/p/:slug/c/:hash', async (req, res, next) => {
   } catch (e) { next(e); }
 });
 
+// Raw commits JSON — paginated. Supports ?limit=N (default 100, max 500) and
+// ?offset=N. The bundle has the full list cached; this just slices it.
+app.get('/api/projects/:slug/commits', async (req, res, next) => {
+  const p = PROJECTS[req.params.slug];
+  if (!p) return res.status(404).json({ error: 'no-such-project' });
+  try {
+    const { commits } = await getProjectBundle(req.params.slug, p);
+    const limit  = Math.min(Math.max(parseInt(req.query.limit,  10) || 100, 1), 500);
+    const offset = Math.max(parseInt(req.query.offset, 10) || 0, 0);
+    res.setHeader('Cache-Control', 'public, max-age=30, stale-while-revalidate=60');
+    res.json({
+      slug: req.params.slug,
+      total: commits.length,
+      limit, offset,
+      commits: commits.slice(offset, offset + limit).map(c => ({
+        hash: c.shortHash, date: c.dateISO, author: c.author, subject: c.subject, body: c.body,
+      })),
+    });
+  } catch (e) { next(e); }
+});
+
 // Atom feed per project — recent N commits in standards-compliant Atom 1.0.
 // Lets external tools subscribe to a project's build journal (e.g. Feedly,
 // NetNewsWire). Pulls from the same bundle cache as the HTML page.
diff --git a/test/smoke.js b/test/smoke.js
index 11a5787..708d657 100644
--- a/test/smoke.js
+++ b/test/smoke.js
@@ -133,6 +133,13 @@ function check(name, cond, hint = '') {
   r = await fetch('/p/butlr');
   check('butlr: feed autodiscover',/rel="alternate" type="application\/atom\+xml"/.test(r.body));
 
+  // 12. /api/projects/:slug/commits — paginated raw JSON
+  r = await fetch('/api/projects/butlr/commits?limit=3');
+  check('commits api: 200',        r.status === 200);
+  check('commits api: total',      /"total":\d+/.test(r.body));
+  check('commits api: 3 returned', /"limit":3/.test(r.body));
+  check('commits api: hash field', /"hash":"[a-f0-9]{4,}/.test(r.body));
+
   // Report
   if (process.argv.includes('--json')) {
     console.log(JSON.stringify({ base: BASE, pass, fail, total: pass + fail, checks, as_of: new Date().toISOString() }));

← c196cfd feed: Atom 1.0 per project (/p/:slug/feed.atom) + autodiscov  ·  back to build-pages  ·  404: branded page on unknown routes (fleet parity) + 3 smoke 0158ff5 →