From f6d9cb51448c4a52e34fbe0968aa4566d49131ff Mon Sep 17 00:00:00 2001 From: soundsng Date: Fri, 26 Jun 2026 20:16:51 +0100 Subject: [PATCH 1/5] Add WebSocket drain on shutdown and payment-enrollment referential integrity --- .../services/ws-drain.service.ts | 22 ++++++++++++++ .../payment-enrollment-validator.service.ts | 29 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/collaboration/services/ws-drain.service.ts create mode 100644 src/payments/services/payment-enrollment-validator.service.ts diff --git a/src/collaboration/services/ws-drain.service.ts b/src/collaboration/services/ws-drain.service.ts new file mode 100644 index 00000000..57bbcf96 --- /dev/null +++ b/src/collaboration/services/ws-drain.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class WsDrainService { + private draining = false; + private readonly drainPeriodMs = 30000; + + startDrain(): void { + this.draining = true; + setTimeout(() => { + this.draining = false; + }, this.drainPeriodMs); + } + + isDraining(): boolean { + return this.draining; + } + + canAcceptConnection(): boolean { + return !this.draining; + } +} diff --git a/src/payments/services/payment-enrollment-validator.service.ts b/src/payments/services/payment-enrollment-validator.service.ts new file mode 100644 index 00000000..22c2836f --- /dev/null +++ b/src/payments/services/payment-enrollment-validator.service.ts @@ -0,0 +1,29 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { Payment } from '../entities/payment.entity'; + +@Injectable() +export class PaymentEnrollmentValidatorService { + constructor( + @InjectRepository(Payment) + private readonly paymentRepo: Repository, + ) {} + + async validateEnrollmentExists(enrollmentId: string): Promise { + if (!enrollmentId) return true; + const result = await this.paymentRepo.query( + 'SELECT id FROM enrollments WHERE id = $1', + [enrollmentId], + ); + return result.length > 0; + } + + async findOrphanedPayments(): Promise { + return this.paymentRepo.query(` + SELECT p.* FROM payments p + LEFT JOIN enrollments e ON e.id = p."enrollmentId" + WHERE p."enrollmentId" IS NOT NULL AND e.id IS NULL + `); + } +} From e0434f1e69cc319ebfde253bf1376dadc47d09c5 Mon Sep 17 00:00:00 2001 From: soundsng Date: Fri, 26 Jun 2026 21:52:45 +0100 Subject: [PATCH 2/5] fix: ci formatting --- .../services/payment-enrollment-validator.service.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/payments/services/payment-enrollment-validator.service.ts b/src/payments/services/payment-enrollment-validator.service.ts index 22c2836f..a5ddb5c3 100644 --- a/src/payments/services/payment-enrollment-validator.service.ts +++ b/src/payments/services/payment-enrollment-validator.service.ts @@ -20,10 +20,8 @@ export class PaymentEnrollmentValidatorService { } async findOrphanedPayments(): Promise { - return this.paymentRepo.query(` - SELECT p.* FROM payments p - LEFT JOIN enrollments e ON e.id = p."enrollmentId" - WHERE p."enrollmentId" IS NOT NULL AND e.id IS NULL - `); + return this.paymentRepo.query( + 'SELECT p.* FROM payments p LEFT JOIN enrollments e ON e.id = p."enrollmentId" WHERE p."enrollmentId" IS NOT NULL AND e.id IS NULL', + ); } } From 2f6d271393a51af23d8a9c69ff273f794dfcc0d7 Mon Sep 17 00:00:00 2001 From: soundsng Date: Sat, 27 Jun 2026 04:01:29 +0100 Subject: [PATCH 3/5] fix: prettier formatting --- .../services/payment-enrollment-validator.service.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/payments/services/payment-enrollment-validator.service.ts b/src/payments/services/payment-enrollment-validator.service.ts index a5ddb5c3..b804c1a7 100644 --- a/src/payments/services/payment-enrollment-validator.service.ts +++ b/src/payments/services/payment-enrollment-validator.service.ts @@ -12,10 +12,7 @@ export class PaymentEnrollmentValidatorService { async validateEnrollmentExists(enrollmentId: string): Promise { if (!enrollmentId) return true; - const result = await this.paymentRepo.query( - 'SELECT id FROM enrollments WHERE id = $1', - [enrollmentId], - ); + const result = await this.paymentRepo.query('SELECT id FROM enrollments WHERE id = $1', [enrollmentId]); return result.length > 0; } From 83d7844e5b440c5fe183d07cccc50a4d01f8ebe8 Mon Sep 17 00:00:00 2001 From: soundsng Date: Sat, 27 Jun 2026 04:07:08 +0100 Subject: [PATCH 4/5] trigger ci From 38dc20063a1b24bdc221f6e564317f5b8e14a63d Mon Sep 17 00:00:00 2001 From: soundsng Date: Sat, 27 Jun 2026 04:10:15 +0100 Subject: [PATCH 5/5] fix: prettier formatting