UNPKG

vmagic

Version:

vMagic is a RESTFul framework for NodeJS applications.

323 lines (277 loc) 11.4 kB
/*eslint max-statements: ["error", 100], max-lines: ["error", 500]*/ "use strict"; import { parse } from "url"; import FactoryController from "../Controller/FactoryController.js"; import Logger from "../Component/Logger.js"; import pkg from "../../package.json" assert {type: "json"}; const logger = new Logger(); class RequestHandler { constructor(application) { this.application = application; this.factoryController = new FactoryController(this.application); } async init() { await this.factoryController.init(); await this.loadCoreConfig(); } async loadCoreConfig() { const coreConfigPath = `${this.application.configPath}/core.json`; const coreConfigModule = await import(coreConfigPath, { assert: { type: 'json' } }); this.core = coreConfigModule.default; // O JSON será importado como um objeto } /** * [getPayload description] * @param {[type]} request [description] * @param {Function} callback [description] * @return {[type]} [description] */ getPayload(request) { return new Promise((resolve, reject) => { const contentType = request.headers["content-type"]; if (contentType && contentType.indexOf("multipart/form-data") > -1) { resolve({}); } else { let body = ""; request.on("data", data => { body += data; //Too much POST data, kill the connection! if (body.length > 1e6) { request.connection.destroy(); reject(); } }); request.on("end", () => { let payload = null; if (body !== "") { try { payload = JSON.parse(body); } catch (err) { logger.error("[Magic] Could not convert payload to JSON Object."); payload = body; } } logger.info(`[Magic] Payload ${JSON.stringify(payload, null, 4)}`); resolve(payload); }); } }); } /** * Convert the query string to JSON Object. * @param {Object} request - Object request. * @return {Object} Returns the query string. */ getQuery(request) { const query = parse(request.url, true).query; logger.info(`[Magic] QueryString ${JSON.stringify(query, null, 4)}`); return query; } /** * Its returns an object with url parts. * @param {Object} request - Http request. * @return {Object} result */ getParts(request) { let index = null; let params = null; index = request.url.indexOf("?"); if (index > -1) { params = request.url.substring(0, index).split("/"); } else { params = request.url.split("/"); } return [params[1], params[2], params[3]]; } /** * [terminate description] * @param {[type]} request [description] * @param {[type]} response [description] * @param {[type]} data [description] * @param {[type]} statusCode [description] * @return {[type]} [description] */ terminate(request, response, data, statusCode = 200) { this.request = request; this.response = response; this.data = data; if (statusCode) { this.response.writeHead(statusCode, { "Content-Type": "application/json" }); } else { this.response.writeHead(200, { "Content-Type": "application/json" }); } this.response.end(JSON.stringify(this.data)); this.request.connection.destroy(); } responseClient(response, request, controller, result) { const responseHeader = controller.responseHeader || { "Content-Type": "application/json; charset=utf8" }; response.writeHead(controller.statusCode, responseHeader); request.pipe(response); response.end(controller.responseHeader ? result : JSON.stringify(result)); } /** * This function is called whenever http request is received. * @param {Object} request . * @param {Object} response . * @return {VoidFunction} . */ async process(request, response) { if (!Reflect.has(this.core, "cors")) { //Website you wish to allow to connect. response.setHeader("Access-Control-Allow-Origin", "*"); //Request method you wish to allow. response.setHeader("Access-Control-Request-Method", "*"); //Request methods you wish to allow response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, PATCH"); //Request headers you wish to allow response.setHeader("Access-Control-Allow-Headers", "*"); //Set to true if you need the website to include cookies in the requests sent to the API (e.g. in case you use sessions) response.setHeader("Access-Control-Allow-Credentials", true); } else { const origin = request.headers.origin; for (const key in this.core.cors) { if (key === "Access-Control-Allow-Origin") { const allowedOrigins = this.core.cors[key]; if (Array.isArray(allowedOrigins) && allowedOrigins.includes(origin)) { response.setHeader(key, origin); } else if (typeof allowedOrigins === "string") { response.setHeader(key, allowedOrigins); } } else { response.setHeader(key, this.core.cors[key]); } } } const headersInfo = request.headers; headersInfo.url = request.url; headersInfo.method = request.method; headersInfo.statusCode = request.statusCode; logger.info(`[Magic] Request Data ${JSON.stringify(headersInfo, null, "\t")}`); const urlParts = this.getParts(request); const resource = urlParts[0]; let controller = null; let action = null; let id = null; try { let result = null; if (!resource && !urlParts[1]) { logger.warn(`[Magic] Version ${pkg.version}`); this.terminate(request, response, { name: "vMagic", version: pkg.version }); return; } //Get controller instance. controller = await this.factoryController.controller(resource); controller.headersInfo = headersInfo; //It must validate if exists action. const hasNumber = new RegExp("\\d", "gi"); if (!hasNumber.test(urlParts[1]) && urlParts[1] && typeof controller[urlParts[1]] === "undefined") { controller.statusCode = 404; throw new Error(`Action /${resource}/${urlParts[1]} not found.`); } //It must separate what is action and what is id. if (typeof controller[urlParts[1]] !== "undefined") { action = urlParts[1]; id = urlParts[2]; } else { id = urlParts[1]; } if (typeof controller.beforeFilter !== "undefined") { await controller.beforeFilter(); } if (typeof controller.init !== "undefined") { await controller.init(); } //Set status code whether is not exists. if (typeof controller.statusCode === "undefined" || controller.statusCode === "") { controller.statusCode = 200; } /* * If the http request method is GET */ if (request.method === "GET") { controller.query = this.getQuery(request); controller.id = id || null; if (action) { result = await controller[action](request, response); } else { result = await controller.get(request, response); } if (result) { this.responseClient(response, request, controller, result); } } /* * If the http request method is POST */ if (request.method === "POST") { controller.payload = await this.getPayload(request); controller.id = id || null; if (action) { result = await controller[action](request, response); } else { result = await controller.post(request, response); } if (result) { this.responseClient(response, request, controller, result); } } /* * If the http request method is PUT */ if (request.method === "PUT") { controller.payload = await this.getPayload(request); controller.id = id || null; if (action) { result = await controller[action](request, response); } else { result = await controller.put(request, response); } if (result) { this.responseClient(response, request, controller, result); } } /* * If the http request method is DELETE */ if (request.method === "DELETE") { controller.payload = await this.getPayload(request); controller.id = id || null; if (action) { result = await controller[action](request, response); } else { result = await controller.delete(request, response); } if (result) { this.responseClient(response, request, controller, result); } } /* * If the http request method is OPTIONS */ if (request.method === "OPTIONS") { response.writeHead(200); response.end(); } } catch (err) { const objError = { error: { title: "Request Failed", }, }; if (err.message === "RESOURCE_NOT_FOUND") { objError.error.statusCode = 400; objError.error.detail = `Resource /${resource} not found.`; this.terminate(request, response, objError, 400); } else { if (controller) { objError.error.statusCode = controller.statusCode; } else { objError.error.statusCode = 500; } objError.error.detail = err.message; this.terminate(request, response, objError, objError.error.statusCode); } logger.error(objError); } } } export default RequestHandler;