[object Object]

← back to build-pages

api: /api discovery doc listing all 13 endpoints + 3 smoke

929b3db27b4d5de8ca2c573e9c5d15d093dc1761 · 2026-05-13 14:00:05 -0700 · SteveStudio2

Files touched

Diff

commit 929b3db27b4d5de8ca2c573e9c5d15d093dc1761
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Wed May 13 14:00:05 2026 -0700

    api: /api discovery doc listing all 13 endpoints + 3 smoke
---
 server.js     | 30 ++++++++++++++++++++++++++++++
 test/smoke.js |  6 ++++++
 2 files changed, 36 insertions(+)

diff --git a/server.js b/server.js
index 794b070..6df9ae1 100644
--- a/server.js
+++ b/server.js
@@ -602,6 +602,36 @@ app.get('/p/:slug/file', async (req, res, next) => {
   } catch (e) { next(e); }
 });
 
+// /api — top-level discovery doc. Lists every endpoint with a one-line
+// purpose; mirrors the pattern used by asim/sod. Hit this first when
+// orienting external consumers (or yourself two months from now).
+app.get('/api', (_req, res) => {
+  res.setHeader('Cache-Control', 'public, max-age=300, stale-while-revalidate=600');
+  res.json({
+    name: 'build-pages API',
+    base: 'https://projects.agentabrams.com',
+    endpoints: {
+      'GET /api/health':                       'Liveness probe — JSON {ok, counts.projects, uptime_s, ts, as_of}',
+      'GET /api/version':                      'Deploy fingerprint — JSON {name, version, git, node, started_at}',
+      'GET /api/projects':                     'List every project journal (slug, name, repo, blurb)',
+      '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 /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',
+      'GET /p/:slug/file?path=<path>':         'View any tracked file at HEAD',
+      'GET /sitemap.xml':                      'XML sitemap — home + every /p/:slug',
+      'GET /robots.txt':                       'Crawler policy (allow all, sitemap advertised)',
+      'GET /healthz':                          'k8s-style liveness probe (no DB) — JSON {ok, ts}',
+    },
+    notes: [
+      'Bundle cache keyed on HEAD SHA — page hits drop from 80ms cold to ~17ms warm.',
+      'Search is min 2 chars, round-robins across projects so no single project monopolizes the page.',
+    ],
+  });
+});
+
 // JSON API — for the eventual cross-project dashboard.
 app.get('/api/projects', (_req, res) => {
   res.json({
diff --git a/test/smoke.js b/test/smoke.js
index 01bc5f1..e023eae 100644
--- a/test/smoke.js
+++ b/test/smoke.js
@@ -63,6 +63,12 @@ function check(name, cond, hint = '') {
   check('api/projects: 200',       r.status === 200);
   check('api/projects: butlr in list', /"slug":"butlr"/.test(r.body));
 
+  // 4a. /api discovery doc
+  r = await fetch('/api');
+  check('api: 200',                r.status === 200);
+  check('api: name',               /"name":"build-pages API"/.test(r.body));
+  check('api: endpoints map',      /"endpoints":/.test(r.body));
+
   // 4b. /api/projects/:slug detail
   r = await fetch('/api/projects/butlr');
   check('api/projects/butlr: 200', r.status === 200);

← e2faa47 search: fleet search ?q= deep-link — hydrate input + replace  ·  back to build-pages  ·  ui: footer links to /api + /sitemap.xml (asim/sod parity) + 70698d8 →