feat(10-02): register TendersModule with marketplace self-seed + singleton poll config
- tenders.seed.ts seeds slug 'tender-radar' (isSystem: true, category 'procurement') - tenders.module.ts self-seeds registry on boot (mirrors DkvModule pattern) - upserts singleton doe-opendata TenderSourcePollConfig row (global, no forTenant()) - TendersModule added to app.module.ts imports
This commit is contained in:
@@ -20,6 +20,7 @@ import { PrismaModule } from './prisma/prisma.module';
|
|||||||
import { SettingsModule } from './settings/settings.module';
|
import { SettingsModule } from './settings/settings.module';
|
||||||
import { TenantGuard } from './tenant/tenant.guard';
|
import { TenantGuard } from './tenant/tenant.guard';
|
||||||
import { TenantModule } from './tenant/tenant.module';
|
import { TenantModule } from './tenant/tenant.module';
|
||||||
|
import { TendersModule } from './tenders/tenders.module';
|
||||||
import { UserModule } from './user/user.module';
|
import { UserModule } from './user/user.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
@@ -41,6 +42,7 @@ import { UserModule } from './user/user.module';
|
|||||||
SettingsModule,
|
SettingsModule,
|
||||||
DkvModule,
|
DkvModule,
|
||||||
FavoritesModule,
|
FavoritesModule,
|
||||||
|
TendersModule,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
// Global JWT guard: all routes require auth unless @Public()
|
// Global JWT guard: all routes require auth unless @Public()
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
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 { seedTendersModule } from './tenders.seed';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NestJS module for the Ausschreibungs-Radar feature.
|
||||||
|
*
|
||||||
|
* Wave 2 (this plan) only registers the module in the marketplace and
|
||||||
|
* seeds the singleton DÖE poll config so the shared poll is
|
||||||
|
* admin-drivable. Ingestion/adapter/scheduler/controller wiring is
|
||||||
|
* added in Plans 03-05.
|
||||||
|
*
|
||||||
|
* PrismaModule is global (no explicit import needed).
|
||||||
|
*
|
||||||
|
* Seeds itself into the module registry on application startup via
|
||||||
|
* OnModuleInit lifecycle hook — same pattern as DkvModule.
|
||||||
|
*/
|
||||||
|
@Module({
|
||||||
|
imports: [ModuleRegistryModule],
|
||||||
|
controllers: [],
|
||||||
|
providers: [],
|
||||||
|
})
|
||||||
|
export class TendersModule implements OnModuleInit {
|
||||||
|
private readonly logger = new Logger(TendersModule.name);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly moduleRegistryService: ModuleRegistryService,
|
||||||
|
private readonly prisma: PrismaService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async onModuleInit(): Promise<void> {
|
||||||
|
try {
|
||||||
|
await seedTendersModule(this.moduleRegistryService);
|
||||||
|
this.logger.log('Ausschreibungs-Radar module seeded in registry');
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('Failed to seed Ausschreibungs-Radar module', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Singleton poll config — global, RLS-exempt (D-03). Do NOT use
|
||||||
|
// forTenant() here: this row is shared platform-wide, not per-tenant.
|
||||||
|
// isActive defaults true so the platform-global DÖE poll (D-04
|
||||||
|
// hourly) runs regardless of tenant activation; the day-cursor
|
||||||
|
// gate (Plan 04) makes repeated ticks idempotent.
|
||||||
|
await this.prisma.tenderSourcePollConfig.upsert({
|
||||||
|
where: { sourceType: 'doe-opendata' },
|
||||||
|
update: {},
|
||||||
|
create: {
|
||||||
|
sourceType: 'doe-opendata',
|
||||||
|
pollIntervalMin: 60,
|
||||||
|
isActive: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.logger.log('doe-opendata poll config seeded');
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error('Failed to seed doe-opendata poll config', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { ModuleRegistryService } from '../module-registry/module-registry.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<void> {
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user