import { ModuleRegistryService } from '../module-registry/module-registry.service'; import type { PrismaService } from '../prisma/prisma.service'; /** * Seeds the tender-radar module into the module registry. * * Called during TendersModule initialization to ensure the * "tender-radar" module record exists in the database (CONFIG-01). * * isSystem: true registers the module in the registry but does NOT * auto-activate it per tenant. Admin must activate via Marketplace UI. */ export async function seedTendersModule( moduleRegistryService: ModuleRegistryService, ): Promise { await moduleRegistryService.seedModule({ slug: 'tender-radar', name: 'Ausschreibungs-Radar', version: '1.0.0', category: 'procurement', description: { de: 'Deutsche Ausschreibungen automatisch erfassen und durchsuchen', en: 'Automatically track and search German public tenders', }, isSystem: true, }); } /** The one genuinely national/global RSS source (RESEARCH.md Open Question 3). */ export const SERVICE_BUND_RSS_URL = 'https://www.service.bund.de/Content/Globals/Functions/RSSFeed/RSSGenerator_Ausschreibungen.xml'; /** * Seeds the single default-active service.bund.de RSS feed row — the one * genuinely national/global RSS source (RESEARCH.md Open Question 3, * Phase 14, Plan 02, D-14). subreport-elvis has no single canonical URL * (per-municipality instances, RESEARCH.md Pitfall 2) — deliberately ZERO * subreport-elvis rows are ever seeded; users add the municipality feeds * relevant to them via the RSS-Feeds UI (Plan 14-02 Task 3). * * Extracted out of `TendersModule.onModuleInit` (Phase 17, Plan 02, Task 3) * — same "logic lives in a plain testable function, the module's lifecycle * hook only wraps it in try/catch + logging" pattern as `seedTendersModule` * above — so the idempotency behavior below can be exercised directly by a * unit test instead of only through a full Nest bootstrap. * * Phase 17, Plan 02 (D-02): "upsert on url" stopped working when the * unique index moved to `(userId, url)` — Prisma's generated * compound-unique input type requires `userId` as a plain `string` (not * `string | null | undefined`), so a platform-wide row (`userId = null`) * can never be addressed through it. This function instead FINDS a * platform-wide row with this url first, and only creates one if none * exists — an ordinary equality condition has no such requirement. Still * idempotent across repeated app starts (proven in tenders.seed.spec.ts). */ export async function seedServiceBundRssFeed(prisma: PrismaService): Promise { const existing = await prisma.tenderRssFeedSource.findFirst({ where: { userId: null, url: SERVICE_BUND_RSS_URL }, }); if (existing) return; await prisma.tenderRssFeedSource.create({ data: { url: SERVICE_BUND_RSS_URL, label: 'service-bund', isActive: true, }, }); }