INSIGHT-MVP/packages/crm-service/src/health/health.controller.ts
Thomas Reitz 63cb05d4d8 feat(crm): Phase 2.2-2.4 backend + contract files — vollständige CRM-Service Implementierung
- Phase 2.3 Forecast: probability-Feld in PipelineStage, GET /crm/deals/forecast Endpoint
- Phase 2.2 Import: ImportModule mit preview/execute/history Endpoints (CSV, XLSX, vCard)
- Phase 2.4 Enrichment: EnrichmentModule mit /enrich + /settings/integrations/north-data
- Contracts: ContractsModule mit CRUD + File-Upload Endpoints (Multer, max 25MB)
- Migrations: 20260312_contract_files, 20260312_phase23_forecast
- docker-compose.crm.yml: uploads Volume für Vertragsdokumente

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 22:06:58 +01:00

103 lines
3.1 KiB
TypeScript

import { Controller, Get, Inject, Optional } from '@nestjs/common';
import { ApiTags, ApiOperation } from '@nestjs/swagger';
import { Public } from '../common/decorators/public.decorator';
import { CrmPrismaService } from '../prisma/crm-prisma.service';
import { RedisService } from '../redis/redis.service';
import { LexwareClientService } from '../lexware/lexware-client.service';
import { EnrichmentService } from '../enrichment/enrichment.service';
interface HealthResponse {
status: 'ok' | 'error';
service: string;
timestamp: string;
version: string;
services: {
database: 'up' | 'down';
redis: 'up' | 'down';
lexware: 'up' | 'down' | 'unconfigured';
enrichment: 'up' | 'down' | 'unconfigured';
};
}
@ApiTags('Health')
@Controller('health')
export class HealthController {
constructor(
private readonly prisma: CrmPrismaService,
private readonly redis: RedisService,
@Optional() @Inject(LexwareClientService)
private readonly lexwareClient?: LexwareClientService,
@Optional() @Inject(EnrichmentService)
private readonly enrichmentService?: EnrichmentService,
) {}
@Get()
@Public()
@ApiOperation({ summary: 'Health-Check fuer CRM-Service' })
async check(): Promise<HealthResponse> {
const [dbStatus, redisStatus, lexwareStatus, enrichmentStatus] =
await Promise.allSettled([
this.checkDatabase(),
this.checkRedis(),
this.checkLexware(),
this.checkEnrichment(),
]);
const dbOk = dbStatus.status === 'fulfilled' && dbStatus.value;
const redisOk = redisStatus.status === 'fulfilled' && redisStatus.value;
const lexwareResult: 'up' | 'down' | 'unconfigured' =
lexwareStatus.status === 'fulfilled'
? lexwareStatus.value
: 'down';
const enrichmentResult: 'up' | 'down' | 'unconfigured' =
enrichmentStatus.status === 'fulfilled'
? enrichmentStatus.value
: 'down';
// "unconfigured" ist kein Fehler
const allUp = dbOk && redisOk
&& lexwareResult !== 'down'
&& enrichmentResult !== 'down';
return {
status: allUp ? 'ok' : 'error',
service: 'crm-service',
timestamp: new Date().toISOString(),
version: '0.3.0',
services: {
database: dbOk ? 'up' : 'down',
redis: redisOk ? 'up' : 'down',
lexware: lexwareResult,
enrichment: enrichmentResult,
},
};
}
private async checkDatabase(): Promise<boolean> {
try {
await this.prisma.$queryRaw`SELECT 1`;
return true;
} catch {
return false;
}
}
private async checkRedis(): Promise<boolean> {
try {
const pong = await this.redis.ping();
return pong === 'PONG';
} catch {
return false;
}
}
private async checkLexware(): Promise<'up' | 'down' | 'unconfigured'> {
if (!this.lexwareClient) return 'unconfigured';
return this.lexwareClient.isHealthy();
}
private async checkEnrichment(): Promise<'up' | 'down' | 'unconfigured'> {
if (!this.enrichmentService) return 'unconfigured';
return this.enrichmentService.isHealthy();
}
}