UNPKG

hook-engine

Version:

Production-grade webhook engine with comprehensive adapter support, security, reliability, structured logging, and CLI tools.

587 lines (585 loc) 19.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.multiTenantTemplates = exports.tenantTemplates = exports.pipelineTemplates = exports.destinationTemplates = exports.transformationTemplates = exports.filterTemplates = void 0; exports.createCustomPipeline = createCustomPipeline; exports.mergeFilters = mergeFilters; /** * Configuration templates for common webhook processing scenarios */ // ============================================================================ // FILTER TEMPLATES // ============================================================================ exports.filterTemplates = { /** * CI/CD Pipeline Events */ cicd: { types: [ 'push', 'pull_request.opened', 'pull_request.synchronize', 'workflow_run.completed', 'release.published' ], tags: ['ci-cd', 'automation', 'deployment'], priority: ['normal', 'high', 'critical'] }, /** * Critical Production Events */ production: { types: [ 'push', 'release.published', 'workflow_run.completed' ], tags: ['main-branch', 'deployment', 'production'], priority: ['high', 'critical'], customFilter: (event) => { // Only main/master branch events const branch = event.payload.ref?.replace('refs/heads/', '') || event.payload.pull_request?.head?.ref || event.payload.workflow_run?.head_branch; return branch === 'main' || branch === 'master'; } }, /** * Security-Related Events */ security: { types: [ 'issues.opened', 'pull_request.opened', 'workflow_run.completed' ], tags: ['security', 'vulnerability', 'urgent'], priority: ['high', 'critical'] }, /** * Development Events (Non-Critical) */ development: { types: [ 'push', 'pull_request.opened', 'pull_request.synchronize', 'issues.opened' ], tags: ['development', 'feature'], priority: ['low', 'normal'], customFilter: (event) => { // Exclude main/master branch const branch = event.payload.ref?.replace('refs/heads/', '') || event.payload.pull_request?.head?.ref; return branch !== 'main' && branch !== 'master'; } }, /** * Team Collaboration Events */ collaboration: { types: [ 'pull_request.opened', 'pull_request.review_requested', 'issues.opened', 'issues.assigned' ], tags: ['collaboration', 'review-needed', 'team'] } }; // ============================================================================ // TRANSFORMATION TEMPLATES // ============================================================================ exports.transformationTemplates = { /** * Slack Message Formatter */ slackFormatter: { type: 'javascript', script: ` (events) => events.map(event => ({ ...event, payload: { ...event.payload, slack_message: { text: \`\${event.type} event from \${event.source}\`, blocks: [ { type: "section", text: { type: "mrkdwn", text: \`*\${event.type}* in \${event.metadata?.repository || 'unknown'}\` } }, { type: "context", elements: [ { type: "mrkdwn", text: \`Priority: \${event.priority || 'normal'} | Time: \${new Date(event.timestamp * 1000).toISOString()}\` } ] } ] } } })) ` }, /** * Email Formatter */ emailFormatter: { type: 'javascript', script: ` (events) => events.map(event => ({ ...event, payload: { ...event.payload, email: { subject: \`[\${event.source.toUpperCase()}] \${event.type} - \${event.metadata?.repository || 'Repository'}\`, body: \` Event: \${event.type} Source: \${event.source} Repository: \${event.metadata?.repository || 'N/A'} Sender: \${event.metadata?.sender || 'N/A'} Priority: \${event.priority || 'normal'} Time: \${new Date(event.timestamp * 1000).toISOString()} Details: \${JSON.stringify(event.payload, null, 2)} \`, priority: event.priority === 'critical' ? 'high' : 'normal' } } })) ` }, /** * Jira Issue Creator */ jiraFormatter: { type: 'javascript', script: ` (events) => events.filter(event => event.type.includes('workflow_run') && event.payload.workflow_run?.conclusion === 'failure' ).map(event => ({ ...event, payload: { ...event.payload, jira_issue: { project: "DEVOPS", issuetype: "Bug", summary: \`Build failure in \${event.metadata?.repository}\`, description: \` Workflow: \${event.payload.workflow_run?.name} Branch: \${event.payload.workflow_run?.head_branch} Commit: \${event.payload.workflow_run?.head_sha} URL: \${event.payload.workflow_run?.html_url} \`, priority: event.priority === 'critical' ? 'Highest' : 'High', labels: ['automation', 'ci-cd', 'build-failure'] } } })) ` }, /** * Metrics Extractor */ metricsExtractor: { type: 'javascript', script: ` (events) => events.map(event => ({ ...event, payload: { ...event.payload, metrics: { event_type: event.type, source: event.source, tenant: event.tenant, priority: event.priority, repository: event.metadata?.repository, branch: event.payload.ref?.replace('refs/heads/', '') || event.payload.pull_request?.head?.ref || event.payload.workflow_run?.head_branch, timestamp: event.timestamp, tags: event.tags } } })) ` } }; // ============================================================================ // DESTINATION TEMPLATES // ============================================================================ exports.destinationTemplates = { slack: (webhookUrl) => ({ type: 'webhook', config: { url: webhookUrl, method: 'POST', headers: { 'Content-Type': 'application/json' }, payloadPath: 'payload.slack_message' } }), discord: (webhookUrl) => ({ type: 'webhook', config: { url: webhookUrl, method: 'POST', headers: { 'Content-Type': 'application/json' }, payloadPath: 'payload.discord_message' } }), email: (smtpConfig) => ({ type: 'custom', config: { type: 'email', smtp: smtpConfig, payloadPath: 'payload.email' } }), jira: (jiraConfig) => ({ type: 'custom', config: { type: 'jira', baseUrl: jiraConfig.baseUrl, auth: jiraConfig.auth, payloadPath: 'payload.jira_issue' } }), database: (connectionString, table) => ({ type: 'database', config: { connectionString, table, columns: { id: 'id', type: 'type', source: 'source', timestamp: 'timestamp', payload: 'payload', tenant: 'tenant' } } }), queue: (queueUrl) => ({ type: 'queue', config: { url: queueUrl, serializer: 'json' } }) }; // ============================================================================ // PIPELINE TEMPLATES // ============================================================================ exports.pipelineTemplates = { /** * CI/CD Pipeline with Slack notifications */ cicdSlack: (slackWebhookUrl) => ({ id: 'cicd-slack', name: 'CI/CD Slack Notifications', filters: [exports.filterTemplates.cicd], transformations: [exports.transformationTemplates.slackFormatter], routes: [ { id: 'slack-cicd', name: 'Slack CI/CD Channel', filter: exports.filterTemplates.cicd, destination: exports.destinationTemplates.slack(slackWebhookUrl), enabled: true, priority: 100 } ], enabled: true, priority: 100 }), /** * Production monitoring with multiple channels */ productionMonitoring: (config) => ({ id: 'production-monitoring', name: 'Production Event Monitoring', filters: [exports.filterTemplates.production], transformations: [ exports.transformationTemplates.slackFormatter, exports.transformationTemplates.emailFormatter, exports.transformationTemplates.jiraFormatter ], routes: [ { id: 'slack-production', name: 'Slack Production Alerts', filter: exports.filterTemplates.production, destination: exports.destinationTemplates.slack(config.slackUrl), enabled: true, priority: 200 }, { id: 'email-critical', name: 'Email Critical Alerts', filter: { ...exports.filterTemplates.production, priority: ['critical'] }, destination: exports.destinationTemplates.email(config.emailConfig), enabled: true, priority: 300 }, { id: 'jira-failures', name: 'Jira Build Failures', filter: { types: ['workflow_run.completed'], customFilter: (event) => event.payload.workflow_run?.conclusion === 'failure' }, destination: exports.destinationTemplates.jira(config.jiraConfig), enabled: true, priority: 150 } ], enabled: true, priority: 200 }), /** * Security event pipeline */ securityPipeline: (config) => ({ id: 'security-pipeline', name: 'Security Event Processing', filters: [exports.filterTemplates.security], transformations: [ exports.transformationTemplates.slackFormatter, exports.transformationTemplates.emailFormatter ], routes: [ { id: 'slack-security', name: 'Slack Security Channel', filter: exports.filterTemplates.security, destination: exports.destinationTemplates.slack(config.slackUrl), enabled: true, priority: 300 }, { id: 'email-security', name: 'Email Security Team', filter: exports.filterTemplates.security, destination: exports.destinationTemplates.email(config.emailConfig), enabled: true, priority: 400 } ], enabled: true, priority: 300 }), /** * Development team collaboration */ teamCollaboration: (slackWebhookUrl) => ({ id: 'team-collaboration', name: 'Team Collaboration Events', filters: [exports.filterTemplates.collaboration], transformations: [exports.transformationTemplates.slackFormatter], routes: [ { id: 'slack-team', name: 'Slack Team Channel', filter: exports.filterTemplates.collaboration, destination: exports.destinationTemplates.slack(slackWebhookUrl), enabled: true, priority: 50 } ], enabled: true, priority: 50 }) }; // ============================================================================ // TENANT TEMPLATES // ============================================================================ exports.tenantTemplates = { /** * Startup tenant (lower limits) */ startup: (tenantId, customPipelines = []) => ({ tenantId, pipelines: [ exports.pipelineTemplates.teamCollaboration(`https://hooks.slack.com/services/${tenantId}/team`), ...customPipelines ], rateLimits: { eventsPerSecond: 10, burstSize: 50, windowSize: 1000 }, allowedSources: ['github', 'gitlab', 'bitbucket'] }), /** * Enterprise tenant (higher limits) */ enterprise: (tenantId, customPipelines = []) => ({ tenantId, pipelines: [ exports.pipelineTemplates.cicdSlack(`https://hooks.slack.com/services/${tenantId}/cicd`), exports.pipelineTemplates.productionMonitoring({ slackUrl: `https://hooks.slack.com/services/${tenantId}/production`, emailConfig: { /* tenant-specific email config */}, jiraConfig: { /* tenant-specific jira config */} }), exports.pipelineTemplates.securityPipeline({ slackUrl: `https://hooks.slack.com/services/${tenantId}/security`, emailConfig: { /* tenant-specific email config */} }), ...customPipelines ], rateLimits: { eventsPerSecond: 100, burstSize: 500, windowSize: 1000 }, allowedSources: ['github', 'gitlab', 'bitbucket', 'jenkins', 'circleci'] }), /** * Development tenant (relaxed limits for testing) */ development: (tenantId, customPipelines = []) => ({ tenantId, pipelines: [ exports.pipelineTemplates.teamCollaboration(`https://hooks.slack.com/services/${tenantId}/dev`), ...customPipelines ], rateLimits: { eventsPerSecond: 50, burstSize: 200, windowSize: 1000 } }) }; // ============================================================================ // MULTI-TENANT CONFIGURATION TEMPLATES // ============================================================================ exports.multiTenantTemplates = { /** * Basic multi-tenant setup */ basic: () => ({ defaultRateLimit: { eventsPerSecond: 20, burstSize: 100, windowSize: 1000 }, defaultIsolation: { enableResourceIsolation: true, maxMemoryPerTenant: 50, // 50MB maxConcurrentEvents: 25, enableNetworkIsolation: false }, enableTenantMetrics: true, enableGlobalRateLimit: false }), /** * High-performance multi-tenant setup */ highPerformance: () => ({ defaultRateLimit: { eventsPerSecond: 100, burstSize: 500, windowSize: 1000 }, defaultIsolation: { enableResourceIsolation: true, maxMemoryPerTenant: 200, // 200MB maxConcurrentEvents: 100, enableNetworkIsolation: false }, enableTenantMetrics: true, enableGlobalRateLimit: true, globalRateLimit: { eventsPerSecond: 1000, burstSize: 2000, windowSize: 1000 } }), /** * Secure multi-tenant setup */ secure: () => ({ defaultRateLimit: { eventsPerSecond: 50, burstSize: 200, windowSize: 1000 }, defaultIsolation: { enableResourceIsolation: true, maxMemoryPerTenant: 100, // 100MB maxConcurrentEvents: 50, enableNetworkIsolation: true, allowedDomains: ['*.company.com', 'api.github.com'], blockedDomains: ['*.suspicious.com'] }, enableTenantMetrics: true, enableGlobalRateLimit: true, globalRateLimit: { eventsPerSecond: 500, burstSize: 1000, windowSize: 1000 } }) }; // ============================================================================ // HELPER FUNCTIONS // ============================================================================ /** * Create a custom pipeline from template */ function createCustomPipeline(id, name, filters, transformations, routes, options = {}) { return { id, name, filters, transformations, routes, enabled: options.enabled ?? true, priority: options.priority ?? 100 }; } /** * Merge multiple filters with AND logic */ function mergeFilters(...filters) { const merged = {}; filters.forEach(filter => { if (filter.types) { merged.types = merged.types ? merged.types.filter(t => filter.types.includes(t)) : filter.types; } if (filter.sources) { merged.sources = merged.sources ? merged.sources.filter(s => filter.sources.includes(s)) : filter.sources; } if (filter.tenants) { merged.tenants = merged.tenants ? merged.tenants.filter(t => filter.tenants.includes(t)) : filter.tenants; } if (filter.tags) { merged.tags = merged.tags ? [...merged.tags, ...filter.tags] : filter.tags; } if (filter.priority) { merged.priority = merged.priority ? merged.priority.filter(p => filter.priority.includes(p)) : filter.priority; } }); return merged; }