bugwarden
Version:
BugWarden is an open-source Node.js module that guards your applications against bugs and errors. With integrated error handling and reporting, it ensures real-time monitoring of server-side issues, alerting you on services like Slack and email, helping y
336 lines (330 loc) • 11.3 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
// src/app.ts
var app_exports = {};
__export(app_exports, {
bugwarden: () => bugwarden
});
module.exports = __toCommonJS(app_exports);
// src/bugwarden_log_properties.ts
var BugwardenLogProperties = class {
constructor(ip, timestamp, method, originalURL, httpVersion, statusCode, contentLength, referrer, userAgent, responseTime) {
this.ip = ip;
this.timestamp = timestamp;
this.method = method;
this.originalURL = originalURL;
this.httpVersion = httpVersion;
this.statusCode = statusCode;
this.contentLength = contentLength;
this.referrer = referrer;
this.userAgent = userAgent;
this.responseTime = responseTime;
this.ip = ip;
this.timestamp = timestamp;
this.method = method;
this.originalURL = originalURL;
this.httpVersion = httpVersion;
this.statusCode = statusCode;
this.contentLength = contentLength;
this.referrer = referrer;
this.userAgent = userAgent;
this.responseTime = responseTime;
}
};
// src/bugwarden_log_level_color_palette.ts
var BugwardenLogLevelColorsPalette = {
VERBOSE: "\x1B[90m",
// Gray
LOG: "\x1B[36m",
DEBUG: "\x1B[34m",
// Blue
ERROR: "\x1B[31m",
// Red
WARNING: "\x1B[33m"
// Yellow
};
// src/utility.ts
function getCurrentTimestamp() {
const now = /* @__PURE__ */ new Date();
return now.toLocaleString("en-US", { hour12: true });
}
function coloredLogs(text, statusCode) {
let colorCode = statusCode >= 200 && statusCode < 300 ? "\x1B[32m" : statusCode >= 300 && statusCode < 400 ? "\x1B[36m" : statusCode >= 400 && statusCode < 500 ? "\x1B[33m" : statusCode >= 500 ? "\x1B[31m" : "\x1B[0m";
return `${colorCode}${text}\x1B[0m`;
}
function bugwardenLog(message, logLevel = "LOG") {
const timestamp = getCurrentTimestamp();
const logColor = BugwardenLogLevelColorsPalette[logLevel];
const logMessage = `${logColor}${timestamp} - ${logLevel} [Bugwarden] ${message}\x1B[0m`;
console.log(logMessage);
}
function processLog(req, res, elapsedTime, logging) {
let log = "";
const statusCode = res.statusCode;
if (Array.isArray(logging)) {
const customLogs = [];
logging.forEach((log2) => {
switch (log2) {
case "ip":
customLogs.push(`${"ip" /* IP */}: ${req.ip}`);
break;
case "timestamp":
customLogs.push(
`${"timestamp" /* TIMESTAMP */}: ${(/* @__PURE__ */ new Date()).toUTCString()}`
);
break;
case "method":
customLogs.push(`${"method" /* METHOD */}: ${req.method}`);
break;
case "originalURL":
customLogs.push(
`${"original-url" /* ORIGINAL_URL */}: ${req.originalUrl}`
);
break;
case "httpVersion":
customLogs.push(
`${"http-version" /* HTTP_VERSION */}: HTTP/${req.httpVersion}`
);
break;
case "statusCode":
customLogs.push(
`${"status-code" /* STATUS_CODE */}: ${res.statusCode}`
);
break;
case "contentLength":
customLogs.push(
`${"content-length" /* CONTENT_LENGTH */}: ${res.getHeader("content-length") || 0}`
);
break;
case "referrer":
customLogs.push(
`${"referer" /* REFERRER */}: ${req.get("referrer") || "-"}`
);
break;
case "userAgent":
customLogs.push(
`${"user-agent" /* USER_AGENT */}: ${req.get("user-agent")}`
);
break;
case "responseTime":
customLogs.push(
`${"response-time" /* RESPONSE_TIME */}: ${elapsedTime}ms`
);
break;
default:
break;
}
});
log = customLogs.join("\n");
} else if (logging === true || logging === void 0) {
const allProperties = new BugwardenLogProperties(
`${"ip" /* IP */}: ${req.ip}`,
// ip
`${"timestamp" /* TIMESTAMP */}: ${(/* @__PURE__ */ new Date()).toUTCString()}`,
// timestamp
`${"method" /* METHOD */}: ${req.method}`,
// method
`${"original-url" /* ORIGINAL_URL */}: ${req.originalUrl}`,
// original url
`${"http-version" /* HTTP_VERSION */}: HTTP/${req.httpVersion}`,
// http version
`${"status-code" /* STATUS_CODE */}: ${res.statusCode}`,
// status code
`${"content-length" /* CONTENT_LENGTH */}: ${res.getHeader("content-length") || 0}`,
// content length
`${"referer" /* REFERRER */}: ${req.get("referrer") || "-"}`,
// referrer
`${"user-agent" /* USER_AGENT */}: ${req.get("user-agent")}`,
// user agent
`${"response-time" /* RESPONSE_TIME */}: ${elapsedTime}ms`
// response time
);
log = Object.values(allProperties).join("\n");
} else {
log = "";
}
return log ? "\n" + coloredLogs(log, statusCode) + "\n" : "";
}
function processSlackNotification(slackConfiguration, req, res, timestamp, elapsedTime) {
return __async(this, null, function* () {
var _a, _b, _c, _d, _e, _f, _g;
const originalUrl = ((_a = req.route) == null ? void 0 : _a.path) || req.originalUrl;
const statusCode = res.statusCode;
let isStatusCodeIncluded = false;
let isEndpointIncluded = false;
if (!((_b = slackConfiguration.webhookUrl) == null ? void 0 : _b.length)) {
bugwardenLog("Please provide a webhook URL for sending slack notification");
return;
}
const webhookUrl = slackConfiguration.webhookUrl;
for (const config of slackConfiguration == null ? void 0 : slackConfiguration.notificationConfig) {
if (!((_c = config.message) == null ? void 0 : _c.length) || !((_d = config.onStatus) == null ? void 0 : _d.length) || !((_e = config.routes) == null ? void 0 : _e.length)) {
bugwardenLog(
"Config should include all <message> <routes> and <onStatus>",
"ERROR"
);
break;
}
const onStatuses = (_f = config.onStatus) == null ? void 0 : _f.split(",");
const routes = (_g = config.routes) == null ? void 0 : _g.split(",");
const message = config.message.replace(`{${"ip" /* IP */}}`, `${req == null ? void 0 : req.ip}`).replace(
`{${"timestamp" /* TIMESTAMP */}}`,
timestamp.toDateString()
).replace(`{${"method" /* METHOD */}}`, req.method).replace(`{${"original-url" /* ORIGINAL_URL */}}`, originalUrl).replace(`{${"http-version" /* HTTP_VERSION */}}`, req.httpVersion).replace(`{${"status-code" /* STATUS_CODE */}}`, `${statusCode}`).replace(
`{${"content-length" /* CONTENT_LENGTH */}}`,
`${res.getHeader("content-length") || 0}`
).replace(
`{${"referer" /* REFERRER */}}`,
`${req.get("referrer") || "-"}`
).replace(`${"response-time" /* RESPONSE_TIME */}`, `${elapsedTime}`).replace(
`{${"user-agent" /* USER_AGENT */}}`,
`${req.get("user-agent")}`
);
if (routes.includes("all")) {
isStatusCodeIncluded = true;
} else {
for (const status of onStatuses) {
let found = false;
switch (status) {
case "1xx":
if (statusCode >= 100 && statusCode < 200) {
isStatusCodeIncluded = true;
found = true;
}
break;
case "2xx":
if (statusCode >= 200 && statusCode < 300) {
isStatusCodeIncluded = true;
found = true;
}
break;
case "3xx":
if (statusCode >= 300 && statusCode < 400) {
isStatusCodeIncluded = true;
found = true;
}
break;
case "4xx":
if (statusCode >= 400 && statusCode < 500) {
isStatusCodeIncluded = true;
found = true;
}
break;
case "5xx":
if (statusCode >= 500 && statusCode < 600) {
isStatusCodeIncluded = true;
found = true;
}
break;
default:
break;
}
if (found)
break;
}
}
for (const route of routes) {
if (route.includes("*")) {
const startingOfEndpoint = route.replace("/*", "");
if (originalUrl.startsWith(startingOfEndpoint)) {
isEndpointIncluded = true;
break;
}
} else {
if (originalUrl === route) {
isEndpointIncluded = true;
break;
}
}
}
if (isStatusCodeIncluded && isEndpointIncluded) {
yield postSlackNotification(webhookUrl, message);
}
}
});
}
function postSlackNotification(webhookUrl, text) {
return __async(this, null, function* () {
try {
yield fetch(webhookUrl, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
text
})
});
} catch (e) {
bugwardenLog(`Cannot access slack webhook url:${e}`, "ERROR");
}
});
}
// src/app.ts
function bugwarden(options) {
return (req, res, next) => {
const startTimeMS = Date.now();
res.on("finish", () => __async(this, null, function* () {
const elapsedTime = Date.now() - startTimeMS;
const timestamp = /* @__PURE__ */ new Date();
const allowedAppLogs = processLog(
req,
res,
elapsedTime,
options == null ? void 0 : options.logging
);
if (allowedAppLogs)
console.log(allowedAppLogs);
if (options == null ? void 0 : options.configureSlackNotification) {
yield processSlackNotification(
options.configureSlackNotification,
req,
res,
timestamp,
elapsedTime
);
}
}));
next();
};
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
bugwarden
});
;