import { Controller, Post, Get, Body, Param, Res, Logger } from '@nestjs/common'; import type { Response } from 'express'; import { MessagingProvider } from './providers/messaging-provider.interface'; import { MessagingService } from './messaging.service'; import { QrService } from './qr.service'; @Controller('api/messaging') export class MessagingController { private readonly logger = new Logger(MessagingController.name); constructor( private readonly provider: MessagingProvider, private readonly messaging: MessagingService, private readonly qr: QrService, ) {} @Post('webhook') async webhook(@Body() body: any) { this.logger.log(`[WA-WEBHOOK] Received: ${JSON.stringify(body).substring(0, 500)}`); if (!this.provider.validateWebhook(body)) { this.logger.warn('[WA-WEBHOOK] Validation failed — ignoring'); return { status: 'ignored', reason: 'validation failed' }; } const message = this.provider.parseInbound(body); if (!message) { this.logger.log('[WA-WEBHOOK] Non-message event — skipped'); return { status: 'ok', type: body?.type ?? 'unknown' }; } // Handle async — don't block webhook response this.messaging.handleInbound(message).catch(err => { this.logger.error(`[WA-WEBHOOK] handleInbound failed: ${err.message}`); }); return { status: 'ok' }; } // Serve QR code images — Gupshup needs a public URL to send images @Get('qr/:appointmentId') async serveQr(@Param('appointmentId') appointmentId: string, @Res() res: Response) { const png = this.qr.get(appointmentId); if (!png) { res.status(404).json({ error: 'QR code not found or expired' }); return; } res.set('Content-Type', 'image/png'); res.set('Cache-Control', 'public, max-age=86400'); res.send(png); } }