UNPKG

@thisisagile/easy-express

Version:

Straightforward library for building domain-driven microservice architectures

238 lines (228 loc) 10.6 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; 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 __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { AuthError: () => AuthError, ExpressProvider: () => ExpressProvider, NamespaceContext: () => NamespaceContext, authError: () => authError, checkLabCoat: () => checkLabCoat, checkScope: () => checkScope, checkToken: () => checkToken, checkUseCase: () => checkUseCase, correlation: () => correlation, error: () => error, isAuthError: () => isAuthError, notFound: () => notFound, requestContext: () => requestContext, security: () => security, service: () => service }); module.exports = __toCommonJS(src_exports); // src/express/AuthError.ts var import_easy = require("@thisisagile/easy"); var AuthError = class extends Error { status; constructor({ name, status }) { super(name); this.name = "AuthenticationError"; this.status = status; } }; var authError = (status) => new AuthError(status); var isAuthError = (e) => (0, import_easy.isError)(e) && e.name === "AuthenticationError"; // src/express/CorrelationHandler.ts var import_easy2 = require("@thisisagile/easy"); var correlation = (req, res, next) => { res.setHeader(import_easy2.HttpHeader.Correlation, import_easy2.ctx.request.correlationId = req?.header(import_easy2.HttpHeader.Correlation) ?? (0, import_easy2.toUuid)()); next(); }; // src/express/ErrorHandler.ts var import_easy3 = require("@thisisagile/easy"); var import_easy_service = require("@thisisagile/easy-service"); var toResponse = (status, errors = []) => ({ status, body: import_easy3.rest.toError(status, errors) }); var toBody = ({ origin, options }) => { return (0, import_easy3.choose)(origin).type(isAuthError, (ae) => toResponse((0, import_easy3.toHttpStatus)(ae.status), [(0, import_easy3.toResult)(ae.message)])).type(import_easy3.isDoesNotExist, (e) => toResponse(options?.onNotFound ?? import_easy3.HttpStatus.NotFound, [(0, import_easy3.toResult)(e.reason ?? e.message)])).type(import_easy3.isError, (e) => toResponse(import_easy3.HttpStatus.InternalServerError, [(0, import_easy3.toResult)(e.message)])).type(import_easy3.isResults, (r) => toResponse(options?.onError ?? import_easy3.HttpStatus.BadRequest, r.results)).type(import_easy3.isResponse, (r) => toResponse(import_easy3.HttpStatus.InternalServerError, r.body.error?.errors)).type(import_easy3.isException, (e) => toResponse(options?.onError ?? import_easy3.HttpStatus.BadRequest, [(0, import_easy3.toResult)(e.reason ?? e.message)])).type(import_easy3.isText, (t) => toResponse(options?.onError ?? import_easy3.HttpStatus.BadRequest, [(0, import_easy3.toResult)((0, import_easy3.asString)(t))])).else(() => toResponse(import_easy3.HttpStatus.InternalServerError, [(0, import_easy3.toResult)("Unknown error")])); }; var error = (e, req, res, _next) => { let response; (0, import_easy3.tryTo)(() => (0, import_easy_service.toOriginatedError)(e)).map((oe) => toBody(oe)).accept((r) => response = r).accept((r) => import_easy3.ctx.request.lastError = r.status.isServerError ? r.body.error?.errors[0]?.message : void 0).accept((r) => import_easy3.ctx.request.lastErrorStack = r.status.isServerError ? e.stack : void 0).recover(() => response).accept((r) => res.status(r.status.status).json(r.body)); }; // src/express/ExpressProvider.ts var import_express = __toESM(require("express")); // src/express/SecurityHandler.ts var import_passport = __toESM(require("passport")); var import_passport_jwt = require("passport-jwt"); var import_easy4 = require("@thisisagile/easy"); var checkLabCoat = () => (req, res, next) => next((0, import_easy4.ifFalse)(import_easy4.Environment.Dev.equals(import_easy4.ctx.env.name), authError(import_easy4.HttpStatus.Forbidden))); var checkToken = () => import_passport.default.authenticate("jwt", { session: false, failWithError: true }); var checkScope = (scope) => (req, res, next) => next((0, import_easy4.ifFalse)(req.user?.scopes?.includes(scope.id), authError(import_easy4.HttpStatus.Forbidden))); var checkUseCase = (uc) => (req, res, next) => next((0, import_easy4.ifFalse)(req.user?.usecases?.includes(uc.id), authError(import_easy4.HttpStatus.Forbidden))); var wrapSecretOrKeyProvider = (p) => p ? (request, rawJwtToken, done) => p(request, rawJwtToken).then((t) => done(null, t)).catch((e) => done(e)) : void 0; var security = ({ jwtStrategyOptions } = {}) => { jwtStrategyOptions ??= {}; if ("secretOrKeyProvider" in jwtStrategyOptions) jwtStrategyOptions.secretOrKeyProvider = wrapSecretOrKeyProvider(jwtStrategyOptions.secretOrKeyProvider); else if (!("secretOrKey" in jwtStrategyOptions)) jwtStrategyOptions.secretOrKey = import_easy4.ctx.env.get("tokenPublicKey"); const strategy = new import_passport_jwt.Strategy( { jwtFromRequest: import_passport_jwt.ExtractJwt.fromAuthHeaderAsBearerToken(), passReqToCallback: true, ...jwtStrategyOptions }, (req, payload, done) => { import_easy4.ctx.request.token = payload; import_easy4.ctx.request.jwt = import_passport_jwt.ExtractJwt.fromAuthHeaderAsBearerToken()(req) ?? ""; done(null, payload); } ); import_passport.default.use(strategy); return import_passport.default.initialize(); }; // src/express/ExpressProvider.ts var import_easy5 = require("@thisisagile/easy"); var import_easy_service2 = require("@thisisagile/easy-service"); var ExpressProvider = class { constructor(app = (0, import_express.default)()) { this.app = app; this.app.set("trust proxy", ["loopback", "linklocal", "uniquelocal"]); } use = (handler) => { this.app.use(handler); }; route = (service2, resource) => { const { route, endpoints, middleware } = (0, import_easy_service2.routes)(resource); const router = import_express.default.Router({ mergeParams: true }); if (!(0, import_easy5.isEmpty)(middleware)) router.all(route.route(service2.name), middleware); endpoints.forEach(({ endpoint, verb, requires, middleware: middleware2 }) => { console.log(verb.verb.code, route.route(service2.name)); router[verb.verb.toString()]( route.route(service2.name), ...this.addSecurityMiddleware(requires), ...middleware2, this.handle(endpoint, verb.options, requires) ); }); this.app.use(router); }; listen = (port, message = `Service is listening on port ${port}.`) => { this.app.listen(port, () => { console.log(message); }); }; addSecurityMiddleware(requires) { const middleware = []; if (requires.labCoat) middleware.push(checkLabCoat()); if (requires.token) middleware.push(checkToken()); if (requires.scope) middleware.push(checkScope(requires.scope)); if (requires.uc) middleware.push(checkUseCase(requires.uc)); return middleware; } handle = (endpoint, options, requires) => (req, res, next) => endpoint((0, import_easy5.toReq)(req)).then((r) => this.toResponse(res, r, (0, import_easy_service2.toVerbOptions)(options))).catch((error2) => next((0, import_easy_service2.toOriginatedError)(error2, options))); toResponse(res, result, options) { res.status(options.onOk.status); res.type(options.type.code); if (options.cache.enabled) res.setHeader(options.cache.name, options.cache.value()); (this[options.type.name] ?? this.json)(res, result, options); } // Handling responses depending on content type json(res, result, options) { if (import_easy5.HttpStatus.NoContent.equals(options.onOk)) { res.send(); } else { res.json(import_easy5.rest.toData(options.onOk, (0, import_easy5.toList)(result), result?.total, result?.meta)); } } rawJson(res, result, options) { if (import_easy5.HttpStatus.NoContent.equals(options.onOk)) { res.send(); } else { res.json(result); } } stream(res, result) { res.end(result); } text(res, data) { res.send(data); } }; var service = (name) => new import_easy_service2.Service(name, new ExpressProvider()); // src/express/NotFoundHandler.ts var import_easy6 = require("@thisisagile/easy"); var import_easy_service3 = require("@thisisagile/easy-service"); var notFound = (req, res, next) => { next((0, import_easy_service3.toOriginatedError)(import_easy6.Exception.DoesNotExist)); }; // src/express/RequestContextHandler.ts var import_easy7 = require("@thisisagile/easy"); var requestContext = (req, res, next) => import_easy7.ctx.request.create(() => next()); // src/types/NamespaceContext.ts var import_cls_hooked = require("cls-hooked"); var import_easy8 = require("@thisisagile/easy"); var NamespaceContext = class extends import_easy8.BaseRequestContext { namespace = (0, import_cls_hooked.createNamespace)("context"); get(key) { return this.namespace.get(key); } set(key, value) { return this.namespace.set(key, value); } create = (f) => this.namespace.run(f); }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { AuthError, ExpressProvider, NamespaceContext, authError, checkLabCoat, checkScope, checkToken, checkUseCase, correlation, error, isAuthError, notFound, requestContext, security, service }); //# sourceMappingURL=index.js.map