Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update/add shield #187

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
5 changes: 5 additions & 0 deletions servers/backend-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@
"express": "^4.17.1",
"graphql": "^14.7.0",
"graphql-bigint": "^1.0.0",
"graphql-middleware": "^4.0.2",
"graphql-nats-subscriptions": "^1.4.2",
"graphql-shield": "7.3.5",
"graphql-subscriptions": "^1.2.0",
"graphql-tools": "^4.0.8",
"graphql-type-json": "^0.3.1",
Expand All @@ -116,6 +118,9 @@
"pm2": "^4.2.1",
"rimraf": "^3.0.0"
},
"peerDependencies": {
"@cdm-logger/core": "*"
},
"typescript": {
"definition": "dist/main.d.ts"
}
Expand Down
60 changes: 60 additions & 0 deletions servers/backend-server/src/middleware/moleculer-inter-namespace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { ServiceBroker, Middleware } from 'moleculer';
import * as _ from 'lodash';
import { logger } from '@cdm-logger/server';

export const InterNamespaceMiddleware = function (options): Middleware {
if (!Array.isArray(options)) {
throw new Error('Must be an Array');
}

let thisBroker: ServiceBroker;
const brokers: { [key: string]: ServiceBroker } = {};

return {
created(broker: ServiceBroker) {
thisBroker = broker;
options.forEach((nsOpts) => {
if (_.isString(nsOpts)) {
nsOpts = {
namespace: nsOpts,
};
}
const ns = nsOpts.namespace;

const brokerOpts = _.defaultsDeep(
{},
nsOpts,
{ nodeID: null, middlewares: null, created: null, started: null },
broker.options,
);
brokers[ns] = new ServiceBroker(brokerOpts);
});
},

started() {
return Promise.all(Object.values(brokers).map((b) => b.start()));
},

stopped() {
return Promise.all(Object.values(brokers).map((b) => b.stop()));
},

call(next) {
return function (actionName, params, opts = {}) {
if (_.isString(actionName) && actionName.includes('@')) {
const [action, namespace] = actionName.split('@');

if (brokers[namespace]) {
return brokers[namespace].call(action, params, opts);
}
if (namespace === thisBroker.namespace) {
return next(action, params, opts);
}
throw new Error(`Unknown namespace: ${namespace}`);
}

return next(actionName, params, opts);
};
},
};
};
155 changes: 110 additions & 45 deletions servers/backend-server/src/stack-server.ts
Original file line number Diff line number Diff line change
@@ -1,112 +1,155 @@
import * as http from 'http';
import * as express from 'express';
import { logger, logger as serverLogger } from '@cdm-logger/server';
import { Feature } from '@common-stack/server-core';
import { Container, ContainerModule, interfaces } from 'inversify';
import { ServiceBroker } from 'moleculer';
import { CommonType } from '@common-stack/core';
import * as _ from 'lodash';
import { applyMiddleware } from 'graphql-middleware';
import { shield } from 'graphql-shield';
import { CdmLogger } from '@cdm-logger/core';
import { expressApp } from './express-app';
import { GraphqlServer } from './server-setup/graphql-server';
import { config } from './config';
import { logger as serverLogger } from '@cdm-logger/server';
import { ConnectionBroker } from './connectors/connection-broker';
import { Feature } from '@common-stack/server-core';
import { ContainerModule, interfaces, Container } from 'inversify';
import { ServiceBroker, ServiceSettingSchema } from 'moleculer';
import * as brokerConfig from './config/moleculer.config';
import modules, { settings } from './modules';
import { GatewaySchemaBuilder } from './api/schema-builder';
import { WebsocketMultiPathServer } from './server-setup/websocket-multipath-update';
import { IModuleService } from './interfaces';
import { CommonType } from '@common-stack/core';
import * as _ from 'lodash';
import {migrate} from './utils/migrations';
import { CdmLogger } from '@cdm-logger/core';
type ILogger = CdmLogger.ILogger;
import { migrate } from './utils/migrations';
import { InterNamespaceMiddleware } from './middleware/moleculer-inter-namespace';
// This is temp and will be replaced one we add support for rules in Feature

type ILogger = CdmLogger.ILogger;

function startListening(port) {
let server = this;
const server = this;
return new Promise((resolve) => {
server.listen(port, resolve);
});
}

const infraModule =
({ broker, pubsub, logger }) => new ContainerModule((bind: interfaces.Bind) => {
const infraModule = ({ broker, pubsub, mongoClient, logger }) =>
new ContainerModule((bind: interfaces.Bind) => {
bind('Logger').toConstantValue(logger);
bind(CommonType.LOGGER).toConstantValue(logger);
bind('Environment').toConstantValue(config.NODE_ENV || 'development');
bind(CommonType.ENVIRONMENT).toConstantValue(config.NODE_ENV || 'development');
bind('PubSub').toConstantValue(pubsub);
bind(CommonType.PUBSUB).toConstantValue(pubsub);
bind('MoleculerBroker').toConstantValue(broker);
bind(CommonType.MOLECULER_BROKER).toConstantValue(broker);
bind('MoleculerBroker').toConstantValue(broker);
bind('MongoDBConnection').toConstantValue(mongoClient);
});


/**
* Controls the lifecycle of the Application Server
*
* @export
* @class StackServer
*/
export class StackServer {
public httpServer: http.Server & { startListening?: (port) => void };

public httpServer: http.Server & { startListening?: (port) => void; };
private app: express.Express;

private logger: ILogger;

private connectionBroker: ConnectionBroker;

private mainserviceBroker: ServiceBroker;

private microserviceBroker: ServiceBroker;

private multiPathWebsocket: WebsocketMultiPathServer;

private serviceContainer: Container;

private microserviceContainer: Container;

constructor() {
this.logger = serverLogger.child({ className: 'StackServer' });
}

public async initialize() {
public async initialize() {
this.logger.info('StackServer initializing');

this.connectionBroker = new ConnectionBroker(brokerConfig.transporter, this.logger);
const redisClient = this.connectionBroker.redisDataloaderClient;

const mongoClient = await this.connectionBroker.mongoConnection;

// Moleculer Broker Setup
this.microserviceBroker = new ServiceBroker({
this.mainserviceBroker = new ServiceBroker({
...brokerConfig,
middlewares: [
InterNamespaceMiddleware([
{
namespace: 'api-admin',
transporter: brokerConfig.transporter,
},
]),
],
started: async () => {
// start DB migration
await migrate(mongoClient, this.serviceContainer);
await modules.preStart(this.serviceContainer);
if (config.NODE_ENV === 'development') {
await modules.microservicePreStart(this.microserviceContainer);
// await modules.microservicePreStart(this.micorserviceContainer);
}

await modules.postStart(this.serviceContainer);
await migrate(mongoClient, this.serviceContainer);
// start DB migration

if (config.NODE_ENV === 'development') {
await modules.microservicePostStart(this.microserviceContainer);
// await modules.microservicePostStart(this.micorserviceContainer);
}
},
// created,
created: async () => {

// created,
async created() {
return Promise.resolve();
},
});

if (config.NODE_ENV === 'development') {
this.microserviceBroker = new ServiceBroker({
...brokerConfig,
nodeID: 'node-broker-2',
started: async () => {
await modules.microservicePreStart(this.microserviceContainer);
await modules.microservicePostStart(this.microserviceContainer);
},
// created,
created: async () => Promise.resolve(),
});
}
const pubsub = await this.connectionBroker.graphqlPubsub;
const InfraStructureFeature = new Feature({
createContainerFunc: [
() => infraModule({
broker: this.microserviceBroker,
pubsub, logger: serverLogger,
})],
() =>
infraModule({
broker: this.mainserviceBroker,
pubsub,
mongoClient,
logger: serverLogger,
}),
],
createServiceFunc: (container) => ({ moleculerBroker: container.get(CommonType.MOLECULER_BROKER) }),
createHemeraContainerFunc: [
() => infraModule({
broker: this.microserviceBroker,
pubsub, logger: serverLogger,
})],
() =>
infraModule({
broker: this.mainserviceBroker,
pubsub,
mongoClient,
logger: serverLogger,
}),
],
});
const allModules = new Feature(InfraStructureFeature, modules);

const executableSchema = await (new GatewaySchemaBuilder({
const allModules = new Feature(InfraStructureFeature, modules as Feature);
let executableSchema = await new GatewaySchemaBuilder({
schema: allModules.schemas,
resolvers: allModules.createResolvers({
pubsub,
Expand All @@ -115,7 +158,16 @@ export class StackServer {
}),
directives: allModules.createDirectives({ logger: this.logger }),
logger: serverLogger,
})).build();
}).build();

executableSchema = applyMiddleware(
executableSchema,
// we can import rules from all modules and use lodash.merge to merge
// them all together before passing to graphQl shield
shield(modules.rules, {
allowExternalErrors: true,
}),
);

// set the service container
this.serviceContainer = await allModules.createContainers({ ...settings, mongoConnection: mongoClient });
Expand All @@ -125,30 +177,31 @@ export class StackServer {
serviceContext: createServiceContext,
dataSource: allModules.createDataSource(),
defaultPreferences: allModules.createDefaultPreferences(),
createContext: async (req, res) => await allModules.createContext(req, res),
createContext: async (req, res) => allModules.createContext(req, res),
logger: serverLogger,
schema: executableSchema,
};
allModules.loadMainMoleculerService({
broker: this.microserviceBroker,
broker: this.mainserviceBroker,
container: this.serviceContainer,
settings: settings,
settings,
});
if (config.NODE_ENV === 'development') {
this.microserviceContainer = await allModules.createHemeraContainers({ ...settings, mongoConnection: mongoClient });
this.microserviceContainer = await allModules.createHemeraContainers({
...settings,
mongoConnection: mongoClient,
});
allModules.loadClientMoleculerService({
broker: this.microserviceBroker,
container: this.microserviceContainer,
settings: settings,
settings,
});
}

// intialize Servers
// initialize Servers
this.httpServer = http.createServer();
this.app = await expressApp(serviceBroker, null, this.httpServer);



this.httpServer.startListening = startListening.bind(this.httpServer);
this.httpServer.on('request', this.app);
this.httpServer.on('close', () => {
Expand All @@ -162,14 +215,23 @@ export class StackServer {
this.multiPathWebsocket = new WebsocketMultiPathServer(serviceBroker, redisClient, customWebsocket);
this.httpServer = this.multiPathWebsocket.httpServerUpgrade(this.httpServer);
}
const graphqlServer = new GraphqlServer(this.app, this.httpServer, redisClient, serviceBroker, !customWebsocketEnable);

const graphqlServer = new GraphqlServer(
this.app,
this.httpServer,
redisClient,
serviceBroker,
!customWebsocketEnable,
);

await graphqlServer.initialize();
}

public async start() {
await this.microserviceBroker.start();
if (config.NODE_ENV === 'development') {
await Promise.all([this.mainserviceBroker.start(), this.microserviceBroker.start()]);
} else {
await this.mainserviceBroker.start();
}
}

public async cleanup() {
Expand All @@ -182,6 +244,9 @@ export class StackServer {
if (this.connectionBroker) {
await this.connectionBroker.stop();
}
if (this.mainserviceBroker) {
await this.mainserviceBroker.stop();
}
if (this.microserviceBroker) {
await this.microserviceBroker.stop();
}
Expand Down