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
JavaScript
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