import { ConfigService } from '@nestjs/config'; import { WAHAEvents } from '@waha/structures/enums.dto'; import { CustomHeader, HmacConfiguration, RetriesConfiguration, WebhookConfig, } from '@waha/structures/webhooks.config.dto'; import { plainToInstance } from 'class-transformer'; import { validateSync, ValidationError } from 'class-validator'; enum Env { WHATSAPP_HOOK_URL = 'WHATSAPP_HOOK_URL', WHATSAPP_HOOK_EVENTS = 'WHATSAPP_HOOK_EVENTS', WHATSAPP_HOOK_RETRIES_POLICY = 'WHATSAPP_HOOK_RETRIES_POLICY', WHATSAPP_HOOK_RETRIES_DELAY_SECONDS = 'WHATSAPP_HOOK_RETRIES_DELAY_SECONDS', WHATSAPP_HOOK_RETRIES_ATTEMPTS = 'WHATSAPP_HOOK_RETRIES_ATTEMPTS', WHATSAPP_HOOK_HMAC_KEY = 'WHATSAPP_HOOK_HMAC_KEY', WHATSAPP_HOOK_CUSTOM_HEADERS = 'WHATSAPP_HOOK_CUSTOM_HEADERS', } export class GlobalWebhookConfigConfig { protected _config: WebhookConfig | null = null; constructor(private configService: ConfigService) {} private getUrl(): string | undefined { return this.configService.get(Env.WHATSAPP_HOOK_URL); } private getEvents(): WAHAEvents[] { const value = this.configService.get(Env.WHATSAPP_HOOK_EVENTS, ''); return value ? value.split(',') : []; } private getHmac(): HmacConfiguration | null { const key = this.configService.get(Env.WHATSAPP_HOOK_HMAC_KEY, ''); if (!key) { return null; } return { key: key, }; } private getRetries(): RetriesConfiguration | null { const policy: any = this.configService.get( Env.WHATSAPP_HOOK_RETRIES_POLICY, null, ); const delaySeconds: any = this.configService.get( Env.WHATSAPP_HOOK_RETRIES_DELAY_SECONDS, null, ); const attempts: any = this.configService.get( Env.WHATSAPP_HOOK_RETRIES_ATTEMPTS, null, ); if (!policy && !delaySeconds && !attempts) { return null; } return { policy: policy, delaySeconds: delaySeconds, attempts: attempts, }; } private getCustomHeaders(): CustomHeader[] { // key:value;key2:value const value = this.configService.get(Env.WHATSAPP_HOOK_CUSTOM_HEADERS, ''); if (!value) { return []; } const headers = value.split(';'); return headers.map((header: string) => { const parts = header.split(':'); if (parts.length !== 2) { throw new Error( `${Env.WHATSAPP_HOOK_CUSTOM_HEADERS} - Invalid custom header, no ':' found in '${header}'`, ); } return { name: parts[0], value: parts[1], }; }); } get config() { if (!this._config) { this._config = this.parseWebhookConfig(); } return this._config; } validateConfig(): string | null { const config = this.parseWebhookConfig(); if (!config) { return null; } const errors = validateSync(config); if (errors.length > 0) { return this.formatErrors([], errors, []).join('\n'); } return; } private getEnv(key: string): string | null { const keys = { url: Env.WHATSAPP_HOOK_URL, events: Env.WHATSAPP_HOOK_EVENTS, 'retries.policy': Env.WHATSAPP_HOOK_RETRIES_POLICY, 'retries.delaySeconds': Env.WHATSAPP_HOOK_RETRIES_DELAY_SECONDS, 'retries.attempts': Env.WHATSAPP_HOOK_RETRIES_ATTEMPTS, 'hmac.key': Env.WHATSAPP_HOOK_HMAC_KEY, }; return keys[key] || null; } private formatErrors( lines: string[], errors: ValidationError[], path: string[], ) { for (const err of errors) { path = [...path, err.property]; for (const key in err.constraints) { if (!err.constraints.hasOwnProperty(key)) { continue; } const property = path.join('.'); const env = this.getEnv(property); if (env) { const value = this.configService.get(env, ''); const line = `- ${env} (${property}): '${value}' => '${err.value}' - ${err.constraints[key]}`; lines.push(line); } else { const line = `- ${property}: '${err.value}' - ${err.constraints[key]}`; lines.push(line); } } if (err.children) { this.formatErrors(lines, err.children, path); } } return lines; } private parseWebhookConfig(): WebhookConfig | null { const url = this.getUrl(); const events = this.getEvents(); if (!url || events.length === 0) { return null; } const data: WebhookConfig = { url: url, events: events, hmac: this.getHmac(), retries: this.getRetries(), customHeaders: this.getCustomHeaders(), }; return plainToInstance(WebhookConfig, data, { enableImplicitConversion: true, }); } }