UNPKG

@getanthill/datastore

Version:

Event-Sourced Datastore

153 lines (121 loc) 3.73 kB
import type { ResponseError, Services } from '../typings'; import express, { ErrorRequestHandler, RequestHandler } from 'express'; import bodyParser from 'body-parser'; import { authenticate, OpenAPIMiddleware, errorHandler, getTokensByRole, authz, obligations, } from './middleware'; import { build as buildSpec } from './spec'; import modelsRoutes from './models'; import adminRoutes from './admin'; import streamRoutes from './stream'; import aggregateRoutes from './aggregate'; async function initOpenApi(services: Services) { const cacheEntry = await services.models.getFromCache( 'open_api_specification', ); const oas = cacheEntry ? JSON.parse(cacheEntry.oas) : buildSpec(services.models); if (!cacheEntry) { services.models.setToCache('open_api_specification', { oas: JSON.stringify(oas), }); } services.telemetry.logger.debug( '[API] Building Open API 3.0 Specification...', ); return new OpenAPIMiddleware( { secret: services.config.security.apiSecret, specification: oas, warnOnInvalidSpecificationOnly: services.config.features.api.openAPI.warnOnInvalidSpecificationOnly, telemetry: services.telemetry, services, }, services.config.mode === 'development' || services.config.features.api.updateSpecOnModelsChange === true ? async () => { const _oas = buildSpec(services.models); services.models.setToCache('open_api_specification', { oas: JSON.stringify(_oas), }); return _oas; } : null, ); } async function routes(services: Services) { const app = express.Router({ mergeParams: true }); let openApi; app .use(bodyParser.urlencoded({ extended: false })) .use(bodyParser.json({ limit: services.config.features.api.json.limit })); if (services.config.authz.isEnabled === true) { app.use(authz(services)); } if (services.config.features.api.openAPI.isEnabled === true) { openApi = await initOpenApi(services); app.use(openApi.registerInputValidation()); } app.use(obligations(services)); if (services.config.features.api.aggregate === true) { services.telemetry.logger.info('[Feature] Aggregation API enabled'); app.use( '/aggregate', (req, res, next) => { req.setTimeout(services.config.features.api.timeout.aggregate); next(); }, authenticate(getTokensByRole(services.config.security.tokens, 'read')), aggregateRoutes(services), ); } app .use('/admin', adminRoutes(services, openApi)) .use( '/stream', authenticate(getTokensByRole(services.config.security.tokens, 'read')), streamRoutes(services), ) .use( '/:model', (req, res, next) => { if (req.params.model.startsWith('internal_')) { const err: ResponseError = new Error('Protected model'); err.status = 403; return next(err); } req.setTimeout(services.config.features.api.timeout.models); next(); }, modelsRoutes(services), ); app.use(obligations(services)); if (openApi && services.config.features.api.openAPI.isEnabled === true) { app.use(openApi.registerOutputValidation()); } /* istanbul ignore next */ const jsonMiddleware = ( _req: express.Request, res: express.Response, next: express.NextFunction, ) => { // @ts-ignore if (res.body) { // @ts-ignore return res.json(res.body); } /* istanbul ignore next */ next(); }; app.use(jsonMiddleware as RequestHandler); app.use(errorHandler(services) as ErrorRequestHandler); return app; } export default routes;