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

@vmm/service

Package Overview
Dependencies
Maintainers
9
Versions
42
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@vmm/service - npm Package Compare versions

Comparing version 1.0.89 to 1.0.91

2

package.json
{
"name": "@vmm/service",
"version": "1.0.89",
"version": "1.0.91",
"main": "src/index.js",

@@ -5,0 +5,0 @@ "description": "VMM Service library",

@@ -1,154 +0,151 @@

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
'use strict';
var __importDefault =
(this && this.__importDefault) ||
function (mod) {
return mod && mod.__esModule ? mod : { default: mod };
};
Object.defineProperty(exports, '__esModule', { value: true });
exports.Argv = void 0;
const lodash_1 = __importDefault(require("lodash"));
const lodash_1 = __importDefault(require('lodash'));
class Argv {
_appInfo;
_error;
_argDefines;
_args = {};
_parsedArgs = {};
constructor(argDefines, appInfo) {
this._appInfo = appInfo;
this._argDefines = argDefines;
this._addDefaultArg();
this._initDefaultValues();
this._parse();
this._applyCmdDefines();
constructor(argDefines, appInfo) {
this._args = {};
this._parsedArgs = {};
this._appInfo = appInfo;
this._argDefines = argDefines;
this._addDefaultArg();
this._initDefaultValues();
this._parse();
this._applyCmdDefines();
}
_initDefaultValues() {
for (const k of Object.keys(this._argDefines)) {
this._args[k] = this._argDefines[k].default;
}
_initDefaultValues() {
for (const k of Object.keys(this._argDefines)) {
this._args[k] = this._argDefines[k].default;
}
_addDefaultArg() {
this._argDefines['help'] = {
short: 'h',
description: 'show help message',
default: false,
};
this._argDefines['version'] = {
short: 'v',
description: 'show current version',
default: false,
};
}
_parse() {
let lastArg = null;
this._parsedArgs = {};
process.argv.forEach((arg, i) => {
if (i === 0 || i === 1) return;
if (arg.startsWith('-')) {
this._parsedArgs[arg] = true;
lastArg = arg;
} else {
if (lastArg) {
this._parsedArgs[lastArg] = arg;
}
}
});
}
_setParseError(err) {
if (this._error) {
this._error = this._error + '\n' + err;
} else {
this._error = err;
}
_addDefaultArg() {
this._argDefines["help"] = {
short: "h",
description: "show help message",
default: false
};
this._argDefines["version"] = {
short: "v",
description: "show current version",
default: false
};
}
_parse() {
let lastArg = null;
this._parsedArgs = {};
process.argv.forEach((arg, i) => {
if (i === 0 || i === 1)
return;
if (arg.startsWith("-")) {
this._parsedArgs[arg] = true;
lastArg = arg;
}
else {
if (lastArg) {
this._parsedArgs[lastArg] = arg;
}
}
});
}
_setParseError(err) {
if (this._error) {
this._error = this._error + "\n" + err;
}
_setArgValue(key, value) {
switch (typeof this._argDefines[key].default) {
case 'string':
if (typeof value === 'string') {
this._args[key] = value;
} else {
this._setParseError(`Error Arg: ${key}`);
}
else {
this._error = err;
}
}
_setArgValue(key, value) {
switch (typeof this._argDefines[key].default) {
case "string":
if (typeof value === "string") {
this._args[key] = value;
}
else {
this._setParseError(`Error Arg: ${key}`);
}
break;
case "boolean":
if (typeof value === "boolean") {
this._args[key] = value;
}
else if (typeof value === "string") {
switch (value.toLowerCase()) {
case "yes":
case "true":
case "1":
this._args[key] = true;
break;
default:
this._args[key] = false;
}
}
break;
case "number":
this._args[key] = Number(value);
break;
break;
case 'boolean':
if (typeof value === 'boolean') {
this._args[key] = value;
} else if (typeof value === 'string') {
switch (value.toLowerCase()) {
case 'yes':
case 'true':
case '1':
this._args[key] = true;
break;
default:
this._setParseError(`Error Arg: ${key}`);
this._args[key] = false;
}
}
break;
case 'number':
this._args[key] = Number(value);
break;
default:
this._setParseError(`Error Arg: ${key}`);
}
_applyCmdDefines() {
for (const key of Object.keys(this._argDefines)) {
const longArg = `--${lodash_1.default.kebabCase(key)}`;
const shortArg = `-${this._argDefines[key].short}`;
if (this._parsedArgs[longArg]) {
this._setArgValue(key, this._parsedArgs[longArg]);
this._parsedArgs[`--${lodash_1.default.kebabCase(key)}`] = undefined;
}
else if (this._parsedArgs[shortArg]) {
this._setArgValue(key, this._parsedArgs[shortArg]);
this._parsedArgs[`-${this._argDefines[key].short}`] = undefined;
}
}
}
_applyCmdDefines() {
for (const key of Object.keys(this._argDefines)) {
const longArg = `--${lodash_1.default.kebabCase(key)}`;
const shortArg = `-${this._argDefines[key].short}`;
if (this._parsedArgs[longArg]) {
this._setArgValue(key, this._parsedArgs[longArg]);
this._parsedArgs[`--${lodash_1.default.kebabCase(key)}`] = undefined;
} else if (this._parsedArgs[shortArg]) {
this._setArgValue(key, this._parsedArgs[shortArg]);
this._parsedArgs[`-${this._argDefines[key].short}`] = undefined;
}
}
_printHelp() {
if (this._error) {
console.log("ERROR:" + this._error);
}
if (this._appInfo) {
console.log(this._appInfo.name + " v" + this._appInfo.version);
console.log(this._appInfo.description);
}
console.log("Arguments:");
lodash_1.default.forEach(this._argDefines, (arg, key) => {
console.log(` -${arg.short} [--${lodash_1.default.kebabCase(key)}], ${arg.description} ,default: ${arg.default}`);
});
}
_printHelp() {
if (this._error) {
console.log('ERROR:' + this._error);
}
check(bExit) {
lodash_1.default.forEach(this._parsedArgs, (arg, key) => {
if (arg !== undefined) {
this._setParseError(`invalid arg: ${key}`);
}
});
if (this._args["help"]) {
this._printHelp();
if (bExit) {
process.exit(1);
}
}
if (this._args["version"]) {
console.log(this._appInfo.version);
if (bExit) {
process.exit(1);
}
}
if (this._error) {
this._printHelp();
if (bExit) {
process.exit(1);
}
}
return this;
if (this._appInfo) {
console.log(this._appInfo.name + ' v' + this._appInfo.version);
console.log(this._appInfo.description);
}
args() {
return this._args;
console.log('Arguments:');
lodash_1.default.forEach(this._argDefines, (arg, key) => {
console.log(
` -${arg.short} [--${lodash_1.default.kebabCase(key)}], ${
arg.description
} ,default: ${arg.default}`,
);
});
}
check(bExit) {
lodash_1.default.forEach(this._parsedArgs, (arg, key) => {
if (arg !== undefined) {
this._setParseError(`invalid arg: ${key}`);
}
});
if (this._args['help']) {
this._printHelp();
if (bExit) {
process.exit(1);
}
}
if (this._args['version']) {
console.log(this._appInfo.version);
if (bExit) {
process.exit(1);
}
}
if (this._error) {
this._printHelp();
if (bExit) {
process.exit(1);
}
}
return this;
}
args() {
return this._args;
}
}
exports.Argv = Argv;

@@ -1,140 +0,168 @@

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
'use strict';
var __importDefault =
(this && this.__importDefault) ||
function (mod) {
return mod && mod.__esModule ? mod : { default: mod };
};
Object.defineProperty(exports, '__esModule', { value: true });
exports.Mongo = void 0;
const mongodb_1 = require("mongodb");
const lodash_1 = __importDefault(require("lodash"));
const logger_1 = require("./logger");
const mongodb_1 = require('mongodb');
const lodash_1 = __importDefault(require('lodash'));
const logger_1 = require('./logger');
const log = (0, logger_1.logger)(`@vmm/service:MongoDb`);
class Mongo {
_url;
_options;
_modName;
_client = null;
_db = null;
_collections = {};
_dbCollectionsDefine;
constructor(url, options, modName, dbCollectionsDefine) {
log.debug('using db defines:', Object.keys(dbCollectionsDefine));
this._dbCollectionsDefine = dbCollectionsDefine;
this._url = url;
this._options = options;
this._modName = modName;
constructor(url, options, modName, dbCollectionsDefine) {
this._client = null;
this._db = null;
this._collections = {};
log.debug('using db defines:', Object.keys(dbCollectionsDefine));
this._dbCollectionsDefine = dbCollectionsDefine;
this._url = url;
this._options = options;
this._modName = modName;
}
getMongoClient() {
if (this._client === null) throw new Error('Mongodb client Invalid!!');
return this._client;
}
collections() {
return this._collections;
}
async connect() {
log.debug('connect to mongodb');
this._client = await mongodb_1.MongoClient.connect(
this._url,
this._options,
);
this._db = await this._client.db(this._modName);
this._monitorDbEvent();
await this._ensureSchemaCollections();
lodash_1.default.forEach(this._collections, async (coll, name) => {
await this._ensureCollectionIndexes(
coll,
this._dbCollectionsDefine[name].indexSchema,
);
});
log.debug('open mongodb successed');
}
async _ensureSchemaCollections() {
log.debug('_ensureSchemaCollections:');
if (!this._db) return;
const curColls = lodash_1.default.keyBy(
await this._db.collections(),
'collectionName',
);
const modCollDefines = lodash_1.default.pickBy(
this._dbCollectionsDefine,
(v) => !lodash_1.default.get(v.collOptions, '_extDb'),
);
log.debug('modCollDefines:', Object.keys(modCollDefines));
const externCollDefines = lodash_1.default.pickBy(
this._dbCollectionsDefine,
(v) => !!lodash_1.default.get(v.collOptions, '_extDb'),
);
log.debug('externCollDefines:', externCollDefines);
for (const colName of Object.keys(curColls)) {
if (modCollDefines[colName]) {
log.debug('open existed collection:', colName);
this._collections[colName] = curColls[colName];
} else {
if (!colName.startsWith('_') && !colName.startsWith('system.')) {
const d = new Date();
const name =
'_unused_' +
colName +
`_${d.getFullYear()}_${d.getMonth()}_${d.getDate()}`;
await this._db.renameCollection(colName, name);
log.info('rename unused collection:', name);
} else {
log.info('unused collection:', colName);
}
}
}
getMongoClient() {
if (this._client === null)
throw new Error('Mongodb client Invalid!!');
return this._client;
for (const newColl of lodash_1.default.difference(
Object.keys(modCollDefines),
Object.keys(curColls),
)) {
this._collections[newColl] = await this._db.createCollection(
newColl,
this._dbCollectionsDefine[newColl].collOptions,
);
log.debug('create new collection:', newColl);
}
collections() {
return this._collections;
for (const k of Object.keys(externCollDefines)) {
const v = externCollDefines[k];
if (!v) continue;
const extDbInfo = lodash_1.default.get(v, 'collOptions._extDb');
if (!this._client || !extDbInfo) return;
const externDb = this._client.db(extDbInfo.db);
const extColls = lodash_1.default.keyBy(
await externDb.collections(),
'collectionName',
);
if (!extColls[extDbInfo.col]) {
log.debug('create extern collection ok:', extDbInfo);
this._collections[k] = await externDb.createCollection(
extDbInfo.col,
lodash_1.default.omit(v.collOptions, '_extDb'),
);
} else {
log.debug('open extern collection ok:', extDbInfo);
this._collections[k] = extColls[extDbInfo.col];
}
}
async connect() {
log.debug('connect to mongodb');
this._client = await mongodb_1.MongoClient.connect(this._url, this._options);
this._db = await this._client.db(this._modName);
this._monitorDbEvent();
await this._ensureSchemaCollections();
lodash_1.default.forEach(this._collections, async (coll, name) => {
await this._ensureCollectionIndexes(coll, this._dbCollectionsDefine[name].indexSchema);
});
log.debug('open mongodb successed');
}
async _ensureCollectionIndexes(coll, indexSchemas) {
if (lodash_1.default.isEmpty(indexSchemas)) {
return;
}
async _ensureSchemaCollections() {
log.debug('_ensureSchemaCollections:');
if (!this._db)
return;
const curColls = lodash_1.default.keyBy(await this._db.collections(), 'collectionName');
const modCollDefines = lodash_1.default.pickBy(this._dbCollectionsDefine, (v) => !lodash_1.default.get(v.collOptions, '_extDb'));
log.debug('modCollDefines:', Object.keys(modCollDefines));
const externCollDefines = lodash_1.default.pickBy(this._dbCollectionsDefine, (v) => !!lodash_1.default.get(v.collOptions, '_extDb'));
log.debug('externCollDefines:', externCollDefines);
for (const colName of Object.keys(curColls)) {
if (modCollDefines[colName]) {
log.debug('open existed collection:', colName);
this._collections[colName] = curColls[colName];
}
else {
if (!colName.startsWith('_') && !colName.startsWith('system.')) {
const d = new Date();
const name = '_unused_' + colName + `_${d.getFullYear()}_${d.getMonth()}_${d.getDate()}`;
await this._db.renameCollection(colName, name);
log.info('rename unused collection:', name);
}
else {
log.info('unused collection:', colName);
}
}
}
for (const newColl of lodash_1.default.difference(Object.keys(modCollDefines), Object.keys(curColls))) {
this._collections[newColl] = await this._db.createCollection(newColl, this._dbCollectionsDefine[newColl].collOptions);
log.debug('create new collection:', newColl);
}
for (const k of Object.keys(externCollDefines)) {
const v = externCollDefines[k];
if (!v)
continue;
const extDbInfo = lodash_1.default.get(v, 'collOptions._extDb');
if (!this._client || !extDbInfo)
return;
const externDb = this._client.db(extDbInfo.db);
const extColls = lodash_1.default.keyBy(await externDb.collections(), 'collectionName');
if (!extColls[extDbInfo.col]) {
log.debug('create extern collection ok:', extDbInfo);
this._collections[k] = await externDb.createCollection(extDbInfo.col, lodash_1.default.omit(v.collOptions, '_extDb'));
}
else {
log.debug('open extern collection ok:', extDbInfo);
this._collections[k] = extColls[extDbInfo.col];
}
}
const indexsArray = await coll.indexes();
const indexes = lodash_1.default.keyBy(indexsArray, 'name');
log.debug(
'ensure collection indexes:',
coll.collectionName,
Object.keys(indexSchemas).join(),
);
for (const key of Object.keys(indexes)) {
if (key.startsWith('_id')) continue;
if (!lodash_1.default.isPlainObject(indexSchemas[key])) {
await coll.dropIndex(key);
delete indexes[key];
log.info('drop invalid index:', coll.collectionName, key);
}
}
async _ensureCollectionIndexes(coll, indexSchemas) {
if (lodash_1.default.isEmpty(indexSchemas)) {
return;
}
const indexsArray = await coll.indexes();
const indexes = lodash_1.default.keyBy(indexsArray, 'name');
log.debug('ensure collection indexes:', coll.collectionName, Object.keys(indexSchemas).join());
for (const key of Object.keys(indexes)) {
if (key.startsWith('_id'))
continue;
if (!lodash_1.default.isPlainObject(indexSchemas[key])) {
await coll.dropIndex(key);
delete indexes[key];
log.info('drop invalid index:', coll.collectionName, key);
}
}
for (const key of Object.keys(indexSchemas)) {
if (lodash_1.default.isEmpty(indexes[key])) {
log.info('create new index:', coll.collectionName, key, indexSchemas[key]);
await coll.createIndex(indexSchemas[key].fields, {
name: key,
...indexSchemas[key].options,
});
}
}
}
_monitorDbEvent() {
if (!this._db)
return;
this._db.on('drop', () => {
log.debug('mongodb drop:');
for (const key of Object.keys(indexSchemas)) {
if (lodash_1.default.isEmpty(indexes[key])) {
log.info(
'create new index:',
coll.collectionName,
key,
indexSchemas[key],
);
await coll.createIndex(indexSchemas[key].fields, {
name: key,
...indexSchemas[key].options,
});
this._db.on('close', () => {
log.debug('mongodb close:');
});
this._db.on('error', (err) => {
log.debug('mongodb error:', err);
});
this._db.on('timeout', () => {
log.debug('mongodb timeout:');
});
this._db.on('reconnect', () => {
log.debug('mongodb reconnect:');
});
}
}
}
_monitorDbEvent() {
if (!this._db) return;
this._db.on('drop', () => {
log.debug('mongodb drop:');
});
this._db.on('close', () => {
log.debug('mongodb close:');
});
this._db.on('error', (err) => {
log.debug('mongodb error:', err);
});
this._db.on('timeout', () => {
log.debug('mongodb timeout:');
});
this._db.on('reconnect', () => {
log.debug('mongodb reconnect:');
});
}
}
exports.Mongo = Mongo;

@@ -1,316 +0,371 @@

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
'use strict';
var __importDefault =
(this && this.__importDefault) ||
function (mod) {
return mod && mod.__esModule ? mod : { default: mod };
};
Object.defineProperty(exports, '__esModule', { value: true });
exports.Service = exports.SessionCrypto = exports.SESS_START = void 0;
const express_1 = __importDefault(require("express"));
const body_parser_1 = __importDefault(require("body-parser"));
const logger_1 = require("./logger");
const express_1 = __importDefault(require('express'));
const body_parser_1 = __importDefault(require('body-parser'));
const logger_1 = require('./logger');
const log = (0, logger_1.logger)(`@vmm/service:Service`);
const crypto_1 = __importDefault(require("crypto"));
const assert_1 = __importDefault(require("assert"));
const http_1 = __importDefault(require("http"));
const lodash_1 = __importDefault(require("lodash"));
const base64url_1 = __importDefault(require("base64url"));
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
const restDbApi_1 = require("./restDbApi");
const crypto_1 = __importDefault(require('crypto'));
const assert_1 = __importDefault(require('assert'));
const http_1 = __importDefault(require('http'));
const lodash_1 = __importDefault(require('lodash'));
const base64url_1 = __importDefault(require('base64url'));
const jsonwebtoken_1 = __importDefault(require('jsonwebtoken'));
const restDbApi_1 = require('./restDbApi');
exports.SESS_START = '-cs-';
class SessionCrypto {
_key;
constructor(key) {
(0, assert_1.default)(key.length > 0);
let k = key;
while (k.length < 32) {
k = k + k;
}
this._key = Buffer.from(k.slice(0, 32));
constructor(key) {
(0, assert_1.default)(key.length > 0);
let k = key;
while (k.length < 32) {
k = k + k;
}
dec(data) {
let ret = {};
try {
const dec = crypto_1.default.createDecipheriv('aes-256-cbc', this._key, this._key.slice(16));
const buf = base64url_1.default.toBuffer(data);
ret = JSON.parse(Buffer.concat([dec.update(buf), dec.final()]).toString());
}
catch (e) {
log.error('dec session fail:', e.message);
throw 'session crypto fail';
}
return ret;
this._key = Buffer.from(k.slice(0, 32));
}
dec(data) {
let ret = {};
try {
const dec = crypto_1.default.createDecipheriv(
'aes-256-cbc',
this._key,
this._key.slice(16),
);
const buf = base64url_1.default.toBuffer(data);
ret = JSON.parse(
Buffer.concat([dec.update(buf), dec.final()]).toString(),
);
} catch (e) {
log.error('dec session fail:', e.message);
throw 'session crypto fail';
}
enc(data) {
(0, assert_1.default)(lodash_1.default.isPlainObject(data));
const enc = crypto_1.default.createCipheriv('aes-256-cbc', this._key, this._key.slice(16));
const encBuf = Buffer.concat([
enc.update(JSON.stringify(data)),
enc.final(),
]);
return base64url_1.default.encode(encBuf);
}
return ret;
}
enc(data) {
(0, assert_1.default)(lodash_1.default.isPlainObject(data));
const enc = crypto_1.default.createCipheriv(
'aes-256-cbc',
this._key,
this._key.slice(16),
);
const encBuf = Buffer.concat([
enc.update(JSON.stringify(data)),
enc.final(),
]);
return base64url_1.default.encode(encBuf);
}
}
exports.SessionCrypto = SessionCrypto;
class Service {
expressApp = (0, express_1.default)();
router = express_1.default.Router();
_options;
_name;
_sessionCrypto;
_servicePath;
api;
httpServer;
constructor(options) {
if (lodash_1.default.isEmpty(options.projectConfig.sessKey)) {
options.projectConfig.sessKey = crypto_1.default.randomBytes(8).toString('hex');
options.projectConfig._save();
}
log.debug('---> using sessKey:', options.projectConfig.sessKey);
options.projectConfig._event.on('load', () => {
log.debug('---> load sessKey:', options.projectConfig.sessKey);
this._sessionCrypto = new SessionCrypto(options.projectConfig.sessKey);
constructor(options) {
this.expressApp = (0, express_1.default)();
this.router = express_1.default.Router();
if (lodash_1.default.isEmpty(options.projectConfig.sessKey)) {
options.projectConfig.sessKey = crypto_1.default
.randomBytes(8)
.toString('hex');
options.projectConfig._save();
}
log.debug('---> using sessKey:', options.projectConfig.sessKey);
options.projectConfig._event.on('load', () => {
log.debug('---> load sessKey:', options.projectConfig.sessKey);
this._sessionCrypto = new SessionCrypto(options.projectConfig.sessKey);
});
this._sessionCrypto = new SessionCrypto(options.projectConfig.sessKey);
if (lodash_1.default.isEmpty(options.host)) {
options.host = '127.0.0.1';
}
if (lodash_1.default.isEmpty(options.sessionExpire)) {
options.sessionExpire = 8 * 60 * 60;
}
this._name = options.api.pkg.name;
this._options = options;
this._servicePath = `/api/${lodash_1.default
.kebabCase(this._name)
.replace(/-(api|service)$/, '')}`;
this.api = lodash_1.default.clone(options.api.api);
log.debug('api using service path :', this._servicePath);
this.router.use(this._responseHeaderOriginHandle.bind(this));
this.router.use('/_ping', this._pingHandle.bind(this));
this.router.use(this._logHandle.bind(this));
if (options.customApi) {
Object.keys(options.customApi).forEach((k) => {
log.debug('-- using customApi:', `/${lodash_1.default.kebabCase(k)}`);
this.router.use(
`/${lodash_1.default.kebabCase(k)}`,
options.customApi[k],
);
});
}
this.router.use(
body_parser_1.default.json({
type: 'application/json',
limit: '500kb',
}),
);
Object.keys(this.api).forEach((apiName) => {
if (apiName.startsWith('_')) return;
log.debug(
'using api:',
apiName,
this._servicePath + '/' + lodash_1.default.kebabCase(apiName),
);
this.router.use(
`/${lodash_1.default.kebabCase(apiName)}`,
this._createApiHandle(
this._options.api.api[apiName],
() => this.api[apiName],
),
);
});
if (this._options.api.db) {
lodash_1.default.forEach(this._options.api.db, (restDb, apiName) => {
const dbApis = (0, restDbApi_1.createDbApi)(
this._options.mongo,
restDb,
);
lodash_1.default.forEach(restDb, (dbMethod, methodName) => {
const apiPath = `/db/${lodash_1.default.kebabCase(
apiName,
)}/${lodash_1.default.kebabCase(methodName)}`;
this.router.use(
apiPath,
this._createApiHandle(dbMethod, () => dbApis[methodName]),
);
});
this._sessionCrypto = new SessionCrypto(options.projectConfig.sessKey);
if (lodash_1.default.isEmpty(options.host)) {
options.host = '127.0.0.1';
}
if (lodash_1.default.isEmpty(options.sessionExpire)) {
options.sessionExpire = 8 * 60 * 60;
}
this._name = options.api.pkg.name;
this._options = options;
this._servicePath = `/api/${lodash_1.default.kebabCase(this._name).replace(/-(api|service)$/, '')}`;
this.api = lodash_1.default.clone(options.api.api);
log.debug('api using service path :', this._servicePath);
this.router.use(this._responseHeaderOriginHandle.bind(this));
this.router.use('/_ping', this._pingHandle.bind(this));
this.router.use(this._logHandle.bind(this));
if (options.customApi) {
Object.keys(options.customApi).forEach((k) => {
log.debug('-- using customApi:', `/${lodash_1.default.kebabCase(k)}`);
console.log('=========', options.customApi[k]);
this.router.use(`/${lodash_1.default.kebabCase(k)}`, options.customApi[k]);
});
}
this.router.use(body_parser_1.default.json({
type: 'application/json',
limit: '500kb',
}));
Object.keys(this.api).forEach((apiName) => {
if (apiName.startsWith('_'))
return;
log.debug('using api:', apiName, this._servicePath + '/' + lodash_1.default.kebabCase(apiName));
this.router.use(`/${lodash_1.default.kebabCase(apiName)}`, this._createApiHandle(this._options.api.api[apiName], () => this.api[apiName]));
});
if (this._options.api.db) {
lodash_1.default.forEach(this._options.api.db, (restDb, apiName) => {
const dbApis = (0, restDbApi_1.createDbApi)(this._options.mongo, restDb);
lodash_1.default.forEach(restDb, (dbMethod, methodName) => {
const apiPath = `/db/${lodash_1.default.kebabCase(apiName)}/${lodash_1.default.kebabCase(methodName)}`;
this.router.use(apiPath, this._createApiHandle(dbMethod, () => dbApis[methodName]));
});
log.debug('using rest db api:', apiName, Object.keys(restDb).join());
});
}
this.expressApp.use(body_parser_1.default.urlencoded({ extended: false }));
this.expressApp.use(body_parser_1.default.json());
this.expressApp.use(this._servicePath, this.router);
this.httpServer = http_1.default.createServer(this.expressApp);
log.debug('using rest db api:', apiName, Object.keys(restDb).join());
});
}
_responseHeaderOriginHandle(req, res, next) {
res.header('Access-Control-Allow-Origin', req.header('origin'));
res.header('Access-Control-Allow-Credentials', 'true');
if (req.header('Access-Control-Request-Method')) {
res.header('Access-Control-Allow-Method', req.header('Access-Control-Request-Method'));
this.expressApp.use(body_parser_1.default.urlencoded({ extended: false }));
this.expressApp.use(body_parser_1.default.json());
this.expressApp.use(this._servicePath, this.router);
this.httpServer = http_1.default.createServer(this.expressApp);
}
_responseHeaderOriginHandle(req, res, next) {
res.header('Access-Control-Allow-Origin', req.header('origin'));
res.header('Access-Control-Allow-Credentials', 'true');
if (req.header('Access-Control-Request-Method')) {
res.header(
'Access-Control-Allow-Method',
req.header('Access-Control-Request-Method'),
);
}
if (req.header('Access-Control-Request-Headers')) {
res.header(
'Access-Control-Allow-Headers',
req.header('Access-Control-Request-Headers'),
);
}
let aceHeaders = '';
lodash_1.default.forEach(req.headers, (v, k) => {
if (k.startsWith(exports.SESS_START)) {
if (aceHeaders.length == 0) {
aceHeaders = aceHeaders + k;
} else {
aceHeaders = aceHeaders + ', ' + k;
}
if (req.header('Access-Control-Request-Headers')) {
res.header('Access-Control-Allow-Headers', req.header('Access-Control-Request-Headers'));
}
let aceHeaders = '';
lodash_1.default.forEach(req.headers, (v, k) => {
if (k.startsWith(exports.SESS_START)) {
if (aceHeaders.length == 0) {
aceHeaders = aceHeaders + k;
}
else {
aceHeaders = aceHeaders + ', ' + k;
}
}
});
res.header('Access-Control-Expose-Headers', aceHeaders);
res.header('Access-Control-Allow-Credentials', 'true');
if (req.method === 'OPTIONS') {
res.status(204).end();
return;
}
next();
}
});
res.header('Access-Control-Expose-Headers', aceHeaders);
res.header('Access-Control-Allow-Credentials', 'true');
if (req.method === 'OPTIONS') {
res.status(204).end();
return;
}
_logHandle(req, res, next) {
log.debug('http request:', req.socket.remoteAddress, req.originalUrl, req.headers);
next();
next();
}
_logHandle(req, res, next) {
log.debug(
'http request:',
req.socket.remoteAddress,
req.originalUrl,
req.headers,
);
next();
}
_pingHandle(req, res, next) {
res.status(200).json({ ret: 'ok' });
}
buildClientSession(name, sess) {
sess.iss = name;
delete sess.iat;
delete sess.exp;
sess.server = this._sessionCrypto.enc(sess.server || {});
log.debug('update jwt session ', name, sess);
const s = jsonwebtoken_1.default.sign(
sess,
this._options.projectConfig.sessKey,
{
expiresIn:
sess._expire > 0 ? sess._expire : this._options.sessionExpire,
},
);
return s;
}
decoderClientSession(sessName, csData) {
if (lodash_1.default.isEmpty(csData.trim())) {
return null;
}
_pingHandle(req, res, next) {
res.status(200).json({ ret: 'ok' });
let sess;
try {
sess = jsonwebtoken_1.default.verify(
csData,
this._options.projectConfig.sessKey,
);
} catch (e) {
log.error('verify jwt fail:', sessName, e.message);
return null;
}
buildClientSession(name, sess) {
sess.iss = name;
delete sess.iat;
delete sess.exp;
sess.server = this._sessionCrypto.enc(sess.server || {});
log.debug('update jwt session ', name, sess);
const s = jsonwebtoken_1.default.sign(sess, this._options.projectConfig.sessKey, {
expiresIn: sess._expire > 0 ? sess._expire : this._options.sessionExpire,
});
return s;
try {
sess.server = this._sessionCrypto.dec(sess.server);
} catch (e) {
log.error('decrypt session server fail:', sessName, csData);
return null;
}
decoderClientSession(sessName, csData) {
if (lodash_1.default.isEmpty(csData.trim())) {
return null;
if (sess.iss !== sessName) {
log.error(`jwt session iss fail,sess=${sessName},iss=${sess.iss}`);
return null;
}
return sess;
}
_parseSession(req) {
const cs = {};
const fnGetCs = (obj) => {
if (typeof obj !== 'object') return {};
const ret = {};
lodash_1.default.forEach(obj, (v, k) => {
if (k.startsWith(exports.SESS_START)) {
ret[k.slice(4)] = v;
}
let sess;
try {
sess = jsonwebtoken_1.default.verify(csData, this._options.projectConfig.sessKey);
}
catch (e) {
log.error('verify jwt fail:', sessName, e.message);
return null;
}
try {
sess.server = this._sessionCrypto.dec(sess.server);
}
catch (e) {
log.error('decrypt session server fail:', sessName, csData);
return null;
}
if (sess.iss !== sessName) {
log.error(`jwt session iss fail,sess=${sessName},iss=${sess.iss}`);
return null;
}
return sess;
});
return ret;
};
lodash_1.default.assign(cs, fnGetCs(req.headers), fnGetCs(req.query));
const retcs = {};
lodash_1.default.forEach(cs, (sessData, sessName) => {
const _sessName = lodash_1.default.camelCase(sessName);
const sess = this.decoderClientSession(_sessName, sessData);
if (sess) {
retcs[_sessName] = sess;
}
});
return retcs;
}
_checkApiPremissions(definedApi, sessions) {
log.debug('check api premissions: need=', definedApi._actor);
if (lodash_1.default.isEmpty(definedApi._actor)) {
return true;
}
_parseSession(req) {
const cs = {};
const fnGetCs = (obj) => {
if (typeof obj !== 'object')
return {};
const ret = {};
lodash_1.default.forEach(obj, (v, k) => {
if (k.startsWith(exports.SESS_START)) {
ret[k.slice(4)] = v;
}
});
return ret;
};
lodash_1.default.assign(cs, fnGetCs(req.headers), fnGetCs(req.query));
const retcs = {};
lodash_1.default.forEach(cs, (sessData, sessName) => {
const _sessName = lodash_1.default.camelCase(sessName);
const sess = this.decoderClientSession(_sessName, sessData);
if (sess) {
retcs[_sessName] = sess;
}
});
return retcs;
let needActors = [];
if (lodash_1.default.isString(definedApi._actor)) {
needActors.push(definedApi._actor);
} else if (lodash_1.default.isArray(definedApi._actor)) {
needActors = definedApi._actor;
} else {
log.error(
'invalid defined API actors',
JSON.stringify(definedApi, null, 2),
);
return false;
}
_checkApiPremissions(definedApi, sessions) {
log.debug('check api premissions: need=', definedApi._actor);
if (lodash_1.default.isEmpty(definedApi._actor)) {
return true;
console.log('----sessions', sessions);
const userActors = lodash_1.default.get(sessions, [
'user',
'client',
'actor',
]);
if (lodash_1.default.isEmpty(userActors)) {
log.error('invalid user actor', userActors);
return false;
}
if (
lodash_1.default.intersection(needActors, userActors).length !==
needActors.length
) {
log.error('invalid user actors,need:', needActors, ',user:', userActors);
return false;
}
log.debug('check user premissions successed. ', userActors);
return true;
}
_createApiHandle(preDefinedApi, createApiFunc) {
return async (req, res) => {
log.debug('api handle:', req.originalUrl);
try {
const sessions = this._parseSession(req);
for (const name of preDefinedApi._sessions) {
(0, assert_1.default)(
this._options.api.session[name],
`session define of ${name} not existed`,
);
if (!lodash_1.default.isPlainObject(sessions[name])) {
log.debug('set default sessions:', name);
sessions[name] = lodash_1.default.cloneDeep(
this._options.api.session[name],
);
}
Object.defineProperty(sessions[name], 'update', {
value: function (expireSeconds) {
this._updateFlag = true;
this._expire = expireSeconds;
},
enumerable: false,
});
Object.defineProperty(sessions[name], '_updateFlag', {
value: false,
enumerable: false,
writable: true,
});
Object.defineProperty(sessions[name], '_expire', {
value: -1,
enumerable: false,
writable: true,
});
}
let needActors = [];
if (lodash_1.default.isString(definedApi._actor)) {
needActors.push(definedApi._actor);
if (!this._checkApiPremissions(preDefinedApi, sessions)) {
res.status(401).end('invalid user actor for ' + req.originalUrl);
return;
}
else if (lodash_1.default.isArray(definedApi._actor)) {
needActors = definedApi._actor;
let result = {};
const apiFunc = createApiFunc();
if (typeof apiFunc === 'function') {
result = await apiFunc(req.body, sessions, req, res);
} else {
throw new Error('service api invalid');
}
else {
log.error('invalid defined API actors', JSON.stringify(definedApi, null, 2));
return false;
if (result['_res_end']) {
res.end();
return;
}
console.log('----sessions', sessions);
const userActors = lodash_1.default.get(sessions, ['user', 'client', 'actor']);
if (lodash_1.default.isEmpty(userActors)) {
log.error('invalid user actor', userActors);
return false;
lodash_1.default.forEach(sessions, (sess, name) => {
if (!sess._updateFlag) return;
const s = this.buildClientSession(name, sess);
res.header(
`${exports.SESS_START}${lodash_1.default.kebabCase(name)}`,
s,
);
});
res.json(result);
} catch (e) {
if (typeof e === 'object') {
res.status(e.status ? e.status : 500).end(e.message);
} else if (typeof e === 'string') {
res.status(500).end(e);
} else {
res.status(500).end('unknown error');
}
if (lodash_1.default.intersection(needActors, userActors).length !== needActors.length) {
log.error('invalid user actors,need:', needActors, ',user:', userActors);
return false;
}
log.debug('check user premissions successed. ', userActors);
return true;
}
_createApiHandle(preDefinedApi, createApiFunc) {
return async (req, res) => {
log.debug('api handle:', req.originalUrl);
try {
const sessions = this._parseSession(req);
for (const name of preDefinedApi._sessions) {
(0, assert_1.default)(this._options.api.session[name], `session define of ${name} not existed`);
if (!lodash_1.default.isPlainObject(sessions[name])) {
log.debug('set default sessions:', name);
sessions[name] = lodash_1.default.cloneDeep(this._options.api.session[name]);
}
Object.defineProperty(sessions[name], 'update', {
value: function (expireSeconds) {
this._updateFlag = true;
this._expire = expireSeconds;
},
enumerable: false,
});
Object.defineProperty(sessions[name], '_updateFlag', {
value: false,
enumerable: false,
writable: true,
});
Object.defineProperty(sessions[name], '_expire', {
value: -1,
enumerable: false,
writable: true,
});
}
if (!this._checkApiPremissions(preDefinedApi, sessions)) {
res.status(401).end('invalid user actor for ' + req.originalUrl);
return;
}
let result = {};
const apiFunc = createApiFunc();
if (typeof apiFunc === 'function') {
result = await apiFunc(req.body, sessions, req, res);
}
else {
throw new Error('service api invalid');
}
if (result['_res_end']) {
res.end();
return;
}
lodash_1.default.forEach(sessions, (sess, name) => {
if (!sess._updateFlag)
return;
const s = this.buildClientSession(name, sess);
res.header(`${exports.SESS_START}${lodash_1.default.kebabCase(name)}`, s);
});
res.json(result);
}
catch (e) {
if (typeof e === 'object') {
res.status(e.status ? e.status : 500).end(e.message);
}
else if (typeof e === 'string') {
res.status(500).end(e);
}
else {
res.status(500).end('unknown error');
}
log.error('api exec fail:', e);
}
};
}
async listen() {
this.httpServer.listen(this._options.port, '0.0.0.0', 20, () => {
log.info('service listen in port successed:', this._options.port);
});
}
log.error('api exec fail:', e);
}
};
}
async listen() {
this.httpServer.listen(this._options.port, '0.0.0.0', 20, () => {
log.info('service listen in port successed:', this._options.port);
});
}
}
exports.Service = Service;
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