From 32441678c1ec1b3a74bfe0ed40221cac0d7e9e82 Mon Sep 17 00:00:00 2001 From: Schalli Date: Wed, 22 Jul 2026 09:14:40 +0200 Subject: [PATCH] feat(12-02): register TenderMailService/TenderDigestScheduler, import SettingsModule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TendersModule imports SettingsModule so TenderMailService can inject SettingsService (getDecryptedSmtpConfig), and registers TenderMailService + TenderDigestScheduler as providers alongside the existing TenderMatchingService (12-01). ScheduleModule.forRoot() is already global in AppModule — not re-imported. DI graph verified resolvable via npx tsc --noEmit; full apps/api suite green (192/192). Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/api/src/tenders/tenders.module.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/apps/api/src/tenders/tenders.module.ts b/apps/api/src/tenders/tenders.module.ts index 20ac873..d493a4a 100644 --- a/apps/api/src/tenders/tenders.module.ts +++ b/apps/api/src/tenders/tenders.module.ts @@ -2,9 +2,12 @@ import { Logger, Module, OnModuleInit } from '@nestjs/common'; import { ModuleRegistryModule } from '../module-registry/module-registry.module'; import { ModuleRegistryService } from '../module-registry/module-registry.service'; import { PrismaService } from '../prisma/prisma.service'; +import { SettingsModule } from '../settings/settings.module'; import { DoeOpenDataAdapter } from './adapters/doe-opendata.adapter'; import { seedTendersModule } from './tenders.seed'; +import { TenderDigestScheduler } from './tender-digest.scheduler'; import { TenderIngestionService } from './tender-ingestion.service'; +import { TenderMailService } from './tender-mail.service'; import { TenderMatchingService } from './tender-matching.service'; import { TenderNormalizerService } from './tender-normalizer.service'; import { TenderSavedSearchService } from './tender-saved-search.service'; @@ -38,11 +41,21 @@ import { TendersController } from './tenders.controller'; * D-07) — reuses buildTenderWhere against every active TenderSavedSearch * profile and upserts TenderMatch rows (matched-vs-notified state, D-06). * + * Phase 12, Plan 02 (NOTIFY-01/04) adds the digest delivery channel: + * TenderMailService (a structural DkvMailService clone — fresh nodemailer + * transport per send via SettingsService.getDecryptedSmtpConfig(tenantId), + * never a cached/global mailer, D-08) and TenderDigestScheduler (a SINGLE + * global @nestjs/schedule cron, mirroring TenderSchedulerService's + * poll-once-fan-out-many pattern rather than DkvSchedulerService's + * single-tenant pattern — Pitfall 1). SettingsModule is imported so + * TenderMailService can inject SettingsService. ScheduleModule.forRoot() + * is already registered globally in AppModule — not re-imported here. + * * Seeds itself into the module registry on application startup via * OnModuleInit lifecycle hook — same pattern as DkvModule. */ @Module({ - imports: [ModuleRegistryModule], + imports: [ModuleRegistryModule, SettingsModule], controllers: [TendersController], providers: [ DoeOpenDataAdapter, @@ -52,6 +65,8 @@ import { TendersController } from './tenders.controller'; TenderTriageService, TenderSavedSearchService, TenderMatchingService, + TenderMailService, + TenderDigestScheduler, ], }) export class TendersModule implements OnModuleInit {