@ozmap/logger
Version:
DevOZ logger module.
150 lines (149 loc) • 5.23 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkRequestHeader = exports.setupLogServer = void 0;
const cluster_1 = __importDefault(require("cluster"));
const http_1 = __importDefault(require("http"));
const errors_1 = require("./errors");
const routes_1 = __importDefault(require("./routes"));
/**
* Function to create HTTP server on the primary process.
*
* @param port The interface port for the server to listen on.
* @param address The interface address for the server to listen on.
* @param logger The logger instance.
* @returns The HTTP server that was setup.
*/
function setupLogServer(port, address, logger) {
var _a;
if (cluster_1.default.isWorker)
return;
if ((_a = process.env.OZLOGGER_HTTP) === null || _a === void 0 ? void 0 : _a.match(/false/i))
return;
process.env.OZLOGGER_HTTP = 'false';
return http_1.default
.createServer(handleRequest(logger))
.listen(port, address, () => {
logger.info(`Log server started listening at ${address}:${port}`);
});
}
exports.setupLogServer = setupLogServer;
/**
* Closure for handling incoming HTTP requests.
*
* @param logger The logger client.
* @returns The HTTP request handler.
*/
function handleRequest(logger) {
return async (req, res) => {
const reqIsJson = isContentTypeJson(req.headers);
const resIsJson = isAcceptTypeJson(req.headers);
Object.assign(req, { reqIsJson, resIsJson });
if (req.method !== 'GET') {
try {
await parseBody(req, reqIsJson ? 'json' : 'text');
}
catch (e) {
if (e instanceof errors_1.HttpError)
return e.respond(res, resIsJson);
logger.error(e);
return new errors_1.HttpError(`Something went wrong while processing your request content.`, 500).respond(res, resIsJson);
}
}
return router(req, res).catch((e) => {
if (e instanceof errors_1.HttpError)
return e.respond(res, resIsJson);
logger.error(e);
return new errors_1.HttpError(`Something went wrong while handling your request.`, 500).respond(res, resIsJson);
});
};
}
/**
* Method for processing request contents.
*
* @param req The incoming HTTP request.
* @param type The content type.
*/
async function parseBody(req, type) {
const data = [];
for await (const chunk of req) {
data.push(chunk);
if (limit(Buffer.concat(data), 5)) {
req.socket.destroy();
throw new errors_1.HttpError('Content too large', 413);
}
}
try {
Object.assign(req, {
body: type === 'json'
? JSON.parse(Buffer.concat(data).toString())
: Buffer.concat(data).toString()
});
}
catch (e) {
throw new errors_1.HttpError('Unprocessable content', 422);
}
}
/**
* Function for checking if HTTP header
* is present and has the given value.
*
* @param headers The HTTP request headers.
* @param header The HTTP header being checked.
* @param value The HTTP header value being verified.
* @returns If the header is present and has the given value.
*/
function checkRequestHeader(headers, header, value) {
var _a, _b, _c;
if (!(header in headers))
return false;
return Array.isArray(headers[header])
? (_a = headers[header].some((h) => h.trim() === value)) !== null && _a !== void 0 ? _a : false
: (_c = (_b = headers[header]) === null || _b === void 0 ? void 0 : _b.split(',').some((h) => h.trim() === value)) !== null && _c !== void 0 ? _c : false;
}
exports.checkRequestHeader = checkRequestHeader;
/**
* Validation method for Content-Type HTTP
* header having the application/json type.
*
* @param headers The HTTP request headers.
* @returns If the request content is JSON.
*/
function isContentTypeJson(headers) {
return checkRequestHeader(headers, 'content-type', 'application/json');
}
/**
* Validation method for Accept HTTP header
* having the application/json type.
*
* @param headers The HTTP request headers.
* @returns If JSON is accepted as response.
*/
function isAcceptTypeJson(headers) {
return checkRequestHeader(headers, 'accept', 'application/json');
}
/**
* Function for checking if data reached the allowed limit.
*
* @param data The current data being checked.
* @param mb The limit data size.
* @returns Whether or not the limit has been reached.
*/
function limit(data, mb) {
return data.length > mb * 1000000;
}
/**
* Router function to handle actions for different routes.
*
* @param req Incoming HTTP request.
* @param res Outgoing HTTP response.
*/
async function router(req, res) {
const route = `${req.method} ${req.url}`;
if (!(route in routes_1.default))
throw new errors_1.HttpError('Not found', 404);
await routes_1.default[route](req, res);
return res.writeHead(200).end();
}