UNPKG

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
"use strict"; 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 });