import { Request, Response, Router } from 'express'; import { DyFM_AnyError, DyFM_Error, DyFM_ErrorLevel, DyFM_Error_Settings, DyFM_HttpCallType, DyFM_Log, DyFM_getConstructionStackLocation, DyFM_getLocalStackLocation } from '@futdevpro/fsm-dynamo'; import { DyNTS_global_settings } from '../../_collections/global-settings.const'; import { DyNTS_RouteSecurity } from '../../_enums/route-security.enum'; import { DyNTS_Endpoint_Params } from '../../_models/control-models/endpoint-params.control-model'; import { DyNTS_RoutingModule_Settings } from '../../_models/interfaces/routing-module-settings.interface'; import { DyNTS_Controller } from './controller.service'; /** * Routing Module * Handling route setup and control * * (You'll need to add them to a {@link DyNTS_App} through the {@link DyNTS_App.getRoutingModules} method) * * You can add routes and controllers for them * @example * // Setting up Routes * setupRoutingModules(): void { * this.httpPort = env.port; * this.routingModules = [ * new DyNTS_RoutingModule({ * route: '/user', * controllers: [ * UserController.getInstance(), * UserDataController.getInstance(), * UserOptionsController.getInstance(), * UserStatisticsController.getInstance(), * UserAchievementsController.getInstance(), * UserNotificationsController.getInstance() * ] * }), * new DyNTS_RoutingModule({ * route: '/match', * controllers: [ * MatchController.getInstance(), * MatchDistributionController.getInstance(), * MatchStatisticsController.getInstance(), * ] * }), * new DyNTS_RoutingModule({ * route: '/server', * controllers: [ * ServerController.getInstance(), * ] * }), * getTestRoutingModule(), * getUsageRoutingModule() * ]; * } */ export class DyNTS_RoutingModule { /* serviceName: string; */ security: DyNTS_RouteSecurity; readonly route: string; private readonly controllers: DyNTS_Controller[]; endpoints: DyNTS_Endpoint_Params[] = []; readonly openRouter: Router = Router(); readonly secureRouter: Router = Router(); readonly stackLocation: string; get logSetup(): boolean { return DyNTS_global_settings.log_settings.setup; } defaultErrorUserMsg = `We encountered an uncaught BackEnd Build Error, ` + `\nplease contact the responsible development team.`; constructor( set: DyNTS_RoutingModule_Settings ){ try { /* this.serviceName = set.route.replace('/', '') + 'RoutingModule'; */ this.route = DyNTS_global_settings.baseUrl + set.route; this.controllers = set.controllers ?? []; this.stackLocation = DyFM_getConstructionStackLocation(); this.setupRoutes(); this.mountRoutes(set.securityOverride); if (this.logSetup) console.log( `routing module setup done: ${this.route} security: ${this.security}\n` ); } catch (error) { /* if ( DyNTS_global_settings.log_settings.highDetailedLogs || !(error instanceof DyFM_Error) ) { DyFM_Log.H_error( `Routing module setup failed (${this.route}).`, `\n ERROR:`, error ); } else { error.logSimple( `Routing module setup failed (${this.route}).` + '\nmessages:\n' + error._messages.join(' \n') ); } */ /* DyFM_Log.error(`\nRouting module setup failed (${this.route})`, error); */ throw new DyFM_Error({ ...this.getDefaultErrorSettings( 'DyNTS_RoutingModule.constructor', error ), message: `Routing module setup failed (${this.route}).`, errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-RMS-000`, __localStack: this.stackLocation, additionalContent: set, }); } } /** * You must setup endpoints and required services for this function */ private setupRoutes(): void { this.controllers.forEach((controller: DyNTS_Controller): void => { try { controller.setupEndpoints(); } catch (error) { throw new DyFM_Error({ ...this.getDefaultErrorSettings( 'setupRoutes', error ), message: `DyNTS_Controller setup failed. (${controller?.constructor?.name}) ` + `Please check the setupEndpoints() method.`, errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-RMS-SR1`, level: DyFM_ErrorLevel.critical, }); } if (controller?.endpoints?.length) { this.endpoints.push(...controller.endpoints); } }); } /** * * @param securityOverride */ private mountRoutes(securityOverride?: DyNTS_RouteSecurity): void { if (securityOverride) { this.security = securityOverride; } this.endpoints.forEach((endpoint: DyNTS_Endpoint_Params): void => { if (securityOverride) { endpoint.security = securityOverride; } else { if (!endpoint.security) { DyFM_Log.error(`Endpoint security not set: ${endpoint.endpoint}\n`); } else if (!this.security) { this.security = endpoint.security; } else if (this.security !== endpoint.security) { this.security = DyNTS_RouteSecurity.both; } } if (this.logSetup) console.log( `endpoint mount (${endpoint.security}): ${this.route}${endpoint.endpoint}` ); const existingEndPoints: DyNTS_Endpoint_Params[] = this.endpoints.filter( (e_params: DyNTS_Endpoint_Params): boolean => e_params.endpoint === endpoint.endpoint && e_params.type === endpoint.type ); if (1 < existingEndPoints.length) { const error = new Error(`ENDPOINT DUPLICATION: ${endpoint.endpoint}`); const errorStack: string[] = error.stack.split('\n'); errorStack.splice(1, 4); error.stack = errorStack.join('\n'); throw error; } try { if (endpoint.security !== DyNTS_RouteSecurity.secure) { this.mountOpenRoute(endpoint); } if (endpoint.security !== DyNTS_RouteSecurity.open) { this.mountSecureRoute(endpoint); } } catch (error) { DyFM_Log.error(`\nRouting module setup failed (${this.route})`, error); throw new DyFM_Error({ ...this.getDefaultErrorSettings( 'mountRoutes', new Error(`DYNAMO-NTS ERROR: Failed to mount routes.`) ), errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-RMS-100`, }); } }); } /** * * @param endpointParams */ private mountOpenRoute(endpointParams: DyNTS_Endpoint_Params): void { switch (endpointParams.type) { case DyFM_HttpCallType.get: this.openRouter.get(endpointParams.endpoint, endpointParams.getFullExecution()); break; case DyFM_HttpCallType.post: this.openRouter.post(endpointParams.endpoint, endpointParams.getFullExecution()); break; case DyFM_HttpCallType.put: this.openRouter.put(endpointParams.endpoint, endpointParams.getFullExecution()); break; case DyFM_HttpCallType.patch: this.openRouter.patch(endpointParams.endpoint, endpointParams.getFullExecution()); break; case DyFM_HttpCallType.delete: this.openRouter.delete(endpointParams.endpoint, endpointParams.getFullExecution()); break; default: DyFM_Log.error(`INVALID route type: ${endpointParams.type} - ${endpointParams.name}`); throw new DyFM_Error({ ...this.getDefaultErrorSettings( 'mountOpenRoute', new Error( `DYNAMO-NTS ERROR: INVALID route type: ` + `${endpointParams.type} - ${endpointParams.name}` ), ), errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-RMS-200`, }); } } /** * * @param endpointParams */ private mountSecureRoute(endpointParams: DyNTS_Endpoint_Params): void { switch (endpointParams.type) { case DyFM_HttpCallType.get: this.secureRouter.get(endpointParams.endpoint, endpointParams.getFullExecution()); break; case DyFM_HttpCallType.post: this.secureRouter.post(endpointParams.endpoint, endpointParams.getFullExecution()); break; case DyFM_HttpCallType.put: this.secureRouter.put(endpointParams.endpoint, endpointParams.getFullExecution()); break; case DyFM_HttpCallType.patch: this.secureRouter.patch(endpointParams.endpoint, endpointParams.getFullExecution()); break; case DyFM_HttpCallType.delete: this.secureRouter.delete(endpointParams.endpoint, endpointParams.getFullExecution()); break; default: DyFM_Log.error( `DYNAMO-NTS ERROR: INVALID route type: ${endpointParams.type} - ${endpointParams.name}` ); throw new DyFM_Error({ ...this.getDefaultErrorSettings( 'mountSecureRoute', new Error( `DYNAMO-NTS ERROR: INVALID route type: ` + `${endpointParams.type} - ${endpointParams.name}` ), ), errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-RMS-300`, }); } } private getDefaultErrorSettings( fnName: string, error: DyFM_AnyError ): DyFM_Error_Settings { return { status: 500, message: (error as Error)?.message ?? (error as DyFM_Error)?._message ?? `${fnName} was UNSUCCESSFUL (NTS)`, userMessage: (error as DyFM_Error)?.__userMessage ?? this.defaultErrorUserMsg, addECToUserMsg: true, error: error, issuerService: this.constructor?.name, systemVersion: DyNTS_global_settings.systemVersion, }; } }