mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage-server
synced 2026-05-18 20:08:19 +00:00
- QrService: generates QR PNG from appointment data, cached in-memory
- GET /api/messaging/qr/:appointmentId serves the image (Gupshup needs URL)
- sendImage added to MessagingProvider + GupshupProvider
- send_appointment_qr tool registered in ToolRegistry
- Flow JSON updated: QR sent after booking confirmation
- Variable interpolation now supports dot notation ({{result.field}})
- SIDECAR_PUBLIC_URL env var for the QR image URL
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
53 lines
1.9 KiB
TypeScript
53 lines
1.9 KiB
TypeScript
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);
|
|
}
|
|
}
|