import { schemas } from '@rocket.chat/core-typings'; import type { Route } from '@rocket.chat/http-router'; import { ajv, isOpenAPIJSONEndpoint } from '@rocket.chat/rest-typings'; import express from 'express'; import { WebApp } from 'meteor/webapp'; import swaggerUi from 'swagger-ui-express'; import { settings } from '../../settings'; import { API } from '../api'; import { getTrimmedServerVersion } from '../lib/getTrimmedServerVersion'; const app = express(); const getTypedRoutes = ( typedRoutes: Record>, { withUndocumented = false }: { withUndocumented?: boolean } = {}, ): Record> => { if (withUndocumented) { return typedRoutes; } return Object.entries(typedRoutes).reduce( (acc, [path, methods]) => { const filteredMethods = Object.entries(methods) .filter(([_, options]) => !options?.tags?.includes('Missing Documentation')) .reduce( (acc, [method, options]) => { acc[method] = options; return acc; }, {} as Record, ); if (Object.keys(filteredMethods).length > 0) { acc[path] = filteredMethods; } return acc; }, {} as Record>, ); }; const makeOpenAPIResponse = (paths: Record>) => ({ openapi: '3.0.3', info: { title: 'Rocket.Chat API', description: 'Rocket.Chat API', version: getTrimmedServerVersion(), }, servers: [ { url: settings.get('Site_Url'), }, ], components: { securitySchemes: { userId: { type: 'apiKey', in: 'header', name: 'X-User-Id', }, authToken: { type: 'apiKey', in: 'header', name: 'X-Auth-Token', }, }, schemas: schemas.components.schemas, }, schemas: schemas.components.schemas, paths, }); const openApiResponseSchema = ajv.compile>({ type: 'object', properties: { openapi: { type: 'string' }, info: { type: 'object' }, servers: { type: 'array' }, components: { type: 'object' }, paths: { type: 'object' }, schemas: { type: 'object' }, success: { type: 'boolean', enum: [true] }, }, required: ['openapi', 'info', 'paths', 'success'], additionalProperties: false, }); API.default.get( 'docs/json', { authRequired: false, query: isOpenAPIJSONEndpoint, response: { 200: openApiResponseSchema, }, }, function action() { const { withUndocumented = false } = this.queryParams; return API.default.success(makeOpenAPIResponse(getTypedRoutes(API.api.typedRoutes, { withUndocumented }))); }, ); app.use( '/api-docs', swaggerUi.serve, swaggerUi.setup(null, { swaggerOptions: { url: `${settings.get('Site_Url')}/api/docs/json`, }, }), ); WebApp.connectHandlers.use(app);