moleculer-web
Advanced tools
Comparing version 0.10.7 to 0.11.0-beta1
@@ -0,1 +1,96 @@ | ||
<a name="0.11.0-beta1"></a> | ||
# 0.11.0-beta1 (2024-12-04) | ||
## Changes | ||
### Updated `path-ro-regexp` librarz | ||
The `path-to-regexp` has been updated to 8.x.x. It contains many breaking changes in the path resolving. Check the [documentation](https://github.com/pillarjs/path-to-regexp?tab=readme-ov-file#express--4x) of library to how migrate your alias paths. | ||
**Optional parameter alias path** | ||
``` | ||
// Old way | ||
"GET user/:name?": "user.get" | ||
// New way | ||
"GET user{/:name}": "user.get" | ||
``` | ||
**Repeating parameter alias path** | ||
``` | ||
// Old way | ||
"GET /users/*username": "user.resolveUsersByNames", | ||
// New way | ||
"GET /users/:username*": "user.resolveUsersByNames", | ||
``` | ||
### Using 0.15 new streaming solution | ||
The moleculer-web@0.11.x supports Moleculer v0.15.x including the new streaming solution. It means, it doesn't support 0.13 and 0.14 moleculer versions. | ||
Thanks for the new solution, the multipart fields and request parameters are sent via `ctx.params` instead of meta and the file stream is available in `ctx.stream` in action handlers. | ||
**Example** | ||
```js | ||
module.exports = { | ||
name: "file", | ||
actions: { | ||
save: { | ||
handler(ctx) { | ||
return new this.Promise((resolve, reject) => { | ||
const filePath = path.join(uploadDir, ctx.params.$filename); | ||
const f = fs.createWriteStream(filePath); | ||
f.on("close", () => { | ||
// File written successfully | ||
this.logger.info(`Uploaded file stored in '${filePath}'`); | ||
resolve({ filePath }); | ||
}); | ||
ctx.stream.on("error", err => { | ||
this.logger.info("File error received", err.message); | ||
reject(err); | ||
// Destroy the local file | ||
f.destroy(err); | ||
}); | ||
f.on("error", () => { | ||
// Remove the errored file. | ||
fs.unlinkSync(filePath); | ||
}); | ||
ctx.stream.pipe(f); | ||
}); | ||
} | ||
} | ||
} | ||
}; | ||
``` | ||
Example content of `ctx.params`: | ||
```js | ||
{ | ||
// Multipart file properties | ||
$fieldname: "myfile", | ||
$filename: "avatar.png", | ||
$encoding: "7bit", | ||
$mimetype: "image/png", | ||
// Other multipart fields | ||
// e.g.: `<input type="text" name="name" id="name" value="Test User">` | ||
name: "Test User", | ||
// Request path parameter, e.g.: `/upload/single/1234` | ||
id: "1234" | ||
} | ||
``` | ||
----------------------------- | ||
<a name="0.10.7"></a> | ||
@@ -2,0 +97,0 @@ # 0.10.7 (2023-11-12) |
@@ -0,22 +1,42 @@ | ||
import { IParseOptions } from "qs"; | ||
import type { | ||
ActionEndpoint, | ||
ActionSchema, | ||
CallingOptions, | ||
Context, | ||
LogLevels, | ||
Service, | ||
ServiceBroker, | ||
ServiceSchema | ||
} from "moleculer"; | ||
import { Errors } from "moleculer"; | ||
interface RestSchema { | ||
path?: string; | ||
method?: "GET" | "POST" | "DELETE" | "PUT" | "PATCH"; | ||
fullPath?: string; | ||
basePath?: string; | ||
} | ||
import "moleculer"; | ||
declare module "moleculer" { | ||
interface ActionSchema { | ||
rest?: RestSchema | RestSchema[] | string | string[] | null; | ||
} | ||
interface ServiceSettingSchema { | ||
rest?: string | string[] | null; | ||
} | ||
} | ||
import { IncomingMessage, ServerResponse } from "http"; | ||
import type { Server as NetServer } from 'net'; | ||
import type { Server as TLSServer } from 'tls'; | ||
import type { Server as HttpServer } from 'http'; | ||
import type { Server as HttpsServer } from 'https'; | ||
import type { Http2SecureServer, Http2Server } from 'http2'; | ||
declare module "moleculer-web" { | ||
import { IncomingMessage, ServerResponse } from "http"; | ||
import type { | ||
ActionEndpoint, | ||
ActionSchema, | ||
CallingOptions, | ||
Context, | ||
LogLevels, | ||
Service, | ||
ServiceBroker, | ||
ServiceSchema, | ||
} from "moleculer"; | ||
import { Errors } from "moleculer"; | ||
import { IParseOptions } from 'qs'; | ||
import type { Server as NetServer } from 'net'; | ||
import type { Server as TLSServer } from 'tls'; | ||
import type { Server as HttpServer } from 'http'; | ||
import type { Server as HttpsServer } from 'https'; | ||
import type { Http2SecureServer, Http2Server } from 'http2'; | ||
// RateLimit | ||
@@ -23,0 +43,0 @@ export type generateRateLimitKey = (req: IncomingMessage) => string; |
@@ -0,0 +0,0 @@ /* |
{ | ||
"name": "moleculer-web", | ||
"version": "0.10.7", | ||
"version": "0.11.0-beta1", | ||
"description": "Official API Gateway service for Moleculer framework", | ||
@@ -15,3 +15,3 @@ "main": "index.js", | ||
"lint:fix": "eslint --ext=.js --fix src", | ||
"deps": "npm-check -u", | ||
"deps": "ncu -i --format group", | ||
"postdeps": "npm test", | ||
@@ -36,35 +36,39 @@ "coverall": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js", | ||
"devDependencies": { | ||
"@sinonjs/fake-timers": "^8.0.1", | ||
"benchmarkify": "^3.0.0", | ||
"compression": "^1.7.4", | ||
"@sinonjs/fake-timers": "^13.0.5", | ||
"benchmarkify": "^4.0.0", | ||
"compression": "^1.7.5", | ||
"coveralls": "^3.1.1", | ||
"cross-env": "^7.0.3", | ||
"eslint": "^8.0.1", | ||
"eslint": "^9.16.0", | ||
"eslint-config-prettier": "^9.1.0", | ||
"eslint-plugin-node": "^11.1.0", | ||
"eslint-plugin-promise": "^6.1.1", | ||
"eslint-plugin-security": "^1.4.0", | ||
"express": "^4.17.1", | ||
"fakerator": "^0.3.4", | ||
"formidable": "^1.2.6", | ||
"jest": "^27.2.5", | ||
"jest-cli": "^27.2.5", | ||
"jsonwebtoken": "^8.5.1", | ||
"mime-types": "^2.1.33", | ||
"mkdirp": "^1.0.4", | ||
"moleculer": "^0.14.17", | ||
"moleculer-repl": "^0.7.0", | ||
"nats": "^2.2.0", | ||
"nodemon": "^2.0.13", | ||
"socket.io": "^4.7.1", | ||
"eslint-plugin-prettier": "^5.2.1", | ||
"eslint-plugin-security": "^3.0.1", | ||
"express": "^4.21.1", | ||
"fakerator": "^0.3.6", | ||
"formidable": "^3.5.2", | ||
"jest": "^29.7.0", | ||
"jest-cli": "^29.7.0", | ||
"jsonwebtoken": "^9.0.2", | ||
"mime-types": "^2.1.35", | ||
"mkdirp": "^3.0.1", | ||
"moleculer": "next", | ||
"moleculer-repl": "^0.7.4", | ||
"nats": "^2.28.2", | ||
"nodemon": "^3.1.7", | ||
"npm-check-updates": "^17.1.11", | ||
"prettier": "^3.4.2", | ||
"socket.io": "^4.8.1", | ||
"spdy": "^4.0.2", | ||
"supertest": "^6.1.6", | ||
"webpack": "^5.88.1", | ||
"webpack-dev-middleware": "^5.2.1" | ||
"supertest": "^7.0.0", | ||
"webpack": "^5.97.0", | ||
"webpack-dev-middleware": "^7.4.2" | ||
}, | ||
"peerDependencies": { | ||
"moleculer": "^0.13.0 || ^0.14.0" | ||
"moleculer": "^0.15.0" | ||
}, | ||
"dependencies": { | ||
"@fastify/busboy": "^1.0.0", | ||
"body-parser": "^1.19.0", | ||
"@fastify/busboy": "^3.1.0", | ||
"@types/qs": "^6.9.17", | ||
"body-parser": "^1.20.3", | ||
"es6-error": "^4.1.1", | ||
@@ -74,10 +78,10 @@ "etag": "^1.8.1", | ||
"isstream": "^0.1.2", | ||
"kleur": "^4.1.4", | ||
"kleur": "^4.1.5", | ||
"lodash": "^4.17.21", | ||
"path-to-regexp": "^3.1.0", | ||
"qs": "^6.11.0", | ||
"serve-static": "^1.14.1" | ||
"path-to-regexp": "^8.2.0", | ||
"qs": "^6.13.1", | ||
"serve-static": "^1.16.2" | ||
}, | ||
"engines": { | ||
"node": ">= 10.x.x" | ||
"node": ">= 18.x.x" | ||
}, | ||
@@ -84,0 +88,0 @@ "typings": "./index.d.ts", |
120
src/alias.js
/* | ||
* moleculer | ||
* Copyright (c) 2021 MoleculerJS (https://github.com/moleculerjs/moleculer) | ||
* Copyright (c) 2024 MoleculerJS (https://github.com/moleculerjs/moleculer) | ||
* MIT Licensed | ||
@@ -9,6 +9,6 @@ */ | ||
const pathToRegexp = require("path-to-regexp"); | ||
const Busboy = require("@fastify/busboy"); | ||
const kleur = require("kleur"); | ||
const _ = require("lodash"); | ||
const { pathToRegexp } = require("path-to-regexp"); | ||
const Busboy = require("@fastify/busboy"); | ||
const kleur = require("kleur"); | ||
const _ = require("lodash"); | ||
@@ -20,3 +20,2 @@ const { PayloadTooLarge } = require("./errors"); | ||
class Alias { | ||
/** | ||
@@ -65,8 +64,8 @@ * Constructor of Alias | ||
} else if (Array.isArray(action)) { | ||
const mws = _.compact(action.map(mw => { | ||
if (_.isString(mw)) | ||
this.action = mw; | ||
else if(_.isFunction(mw)) | ||
return mw; | ||
})); | ||
const mws = _.compact( | ||
action.map(mw => { | ||
if (_.isString(mw)) this.action = mw; | ||
else if (_.isFunction(mw)) return mw; | ||
}) | ||
); | ||
this.handler = compose.call(service, ...mws); | ||
@@ -81,3 +80,3 @@ } else if (action != null) { | ||
this.fullPath = this.fullPath || (addSlashes(this.route.path) + this.path); | ||
this.fullPath = this.fullPath || addSlashes(this.route.path) + this.path; | ||
if (this.fullPath !== "/" && this.fullPath.endsWith("/")) { | ||
@@ -87,4 +86,5 @@ this.fullPath = this.fullPath.slice(0, -1); | ||
this.keys = []; | ||
this.re = pathToRegexp(this.fullPath, this.keys, route.opts.pathToRegexpOptions || {}); // Options: https://github.com/pillarjs/path-to-regexp#usage | ||
const ptr = pathToRegexp(this.fullPath, route.opts.pathToRegexpOptions || {}); // Options: https://github.com/pillarjs/path-to-regexp?tab=readme-ov-file#pathtoregexp | ||
this.re = ptr.regexp; | ||
this.keys = ptr.keys; | ||
@@ -115,4 +115,3 @@ if (this.type == "multipart") { | ||
if (key.repeat) | ||
params[key.name] = params[key.name].split(key.delimiter); | ||
if (key.repeat) params[key.name] = params[key.name].split(key.delimiter); | ||
} | ||
@@ -143,3 +142,9 @@ | ||
toString() { | ||
return kleur.magenta(_.padStart(this.method, 6)) + " " + kleur.cyan(this.fullPath) + kleur.grey(" => ") + (this.handler != null && this.type !== "multipart" ? "<Function>" : this.action); | ||
return ( | ||
kleur.magenta(_.padStart(this.method, 6)) + | ||
" " + | ||
kleur.cyan(this.fullPath) + | ||
kleur.grey(" => ") + | ||
(this.handler != null && this.type !== "multipart" ? "<Function>" : this.action) | ||
); | ||
} | ||
@@ -154,3 +159,3 @@ | ||
const ctx = req.$ctx; | ||
ctx.meta.$multipart = {}; | ||
const multipartParams = {}; | ||
const promises = []; | ||
@@ -161,3 +166,7 @@ | ||
const busboyOptions = _.defaultsDeep({ headers: req.headers }, this.busboyConfig, this.route.opts.busboyConfig); | ||
const busboyOptions = _.defaultsDeep( | ||
{ headers: req.headers }, | ||
this.busboyConfig, | ||
this.route.opts.busboyConfig | ||
); | ||
const busboy = new Busboy(busboyOptions); | ||
@@ -173,17 +182,28 @@ busboy.on("file", (fieldname, file, filename, encoding, mimetype) => { | ||
numOfFiles++; | ||
promises.push(ctx.call(this.action, file, _.defaultsDeep({}, this.route.opts.callOptions, { meta: { | ||
fieldname: fieldname, | ||
filename: filename, | ||
encoding: encoding, | ||
mimetype: mimetype, | ||
$params: req.$params, | ||
} })).catch(err => { | ||
file.resume(); // Drain file stream to continue processing form | ||
busboy.emit("error", err); | ||
return err; | ||
})); | ||
promises.push( | ||
ctx | ||
.call( | ||
this.action, | ||
_.defaultsDeep( | ||
{ | ||
$fieldname: fieldname, | ||
$filename: filename, | ||
$encoding: encoding, | ||
$mimetype: mimetype | ||
}, | ||
multipartParams, | ||
req.$params | ||
), | ||
_.defaultsDeep({ stream: file }, this.route.opts.callOptions) | ||
) | ||
.catch(err => { | ||
file.resume(); // Drain file stream to continue processing form | ||
busboy.emit("error", err); | ||
return err; | ||
}) | ||
); | ||
}); | ||
busboy.on("field", (field, value) => { | ||
hasField = true; | ||
ctx.meta.$multipart[field] = value; | ||
multipartParams[field] = value; | ||
}); | ||
@@ -194,9 +214,17 @@ | ||
if (!busboyOptions.empty && numOfFiles == 0) | ||
return this.service.sendError(req, res, new MoleculerClientError("File missing in the request")); | ||
return this.service.sendError( | ||
req, | ||
res, | ||
new MoleculerClientError("File missing in the request") | ||
); | ||
// Call the action if no files but multipart fields | ||
if (numOfFiles == 0 && hasField) { | ||
promises.push(ctx.call(this.action, {}, _.defaultsDeep({}, this.route.opts.callOptions, { meta: { | ||
$params: req.$params, | ||
} }))); | ||
promises.push( | ||
ctx.call( | ||
this.action, | ||
_.defaultsDeep({}, multipartParams, req.$params), | ||
_.defaultsDeep({}, this.route.opts.callOptions) | ||
) | ||
); | ||
} | ||
@@ -206,3 +234,6 @@ | ||
let data = await this.service.Promise.all(promises); | ||
const fileLimit = busboyOptions.limits && busboyOptions.limits.files != null ? busboyOptions.limits.files : null; | ||
const fileLimit = | ||
busboyOptions.limits && busboyOptions.limits.files != null | ||
? busboyOptions.limits.files | ||
: null; | ||
if (numOfFiles == 1 && fileLimit == 1) { | ||
@@ -216,4 +247,3 @@ // Remove the array wrapping | ||
this.service.sendResponse(req, res, data, {}); | ||
} catch(err) { | ||
} catch (err) { | ||
/* istanbul ignore next */ | ||
@@ -233,11 +263,17 @@ this.service.sendError(req, res, err); | ||
if (_.isFunction(busboyOptions.onPartsLimit)) { | ||
busboy.on("partsLimit", () => busboyOptions.onPartsLimit.call(this.service, busboy, this, this.service)); | ||
busboy.on("partsLimit", () => | ||
busboyOptions.onPartsLimit.call(this.service, busboy, this, this.service) | ||
); | ||
} | ||
if (_.isFunction(busboyOptions.onFilesLimit)) { | ||
busboy.on("filesLimit", () => busboyOptions.onFilesLimit.call(this.service, busboy, this, this.service)); | ||
busboy.on("filesLimit", () => | ||
busboyOptions.onFilesLimit.call(this.service, busboy, this, this.service) | ||
); | ||
} | ||
if (_.isFunction(busboyOptions.onFieldsLimit)) { | ||
busboy.on("fieldsLimit", () => busboyOptions.onFieldsLimit.call(this.service, busboy, this, this.service)); | ||
busboy.on("fieldsLimit", () => | ||
busboyOptions.onFieldsLimit.call(this.service, busboy, this, this.service) | ||
); | ||
} | ||
@@ -244,0 +280,0 @@ |
457
src/index.js
/* | ||
* moleculer | ||
* Copyright (c) 2021 MoleculerJS (https://github.com/moleculerjs/moleculer) | ||
* Copyright (c) 2024 MoleculerJS (https://github.com/moleculerjs/moleculer) | ||
* MIT Licensed | ||
@@ -22,6 +22,12 @@ */ | ||
const isReadableStream = require("isstream").isReadable; | ||
const { pipeline } = require('stream'); | ||
const { pipeline } = require("stream"); | ||
const { MoleculerError, MoleculerServerError, ServiceNotFoundError } = require("moleculer").Errors; | ||
const { ServiceUnavailableError, NotFoundError, ForbiddenError, RateLimitExceeded, ERR_ORIGIN_NOT_ALLOWED } = require("./errors"); | ||
const { | ||
ServiceUnavailableError, | ||
NotFoundError, | ||
ForbiddenError, | ||
RateLimitExceeded, | ||
ERR_ORIGIN_NOT_ALLOWED | ||
} = require("./errors"); | ||
@@ -31,3 +37,10 @@ const Alias = require("./alias"); | ||
const { removeTrailingSlashes, addSlashes, normalizePath, composeThen, generateETag, isFresh } = require("./utils"); | ||
const { | ||
removeTrailingSlashes, | ||
addSlashes, | ||
normalizePath, | ||
composeThen, | ||
generateETag, | ||
isFresh | ||
} = require("./utils"); | ||
@@ -39,3 +52,3 @@ const MAPPING_POLICY_ALL = "all"; | ||
if (svc.version != null && svc.settings.$noVersionPrefix !== true) | ||
return (typeof (svc.version) == "number" ? "v" + svc.version : svc.version) + "." + svc.name; | ||
return (typeof svc.version == "number" ? "v" + svc.version : svc.version) + "." + svc.name; | ||
@@ -53,3 +66,2 @@ return svc.name; | ||
module.exports = { | ||
// Default service name | ||
@@ -60,3 +72,2 @@ name: "api", | ||
settings: { | ||
// Exposed port | ||
@@ -125,3 +136,2 @@ port: process.env.PORT || 3000, | ||
actions: { | ||
/** | ||
@@ -134,6 +144,3 @@ * REST request handler | ||
tags: { | ||
params: [ | ||
"req.url", | ||
"req.method" | ||
] | ||
params: ["req.url", "req.method"] | ||
}, | ||
@@ -151,7 +158,5 @@ spanName: ctx => `${ctx.params.req.method} ${ctx.params.req.url}` | ||
if (ctx.requestID) | ||
res.setHeader("X-Request-ID", ctx.requestID); | ||
if (ctx.requestID) res.setHeader("X-Request-ID", ctx.requestID); | ||
if (!req.originalUrl) | ||
req.originalUrl = req.url; | ||
if (!req.originalUrl) req.originalUrl = req.url; | ||
@@ -163,13 +168,10 @@ // Split URL & query params | ||
// Trim trailing slash | ||
if (url.length > 1 && url.endsWith("/")) | ||
url = url.slice(0, -1); | ||
if (url.length > 1 && url.endsWith("/")) url = url.slice(0, -1); | ||
req.parsedUrl = url; | ||
if (!req.query) | ||
req.query = parsed.query; | ||
if (!req.query) req.query = parsed.query; | ||
// Skip if no routes | ||
if (!this.routes || this.routes.length == 0) | ||
return null; | ||
if (!this.routes || this.routes.length == 0) return null; | ||
@@ -188,4 +190,3 @@ let method = req.method; | ||
req.url = req.originalUrl.substring(route.path.length); | ||
if (req.url.length == 0 || req.url[0] !== "/") | ||
req.url = "/" + req.url; | ||
if (req.url.length == 0 || req.url[0] !== "/") req.url = "/" + req.url; | ||
@@ -203,4 +204,3 @@ return this.routeHandler(ctx, route, req, res, found); | ||
req.url = req.originalUrl.substring(route.path.length); | ||
if (req.url.length == 0 || req.url[0] !== "/") | ||
req.url = "/" + req.url; | ||
if (req.url.length == 0 || req.url[0] !== "/") req.url = "/" + req.url; | ||
@@ -288,8 +288,7 @@ return this.routeHandler(ctx, route, req, res); | ||
handler(ctx) { | ||
if (ctx.params.name != null) | ||
return this.removeRouteByName(ctx.params.name); | ||
if (ctx.params.name != null) return this.removeRouteByName(ctx.params.name); | ||
return this.removeRoute(ctx.params.path); | ||
} | ||
}, | ||
} | ||
}, | ||
@@ -306,6 +305,10 @@ | ||
if (this.settings.https && this.settings.https.key && this.settings.https.cert) { | ||
this.server = this.settings.http2 ? http2.createSecureServer(this.settings.https, this.httpHandler) : https.createServer(this.settings.https, this.httpHandler); | ||
this.server = this.settings.http2 | ||
? http2.createSecureServer(this.settings.https, this.httpHandler) | ||
: https.createServer(this.settings.https, this.httpHandler); | ||
this.isHTTPS = true; | ||
} else { | ||
this.server = this.settings.http2 ? http2.createServer(this.httpHandler) : http.createServer(this.httpHandler); | ||
this.server = this.settings.http2 | ||
? http2.createServer(this.httpHandler) | ||
: http.createServer(this.httpHandler); | ||
this.isHTTPS = false; | ||
@@ -316,3 +319,6 @@ } | ||
if (this.settings.httpServerTimeout) { | ||
this.logger.debug("Override default http(s) server timeout:", this.settings.httpServerTimeout); | ||
this.logger.debug( | ||
"Override default http(s) server timeout:", | ||
this.settings.httpServerTimeout | ||
); | ||
this.server.setTimeout(this.settings.httpServerTimeout); | ||
@@ -322,3 +328,6 @@ } | ||
this.server.requestTimeout = this.settings.requestTimeout; | ||
this.logger.debug("Setting http(s) server request timeout to:", this.settings.requestTimeout); | ||
this.logger.debug( | ||
"Setting http(s) server request timeout to:", | ||
this.settings.requestTimeout | ||
); | ||
}, | ||
@@ -336,3 +345,12 @@ | ||
if (this.settings.log4XXResponses || (err && !_.inRange(err.code, 400, 500))) { | ||
this.logger.error(" Request error!", err.name, ":", err.message, "\n", err.stack, "\nData:", err.data); | ||
this.logger.error( | ||
" Request error!", | ||
err.name, | ||
":", | ||
err.message, | ||
"\n", | ||
err.stack, | ||
"\nData:", | ||
err.data | ||
); | ||
} | ||
@@ -385,4 +403,3 @@ this.sendError(req, res, err); | ||
let requestID = req.headers["x-request-id"]; | ||
if (req.headers["x-correlation-id"]) | ||
requestID = req.headers["x-correlation-id"]; | ||
if (req.headers["x-correlation-id"]) requestID = req.headers["x-correlation-id"]; | ||
@@ -404,3 +421,3 @@ let options = { requestID }; | ||
const shouldBreak = this.corsHandler(this.settings, req, res); // check cors settings first | ||
if(shouldBreak) { | ||
if (shouldBreak) { | ||
return; | ||
@@ -445,3 +462,3 @@ } | ||
res.once("close", () => resolve(true)); | ||
res.once("error", (err) => reject(err)); | ||
res.once("error", err => reject(err)); | ||
@@ -453,3 +470,3 @@ try { | ||
const shouldBreak = this.corsHandler(route, req, res); | ||
if(shouldBreak) { | ||
if (shouldBreak) { | ||
return resolve(true); | ||
@@ -465,8 +482,7 @@ } | ||
} | ||
req.$params = params; // eslint-disable-line require-atomic-updates | ||
req.$params = params; | ||
// Resolve action name | ||
let urlPath = req.parsedUrl.slice(route.path.length); | ||
if (urlPath.startsWith("/")) | ||
urlPath = urlPath.slice(1); | ||
if (urlPath.startsWith("/")) urlPath = urlPath.slice(1); | ||
@@ -488,7 +504,6 @@ // Resolve internal services | ||
req.$alias = alias; // eslint-disable-line require-atomic-updates | ||
req.$alias = alias; | ||
// Alias handler | ||
return resolve(await this.aliasHandler(req, res, alias)); | ||
} else if (route.mappingPolicy == MAPPING_POLICY_RESTRICT) { | ||
@@ -499,4 +514,3 @@ // Blocking direct access | ||
if (!action) | ||
return resolve(null); | ||
if (!action) return resolve(null); | ||
@@ -512,3 +526,2 @@ // Not found alias, call services by action name | ||
resolve(result); | ||
} catch (err) { | ||
@@ -554,3 +567,3 @@ reject(err); | ||
if (key) { | ||
const remaining = opts.limit - await store.inc(key); | ||
const remaining = opts.limit - (await store.inc(key)); | ||
if (opts.headers) { | ||
@@ -569,3 +582,7 @@ res.setHeader("X-Rate-Limit-Limit", opts.limit); | ||
if (alias.action) { | ||
const endpoint = this.broker.findNextActionEndpoint(alias.action, route.callOptions, ctx); | ||
const endpoint = this.broker.findNextActionEndpoint( | ||
alias.action, | ||
route.callOptions, | ||
ctx | ||
); | ||
if (endpoint instanceof Error) { | ||
@@ -579,3 +596,6 @@ if (!alias._notDefined && endpoint instanceof ServiceNotFoundError) { | ||
if (endpoint.action.visibility != null && endpoint.action.visibility != "published") { | ||
if ( | ||
endpoint.action.visibility != null && | ||
endpoint.action.visibility != "published" | ||
) { | ||
// Action can't be published | ||
@@ -614,11 +634,15 @@ throw new ServiceNotFoundError({ action: alias.action }); | ||
// Call custom alias handler | ||
if (route.logging && (this.settings.logRequest && this.settings.logRequest in this.logger)) | ||
this.logger[this.settings.logRequest](` Call custom function in '${alias.toString()}' alias`); | ||
if ( | ||
route.logging && | ||
this.settings.logRequest && | ||
this.settings.logRequest in this.logger | ||
) | ||
this.logger[this.settings.logRequest]( | ||
` Call custom function in '${alias.toString()}' alias` | ||
); | ||
await new this.Promise((resolve, reject) => { | ||
alias.handler.call(this, req, res, err => { | ||
if (err) | ||
reject(err); | ||
else | ||
resolve(); | ||
if (err) reject(err); | ||
else resolve(); | ||
}); | ||
@@ -628,8 +652,24 @@ }); | ||
if (alias.action) | ||
return this.callAction(route, alias.action, req, res, alias.type == "stream" ? req : req.$params); | ||
return this.callAction( | ||
route, | ||
alias.action, | ||
req, | ||
res, | ||
req.$params, | ||
alias.type == "stream" | ||
); | ||
else | ||
throw new MoleculerServerError("No alias handler", 500, "NO_ALIAS_HANDLER", { path: req.originalUrl, alias: _.pick(alias, ["method", "path"]) }); | ||
throw new MoleculerServerError("No alias handler", 500, "NO_ALIAS_HANDLER", { | ||
path: req.originalUrl, | ||
alias: _.pick(alias, ["method", "path"]) | ||
}); | ||
} else if (alias.action) { | ||
return this.callAction(route, alias.action, req, res, alias.type == "stream" ? req : req.$params); | ||
return this.callAction( | ||
route, | ||
alias.action, | ||
req, | ||
res, | ||
req.$params, | ||
alias.type == "stream" | ||
); | ||
} | ||
@@ -646,5 +686,6 @@ }, | ||
* @param {Object} params Incoming params from request | ||
* @param {Boolean} isStream Is stream response | ||
* @returns {Promise} | ||
*/ | ||
async callAction(route, actionName, req, res, params) { | ||
async callAction(route, actionName, req, res, params, isStream) { | ||
const ctx = req.$ctx; | ||
@@ -657,3 +698,6 @@ | ||
this.logger[this.settings.logRequest](` Call '${actionName}' action`); | ||
if (this.settings.logRequestParams && this.settings.logRequestParams in this.logger) | ||
if ( | ||
this.settings.logRequestParams && | ||
this.settings.logRequestParams in this.logger | ||
) | ||
this.logger[this.settings.logRequestParams](" Params:", params); | ||
@@ -669,6 +713,4 @@ } | ||
const opts = route.callOptions ? { ...route.callOptions } : {}; | ||
if (params && params.$params) { | ||
// Transfer URL parameters via meta in case of stream | ||
if (!opts.meta) opts.meta = { $params: params.$params }; | ||
else opts.meta.$params = params.$params; | ||
if (isStream) { | ||
opts.stream = req; | ||
} | ||
@@ -682,4 +724,5 @@ | ||
// onAfterCall handling | ||
if (route.onAfterCall) | ||
if (route.onAfterCall) { | ||
data = await route.onAfterCall.call(this, ctx, route, req, res, data); | ||
} | ||
@@ -689,11 +732,8 @@ // Send back the response | ||
if (route.logging) | ||
this.logResponse(req, res, data); | ||
if (route.logging) this.logResponse(req, res, data); | ||
return true; | ||
} catch (err) { | ||
/* istanbul ignore next */ | ||
if (!err) | ||
return; // Cancelling promise chain, no error | ||
if (!err) return; // Cancelling promise chain, no error | ||
@@ -734,4 +774,3 @@ throw err; | ||
/* istanbul ignore next */ | ||
if (!res.statusCode) | ||
res.statusCode = 200; | ||
if (!res.statusCode) res.statusCode = 200; | ||
@@ -747,7 +786,12 @@ // Status code & message | ||
// Redirect | ||
if (res.statusCode==201 || (res.statusCode >= 300 && res.statusCode < 400 && res.statusCode !== 304)) { | ||
if ( | ||
res.statusCode == 201 || | ||
(res.statusCode >= 300 && res.statusCode < 400 && res.statusCode !== 304) | ||
) { | ||
const location = ctx.meta.$location; | ||
/* istanbul ignore next */ | ||
if (!location) { | ||
this.logger.warn(`The 'ctx.meta.$location' is missing for status code '${res.statusCode}'!`); | ||
this.logger.warn( | ||
`The 'ctx.meta.$location' is missing for status code '${res.statusCode}'!` | ||
); | ||
} else { | ||
@@ -794,4 +838,3 @@ res.setHeader("Location", location); | ||
} | ||
if (data == null) | ||
return res.end(); | ||
if (data == null) return res.end(); | ||
@@ -829,6 +872,4 @@ let chunk; | ||
res.setHeader("Content-Type", responseType); | ||
if (_.isString(data)) | ||
chunk = data; | ||
else | ||
chunk = data.toString(); | ||
if (_.isString(data)) chunk = data; | ||
else chunk = data.toString(); | ||
} | ||
@@ -843,4 +884,3 @@ } | ||
// Freshness | ||
if (isFresh(req, res)) | ||
res.statusCode = 304; | ||
if (isFresh(req, res)) res.statusCode = 304; | ||
@@ -860,8 +900,13 @@ if (res.statusCode === 204 || res.statusCode === 304) { | ||
// respond | ||
if (isReadableStream(data)) { //Stream response | ||
if (isReadableStream(data)) { | ||
//Stream response | ||
pipeline(data, res, err => { | ||
if (err) { | ||
this.logger.warn("Stream got an error.", { err, url: req.url, actionName: action.name }) | ||
this.logger.warn("Stream got an error.", { | ||
err, | ||
url: req.url, | ||
actionName: action.name | ||
}); | ||
} | ||
}) | ||
}); | ||
} else { | ||
@@ -889,4 +934,3 @@ res.end(chunk); | ||
send404(req, res) { | ||
if (req.$next) | ||
return req.$next(); | ||
if (req.$next) return req.$next(); | ||
@@ -915,4 +959,3 @@ this.sendError(req, res, new NotFoundError()); | ||
// In middleware mode call the next(err) | ||
if (req.$next) | ||
return req.$next(err); | ||
if (req.$next) return req.$next(err); | ||
@@ -981,3 +1024,3 @@ /* istanbul ignore next */ | ||
*/ | ||
reformatError(err/*, req, res*/) { | ||
reformatError(err /*, req, res*/) { | ||
return _.pick(err, ["name", "message", "code", "type", "data"]); | ||
@@ -995,3 +1038,3 @@ }, | ||
res.writeHead(code, { | ||
"Location": url, | ||
Location: url, | ||
"Content-Length": "0" | ||
@@ -1015,3 +1058,6 @@ }); | ||
if (questionIdx !== -1) { | ||
query = queryString.parse(req.url.substring(questionIdx + 1), this.settings.qsOptions); | ||
query = queryString.parse( | ||
req.url.substring(questionIdx + 1), | ||
this.settings.qsOptions | ||
); | ||
url = req.url.substring(0, questionIdx); | ||
@@ -1041,10 +1087,6 @@ } | ||
coloringStatusCode(code) { | ||
if (code >= 500) | ||
return kleur.red().bold(code); | ||
if (code >= 400 && code < 500) | ||
return kleur.red().bold(code); | ||
if (code >= 300 && code < 400) | ||
return kleur.cyan().bold(code); | ||
if (code >= 200 && code < 300) | ||
return kleur.green().bold(code); | ||
if (code >= 500) return kleur.red().bold(code); | ||
if (code >= 400 && code < 500) return kleur.red().bold(code); | ||
if (code >= 300 && code < 400) return kleur.cyan().bold(code); | ||
if (code >= 200 && code < 300) return kleur.green().bold(code); | ||
@@ -1069,10 +1111,10 @@ /* istanbul ignore next */ | ||
const duration = (diff[0] + diff[1] / 1e9) * 1000; | ||
if (duration > 1000) | ||
time = kleur.red(`[+${Number(duration / 1000).toFixed(3)} s]`); | ||
else | ||
time = kleur.grey(`[+${Number(duration).toFixed(3)} ms]`); | ||
if (duration > 1000) time = kleur.red(`[+${Number(duration / 1000).toFixed(3)} s]`); | ||
else time = kleur.grey(`[+${Number(duration).toFixed(3)} ms]`); | ||
} | ||
if (this.settings.logResponse && this.settings.logResponse in this.logger) | ||
this.logger[this.settings.logResponse](`<= ${this.coloringStatusCode(res.statusCode)} ${req.method} ${kleur.bold(req.originalUrl)} ${time}`); | ||
this.logger[this.settings.logResponse]( | ||
`<= ${this.coloringStatusCode(res.statusCode)} ${req.method} ${kleur.bold(req.originalUrl)} ${time}` | ||
); | ||
@@ -1094,4 +1136,3 @@ /* istanbul ignore next */ | ||
if (_.isString(settings)) { | ||
if (settings.indexOf(origin) !== -1) | ||
return true; | ||
if (settings.indexOf(origin) !== -1) return true; | ||
@@ -1128,3 +1169,2 @@ if (settings.indexOf("*") !== -1) { | ||
writeCorsHeaders(route, req, res, isPreFlight) { | ||
/* istanbul ignore next */ | ||
@@ -1135,4 +1175,3 @@ if (!route.cors) return; | ||
// It's not presented, when it's a local request (origin and target same) | ||
if (!origin) | ||
return; | ||
if (!origin) return; | ||
@@ -1158,3 +1197,6 @@ // Access-Control-Allow-Origin | ||
} else if (Array.isArray(route.cors.exposedHeaders)) { | ||
res.setHeader("Access-Control-Expose-Headers", route.cors.exposedHeaders.join(", ")); | ||
res.setHeader( | ||
"Access-Control-Expose-Headers", | ||
route.cors.exposedHeaders.join(", ") | ||
); | ||
} | ||
@@ -1167,3 +1209,6 @@ | ||
} else if (Array.isArray(route.cors.allowedHeaders)) { | ||
res.setHeader("Access-Control-Allow-Headers", route.cors.allowedHeaders.join(", ")); | ||
res.setHeader( | ||
"Access-Control-Allow-Headers", | ||
route.cors.allowedHeaders.join(", ") | ||
); | ||
} else { | ||
@@ -1201,8 +1246,8 @@ // AllowedHeaders doesn't specified, so we send back from req headers | ||
// Rewrite to for iterator (faster) | ||
return route.whitelist.find(mask => { | ||
if (_.isString(mask)) | ||
return match(action, mask); | ||
else if (_.isRegExp(mask)) | ||
return mask.test(action); | ||
}) != null; | ||
return ( | ||
route.whitelist.find(mask => { | ||
if (_.isString(mask)) return match(action, mask); | ||
else if (_.isRegExp(mask)) return mask.test(action); | ||
}) != null | ||
); | ||
}, | ||
@@ -1243,10 +1288,7 @@ | ||
// Add new route | ||
if (toBottom) | ||
this.routes.push(route); | ||
else | ||
this.routes.unshift(route); | ||
if (toBottom) this.routes.push(route); | ||
else this.routes.unshift(route); | ||
// Reordering routes | ||
if (this.settings.optimizeOrder) | ||
this.optimizeRouteOrder(); | ||
if (this.settings.optimizeOrder) this.optimizeRouteOrder(); | ||
} | ||
@@ -1311,3 +1353,6 @@ | ||
this.logger.debug("Optimized path order: ", this.routes.map(r => r.path)); | ||
this.logger.debug( | ||
"Optimized path order: ", | ||
this.routes.map(r => r.path) | ||
); | ||
}, | ||
@@ -1333,6 +1378,7 @@ | ||
if (!_.isFunction(fn)) { | ||
this.logger.warn("Define 'authorize' method in the service to enable authorization."); | ||
this.logger.warn( | ||
"Define 'authorize' method in the service to enable authorization." | ||
); | ||
route.authorization = null; | ||
} else | ||
route.authorization = fn; | ||
} else route.authorization = fn; | ||
} | ||
@@ -1344,6 +1390,7 @@ if (opts.authentication) { | ||
if (!_.isFunction(fn)) { | ||
this.logger.warn("Define 'authenticate' method in the service to enable authentication."); | ||
this.logger.warn( | ||
"Define 'authenticate' method in the service to enable authentication." | ||
); | ||
route.authentication = null; | ||
} else | ||
route.authentication = fn; | ||
} else route.authentication = fn; | ||
} | ||
@@ -1378,7 +1425,10 @@ | ||
let mw = []; | ||
if (this.settings.use && Array.isArray(this.settings.use) && this.settings.use.length > 0) | ||
if ( | ||
this.settings.use && | ||
Array.isArray(this.settings.use) && | ||
this.settings.use.length > 0 | ||
) | ||
mw.push(...this.settings.use); | ||
if (opts.use && Array.isArray(opts.use) && opts.use.length > 0) | ||
mw.push(...opts.use); | ||
if (opts.use && Array.isArray(opts.use) && opts.use.length > 0) mw.push(...opts.use); | ||
@@ -1393,6 +1443,11 @@ if (mw.length > 0) { | ||
// Merge cors settings | ||
route.cors = Object.assign({}, { | ||
origin: "*", | ||
methods: ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE"] | ||
}, this.settings.cors, opts.cors); | ||
route.cors = Object.assign( | ||
{}, | ||
{ | ||
origin: "*", | ||
methods: ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE"] | ||
}, | ||
this.settings.cors, | ||
opts.cors | ||
); | ||
} else { | ||
@@ -1405,13 +1460,19 @@ route.cors = null; | ||
if (rateLimit) { | ||
let opts = Object.assign({}, { | ||
window: 60 * 1000, | ||
limit: 30, | ||
headers: false, | ||
key: (req) => { | ||
return req.headers["x-forwarded-for"] || | ||
req.connection.remoteAddress || | ||
req.socket.remoteAddress || | ||
req.connection.socket.remoteAddress; | ||
} | ||
}, rateLimit); | ||
let opts = Object.assign( | ||
{}, | ||
{ | ||
window: 60 * 1000, | ||
limit: 30, | ||
headers: false, | ||
key: req => { | ||
return ( | ||
req.headers["x-forwarded-for"] || | ||
req.connection.remoteAddress || | ||
req.socket.remoteAddress || | ||
req.connection.socket.remoteAddress | ||
); | ||
} | ||
}, | ||
rateLimit | ||
); | ||
@@ -1422,5 +1483,3 @@ route.rateLimit = opts; | ||
route.rateLimit.store = new opts.StoreFactory(opts.window, opts, this.broker); | ||
else | ||
route.rateLimit.store = new MemoryStore(opts.window, opts, this.broker); | ||
else route.rateLimit.store = new MemoryStore(opts.window, opts, this.broker); | ||
} | ||
@@ -1433,15 +1492,13 @@ | ||
// `onBeforeCall` handler | ||
if (opts.onBeforeCall) | ||
route.onBeforeCall = opts.onBeforeCall; | ||
if (opts.onBeforeCall) route.onBeforeCall = opts.onBeforeCall; | ||
// `onAfterCall` handler | ||
if (opts.onAfterCall) | ||
route.onAfterCall = opts.onAfterCall; | ||
if (opts.onAfterCall) route.onAfterCall = opts.onAfterCall; | ||
// `onError` handler | ||
if (opts.onError) | ||
route.onError = opts.onError; | ||
if (opts.onError) route.onError = opts.onError; | ||
// Create URL prefix | ||
const globalPath = this.settings.path && this.settings.path != "/" ? this.settings.path : ""; | ||
const globalPath = | ||
this.settings.path && this.settings.path != "/" ? this.settings.path : ""; | ||
route.path = addSlashes(globalPath) + (opts.path || ""); | ||
@@ -1462,3 +1519,4 @@ route.path = normalizePath(route.path); | ||
const hasAliases = _.isObject(opts.aliases) && Object.keys(opts.aliases).length > 0; | ||
route.mappingPolicy = hasAliases || opts.autoAliases ? MAPPING_POLICY_RESTRICT : MAPPING_POLICY_ALL; | ||
route.mappingPolicy = | ||
hasAliases || opts.autoAliases ? MAPPING_POLICY_RESTRICT : MAPPING_POLICY_ALL; | ||
} | ||
@@ -1521,3 +1579,5 @@ | ||
const pathName = p[1]; | ||
const pathNameWithoutEndingSlash = pathName.endsWith("/") ? pathName.slice(0, -1) : pathName; | ||
const pathNameWithoutEndingSlash = pathName.endsWith("/") | ||
? pathName.slice(0, -1) | ||
: pathName; | ||
const aliases = { | ||
@@ -1561,5 +1621,8 @@ list: `GET ${pathName}`, | ||
const services = this.broker.registry.getServiceList({ withActions: true, grouping: true }); | ||
const services = this.broker.registry.getServiceList({ | ||
withActions: true, | ||
grouping: true | ||
}); | ||
services.forEach(service => { | ||
if(!service.settings) return; | ||
if (!service.settings) return; | ||
const serviceName = service.fullName || getServiceFullname(service); | ||
@@ -1580,3 +1643,5 @@ | ||
for (let basePath of basePaths) { | ||
basePath = addSlashes(_.isString(basePath) ? basePath : serviceName.replace(SLASH_REGEX, "/")); | ||
basePath = addSlashes( | ||
_.isString(basePath) ? basePath : serviceName.replace(SLASH_REGEX, "/") | ||
); | ||
@@ -1586,6 +1651,8 @@ _.forIn(service.actions, action => { | ||
// Check visibility | ||
if (action.visibility != null && action.visibility != "published") return; | ||
if (action.visibility != null && action.visibility != "published") | ||
return; | ||
// Check whitelist | ||
if (route.hasWhitelist && !this.checkWhitelist(route, action.name)) return; | ||
if (route.hasWhitelist && !this.checkWhitelist(route, action.name)) | ||
return; | ||
@@ -1605,3 +1672,7 @@ let restRoutes = []; | ||
} else if (_.isObject(restRoute)) { | ||
alias = this.parseActionRestObject(restRoute, action.rawName, basePath); | ||
alias = this.parseActionRestObject( | ||
restRoute, | ||
action.rawName, | ||
basePath | ||
); | ||
} | ||
@@ -1653,3 +1724,5 @@ | ||
method: restRoute.method || "*", | ||
path: (restRoute.basePath ? restRoute.basePath : basePath) + (restRoute.path ? restRoute.path : rawName) | ||
path: | ||
(restRoute.basePath ? restRoute.basePath : basePath) + | ||
(restRoute.path ? restRoute.path : rawName) | ||
}); | ||
@@ -1690,3 +1763,2 @@ }, | ||
/** | ||
@@ -1703,4 +1775,3 @@ * Set log level and log registration route related activities | ||
this.logger[this.settings.logRouteRegistration](message); | ||
}, | ||
} | ||
}, | ||
@@ -1719,3 +1790,2 @@ | ||
if (this.settings.server !== false) { | ||
if (_.isObject(this.settings.server)) { | ||
@@ -1738,3 +1808,6 @@ // Use an existing server instance | ||
// Special char for internal services | ||
const specChar = this.settings.internalServiceSpecialChar != null ? this.settings.internalServiceSpecialChar : "~"; | ||
const specChar = | ||
this.settings.internalServiceSpecialChar != null | ||
? this.settings.internalServiceSpecialChar | ||
: "~"; | ||
// eslint-disable-next-line security/detect-non-literal-regexp | ||
@@ -1754,5 +1827,7 @@ this._isscRe = new RegExp(specChar); | ||
if (Array.isArray(this.settings.routes) && this.settings.routes.length == 0) { | ||
this.settings.routes = [{ | ||
path: "/" | ||
}]; | ||
this.settings.routes = [ | ||
{ | ||
path: "/" | ||
} | ||
]; | ||
} | ||
@@ -1766,6 +1841,9 @@ | ||
// Regenerate all auto aliases routes | ||
const debounceTime = this.settings.debounceTime > 0 ? parseInt(this.settings.debounceTime) : 500; | ||
const debounceTime = | ||
this.settings.debounceTime > 0 ? parseInt(this.settings.debounceTime) : 500; | ||
this.regenerateAllAutoAliases = _.debounce(() => { | ||
/* istanbul ignore next */ | ||
this.routes.forEach(route => route.opts.autoAliases && this.regenerateAutoAliases(route)); | ||
this.routes.forEach( | ||
route => route.opts.autoAliases && this.regenerateAutoAliases(route) | ||
); | ||
@@ -1776,3 +1854,2 @@ this.broker.broadcast("$api.aliases.regenerated"); | ||
/** | ||
@@ -1782,4 +1859,3 @@ * Service started lifecycle event handler | ||
started() { | ||
if (this.settings.server === false) | ||
return this.Promise.resolve(); | ||
if (this.settings.server === false) return this.Promise.resolve(); | ||
@@ -1789,8 +1865,12 @@ /* istanbul ignore next */ | ||
this.server.listen(this.settings.port, this.settings.ip, err => { | ||
if (err) | ||
return reject(err); | ||
if (err) return reject(err); | ||
const addr = this.server.address(); | ||
const listenAddr = addr.address == "0.0.0.0" && os.platform() == "win32" ? "localhost" : addr.address; | ||
this.logger.info(`API Gateway listening on ${this.isHTTPS ? "https" : "http"}://${listenAddr}:${addr.port}`); | ||
const listenAddr = | ||
addr.address == "0.0.0.0" && os.platform() == "win32" | ||
? "localhost" | ||
: addr.address; | ||
this.logger.info( | ||
`API Gateway listening on ${this.isHTTPS ? "https" : "http"}://${listenAddr}:${addr.port}` | ||
); | ||
resolve(); | ||
@@ -1809,4 +1889,3 @@ }); | ||
this.server.close(err => { | ||
if (err) | ||
return reject(err); | ||
if (err) return reject(err); | ||
@@ -1813,0 +1892,0 @@ this.logger.info("API Gateway stopped!"); |
/* | ||
* moleculer | ||
* Copyright (c) 2021 MoleculerJS (https://github.com/moleculerjs/moleculer) | ||
* Copyright (c) 2024 MoleculerJS (https://github.com/moleculerjs/moleculer) | ||
* MIT Licensed | ||
@@ -17,3 +17,2 @@ */ | ||
class MemoryStore { | ||
/** | ||
@@ -20,0 +19,0 @@ * Creates an instance of MemoryStore. |
/* | ||
* moleculer | ||
* Copyright (c) 2021 MoleculerJS (https://github.com/moleculerjs/moleculer) | ||
* Copyright (c) 2024 MoleculerJS (https://github.com/moleculerjs/moleculer) | ||
* MIT Licensed | ||
@@ -9,5 +9,5 @@ */ | ||
const _ = require("lodash"); | ||
const fresh = require("fresh"); | ||
const etag = require("etag"); | ||
const _ = require("lodash"); | ||
const fresh = require("fresh"); | ||
const etag = require("etag"); | ||
@@ -24,3 +24,3 @@ const { BadRequestError, ERR_UNABLE_DECODE_PARAM } = require("./errors"); | ||
return decodeURIComponent(param); | ||
} catch (_) { | ||
} catch { | ||
/* istanbul ignore next */ | ||
@@ -33,6 +33,4 @@ throw new BadRequestError(ERR_UNABLE_DECODE_PARAM, { param }); | ||
function removeTrailingSlashes(s) { | ||
if (s.startsWith("/")) | ||
s = s.slice(1); | ||
if (s.endsWith("/")) | ||
s = s.slice(0, -1); | ||
if (s.startsWith("/")) s = s.slice(1); | ||
if (s.endsWith("/")) s = s.slice(0, -1); | ||
return s; //.replace(/\/\//g, "/"); | ||
@@ -61,4 +59,3 @@ } | ||
if (i >= mws.length) { | ||
if (_.isFunction(done)) | ||
return done.call(self, err); | ||
if (_.isFunction(done)) return done.call(self, err); | ||
@@ -71,11 +68,7 @@ /* istanbul ignore next */ | ||
// Call only error middlewares (err, req, res, next) | ||
if (mws[i].length == 4) | ||
mws[i].call(self, err, req, res, err => next(i + 1, err)); | ||
else | ||
next(i + 1, err); | ||
if (mws[i].length == 4) mws[i].call(self, err, req, res, err => next(i + 1, err)); | ||
else next(i + 1, err); | ||
} else { | ||
if (mws[i].length < 4) | ||
mws[i].call(self, req, res, err => next(i + 1, err)); | ||
else | ||
next(i + 1); | ||
if (mws[i].length < 4) mws[i].call(self, req, res, err => next(i + 1, err)); | ||
else next(i + 1); | ||
} | ||
@@ -98,8 +91,9 @@ }; | ||
/* istanbul ignore next */ | ||
if (err instanceof MoleculerError) | ||
return reject(err); | ||
if (err instanceof MoleculerError) return reject(err); | ||
/* istanbul ignore next */ | ||
if (err instanceof Error) | ||
return reject(new MoleculerError(err.message, err.code || err.status, err.type)); // TODO err.stack | ||
return reject( | ||
new MoleculerError(err.message, err.code || err.status, err.type) | ||
); // TODO err.stack | ||
@@ -124,10 +118,7 @@ /* istanbul ignore next */ | ||
function generateETag(body, opt) { | ||
if (_.isFunction(opt)) | ||
return opt.call(this, body); | ||
if (_.isFunction(opt)) return opt.call(this, body); | ||
let buf = !Buffer.isBuffer(body) | ||
? Buffer.from(body) | ||
: body; | ||
let buf = !Buffer.isBuffer(body) ? Buffer.from(body) : body; | ||
return etag(buf, (opt === true || opt === "weak") ? { weak: true } : null); | ||
return etag(buf, opt === true || opt === "weak" ? { weak: true } : null); | ||
} | ||
@@ -146,3 +137,3 @@ | ||
return fresh(req.headers, { | ||
"etag": res.getHeader("ETag"), | ||
etag: res.getHeader("ETag"), | ||
"last-modified": res.getHeader("Last-Modified") | ||
@@ -149,0 +140,0 @@ }); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
139396
16
2979
13
29
+ Added@types/qs@^6.9.17
+ Added@fastify/busboy@3.1.1(transitive)
+ Added@types/qs@6.9.17(transitive)
+ Addedpath-to-regexp@8.2.0(transitive)
- Removed@fastify/busboy@1.2.1(transitive)
- Removedansi-styles@3.2.1(transitive)
- Removedargs@5.0.3(transitive)
- Removedbalanced-match@1.0.2(transitive)
- Removedbrace-expansion@1.1.11(transitive)
- Removedcamelcase@5.0.0(transitive)
- Removedchalk@2.4.2(transitive)
- Removedcolor-convert@1.9.3(transitive)
- Removedcolor-name@1.1.3(transitive)
- Removedconcat-map@0.0.1(transitive)
- Removedescape-string-regexp@1.0.5(transitive)
- Removedeventemitter2@6.4.9(transitive)
- Removedfastest-validator@1.19.0(transitive)
- Removedfs.realpath@1.0.0(transitive)
- Removedglob@7.2.3(transitive)
- Removedhas-flag@3.0.0(transitive)
- Removedinflight@1.0.6(transitive)
- Removedipaddr.js@2.2.0(transitive)
- Removedleven@2.1.0(transitive)
- Removedlru-cache@6.0.0(transitive)
- Removedminimatch@3.1.2(transitive)
- Removedmoleculer@0.14.35(transitive)
- Removedmri@1.1.4(transitive)
- Removednode-fetch@2.7.0(transitive)
- Removedonce@1.4.0(transitive)
- Removedpath-is-absolute@1.0.1(transitive)
- Removedpath-to-regexp@3.3.0(transitive)
- Removedrecursive-watch@1.1.4(transitive)
- Removedsupports-color@5.5.0(transitive)
- Removedtext-decoding@1.0.0(transitive)
- Removedtr46@0.0.3(transitive)
- Removedttl@1.3.1(transitive)
- Removedwebidl-conversions@3.0.1(transitive)
- Removedwhatwg-url@5.0.0(transitive)
- Removedwrappy@1.0.2(transitive)
- Removedyallist@4.0.0(transitive)
Updated@fastify/busboy@^3.1.0
Updatedbody-parser@^1.20.3
Updatedkleur@^4.1.5
Updatedpath-to-regexp@^8.2.0
Updatedqs@^6.13.1
Updatedserve-static@^1.16.2