UNPKG

@vtex/api

Version:
198 lines (197 loc) • 8.83 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.startWorker = void 0; const http_1 = require("http"); const koa_1 = __importDefault(require("koa")); const koa_compress_1 = __importDefault(require("koa-compress")); const koa_router_1 = __importDefault(require("koa-router")); const ramda_1 = require("ramda"); const constants_1 = require("../../constants"); const MetricsAccumulator_1 = require("../../metrics/MetricsAccumulator"); const loaders_1 = require("../loaders"); const console_1 = require("../logger/console"); const loggerTypes_1 = require("../logger/loggerTypes"); const requestMetricsMiddleware_1 = require("../metrics/requestMetricsMiddleware"); const TracerSingleton_1 = require("../tracing/TracerSingleton"); const tracingMiddlewares_1 = require("../tracing/tracingMiddlewares"); const listeners_1 = require("./listeners"); const handlers_1 = require("./runtime/builtIn/handlers"); const middlewares_1 = require("./runtime/builtIn/middlewares"); const events_1 = require("./runtime/events"); const router_1 = require("./runtime/events/router"); const graphql_1 = require("./runtime/graphql"); const http_2 = require("./runtime/http"); const error_1 = require("./runtime/http/middlewares/error"); const rateLimit_1 = require("./runtime/http/middlewares/rateLimit"); const router_2 = require("./runtime/http/router"); const routes_1 = require("./runtime/http/routes"); const statusTrack_1 = require("./runtime/statusTrack"); const tokenBucket_1 = require("./runtime/utils/tokenBucket"); const upSignal = () => { const data = JSON.stringify({ statusTrack: true }); const options = { headers: { 'Content-Type': 'application/json', }, host: 'localhost', method: 'POST', path: '/_up', port: 5000, }; const req = (0, http_1.request)(options); req.write(data); req.end(); }; const isUpSignal = (message) => message === constants_1.UP_SIGNAL; const onMessage = (service) => (message) => { if (isUpSignal(message)) { upSignal(); (0, routes_1.logAvailableRoutes)(service); } else if ((0, statusTrack_1.isStatusTrack)(message)) { (0, statusTrack_1.trackStatus)(); } else { listeners_1.logger.warn({ content: message, message: 'Master sent message', pid: process.pid, }); } }; const createAppHttpHandlers = ({ config: { routes, clients } }, serviceJSON, globalLimiter) => { if (routes && clients) { return Object.keys(routes).reduce((acc, routeId) => { var _a; const serviceRoute = (_a = serviceJSON.routes) === null || _a === void 0 ? void 0 : _a[routeId]; if (!serviceRoute) { throw new Error(`Could not find route: ${routeId}. Please add ${routeId} route in your service.json file`); } const { path: servicePath, public: publicRoute = false, extensible = false, } = serviceRoute; if (publicRoute || extensible) { acc.pub[routeId] = { handler: (0, http_2.createPublicHttpRoute)(clients, routes[routeId], serviceRoute, routeId, globalLimiter), path: servicePath, }; } else { acc.pvt[routeId] = { handler: (0, http_2.createPrivateHttpRoute)(clients, routes[routeId], serviceRoute, routeId, globalLimiter), path: `/:account/:workspace${servicePath.replace(/\*([^/]*)/g, ':$1*')}`, }; } return acc; }, { pub: {}, pvt: {}, }); } return null; }; const routerFromPrivateHttpHandlers = (routes) => Object.values(routes).reduce((router, { handler, path }) => router.all(path, handler), new koa_router_1.default()); const createAppGraphQLHandler = ({ config: { graphql, clients } }, { routes }, globalLimiter) => { const route = routes === null || routes === void 0 ? void 0 : routes[graphql_1.GRAPHQL_ROUTE]; if (graphql && route && clients) { return { pvt: { [graphql_1.GRAPHQL_ROUTE]: { handler: (0, graphql_1.createGraphQLRoute)(graphql, clients, route, graphql_1.GRAPHQL_ROUTE, globalLimiter), path: `/:account/:workspace${route.path}`, }, }, }; } return null; }; const createAppEventHandlers = ({ config: { events, clients } }, serviceJSON, globalLimiter) => { if (events && clients) { return Object.keys(events).reduce((acc, eventId) => { var _a; const serviceEvent = (_a = serviceJSON.events) === null || _a === void 0 ? void 0 : _a[eventId]; acc[eventId] = (0, events_1.createEventHandler)(clients, eventId, events[eventId], serviceEvent, globalLimiter); return acc; }, {}); } return null; }; const createRuntimeHttpHandlers = (appEventHandlers, serviceJSON) => ({ pvt: { __events: { handler: (0, router_1.routerFromEventHandlers)(appEventHandlers), path: '/:account/:workspace/_events', }, __healthCheck: { handler: (0, handlers_1.healthcheckHandler)(serviceJSON), path: '/healthcheck', }, __metricsLogger: { handler: handlers_1.metricsLoggerHandler, path: '/_metrics', }, __statusTrack: { handler: statusTrack_1.statusTrackHandler, path: '/_status', }, __whoami: { handler: (0, handlers_1.whoAmIHandler)(serviceJSON), path: '/:account/:workspace/_whoami', }, }, }); const scaleClientCaches = (scaleFactor, options) => Object.entries(options || {}).forEach(([name, opts]) => { if (opts && opts.memoryCache && scaleFactor > 1) { const previous = opts.memoryCache.storage.max; const current = previous / scaleFactor; opts.memoryCache.storage.max = current; (0, console_1.logOnceToDevConsole)(`Scaling ${name} cache capacity from ${previous} to ${current}`, loggerTypes_1.LogLevel.Warn); } }); const startWorker = (serviceJSON) => { var _a, _b; (0, listeners_1.addProcessListeners)(); const tracer = TracerSingleton_1.TracerSingleton.getTracer(); const app = new koa_1.default(); app.proxy = true; app .use(error_1.error) .use((0, middlewares_1.prometheusLoggerMiddleware)()) .use((0, tracingMiddlewares_1.addTracingMiddleware)(tracer)) .use((0, requestMetricsMiddleware_1.addRequestMetricsMiddleware)()) .use((0, middlewares_1.addMetricsLoggerMiddleware)()) .use((0, rateLimit_1.concurrentRateLimiter)((_a = serviceJSON === null || serviceJSON === void 0 ? void 0 : serviceJSON.rateLimitPerReplica) === null || _a === void 0 ? void 0 : _a.concurrent)) .use((0, koa_compress_1.default)()) .use(middlewares_1.recorderMiddleware); const service = (0, loaders_1.getService)(); const { config: { clients } } = service; if (clients) { scaleClientCaches(serviceJSON.workers, clients.options); } const globalLimiter = (0, tokenBucket_1.createTokenBucket)((_b = serviceJSON === null || serviceJSON === void 0 ? void 0 : serviceJSON.rateLimitPerReplica) === null || _b === void 0 ? void 0 : _b.perMinute); const appHttpHandlers = createAppHttpHandlers(service, serviceJSON, globalLimiter); const appEventHandlers = createAppEventHandlers(service, serviceJSON, globalLimiter); const appGraphQLHandlers = createAppGraphQLHandler(service, serviceJSON, globalLimiter); const runtimeHttpHandlers = createRuntimeHttpHandlers(appEventHandlers, serviceJSON); const httpHandlers = [ appHttpHandlers, appGraphQLHandlers, runtimeHttpHandlers, ] .filter(x => x != null) // TODO: Fix ramda typings. Apparently there was an update that broke things .reduce(ramda_1.mergeDeepRight); if (httpHandlers === null || httpHandlers === void 0 ? void 0 : httpHandlers.pub) { const publicHandlersRouter = (0, router_2.routerFromPublicHttpHandlers)(httpHandlers.pub); app.use(publicHandlersRouter); } if (httpHandlers === null || httpHandlers === void 0 ? void 0 : httpHandlers.pvt) { const privateHandlersRouter = routerFromPrivateHttpHandlers(httpHandlers.pvt); app.use(privateHandlersRouter.routes()); } process.on('message', onMessage(serviceJSON)); return app; }; exports.startWorker = startWorker; global.metrics = new MetricsAccumulator_1.MetricsAccumulator();