import express from 'express'; import swaggerUiDist from 'swagger-ui-dist'; import { getOpenApiSpec } from '../openapiSpec.js'; const swaggerDistPath = swaggerUiDist.getAbsoluteFSPath(); function renderDocsHtml() { return ` Dash API
`; } /** * 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')); } }