mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 17:45:42 +00:00
IAM, package, luggage, app health, rate limit, and more
This commit is contained in:
@@ -439,6 +439,126 @@ export class NotificationsService {
|
||||
</html>`;
|
||||
}
|
||||
|
||||
async sendBoardingPassNotification(params: {
|
||||
passengerId: string | null;
|
||||
contactEmail: string | null;
|
||||
contactPhone: string | null;
|
||||
bookingRef: string;
|
||||
leg: string | null;
|
||||
booking: any;
|
||||
ticket: any;
|
||||
}): Promise<void> {
|
||||
const { passengerId, contactEmail, contactPhone, bookingRef, leg, booking, ticket } = params;
|
||||
|
||||
// Resolve contact — prefer IAM user record, fall back to booking contact fields
|
||||
let email: string | null = contactEmail ?? null;
|
||||
let phone: string | null = contactPhone ?? null;
|
||||
if (passengerId) {
|
||||
const resolved = await this.getRecipientAddress(passengerId, 'EMAIL').catch(() => null);
|
||||
const resolvedPhone = await this.getRecipientAddress(passengerId, 'SMS').catch(() => null);
|
||||
if (resolved) email = resolved;
|
||||
if (resolvedPhone) phone = resolvedPhone;
|
||||
}
|
||||
|
||||
const s = booking.schedule ?? {};
|
||||
const fmt = (d: any) =>
|
||||
d ? new Date(d).toLocaleString('en-GB', { dateStyle: 'medium', timeStyle: 'short' }) : 'TBD';
|
||||
const legLabel = leg ? ` (${leg.replace(/_/g, ' ')})` : '';
|
||||
const origin = s.originStation?.name ?? '';
|
||||
const dest = s.destinationStation?.name ?? '';
|
||||
const train = s.train?.name ?? s.train?.number ?? '';
|
||||
const dep = fmt(s.departureAt);
|
||||
const arr = fmt(s.arrivalAt);
|
||||
|
||||
const seats: { name: string; coach: string; seat: string; cls: string }[] = (booking.seats ?? []).map((bs: any) => ({
|
||||
name: bs.passengerName ?? '',
|
||||
coach: bs.seat?.coach?.number ?? '-',
|
||||
seat: bs.seat?.seatNumber ?? '-',
|
||||
cls: bs.seat?.coach?.coachType?.name ?? '-',
|
||||
}));
|
||||
|
||||
const seatLines = seats.map(s => ` ${s.name} — Coach ${s.coach}, Seat ${s.seat} (${s.cls})`).join('\n');
|
||||
|
||||
const smsText =
|
||||
`EDR Boarding Pass${legLabel}\n` +
|
||||
`Ref: ${bookingRef}\n` +
|
||||
`${origin} → ${dest}\n` +
|
||||
`Train: ${train} | Dep: ${dep}\n` +
|
||||
(seatLines ? `${seatLines}\n` : '') +
|
||||
`Barcode: ${ticket.barcodePayload}`;
|
||||
|
||||
if (phone) {
|
||||
await this.smsClient.sendSms({ to: phone, message: smsText }).catch((e) =>
|
||||
this.logger.error(`Boarding pass SMS failed for ${bookingRef}: ${e?.message}`),
|
||||
);
|
||||
}
|
||||
|
||||
if (email) {
|
||||
const seatRows = seats
|
||||
.map(
|
||||
(s) =>
|
||||
`<tr>
|
||||
<td style="padding:8px;border-bottom:1px solid #eee;">${s.name}</td>
|
||||
<td style="padding:8px;border-bottom:1px solid #eee;">${s.coach}</td>
|
||||
<td style="padding:8px;border-bottom:1px solid #eee;">${s.seat}</td>
|
||||
<td style="padding:8px;border-bottom:1px solid #eee;">${s.cls}</td>
|
||||
</tr>`,
|
||||
)
|
||||
.join('');
|
||||
|
||||
const html = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"></head>
|
||||
<body style="margin:0;font-family:Arial,Helvetica,sans-serif;color:#333;background:#f4f4f4;">
|
||||
<div style="max-width:600px;margin:0 auto;background:#fff;">
|
||||
<div style="background:#0066cc;color:#fff;padding:24px;text-align:center;">
|
||||
<h2 style="margin:0;">Ethio-Djibouti Railway</h2>
|
||||
<p style="margin:8px 0 0;">Boarding Pass${legLabel}</p>
|
||||
</div>
|
||||
<div style="padding:24px;">
|
||||
<p>Booking reference: <strong>${bookingRef}</strong></p>
|
||||
<table style="width:100%;border-collapse:collapse;margin:16px 0;">
|
||||
<tr><td style="padding:8px 0;color:#666;">From</td><td style="text-align:right;"><strong>${origin}</strong></td></tr>
|
||||
<tr><td style="padding:8px 0;color:#666;">To</td><td style="text-align:right;"><strong>${dest}</strong></td></tr>
|
||||
<tr><td style="padding:8px 0;color:#666;">Train</td><td style="text-align:right;">${train}</td></tr>
|
||||
<tr><td style="padding:8px 0;color:#666;">Departs</td><td style="text-align:right;">${dep}</td></tr>
|
||||
<tr><td style="padding:8px 0;color:#666;">Arrives</td><td style="text-align:right;">${arr}</td></tr>
|
||||
</table>
|
||||
<h3 style="margin:16px 0 8px;">Passengers</h3>
|
||||
<table style="width:100%;border-collapse:collapse;">
|
||||
<tr style="color:#666;text-align:left;">
|
||||
<th style="padding:8px;border-bottom:2px solid #eee;">Name</th>
|
||||
<th style="padding:8px;border-bottom:2px solid #eee;">Coach</th>
|
||||
<th style="padding:8px;border-bottom:2px solid #eee;">Seat</th>
|
||||
<th style="padding:8px;border-bottom:2px solid #eee;">Class</th>
|
||||
</tr>
|
||||
${seatRows}
|
||||
</table>
|
||||
<div style="text-align:center;margin:24px 0;">
|
||||
<p style="color:#666;margin:0 0 8px;">QR code for gate scanning</p>
|
||||
<img src="${ticket.qrPayload}" alt="Boarding pass QR" width="180" height="180"
|
||||
style="border:1px solid #eee;padding:8px;background:#fff;" />
|
||||
<p style="color:#666;font-size:12px;margin:8px 0 0;">Barcode: <strong>${ticket.barcodePayload}</strong></p>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align:center;padding:20px;color:#999;font-size:12px;">
|
||||
<p style="margin:0;">© Ethio-Djibouti Railway. All rights reserved.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
const textFallback =
|
||||
`EDR Boarding Pass${legLabel}\nRef: ${bookingRef}\n${origin} → ${dest}\n` +
|
||||
`Train: ${train} | Departs: ${dep} | Arrives: ${arr}\n${seatLines}\n` +
|
||||
`Barcode: ${ticket.barcodePayload}`;
|
||||
|
||||
await this.emailClient
|
||||
.sendEmail({ to: email, subject: `EDR Boarding Pass — ${bookingRef}${legLabel}`, text: textFallback, html })
|
||||
.catch((e) => this.logger.error(`Boarding pass email failed for ${bookingRef}: ${e?.message}`));
|
||||
}
|
||||
}
|
||||
|
||||
@OnEvent('payment.failed')
|
||||
async onPaymentFailed(payload: any) {
|
||||
const booking = payload.booking;
|
||||
|
||||
Reference in New Issue
Block a user