UNPKG

apitally

Version:

Simple API monitoring & analytics for REST APIs built with Express, Fastify, NestJS, AdonisJS, Hono, H3, Elysia, and Koa.

208 lines (199 loc) 7.73 kB
var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); // src/adonisjs/middleware.ts import { performance } from "perf_hooks"; // src/common/consumerRegistry.ts var consumerFromStringOrObject = /* @__PURE__ */ __name((consumer) => { var _a, _b; if (typeof consumer === "string") { consumer = String(consumer).trim().substring(0, 128); return consumer ? { identifier: consumer } : null; } else { consumer.identifier = String(consumer.identifier).trim().substring(0, 128); consumer.name = (_a = consumer.name) == null ? void 0 : _a.trim().substring(0, 64); consumer.group = (_b = consumer.group) == null ? void 0 : _b.trim().substring(0, 64); return consumer.identifier ? consumer : null; } }, "consumerFromStringOrObject"); // src/common/headers.ts function parseContentLength(contentLength) { if (contentLength === void 0 || contentLength === null) { return void 0; } if (typeof contentLength === "number") { return contentLength; } if (typeof contentLength === "string") { const parsed = parseInt(contentLength); return isNaN(parsed) ? void 0 : parsed; } if (Array.isArray(contentLength)) { return parseContentLength(contentLength[0]); } return void 0; } __name(parseContentLength, "parseContentLength"); // src/common/requestLogger.ts import AsyncLock from "async-lock"; import { Buffer as Buffer3 } from "buffer"; import { randomUUID as randomUUID2 } from "crypto"; import { unlinkSync, writeFileSync } from "fs"; import { tmpdir as tmpdir2 } from "os"; import { join as join2 } from "path"; // src/common/sentry.ts var sentry; (async () => { try { sentry = await import("@sentry/node"); } catch (e) { } })(); // src/common/serverErrorCounter.ts import { createHash } from "crypto"; // src/common/tempGzipFile.ts import { Buffer as Buffer2 } from "buffer"; import { randomUUID } from "crypto"; import { createWriteStream, readFile } from "fs"; import { unlink } from "fs/promises"; import { tmpdir } from "os"; import { join } from "path"; import { createGzip } from "zlib"; // src/common/requestLogger.ts var BODY_TOO_LARGE = Buffer3.from("<body too large>"); var BODY_MASKED = Buffer3.from("<masked>"); function convertHeaders(headers) { if (!headers) { return []; } if (headers instanceof Headers) { return Array.from(headers.entries()); } return Object.entries(headers).flatMap(([key, value]) => { if (value === void 0) { return []; } if (Array.isArray(value)) { return value.map((v) => [ key, v ]); } return [ [ key, value.toString() ] ]; }); } __name(convertHeaders, "convertHeaders"); // src/adonisjs/middleware.ts var _ApitallyMiddleware = class _ApitallyMiddleware { async handle(ctx, next) { const client = await ctx.containerResolver.make("apitallyClient"); const logsContext = await ctx.containerResolver.make("apitallyLogsContext"); if (!client.isEnabled() || ctx.request.method().toUpperCase() === "OPTIONS") { await next(); return; } return logsContext.run([], async () => { var _a, _b; const path = (_a = ctx.route) == null ? void 0 : _a.pattern; const timestamp = Date.now() / 1e3; const startTime = performance.now(); await next(); const responseTime = performance.now() - startTime; const requestSize = parseContentLength(ctx.request.header("content-length")); const requestContentType = (_b = ctx.request.header("content-type")) == null ? void 0 : _b.toString(); let responseStatus = ctx.response.getStatus(); let responseHeaders = ctx.response.getHeaders(); let responseSize; let responseContentType; const consumer = ctx.apitallyConsumer ? consumerFromStringOrObject(ctx.apitallyConsumer) : null; client.consumerRegistry.addOrUpdateConsumer(consumer); const onWriteHead = /* @__PURE__ */ __name((statusCode, headers) => { var _a2; responseStatus = statusCode; responseHeaders = headers; responseSize = parseContentLength(headers["content-length"]); responseContentType = (_a2 = headers["content-type"]) == null ? void 0 : _a2.toString(); if (path) { client.requestCounter.addRequest({ consumer: consumer == null ? void 0 : consumer.identifier, method: ctx.request.method(), path, statusCode: responseStatus, responseTime, requestSize, responseSize }); if (responseStatus === 422 && ctx.apitallyError && "code" in ctx.apitallyError && "messages" in ctx.apitallyError && ctx.apitallyError.code === "E_VALIDATION_ERROR" && Array.isArray(ctx.apitallyError.messages)) { ctx.apitallyError.messages.forEach((message) => { client.validationErrorCounter.addValidationError({ consumer: consumer == null ? void 0 : consumer.identifier, method: ctx.request.method(), path, loc: message.field, msg: message.message, type: message.rule }); }); } if (responseStatus === 500 && ctx.apitallyError) { client.serverErrorCounter.addServerError({ consumer: consumer == null ? void 0 : consumer.identifier, method: ctx.request.method(), path, type: ctx.apitallyError.name, msg: ctx.apitallyError.message, traceback: ctx.apitallyError.stack || "" }); } } }, "onWriteHead"); const originalWriteHead = ctx.response.response.writeHead; ctx.response.response.writeHead = (...args) => { originalWriteHead.apply(ctx.response.response, args); onWriteHead(args[0], typeof args[1] === "string" ? args[2] : args[1]); return ctx.response.response; }; if (client.requestLogger.enabled) { const logs = logsContext.getStore(); const onEnd = /* @__PURE__ */ __name((chunk) => { const requestBody = client.requestLogger.config.logRequestBody && client.requestLogger.isSupportedContentType(requestContentType) ? ctx.request.raw() : void 0; const responseBody = client.requestLogger.config.logResponseBody && client.requestLogger.isSupportedContentType(responseContentType) ? chunk : void 0; client.requestLogger.logRequest({ timestamp, method: ctx.request.method(), path, url: ctx.request.completeUrl(true), headers: convertHeaders(ctx.request.headers()), size: requestSize, consumer: consumer == null ? void 0 : consumer.identifier, body: requestBody ? Buffer.from(requestBody) : void 0 }, { statusCode: responseStatus, responseTime: responseTime / 1e3, headers: convertHeaders(responseHeaders), size: responseSize, body: responseBody ? Buffer.from(responseBody) : void 0 }, ctx.apitallyError, logs); }, "onEnd"); const originalEnd = ctx.response.response.end; ctx.response.response.end = (...args) => { originalEnd.apply(ctx.response.response, args); onEnd(typeof args[0] !== "function" ? args[0] : void 0); return ctx.response.response; }; } }); } }; __name(_ApitallyMiddleware, "ApitallyMiddleware"); var ApitallyMiddleware = _ApitallyMiddleware; export { ApitallyMiddleware as default }; //# sourceMappingURL=middleware.js.map