import * as express from 'express' ; import { readFileSync } from 'fs' ; import { getConnectionManager } from 'typeorm' ; import * as bodyParser from 'body-parser' ; import * as _ from 'lodash' ; import * as compression from 'compression' ; import * as parseUrl from 'parseurl' ; import { ActiveExecutions , ActiveWorkflowRunner , Db , ExternalHooks , GenericHelpers , ICustomRequest , IExternalHooksClass , IPackageVersions , ResponseHelper , WaitingWebhooks , } from '.' ; import * as config from '../config' ; require ( 'body-parser-xml' ) ( bodyParser ) ; export function registerProductionWebhooks ( ) { this . app . head ( `/${ this . endpointWebhook }/*` , async ( req , res ) => { const requestUrl = ( req as ICustomRequest ) . parsedUrl ! . pathname ! . slice ( this . endpointWebhook . length + 2 ) ; let response ; try { response = await this . activeWorkflowRunner . executeWebhook ( 'HEAD' , requestUrl , req , res ) ; } catch ( error ) { ResponseHelper . sendErrorResponse ( res , error ) ; return ; } if ( response . noWebhookResponse === true ) { return ; } ResponseHelper . sendSuccessResponse ( res , response . data , true , response . responseCode , response . headers ) ; } ) ; this . app . options ( `/${ this . endpointWebhook }/*` , async ( req , res ) => { const requestUrl = ( req as ICustomRequest ) . parsedUrl ! . pathname ! . slice ( this . endpointWebhook . length + 2 ) ; let allowedMethods ; try { allowedMethods = await this . activeWorkflowRunner . getWebhookMethods ( requestUrl ) ; allowedMethods . push ( 'OPTIONS' ) ; res . append ( 'Allow' , allowedMethods ) ; } catch ( error ) { ResponseHelper . sendErrorResponse ( res , error ) ; return ; } ResponseHelper . sendSuccessResponse ( res , { } , true , 204 ) ; } ) ; this . app . get ( `/${ this . endpointWebhook }/*` , async ( req , res ) => { const requestUrl = ( req as ICustomRequest ) . parsedUrl ! . pathname ! . slice ( this . endpointWebhook . length + 2 ) ; let response ; try { response = await this . activeWorkflowRunner . executeWebhook ( 'GET' , requestUrl , req , res ) ; } catch ( error ) { ResponseHelper . sendErrorResponse ( res , error ) ; return ; } if ( response . noWebhookResponse === true ) { return ; } ResponseHelper . sendSuccessResponse ( res , response . data , true , response . responseCode , response . headers ) ; } ) ; this . app . post ( `/${ this . endpointWebhook }/*` , async ( req , res ) => { const requestUrl = ( req as ICustomRequest ) . parsedUrl ! . pathname ! . slice ( this . endpointWebhook . length + 2 ) ; let response ; try { response = await this . activeWorkflowRunner . executeWebhook ( 'POST' , requestUrl , req , res ) ; } catch ( error ) { ResponseHelper . sendErrorResponse ( res , error ) ; return ; } if ( response . noWebhookResponse === true ) { return ; } ResponseHelper . sendSuccessResponse ( res , response . data , true , response . responseCode , response . headers ) ; } ) ; const waitingWebhooks = new WaitingWebhooks ( ) ; this . app . head ( `/${ this . endpointWebhookWaiting }/*` , async ( req , res ) => { const requestUrl = ( req as ICustomRequest ) . parsedUrl ! . pathname ! . slice ( this . endpointWebhookWaiting . length + 2 ) ; let response ; try { response = await waitingWebhooks . executeWebhook ( 'HEAD' , requestUrl , req , res ) ; } catch ( error ) { ResponseHelper . sendErrorResponse ( res , error ) ; return ; } if ( response . noWebhookResponse === true ) { return ; } ResponseHelper . sendSuccessResponse ( res , response . data , true , response . responseCode , response . headers ) ; } ) ; this . app . get ( `/${ this . endpointWebhookWaiting }/*` , async ( req , res ) => { const requestUrl = ( req as ICustomRequest ) . parsedUrl ! . pathname ! . slice ( this . endpointWebhookWaiting . length + 2 ) ; let response ; try { response = await waitingWebhooks . executeWebhook ( 'GET' , requestUrl , req , res ) ; } catch ( error ) { ResponseHelper . sendErrorResponse ( res , error ) ; return ; } if ( response . noWebhookResponse === true ) { return ; } ResponseHelper . sendSuccessResponse ( res , response . data , true , response . responseCode , response . headers ) ; } ) ; this . app . post ( `/${ this . endpointWebhookWaiting }/*` , async ( req , res ) => { const requestUrl = ( req as ICustomRequest ) . parsedUrl ! . pathname ! . slice ( this . endpointWebhookWaiting . length + 2 ) ; let response ; try { response = await waitingWebhooks . executeWebhook ( 'POST' , requestUrl , req , res ) ; } catch ( error ) { ResponseHelper . sendErrorResponse ( res , error ) ; return ; } if ( response . noWebhookResponse === true ) { return ; } ResponseHelper . sendSuccessResponse ( res , response . data , true , response . responseCode , response . headers ) ; } ) ; } class App { app : express . Application ; activeWorkflowRunner : ActiveWorkflowRunner . ActiveWorkflowRunner ; endpointWebhook : string ; endpointWebhookWaiting : string ; endpointPresetCredentials : string ; externalHooks : IExternalHooksClass ; saveDataErrorExecution : string ; saveDataSuccessExecution : string ; saveManualExecutions : boolean ; executionTimeout : number ; maxExecutionTimeout : number ; timezone : string ; activeExecutionsInstance : ActiveExecutions . ActiveExecutions ; versions : IPackageVersions | undefined ; restEndpoint : string ; protocol : string ; sslKey : string ; sslCert : string ; presetCredentialsLoaded : boolean ; constructor ( ) { this . app = express ( ) ; this . endpointWebhook = config . get ( 'endpoints.webhook' ) as string ; this . endpointWebhookWaiting = config . get ( 'endpoints.webhookWaiting' ) as string ; this . saveDataErrorExecution = config . get ( 'executions.saveDataOnError' ) as string ; this . saveDataSuccessExecution = config . get ( 'executions.saveDataOnSuccess' ) as string ; this . saveManualExecutions = config . get ( 'executions.saveDataManualExecutions' ) as boolean ; this . executionTimeout = config . get ( 'executions.timeout' ) as number ; this . maxExecutionTimeout = config . get ( 'executions.maxTimeout' ) as number ; this . timezone = config . get ( 'generic.timezone' ) as string ; this . restEndpoint = config . get ( 'endpoints.rest' ) as string ; this . activeWorkflowRunner = ActiveWorkflowRunner . getInstance ( ) ; this . activeExecutionsInstance = ActiveExecutions . getInstance ( ) ; this . protocol = config . get ( 'protocol' ) ; this . sslKey = config . get ( 'ssl_key' ) ; this . sslCert = config . get ( 'ssl_cert' ) ; this . externalHooks = ExternalHooks ( ) ; this . presetCredentialsLoaded = false ; this . endpointPresetCredentials = config . get ( 'credentials.overwrite.endpoint' ) as string ; } returns { number } memberof getCurrentDate ( ) { return new Date ( ) ; } async config ( ) { this . versions = await GenericHelpers . getVersions ( ) ; this . app . use ( compression ( ) ) ; this . app . use ( ( req , res , next ) => { ( req as ICustomRequest ) . parsedUrl = parseUrl ( req ) ; req . rawBody = Buffer . from ( '' , 'base64' ) ; next ( ) ; } ) ; this . app . use ( bodyParser . json ( { limit : '16mb' , verify : ( req , res , buf ) => { req . rawBody = buf ; } , } ) ) ; this . app . use ( bodyParser . xml ( { limit : '16mb' , xmlParseOptions : { normalize : true , normalizeTags : true , explicitArray : false , } , } ) ) ; this . app . use ( bodyParser . text ( { limit : '16mb' , verify : ( req , res , buf ) => { req . rawBody = buf ; } , } ) ) ; this . app . use ( bodyParser . urlencoded ( { extended : false , verify : ( req , res , buf ) => { req . rawBody = buf ; } , } ) ) ; if ( process . env . NODE_ENV !== 'production' ) { this . app . use ( ( req , res , next ) => { res . header ( 'Access-Control-Allow-Origin' , 'http://localhost:8080' ) ; res . header ( 'Access-Control-Allow-Methods' , 'GET, POST, OPTIONS, PUT, PATCH, DELETE' ) ; res . header ( 'Access-Control-Allow-Headers' , 'Origin, X-Requested-With, Content-Type, Accept, sessionid' ) ; next ( ) ; } ) ; } this . app . use ( ( req , res , next ) => { if ( Db . collections . Workflow === null ) { const error = new ResponseHelper . ResponseError ( 'Database is not ready!' , undefined , 503 ) ; return ResponseHelper . sendErrorResponse ( res , error ) ; } next ( ) ; } ) ; this . app . get ( '/healthz' , async ( req , res ) => { const connection = getConnectionManager ( ) . get ( ) ; try { if ( ! connection . isConnected ) { throw new Error ( 'No active database connection!' ) ; } await connection . query ( 'SELECT 1' ) ; } catch ( err ) { const error = new ResponseHelper . ResponseError ( 'No Database connection!' , undefined , 503 ) ; return ResponseHelper . sendErrorResponse ( res , error ) ; } const responseData = { status : 'ok' , } ; ResponseHelper . sendSuccessResponse ( res , responseData , true , 200 ) ; } ) ; registerProductionWebhooks . apply ( this ) ; } } export async function start ( ) { const PORT = config . get ( 'port' ) ; const ADDRESS = config . get ( 'listen_address' ) ; const app = new App ( ) ; await app . config ( ) ; let server ; if ( app . protocol === 'https' && app . sslKey && app . sslCert ) { const https = require ( 'https' ) ; const privateKey = readFileSync ( app . sslKey , 'utf8' ) ; const cert = readFileSync ( app . sslCert , 'utf8' ) ; const credentials = { key : privateKey , cert } ; server = https . createServer ( credentials , app . app ) ; } else { const http = require ( 'http' ) ; server = http . createServer ( app . app ) ; } server . listen ( PORT , ADDRESS , async ( ) => { const versions = await GenericHelpers . getVersions ( ) ; console . log ( `n8n ready on ${ ADDRESS }, port ${ PORT }` ) ; console . log ( `Version: ${ versions . cli }` ) ; await app . externalHooks . run ( 'n8n.ready' , [ app ] ) ; } ) ; }