UNPKG

@jeaks03/overseer

Version:

Just another TypeScript Back-End framework

228 lines 19.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const standard_responses_1 = require("../misc/standard-responses"); const requisites_1 = require("../core/requisites"); const route_utils_1 = require("./route-utils"); const http_error_1 = require("../errors/http-error"); const redirect_1 = require("./redirect"); const core_error_1 = require("../errors/core-error"); const converter_1 = require("../converters/converter"); class AsyncRequestHandler { constructor(serverRequest, serverResponse, router) { this.serverRequest = serverRequest; this.serverResponse = serverResponse; this.router = router; } static async doHandle(serverRequest, serverResponse, router) { const handler = new AsyncRequestHandler(serverRequest, serverResponse, router); await handler.handle(); handler.serverResponse.end(); } async handle() { // Handle cross origin headers and OPTIONS requests this.appendInitialHeaders(); if (route_utils_1.RouteUtils.isMethod(this.serverRequest, 'options')) { return this.handleCORS(); } try { await this.checkAvailableRoute(); } catch (e) { if (e.response) { e.handle(this.serverResponse); } else { http_error_1.HttpError.handleServerError(this.serverResponse, e); } } } /** * Handles possible routing scenarios such as: route exists, resource exists or not found */ async checkAvailableRoute() { const route = this.findRoute(); // Handle existing route if (route) { this.foundRoute = route; await this.routeMatched(); return; } // If no route then handle public resources if (route_utils_1.RouteUtils.isMethod(this.serverRequest, 'get') && this.router.resourceMgr.fileOrIndexExists(route_utils_1.RouteUtils.getBaseUrl(this.serverRequest.url))) { this.router.resourceMgr.handleRequest(route_utils_1.RouteUtils.getBaseUrl(this.serverRequest.url), this.serverResponse); return; } // If no public resource then send 404 this.serverResponse.statusCode = 404; this.serverResponse.setHeader('Content-Type', 'application/json'); } /** * Triggered when a route was found */ async routeMatched() { const rawBody = await this.parseRequest(); const messageConverter = MessageConverterScout. using(this.serverRequest, this.foundRoute) .findReadConverter(rawBody); const body = messageConverter.doRead(rawBody); const pathInfo = this.generatePathInfo(body); // Triggers the controller method associated with this route const output = this.handleRoute(pathInfo); // Handle redirect if (output.body instanceof redirect_1.Redirect) { this.handleRedirect(output.body); return; } // Request is done, write output this.writeResponse(output); } /** * Triggers the controller method associated with the route * @param info param to pass to controller method */ handleRoute(info) { try { return { body: this.foundRoute.handle(info), status: this.foundRoute.details.statusCode }; } catch (e) { return this.handleError(e, info); } } /** * Handles the given error * @param e the error * @param info param to pass to error handler method */ handleError(e, info) { let output = {}; if (e instanceof core_error_1.CoreError) { output.body = e.handle(info); output.status = e.getStatusCode(); } else if (e instanceof http_error_1.HttpError) { output = e.response; } else { console.error(e); output = standard_responses_1.INTERVAL_SERVER_ERROR; } return output; } /** * Generates PathInfo for the controller method to use */ generatePathInfo(body) { return { body, raw: { request: this.serverRequest, response: this.serverResponse }, pathParams: route_utils_1.RouteUtils.parsePathParams(this.serverRequest.url, this.foundRoute.details.path), queryParams: route_utils_1.RouteUtils.parseQueryParams(this.serverRequest.url) }; } /** * Parses the raw http request body to a string promise */ parseRequest() { let requestBody = ''; this.serverRequest.on('data', chunk => requestBody += chunk.toString()); return new Promise((resolve) => this.serverRequest.on('end', () => resolve(requestBody))); } /** * Returns the route associated with the current server request */ findRoute() { const parts = route_utils_1.RouteUtils.getUrlPattern(this.serverRequest.url); let tree = this.router.routeTree; for (const part of parts) { if (!tree[part]) { return null; } tree = tree[part]; } if (!tree || !tree[this.router.routeSym]) { return null; } return tree[this.router.routeSym][this.serverRequest.method.toLowerCase()] || null; } /** * Adds necessary headers to avoid CORS issues */ appendInitialHeaders() { this.serverResponse.setHeader('Access-Control-Allow-Origin', '*'); this.serverResponse.setHeader('Access-Control-Allow-Methods', 'GET, POST, PATCH, PUT, DELETE, OPTIONS'); } /** * Handles redirects */ handleRedirect(body) { this.serverResponse.statusCode = 302; this.serverResponse.setHeader('Location', body.url); } /** * Adds necessary headers to avoid CORS issues in case of OPTIONS requests */ handleCORS() { this.serverResponse.setHeader('Access-Control-Allow-Headers', `Accept, Accept-CH, Accept-Charset, Accept-Datetime, Accept-Encoding, Accept-Ext, Accept-Features, Accept-Language, Accept-Params, Accept-Ranges, Access-Control-Allow-Credentials, Access-Control-Allow-Headers, Access-Control-Allow-Methods, Access-Control-Allow-Origin, Access-Control-Expose-Headers, Access-Control-Max-Age, Access-Control-Request-Headers, Access-Control-Request-Method, Age, Allow, Alternates, Authentication-Info, Authorization, C-Ext, C-Man, C-Opt, C-PEP, C-PEP-Info, CONNECT, Cache-Control, Compliance, Connection, Content-Base, Content-Disposition, Content-Encoding, Content-ID, Content-Language, Content-Length, Content-Location, Content-MD5, Content-Range, Content-Script-Type, Content-Security-Policy, Content-Style-Type, Content-Transfer-Encoding, Content-Type, Content-Version, Cookie, Cost, DAV, DELETE, DNT, DPR, Date, Default-Style, Delta-Base, Depth, Derived-From, Destination, Differential-ID, Digest, ETag, Expect, Expires, Ext, From, GET, GetProfile, HEAD, HTTP-date, Host, IM, If, If-Match, If-Modified-Since, If-None-Match, If-Range, If-Unmodified-Since, Keep-Alive, Label, Last-Event-ID, Last-Modified, Link, Location, Lock-Token, MIME-Version, Man, Max-Forwards, Media-Range, Message-ID, Meter, Negotiate, Non-Compliance, OPTION, OPTIONS, OWS, Opt, Optional, Ordering-Type, Origin, Overwrite, P3P, PEP, PICS-Label, POST, PUT, Pep-Info, Permanent, Position, Pragma, ProfileObject, Protocol, Protocol-Query, Protocol-Request, Proxy-Authenticate, Proxy-Authentication-Info, Proxy-Authorization, Proxy-Features, Proxy-Instruction, Public, RWS, Range, Referer, Refresh, Resolution-Hint, Resolver-Location, Retry-After, Safe, Sec-Websocket-Extensions, Sec-Websocket-Key, Sec-Websocket-Origin, Sec-Websocket-Protocol, Sec-Websocket-Version, Security-Scheme, Server, Set-Cookie, Set-Cookie2, SetProfile, SoapAction, Status, Status-URI, Strict-Transport-Security, SubOK, Subst, Surrogate-Capability, Surrogate-Control, TCN, TE, TRACE, Timeout, Title, Trailer, Transfer-Encoding, UA-Color, UA-Media, UA-Pixels, UA-Resolution, UA-Windowpixels, URI, Upgrade, User-Agent, Variant-Vary, Vary, Version, Via, Viewport-Width, WWW-Authenticate, Want-Digest, Warning, Width, X-Content-Duration, X-Content-Security-Policy, X-Content-Type-Options, X-CustomHeader, X-DNSPrefetch-Control, X-Forwarded-For, X-Forwarded-Port, X-Forwarded-Proto, X-Frame-Options, X-Modified, X-OTHER, X-PING, X-PINGOTHER, X-Powered-By, X-Requested-With`); this.serverResponse.statusCode = 200; } /** * Writes to the http response object * @param output what to write */ writeResponse(output) { const converter = MessageConverterScout .using(this.serverRequest, this.foundRoute) .findWriteConverter(output.body); this.serverResponse.statusCode = output.status; this.serverResponse.setHeader('Content-Type', converter.getContentType()); this.serverResponse.write(converter.doWrite(output.body)); } } exports.AsyncRequestHandler = AsyncRequestHandler; class MessageConverterScout { constructor(serverRequest, foundRoute) { this.serverRequest = serverRequest; this.foundRoute = foundRoute; } static using(serverRequest, foundRoute) { return new MessageConverterScout(serverRequest, foundRoute); } findReadConverter(body) { const type = this.findContentType(this.serverRequest.headers["content-type"], this.foundRoute.details.consumes); const converter = requisites_1.Requisites.findAll(converter_1.Converter).find((x) => x.canRead(body, type)); if (!converter) { throw new http_error_1.HttpError(standard_responses_1.UNSUPPORTED_MEDIA_TYPE); } return converter; } findWriteConverter(body) { const type = this.findContentType(this.serverRequest.headers.accept, this.foundRoute.details.produces); const converter = requisites_1.Requisites.findAll(converter_1.Converter).find((x) => x.canWrite(body, type)); if (!converter) { throw new http_error_1.HttpError(standard_responses_1.UNSUPPORTED_MEDIA_TYPE); } return converter; } findContentType(contentTypeHeader, allowedContentTypes) { const contentTypes = contentTypeHeader; let requiredContentTypes; if (contentTypes) { requiredContentTypes = contentTypes.split(',').map(x => x.trim()).map(x => x.split(';')[0]); } let type; if (!requiredContentTypes || contentTypes.includes('*/*')) { type = allowedContentTypes[0]; } else if (requiredContentTypes) { type = requiredContentTypes.find(ctype => allowedContentTypes.includes(ctype)); } return type; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXN5bmMtcmVxdWVzdC1oYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3JvdXRlcy9hc3luYy1yZXF1ZXN0LWhhbmRsZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFFQSxtRUFBMkY7QUFDM0YsbURBQWdEO0FBR2hELCtDQUEyQztBQUMzQyxxREFBaUQ7QUFDakQseUNBQXNDO0FBRXRDLHFEQUFpRDtBQUNqRCx1REFBb0Q7QUFFcEQsTUFBYSxtQkFBbUI7SUFRNUIsWUFBNEIsYUFBOEIsRUFDOUMsY0FBOEIsRUFDOUIsTUFBYztRQUZFLGtCQUFhLEdBQWIsYUFBYSxDQUFpQjtRQUM5QyxtQkFBYyxHQUFkLGNBQWMsQ0FBZ0I7UUFDOUIsV0FBTSxHQUFOLE1BQU0sQ0FBUTtJQUFJLENBQUM7SUFSL0IsTUFBTSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsYUFBOEIsRUFBRSxjQUE4QixFQUFFLE1BQWM7UUFDaEcsTUFBTSxPQUFPLEdBQUcsSUFBSSxtQkFBbUIsQ0FBQyxhQUFhLEVBQUUsY0FBYyxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQy9FLE1BQU0sT0FBTyxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQ3ZCLE9BQU8sQ0FBQyxjQUFjLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDakMsQ0FBQztJQU1PLEtBQUssQ0FBQyxNQUFNO1FBRWhCLG1EQUFtRDtRQUNuRCxJQUFJLENBQUMsb0JBQW9CLEVBQUUsQ0FBQztRQUM1QixJQUFHLHdCQUFVLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxhQUFhLEVBQUUsU0FBUyxDQUFDLEVBQUU7WUFDbkQsT0FBTyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7U0FDNUI7UUFFRCxJQUFJO1lBQ0EsTUFBTSxJQUFJLENBQUMsbUJBQW1CLEVBQUUsQ0FBQztTQUNwQztRQUFDLE9BQU0sQ0FBQyxFQUFFO1lBQ1AsSUFBRyxDQUFDLENBQUMsUUFBUSxFQUFFO2dCQUNYLENBQUMsQ0FB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