project-nomad/admin/app/controllers/openapi_controller.ts

67 lines
2.1 KiB
TypeScript

import { createRequire } from 'node:module'
import { readFileSync } from 'node:fs'
import type { HttpContext } from '@adonisjs/core/http'
import { buildOpenApiDocument } from '#start/openapi/generator'
const require = createRequire(import.meta.url)
/**
* The self-contained Scalar browser bundle, read from the installed
* `@scalar/api-reference` package and cached in memory. Served from this app
* (not a CDN) so the API reference works on an offline appliance.
*/
let scalarBundle: string | null = null
function getScalarBundle(): string {
if (scalarBundle === null) {
const entry = require.resolve('@scalar/api-reference')
const marker = '/node_modules/@scalar/api-reference/'
const root = entry.slice(0, entry.lastIndexOf(marker) + marker.length - 1)
scalarBundle = readFileSync(`${root}/dist/browser/standalone.js`, 'utf-8')
}
return scalarBundle
}
/**
* The reference page mounts Scalar against our locally served bundle and spec.
* Scalar auto-detects the `#api-reference` element and reads `data-url`.
*/
const REFERENCE_HTML = `<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Nomad Admin API Reference</title>
</head>
<body>
<script id="api-reference" data-url="/api/openapi.json"></script>
<script src="/reference/assets/standalone.js"></script>
</body>
</html>`
export default class OpenApiController {
/**
* The generated OpenAPI 3.1 document.
*/
async spec({ response }: HttpContext) {
return response.json(buildOpenApiDocument())
}
/**
* The interactive Scalar API reference page.
*/
async reference({ response }: HttpContext) {
return response.header('content-type', 'text/html').send(REFERENCE_HTML)
}
/**
* The locally bundled Scalar JS (kept off any CDN for offline use).
*/
async standalone({ response }: HttpContext) {
return response
.header('content-type', 'application/javascript; charset=utf-8')
.header('cache-control', 'public, max-age=86400')
.send(getScalarBundle())
}
}