Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

moleculer-web

Package Overview
Dependencies
Maintainers
3
Versions
67
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

moleculer-web - npm Package Compare versions

Comparing version 0.10.7 to 0.11.0-beta1

eslint.config.js

95

CHANGELOG.md

@@ -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)

58

index.d.ts

@@ -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",

/*
* 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 @@

/*
* 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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc