← 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
M server.jsM test/smoke.js
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 →