From 88572f56948ef649419ab3947db4836235cedcac Mon Sep 17 00:00:00 2001 From: Schalli Date: Thu, 23 Jul 2026 09:14:24 +0200 Subject: [PATCH] feat(13-04): NetServerAdapter im Modul registriert (INGEST-02) - NetServerAdapter als Provider ergaenzt, additiv neben DoeOpenDataAdapter in onModuleInit ueber SourceRegistry.register() registriert (Denylist- Gate erlaubt tender24/lhs-vpbw/vergabe.landbw, keine auf der Denylist) - TenderSourcePollConfig-Row fuer 'ai-netserver' geseedet, isActive:false (Aktivierung bleibt Admin/Seed-Entscheidung, D-02) - tsc --noEmit clean, src/tenders-Suite 20 Dateien/205 Tests gruen Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/api/src/tenders/tenders.module.ts | 37 ++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/apps/api/src/tenders/tenders.module.ts b/apps/api/src/tenders/tenders.module.ts index c87d3bf..ecaf466 100644 --- a/apps/api/src/tenders/tenders.module.ts +++ b/apps/api/src/tenders/tenders.module.ts @@ -4,6 +4,7 @@ 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 { NetServerAdapter } from './adapters/netserver.adapter'; import { SourceRegistry } from './source-registry'; import { seedTendersModule } from './tenders.seed'; import { TenderDedupService } from './tender-dedup.service'; @@ -75,12 +76,21 @@ import { TendersController } from './tenders.controller'; * `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. + * + * Phase 13, Plan 04 (INGEST-02): adds `NetServerAdapter` (config-driven, + * serves tender24/lhs-vpbw/vergabe.landbw — none denylisted) as a provider + * and registers it alongside `DoeOpenDataAdapter` in `onModuleInit`. A + * `TenderSourcePollConfig` row is seeded with `isActive: false` — this + * plan does not force-activate the new source; activation is an + * admin/seed decision (Phase 14 UI), matching D-02's "framework ready, + * activation deferred" stance. */ @Module({ imports: [ModuleRegistryModule, SettingsModule], controllers: [TendersController], providers: [ DoeOpenDataAdapter, + NetServerAdapter, SourceRegistry, TenderNormalizerService, TenderDedupService, @@ -102,15 +112,18 @@ export class TendersModule implements OnModuleInit { private readonly prisma: PrismaService, private readonly sourceRegistry: SourceRegistry, private readonly doeAdapter: DoeOpenDataAdapter, + private readonly netServerAdapter: NetServerAdapter, ) {} 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. + // denylisted portal. DoeOpenDataAdapter and NetServerAdapter are both + // legitimate (tender24/lhs-vpbw/vergabe.landbw are not on the + // denylist); this call is additive-only for 13-05's future adapter. this.sourceRegistry.register(this.doeAdapter); + this.sourceRegistry.register(this.netServerAdapter); this.logger.log( `Registered source adapters: ${this.sourceRegistry .activeAdapters() @@ -147,5 +160,25 @@ export class TendersModule implements OnModuleInit { } catch (error) { this.logger.error('Failed to seed doe-opendata poll config', error); } + + try { + // Phase 13, Plan 04 (INGEST-02): seed the ai-netserver poll config + // row so it is admin-drivable, but isActive defaults false — this + // plan builds and tests the adapter, it does not force-activate live + // polling of the 3 NetServer portals (D-02: framework ready, + // activation is a later admin/seed decision, Phase 14 UI). + await this.prisma.tenderSourcePollConfig.upsert({ + where: { sourceType: 'ai-netserver' }, + update: {}, + create: { + sourceType: 'ai-netserver', + pollIntervalMin: 60, + isActive: false, + }, + }); + this.logger.log('ai-netserver poll config seeded (inactive)'); + } catch (error) { + this.logger.error('Failed to seed ai-netserver poll config', error); + } } }