@vtex/api
Version:
VTEX I/O API client
198 lines (197 loc) • 8.83 kB
JavaScript
;
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();