diff --git a/apps/api/src/tenders/tenders.module.ts b/apps/api/src/tenders/tenders.module.ts index 207a510..c87d3bf 100644 --- a/apps/api/src/tenders/tenders.module.ts +++ b/apps/api/src/tenders/tenders.module.ts @@ -4,7 +4,9 @@ import { ModuleRegistryService } from '../module-registry/module-registry.servic import { PrismaService } from '../prisma/prisma.service'; import { SettingsModule } from '../settings/settings.module'; import { DoeOpenDataAdapter } from './adapters/doe-opendata.adapter'; +import { SourceRegistry } from './source-registry'; import { seedTendersModule } from './tenders.seed'; +import { TenderDedupService } from './tender-dedup.service'; import { TenderDigestScheduler } from './tender-digest.scheduler'; import { TenderIngestionService } from './tender-ingestion.service'; import { TenderMailService } from './tender-mail.service'; @@ -61,13 +63,27 @@ import { TendersController } from './tenders.controller'; * * Seeds itself into the module registry on application startup via * OnModuleInit lifecycle hook — same pattern as DkvModule. + * + * Phase 13, Plan 03 (SCHEMA-03): adds `SourceRegistry` (fan-out lookup + + * INGEST-07 denylist gate, Plan 13-02) and `TenderDedupService` + * (three-tier cross-source dedup resolver, Plan 13-03 Task 1) as + * providers. `onModuleInit` registers every known source adapter with the + * registry via `SourceRegistry.register()` BEFORE the scheduler's first + * tick can run — this is the DI-boot-time hook where the denylist gate + * (D-06) structurally applies. `DoeOpenDataAdapter` is registered here; + * this is deliberately the ONE place that grows an additional + * `registry.register(...)` call as 13-04 (NetServer) and 13-05 (cosinex) + * add their adapters in later waves — this plan is Wave 2's sole writer + * of tenders.module.ts, so those additions are purely additive follow-ups. */ @Module({ imports: [ModuleRegistryModule, SettingsModule], controllers: [TendersController], providers: [ DoeOpenDataAdapter, + SourceRegistry, TenderNormalizerService, + TenderDedupService, TenderIngestionService, TenderSchedulerService, TenderTriageService, @@ -84,9 +100,27 @@ export class TendersModule implements OnModuleInit { constructor( private readonly moduleRegistryService: ModuleRegistryService, private readonly prisma: PrismaService, + private readonly sourceRegistry: SourceRegistry, + private readonly doeAdapter: DoeOpenDataAdapter, ) {} async onModuleInit(): Promise { + try { + // D-06/INGEST-07: registration itself is the denylist-gate enforcement + // point — SourceRegistry.register() throws for any adapter serving a + // denylisted portal. DoeOpenDataAdapter is legitimate; this call is + // additive-only for 13-04/13-05's future adapters. + this.sourceRegistry.register(this.doeAdapter); + this.logger.log( + `Registered source adapters: ${this.sourceRegistry + .activeAdapters() + .map((a) => a.sourceType) + .join(', ')}`, + ); + } catch (error) { + this.logger.error('Failed to register tender source adapters', error); + } + try { await seedTendersModule(this.moduleRegistryService); this.logger.log('Ausschreibungs-Radar module seeded in registry');