UNPKG

flexbiz-server

Version:

Flexible Server

27 lines (26 loc) 13.6 kB
const express=require("express"),crypto=require("crypto"); class WebhookManager{constructor($flowEngine$$){if(!$flowEngine$$)throw Error("WebhookManager requires a FlowEngine instance.");this.flowEngine=$flowEngine$$;this.registeredWebhooks=new Map;this.router=express.Router()}attach($app$$,$basePath$$="/webhooks"){this.router.use(express.json({limit:"10mb",verify:($req$$,$res$$,$buf$$)=>{$buf$$&&$buf$$.length&&$req$$.headers["content-type"]?.includes("application/json")&&($req$$.rawBodyBuffer=$buf$$)}}));this.router.use(express.urlencoded({extended:!0,limit:"10mb"})); this.router.use(($req$$,$res$$,$next$$)=>{var $key$$=`${$req$$.method.toLowerCase()}:${$req$$.path}`;$key$$=this.registeredWebhooks.get($key$$);if(!$key$$||$key$$.activeInstances.size===0)return Logger.warn(`[WebhookManager] Request received for inactive or unregistered webhook: ${$req$$.method} ${$req$$.path}`),$res$$.status(404).send({error:"Webhook endpoint is inactive or not found."});$req$$.webhookRegistration=$key$$;$next$$()});$app$$.use($basePath$$,this.router);Logger.info(`[WebhookManager] Attached webhook router to base path: ${$basePath$$}`)}_hasRouteHandler($path$$, $httpMethod$$){const $fullPath$$=$path$$.startsWith("/")?$path$$:"/"+$path$$,$method$$=$httpMethod$$.toLowerCase();return this.router.stack.some($layer$$=>$layer$$.route&&$layer$$.route.path===$fullPath$$&&$layer$$.route.methods[$method$$])}async registerWebhook($fullPath$jscomp$1_path$$,$dataMethod_httpMethod$$,$flowId$$,$nodeId$$,$flowInstanceId$$,$verificationConfig$$=null,$appSecret_verificationMethod$$=null){$fullPath$jscomp$1_path$$=`/${$flowInstanceId$$}${$fullPath$jscomp$1_path$$.startsWith("/")? $fullPath$jscomp$1_path$$:"/"+$fullPath$jscomp$1_path$$}`;$dataMethod_httpMethod$$=$dataMethod_httpMethod$$.toLowerCase();var $dataKey_verificationRegistration$$=`${$dataMethod_httpMethod$$}:${$fullPath$jscomp$1_path$$}`,$dataRegistration_verificationHandler$$=this.registeredWebhooks.get($dataKey_verificationRegistration$$);if(!$dataRegistration_verificationHandler$$&&(Logger.info(`[WebhookManager] First registration for ${$dataMethod_httpMethod$$.toUpperCase()} ${$fullPath$jscomp$1_path$$}. Creating entry.`), $dataRegistration_verificationHandler$$={info:{flowId:$flowId$$,nodeId:$nodeId$$,method:$dataMethod_httpMethod$$,path:$fullPath$jscomp$1_path$$,verificationConfig:null,appSecret:$appSecret_verificationMethod$$},activeInstances:new Set},this.registeredWebhooks.set($dataKey_verificationRegistration$$,$dataRegistration_verificationHandler$$),!this._hasRouteHandler($fullPath$jscomp$1_path$$,$dataMethod_httpMethod$$))){const $dataHandler$$=this.createWebhookHandler($dataKey_verificationRegistration$$, !1,null);this.router[$dataMethod_httpMethod$$]($fullPath$jscomp$1_path$$,$dataHandler$$);Logger.info(`[WebhookManager] Express route handler added for ${$dataMethod_httpMethod$$.toUpperCase()} ${$fullPath$jscomp$1_path$$}`)}$appSecret_verificationMethod$$&&($dataRegistration_verificationHandler$$.info.appSecret=$appSecret_verificationMethod$$,$dataRegistration_verificationHandler$$.info.flowId=$flowId$$,$dataRegistration_verificationHandler$$.info.nodeId=$nodeId$$);$dataRegistration_verificationHandler$$.activeInstances.add($flowInstanceId$$); Logger.info(`[WebhookManager] Instance ${$flowInstanceId$$} added to webhook ${$dataKey_verificationRegistration$$}. Total active: ${$dataRegistration_verificationHandler$$.activeInstances.size}`);$verificationConfig$$&&$verificationConfig$$.challengeQueryParam&&($appSecret_verificationMethod$$=($verificationConfig$$.method||"GET").toLowerCase(),$dataMethod_httpMethod$$=`${$appSecret_verificationMethod$$}:${$fullPath$jscomp$1_path$$}`,$dataKey_verificationRegistration$$=this.registeredWebhooks.get($dataMethod_httpMethod$$), $dataKey_verificationRegistration$$||(Logger.info(`[WebhookManager] First registration for ${$appSecret_verificationMethod$$.toUpperCase()} ${$fullPath$jscomp$1_path$$}. Creating entry.`),$dataKey_verificationRegistration$$={info:{flowId:$flowId$$,nodeId:$nodeId$$,method:$appSecret_verificationMethod$$,path:$fullPath$jscomp$1_path$$,verificationConfig:$verificationConfig$$,appSecret:null},activeInstances:new Set},this.registeredWebhooks.set($dataMethod_httpMethod$$,$dataKey_verificationRegistration$$), this._hasRouteHandler($fullPath$jscomp$1_path$$,$appSecret_verificationMethod$$)||($dataRegistration_verificationHandler$$=this.createWebhookHandler($dataMethod_httpMethod$$,!0,$verificationConfig$$),this.router[$appSecret_verificationMethod$$]($fullPath$jscomp$1_path$$,$dataRegistration_verificationHandler$$),Logger.info(`[WebhookManager] Express route handler added for ${$appSecret_verificationMethod$$.toUpperCase()} ${$fullPath$jscomp$1_path$$}`))),$dataKey_verificationRegistration$$.info.verificationConfig= $verificationConfig$$,$dataKey_verificationRegistration$$.info.flowId=$flowId$$,$dataKey_verificationRegistration$$.info.nodeId=$nodeId$$,$dataKey_verificationRegistration$$.activeInstances.add($flowInstanceId$$),Logger.info(`[WebhookManager] Instance ${$flowInstanceId$$} added to webhook ${$dataMethod_httpMethod$$}. Total active: ${$dataKey_verificationRegistration$$.activeInstances.size}`))}unregisterWebhook($fullPath$jscomp$2_path$$,$dataMethod$jscomp$1_dataRegistration$jscomp$1_httpMethod$$,$flowInstanceId$$, $dataKey$jscomp$1_verificationHttpMethod_verificationRegistration$$="GET"){$fullPath$jscomp$2_path$$=`/${$flowInstanceId$$}${$fullPath$jscomp$2_path$$.startsWith("/")?$fullPath$jscomp$2_path$$:"/"+$fullPath$jscomp$2_path$$}`;$dataMethod$jscomp$1_dataRegistration$jscomp$1_httpMethod$$=$dataMethod$jscomp$1_dataRegistration$jscomp$1_httpMethod$$.toLowerCase();const $verifyMethod$$=$dataKey$jscomp$1_verificationHttpMethod_verificationRegistration$$.toLowerCase();$dataKey$jscomp$1_verificationHttpMethod_verificationRegistration$$= `${$dataMethod$jscomp$1_dataRegistration$jscomp$1_httpMethod$$}:${$fullPath$jscomp$2_path$$}`;$fullPath$jscomp$2_path$$=`${$verifyMethod$$}:${$fullPath$jscomp$2_path$$}`;($dataMethod$jscomp$1_dataRegistration$jscomp$1_httpMethod$$=this.registeredWebhooks.get($dataKey$jscomp$1_verificationHttpMethod_verificationRegistration$$))?$dataMethod$jscomp$1_dataRegistration$jscomp$1_httpMethod$$.activeInstances.delete($flowInstanceId$$)?(Logger.info(`[WebhookManager] Instance ${$flowInstanceId$$} removed from webhook ${$dataKey$jscomp$1_verificationHttpMethod_verificationRegistration$$}. Remaining active: ${$dataMethod$jscomp$1_dataRegistration$jscomp$1_httpMethod$$.activeInstances.size}`), $dataMethod$jscomp$1_dataRegistration$jscomp$1_httpMethod$$.activeInstances.size===0&&(Logger.info(`[WebhookManager] No active instances remaining for ${$dataKey$jscomp$1_verificationHttpMethod_verificationRegistration$$}. Marking as inactive.`),this.registeredWebhooks.delete($dataKey$jscomp$1_verificationHttpMethod_verificationRegistration$$))):Logger.warn(`[WebhookManager] Instance ${$flowInstanceId$$} not found in active list for ${$dataKey$jscomp$1_verificationHttpMethod_verificationRegistration$$}.`): Logger.warn(`[WebhookManager] Cannot unregister instance ${$flowInstanceId$$} from ${$dataKey$jscomp$1_verificationHttpMethod_verificationRegistration$$}: Webhook key not found.`);($dataKey$jscomp$1_verificationHttpMethod_verificationRegistration$$=this.registeredWebhooks.get($fullPath$jscomp$2_path$$))&&$dataKey$jscomp$1_verificationHttpMethod_verificationRegistration$$.activeInstances.delete($flowInstanceId$$)&&(Logger.info(`[WebhookManager] Instance ${$flowInstanceId$$} removed from webhook ${$fullPath$jscomp$2_path$$}. Remaining active: ${$dataKey$jscomp$1_verificationHttpMethod_verificationRegistration$$.activeInstances.size}`), $dataKey$jscomp$1_verificationHttpMethod_verificationRegistration$$.activeInstances.size===0&&(Logger.info(`[WebhookManager] No active instances remaining for ${$fullPath$jscomp$2_path$$}. Marking as inactive.`),this.registeredWebhooks.delete($fullPath$jscomp$2_path$$)))}createWebhookHandler($registrationKey$$,$isVerificationHandler$$,$fixedVerificationConfig$$=null){return async($activeInstanceIds_req$$,$res$$)=>{var $registration$$=$activeInstanceIds_req$$.webhookRegistration||this.registeredWebhooks.get($registrationKey$$); if(!$registration$$||$registration$$.activeInstances.size===0)return Logger.warn(`[WebhookManager] Handler invoked for inactive webhook: ${$activeInstanceIds_req$$.method} ${$activeInstanceIds_req$$.path} (Key: ${$registrationKey$$})`),$res$$.status(404).send({error:"Webhook endpoint is inactive or not found."});const {nodeId:$nodeId$$,verificationConfig:$verificationConfig$$,appSecret:$appSecret$$}=$registration$$.info;var $challengeValue_currentVerificationConfig_signatureHeader$$=$fixedVerificationConfig$$|| $verificationConfig$$;if($isVerificationHandler$$&&$challengeValue_currentVerificationConfig_signatureHeader$$){$registration$$=$challengeValue_currentVerificationConfig_signatureHeader$$.tokenParam;var $expectedToken_signatureHash$$=$challengeValue_currentVerificationConfig_signatureHeader$$.tokenValue;$challengeValue_currentVerificationConfig_signatureHeader$$=$activeInstanceIds_req$$.query[$challengeValue_currentVerificationConfig_signatureHeader$$.challengeQueryParam];var $expectedHash_mode$$= $activeInstanceIds_req$$.query["hub.mode"];if($challengeValue_currentVerificationConfig_signatureHeader$$===void 0||$registration$$&&$expectedHash_mode$$!=="subscribe")return Logger.warn(`[WebhookManager] Invalid verification request for ${$activeInstanceIds_req$$.path}. Missing params or invalid mode.`),$res$$.status(400).send("Bad Request: Invalid verification parameters.");Logger.info(`[WebhookManager] Handling verification for ${$activeInstanceIds_req$$.path}. Challenge=${$challengeValue_currentVerificationConfig_signatureHeader$$}, Mode=${$expectedHash_mode$$}`); if($registration$$&&$expectedToken_signatureHash$$&&$activeInstanceIds_req$$.query[$registration$$]!==$expectedToken_signatureHash$$)return Logger.warn(`[WebhookManager] Verification FAILED for ${$activeInstanceIds_req$$.path}: Invalid verify token.`),$res$$.status(403).send("Forbidden: Invalid verification token");Logger.info(`[WebhookManager] Verification OK for ${$activeInstanceIds_req$$.path}. Responding with challenge.`);return $res$$.status(200).type("text/plain").send(String($challengeValue_currentVerificationConfig_signatureHeader$$))}if($isVerificationHandler$$)return Logger.error(`[WebhookManager] Handler config error for ${$activeInstanceIds_req$$.method} ${$activeInstanceIds_req$$.path}. Key: ${$registrationKey$$}`), $res$$.status(500).send("Internal Server Configuration Error.");if($appSecret$$){$challengeValue_currentVerificationConfig_signatureHeader$$=$activeInstanceIds_req$$.headers["x-hub-signature-256"];const $rawBodyBuffer$$=$activeInstanceIds_req$$.rawBodyBuffer;if(!$challengeValue_currentVerificationConfig_signatureHeader$$)return $res$$.status(403).send("Forbidden: Missing request signature.");if(!$rawBodyBuffer$$)return $res$$.status(400).send("Bad Request: Could not verify signature (missing raw body)."); try{$expectedToken_signatureHash$$=$challengeValue_currentVerificationConfig_signatureHeader$$.split("=")[1];if(!$expectedToken_signatureHash$$)throw Error("Invalid signature format.");$expectedHash_mode$$=crypto.createHmac("sha256",$appSecret$$).update($rawBodyBuffer$$).digest("hex");if(!crypto.timingSafeEqual(Buffer.from($expectedToken_signatureHash$$,"hex"),Buffer.from($expectedHash_mode$$,"hex")))return Logger.warn(`[WebhookManager] Denying request for ${$activeInstanceIds_req$$.path}: Invalid signature.`), $res$$.status(403).send("Forbidden: Invalid request signature.");Logger.info(`[WebhookManager] Signature VERIFIED for ${$activeInstanceIds_req$$.method} ${$activeInstanceIds_req$$.path}.`)}catch($sigError$$){return Logger.error(`[WebhookManager] Error verifying signature for ${$activeInstanceIds_req$$.path}:`,$sigError$$),$res$$.status(400).send("Bad Request: Could not verify signature.")}}else Logger.warn(`[WebhookManager] Proceeding without signature verification for ${$activeInstanceIds_req$$.path} (App Secret not configured).`); const $webhookData$$={body:$activeInstanceIds_req$$.body,query:$activeInstanceIds_req$$.query,headers:$activeInstanceIds_req$$.headers,method:$activeInstanceIds_req$$.method,path:$activeInstanceIds_req$$.path,ip:$activeInstanceIds_req$$.ip};$activeInstanceIds_req$$=Array.from($registration$$.activeInstances);if($activeInstanceIds_req$$.length>0)return Logger.info(`[WebhookManager] Triggering node ${$nodeId$$} in ${$activeInstanceIds_req$$.length} active instance(s) for ${$registrationKey$$}...`), $activeInstanceIds_req$$.map($instanceId$$=>this.flowEngine.triggerNodeInInstance($instanceId$$,$nodeId$$,$webhookData$$).catch($triggerError$$=>{Logger.error(`[WebhookManager] Error triggering node ${$nodeId$$} in instance ${$instanceId$$}:`,$triggerError$$);return{status:"rejected",instanceId:$instanceId$$,error:$triggerError$$}})),$res$$.status(200).send({status:"success",triggeredInstances:$activeInstanceIds_req$$.length});Logger.warn(`[WebhookManager] Received data for ${$registrationKey$$}, but no active instances found. Ignoring.`); return $res$$.status(200).send({status:"ignored",message:"No active flow instances found for this trigger."})}}getActiveWebhooks(){const $activeWebhooks$$=[];for(const [$key$$,$registration$$]of this.registeredWebhooks.entries())$registration$$.activeInstances.size>0&&$activeWebhooks$$.push({key:$key$$,...$registration$$.info,activeInstanceCount:$registration$$.activeInstances.size});return $activeWebhooks$$}}module.exports=WebhookManager;