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..b804c1a7 --- /dev/null +++ b/src/payments/services/payment-enrollment-validator.service.ts @@ -0,0 +1,24 @@ +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', + ); + } +}