UNPKG

@5minds/processcube_engine

Version:

The ProcessCube Engine. Stores and executes BPMNs.

265 lines • 12.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.launch = launch; const api_1 = __importDefault(require("win-ca/api")); const processcube_engine_sdk_1 = require("@5minds/processcube_engine_sdk"); const Contracts_1 = require("../../Contracts"); const IocRegistrations_1 = require("../../Contracts/IocRegistrations"); const BpmnSeeder_1 = __importDefault(require("../../Tools/BpmnSeeder")); const Configurator_1 = __importDefault(require("../../Tools/Configurator")); const DatabaseIntegrityChecker_1 = __importDefault(require("../../Tools/DatabaseAdaptersSequelize/Lib/DatabaseIntegrityChecker")); const Migrator_1 = __importDefault(require("../../Tools/DatabaseAdaptersSequelize/Migrator")); const environment = __importStar(require("../../Tools/Environment")); const ExtensionLoader_1 = __importDefault(require("../../Tools/ExtensionLoader")); const logger = new processcube_engine_sdk_1.Logger('runtime'); process.title = 'Engine Runtime'; function prepareErrorForExtensionErrors(error) { const extensionDir = environment.getExtensionDir(); const stack = error.stack || ''; const regex = new RegExp(` \\(${extensionDir}.[^\\/]+`, 'g'); const maybeExtensionErrorMatch = stack.match(regex); if (maybeExtensionErrorMatch != null) { const dirOfExtension = maybeExtensionErrorMatch[0].replace(' (', ''); error.stack = error.stack.replace(/.*\n/u, error.message + '\n'); error.category = 'extension'; error.message = `Failure in extension (${dirOfExtension}): ${error.message}`; } } function getLogMessageForError(error) { if (error instanceof Error || error instanceof processcube_engine_sdk_1.BaseError) { return '-- An unhandled error was caught! --'; } if (typeof error == 'string') { return ('An error was caught that was not an instance of Error.\n' + 'Please wrap thrown errors in the Error class to include a stack trace for later analysis.' + `\n\nInstead of\n\n throw "${error}";\n\ndo:\n\n throw new Error("${error}");\n\n` + 'If this error originates in a library out of your control, try catching the error and re-throwing it as an Error object to support debugging.'); } return ('An error was caught that was not an instance of Error.\n' + 'Please wrap thrown errors in the Error class to include a stack trace for later analysis.' + `\n\nInstead of\n\n throw "My Error Message";\n\ndo:\n\n throw new Error("My Error Message");\n\n` + 'If this error originates in a library out of your control, try catching the error and re-throwing it as an Error object to support debugging.'); } process.on('exit', (code) => { console.log(`Runtime Thread is exiting with code: ${code}`); console.trace(); }); process.on('uncaughtException', (error) => { prepareErrorForExtensionErrors(error); if (error.name === 'SequelizeConnectionRefusedError' || error.fatal === true) { logger.error(getLogMessageForError(error), { err: error, }); setTimeout(() => process.exit(1), 2000); } else { logger.warn(getLogMessageForError(error), { err: error, }); } }); process.on('unhandledRejection', (error) => { prepareErrorForExtensionErrors(error); if (error.name === 'SequelizeConnectionRefusedError' || error.fatal === true) { logger.error(getLogMessageForError(error), { err: error, }); setTimeout(() => process.exit(1), 2000); } else { logger.warn(getLogMessageForError(error), { err: error, }); } }); process.on('SIGINT', shutdown); process.on('SIGTERM', shutdown); let startupArgs; let runtimeRouter; async function launch(args) { try { await initializeEngine(args); } catch (error) { const errorMessagePrefix = error?.category ? `Error in ${error?.category}: ` : ''; logger.error(`${errorMessagePrefix} Runtime Worker error! exiting process...`, { err: error }); setTimeout(() => process.exit(1), 2000); } } async function initializeEngine(args) { startupArgs = args; await environment.loadConfig(environment.workerThreadIds.runtime); const tlsConfig = Configurator_1.default.tls(); const isWin = process.platform === 'win32'; if ((!tlsConfig || tlsConfig.useWindowsTrustStore !== false) && isWin) { useWindowsTrustStore(); } const packageJson = environment.readPackageJson(); logger.info(`Starting up server application, version ${packageJson.version}...`); if (process.env.enableHttp == 'true') { printHttpInfo(); } await (0, ExtensionLoader_1.default)(startupArgs.container, environment.getExtensionDir()); await setupDatabase(); const serviceFacade = startupArgs.container.get(IocRegistrations_1.IocRegistrationKeys.internal.ServiceFacade); await serviceFacade.init(); await startInternalServices(); await seedBpmns(startupArgs.container); if (environment.isWindowsAndSQLite()) { // We do not start the fetchAndLock-Worker on Windows with SQLite, therefore the Runtime must handle ExternalTaskTimers const externalTaskFetchAndLockService = startupArgs.container.get(IocRegistrations_1.IocRegistrationKeys.api.services.ExternalTaskFetchAndLockService); await externalTaskFetchAndLockService.startExternalTaskExpirationTimers(); } for (const onBeforeResumingCallback of environment.getOnBeforeResumingCallbacks()) { await onBeforeResumingCallback(); } if (!startupArgs.resumeProcessInstancesManually) { await resumeProcessInstances(); } else { serviceFacade.registerServiceFunction('resumeProcessInstances', resumeProcessInstances); } serviceFacade.registerServiceFunction('getExtensionRoutes', getExtensionRoutes); // allows the studio to wait for the engine startup if (typeof process.send === 'function') { process.send('started'); } runtimeRouter = startupArgs.container.get(IocRegistrations_1.IocRegistrationKeys.internal.RuntimeRouter); await runtimeRouter.init(); for (const onReadyCallback of environment.getOnReadyCallbacks()) { await onReadyCallback(); } serviceFacade.emitStartupFinishedMessage(); } async function setupDatabase() { // During integrationtests, these are run by the Main Thread, because the database adapters are also initialized there. if (!process.env.IS_INTEGRATION_TEST_RUN) { logger.info('Running migrations...'); await (0, Migrator_1.default)(); } if (process.env.skipDBIntegrityChecks === 'true') { logger.info('Skipping Database Integrity checks, because --skip-db-integrity-checks was set.'); return; } logger.info('Verifying database integrity'); const validationErrors = await (0, DatabaseIntegrityChecker_1.default)(); if (Object.keys(validationErrors).length > 0) { logger.error('Failure in setup: The database has an incompatible state!', { validationErrors: validationErrors }); // Killing the Process immediately would result in the error not being logged. await new Promise((resolve) => setTimeout(resolve, 10)); process.exit(1); } logger.info('Database has passed integrity checks.'); } async function seedBpmns(container) { if (!process.env.seedDir) { return; } try { const identityService = container.get(IocRegistrations_1.IocRegistrationKeys.internal.IdentityService); const internalIdentity = identityService.getInternalIdentity(); await (0, BpmnSeeder_1.default)(startupArgs.container, process.env.seedDir, internalIdentity); } catch (error) { const errorCategoryPrefix = error.category ? `Failure in ${error.category}: ` : ''; logger.error(`${errorCategoryPrefix}Seeding failed with error: ${error.message.replace(errorCategoryPrefix, '')}`, { seedDir: process.env.seedDir, err: error, }); } } async function startInternalServices() { try { const cronjobService = await startupArgs.container.getAsync(IocRegistrations_1.IocRegistrationKeys.core.services.CronjobService); await cronjobService.start(); logger.trace('CronjobService started.'); const messageEventService = startupArgs.container.get(IocRegistrations_1.IocRegistrationKeys.core.services.MessageEventService); await messageEventService.init(); logger.trace('MessageEventService started.'); const signalEventService = startupArgs.container.get(IocRegistrations_1.IocRegistrationKeys.core.services.SignalEventService); await signalEventService.init(); logger.trace('SignalService started.'); } catch (error) { logger.error('Failed to start the internal services.', { err: error, }); await new Promise((resolve) => setTimeout(resolve, 10)); process.exit(1); } } async function resumeProcessInstances() { const executeProcessService = await startupArgs.container.getAsync(IocRegistrations_1.IocRegistrationKeys.core.services.ExecuteProcessService); await executeProcessService.findAndResumeInterruptedProcessInstances(); } function getExtensionRoutes() { if (!startupArgs.container.isBound(Contracts_1.customHttpRouteTag)) { return []; } const registeredCustomRoutes = startupArgs.container.getAll(Contracts_1.customHttpRouteTag); const customRoutes = registeredCustomRoutes.map((route) => { return { httpRoute: route.httpRoute, method: route.method, options: route.options, }; }); return customRoutes; } function printHttpInfo() { // NOTE: // The configured port may not necessarily be the actual port used by the Http Server! // We do not yet make use of port discovery, but as soon as we do, these values may differ! // The long-term solution would be to get these informations from the Http Server directly, // but in order to do that, the ProcessEngine must have been started up first. const httpConfig = environment.getConfig().httpServer; const configuredAddress = httpConfig.host; const configuredPort = httpConfig.port; const addressHasHttpPrefix = configuredAddress.startsWith('http://') || configuredAddress.startsWith('https://'); const showcaseHttpAddress = addressHasHttpPrefix ? configuredAddress : `http://${configuredAddress}`; logger.info(`Using HTTP endpoint ${showcaseHttpAddress}:${configuredPort}`); logger.info(`Using websocket endpoint ${showcaseHttpAddress}:${configuredPort}`); } function shutdown() { process.exit(0); } function useWindowsTrustStore() { (0, api_1.default)({ inject: true }); } //# sourceMappingURL=RuntimeWorker.js.map