-
-
Notifications
You must be signed in to change notification settings - Fork 25
Expand file tree
/
Copy pathopenapi.js
More file actions
35 lines (29 loc) · 1.56 KB
/
Copy pathopenapi.js
File metadata and controls
35 lines (29 loc) · 1.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// @ts-check
// GET /api/3d/openapi.json — the machine-readable spec for the free 3D API,
// generated live from the catalog (api/_lib/3d-catalog).
//
// Served at `/api/3d/openapi.json` via the rewrite in vercel.json; this file also
// answers `/api/3d/openapi` directly. An agent points its OpenAPI toolchain
// (openapi-generator, LangChain's OpenAPIToolkit, Swagger UI) at this URL and
// gets typed clients / callable tools for every endpoint — no key, no account.
// The doc is never hand-written: paths, params, request bodies, and response
// schemas all derive from the catalog entries, so it can't drift from `/api/3d`.
import { wrap, cors, method, json, rateLimited } from '../_lib/http.js';
import { limits, clientIp } from '../_lib/rate-limit.js';
import { env } from '../_lib/env.js';
import { loadCatalog } from '../_lib/3d-catalog/index.js';
import { buildOpenApiDoc } from '../_lib/3d-catalog/openapi.js';
const VERSION = '1.0.0';
export default wrap(async (req, res) => {
if (cors(req, res, { methods: 'GET,OPTIONS', origins: '*' })) return;
if (!method(req, res, ['GET'])) return;
const ip = clientIp(req);
const rl = await limits.apiIp(ip, { limit: 240, window: '5 m' });
if (!rl.success) return rateLimited(res, rl, 'too many requests to the 3D OpenAPI doc');
const entries = await loadCatalog();
const doc = buildOpenApiDoc(entries, { origin: env.APP_ORIGIN, version: VERSION });
// Catalog changes only on deploy — safe to CDN-cache briefly.
return json(res, 200, doc, {
'cache-control': 'public, s-maxage=300, stale-while-revalidate=600',
});
});