u
This commit is contained in:
111
src/api/routes/docs.js
Normal file
111
src/api/routes/docs.js
Normal file
@@ -0,0 +1,111 @@
|
||||
import express from 'express';
|
||||
import swaggerUiDist from 'swagger-ui-dist';
|
||||
import { getOpenApiSpec } from '../openapiSpec.js';
|
||||
|
||||
const swaggerDistPath = swaggerUiDist.getAbsoluteFSPath();
|
||||
|
||||
function renderDocsHtml() {
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Dash API</title>
|
||||
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" />
|
||||
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
|
||||
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
|
||||
<style>
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
*, *:before, *:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
background: #fafafa;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
<script src="./swagger-ui-bundle.js"></script>
|
||||
<script src="./swagger-ui-standalone-preset.js"></script>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
window.ui = SwaggerUIBundle({
|
||||
url: './openapi.json',
|
||||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
plugins: [
|
||||
SwaggerUIBundle.plugins.DownloadUrl
|
||||
],
|
||||
layout: 'StandaloneLayout',
|
||||
requestInterceptor: (req) => {
|
||||
req.credentials = 'include';
|
||||
return req;
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers documentation and interactive API tester under /docs/.
|
||||
* @param {import('express').Application} app - Express app
|
||||
* @param {string} [basePath=''] - Base path (e.g. '/api')
|
||||
*/
|
||||
export function register(app, basePath = '') {
|
||||
const docsRouter = express.Router();
|
||||
|
||||
// Redirect /docs (or /api/docs) to /docs/ so relative assets resolve properly
|
||||
docsRouter.use((req, res, next) => {
|
||||
const rawPath = req.originalUrl.split('?')[0];
|
||||
if (rawPath === '/docs' || rawPath === '/api/docs' || rawPath === '/api/docs/') {
|
||||
const query = req.originalUrl.includes('?') ? '?' + req.originalUrl.split('?')[1] : '';
|
||||
return res.redirect(301, '/docs/' + query);
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
// Serve OpenAPI 3.0 specification JSON
|
||||
docsRouter.get('/openapi.json', (_req, res) => {
|
||||
try {
|
||||
const spec = getOpenApiSpec();
|
||||
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
||||
res.send(JSON.stringify(spec, null, 2));
|
||||
} catch (err) {
|
||||
console.error('Failed to generate OpenAPI spec:', err);
|
||||
res.status(500).json({ error: 'Failed to generate OpenAPI specification' });
|
||||
}
|
||||
});
|
||||
|
||||
// Serve HTML UI for /docs/ and /docs/index.html
|
||||
docsRouter.get('/', (_req, res) => {
|
||||
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
||||
res.send(renderDocsHtml());
|
||||
});
|
||||
|
||||
docsRouter.get('/index.html', (_req, res) => {
|
||||
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
||||
res.send(renderDocsHtml());
|
||||
});
|
||||
|
||||
// Serve static assets from swagger-ui-dist (CSS, JS bundles, icons)
|
||||
docsRouter.use(express.static(swaggerDistPath, { index: false }));
|
||||
|
||||
// Mount on /docs
|
||||
app.use('/docs', docsRouter);
|
||||
|
||||
// Also mount on /api/docs and alias /api/openapi.json if basePath is set
|
||||
if (basePath) {
|
||||
app.use(basePath + '/docs', docsRouter);
|
||||
app.get(basePath + '/openapi.json', (_req, res) => res.redirect(301, '/docs/openapi.json'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user