We use cookies to enhance your experience on the site
CodeWorlds

Health Checks - Fort Status Diagnostics

Chief engineer! Consul Caesar.js needs your expertise in monitoring the technical condition of our fort. Health Checks are like systematic inspections of all cohort systems - from siege engines to standard condition. Only through regular diagnostics can we ensure that our fort is ready for every campaign!

In a cohort, the failure of a critical system can mean the difference between life and death. That is why experienced engineers regularly check the engine status, rampart integrity, water levels in the cisterns, and navigation system functionality. Health Checks in NestJS work similarly - they monitor key application components.

Basic Health Checks System

Health Check Module Configuration

1// health/health.module.ts
2import { Module } from '@nestjs/common';
3import { TerminusModule } from '@nestjs/terminus';
4import { HttpModule } from '@nestjs/axios';
5import { TypeOrmModule } from '@nestjs/typeorm';
6import { LegionaryHealthController } from './legionaries-health.controller';
7import { LegionaryHealthService } from './legionaries-health.service';
8import { TributeHealthIndicator } from './indicators/tribute-health.indicator';
9import { LegionHealthIndicator } from './indicators/legion-health.indicator';
10
11@Module({
12 imports: [
13 TerminusModule,
14 HttpModule,
15 TypeOrmModule.forFeature([/* entities */])
16 ],
17 controllers: [LegionaryHealthController],
18 providers: [
19 LegionaryHealthService,
20 TributeHealthIndicator,
21 LegionHealthIndicator
22 ]
23})
24export class HealthModule {}

Main Health Controller

1// health/legionaries-health.controller.ts
2import { Controller, Get, Param } from '@nestjs/common';
3import {
4 HealthCheckService,
5 HealthCheck,
6 TypeOrmHealthIndicator,
7 HttpHealthIndicator,
8 MemoryHealthIndicator,
9 DiskHealthIndicator
10} from '@nestjs/terminus';
11import { LegionaryHealthService } from './legionaries-health.service';
12import { TributeHealthIndicator } from './indicators/tribute-health.indicator';
13import { LegionHealthIndicator } from './indicators/legion-health.indicator';
14
15@Controller('health')
16export class LegionaryHealthController {
17 constructor(
18 private health: HealthCheckService,
19 private db: TypeOrmHealthIndicator,
20 private http: HttpHealthIndicator,
21 private memory: MemoryHealthIndicator,
22 private disk: DiskHealthIndicator,
23 private legionariesHealth: LegionaryHealthService,
24 private tributeHealth: TributeHealthIndicator,
25 private legionHealth: LegionHealthIndicator
26 ) {}
27
28 @Get()
29 @HealthCheck()
30 checkLegionHealth() {
31 return this.health.check([
32 // Basic fort systems
33 () => this.db.pingCheck('database', { timeout: 1500 }),
34 () => this.memory.checkHeap('memory_heap', 150 * 1024 * 1024),
35 () => this.memory.checkRSS('memory_rss', 150 * 1024 * 1024),
36 () => this.disk.checkStorage('storage', { thresholdPercent: 0.8, path: '/' }),
37 
38 // Specialized systems
39 () => this.tributeHealth.checkTributeSystem('tribute_system'),
40 () => this.legionHealth.checkLegionSystem('legion_system'),
41 () => this.legionariesHealth.checkLegionIntegrity('cohort_integrity'),
42 () => this.legionariesHealth.checkWeatherSystem('weather_system')
43 ]);
44 }
45
46 @Get('quick')
47 @HealthCheck()
48 quickHealthCheck() {
49 return this.health.check([
50 () => this.db.pingCheck('database', { timeout: 500 }),
51 () => this.legionariesHealth.checkBasicSystems('basic_systems')
52 ]);
53 }
54
55 @Get('detailed')
56 @HealthCheck()
57 detailedHealthCheck() {
58 return this.health.check([
59 // Basic systems
60 () => this.db.pingCheck('database', { timeout: 3000 }),
61 () => this.memory.checkHeap('memory_heap', 200 * 1024 * 1024),
62 () => this.memory.checkRSS('memory_rss', 200 * 1024 * 1024),
63 () => this.disk.checkStorage('disk_usage', { thresholdPercent: 0.9, path: '/' }),
64 
65 // External systems
66 () => this.http.pingCheck('weather_api', 'https://api.weather.legionaries/status'),
67 () => this.http.pingCheck('tribute_market', 'https://market.tributes.com/health'),
68 
69 // Functional systems
70 () => this.tributeHealth.checkTributeSystem('tribute_system'),
71 () => this.tributeHealth.checkTributeIntegrity('tribute_integrity'),
72 () => this.legionHealth.checkLegionSystem('legion_system'),
73 () => this.legionHealth.checkLegionMorale('legion_morale'),
74 
75 // Operational systems
76 () => this.legionariesHealth.checkLegionIntegrity('cohort_integrity'),
77 () => this.legionariesHealth.checkWeatherSystem('weather_system'),
78 () => this.legionariesHealth.checkNavigationSystem('navigation_system'),
79 () => this.legionariesHealth.checkCommunicationSystem('communication_system')
80 ]);
81 }
82
83 @Get('systems/:system')
84 @HealthCheck()
85 checkSpecificSystem(@Param('system') system: string) {
86 const systemChecks = {
87 'tribute': () => this.tributeHealth.checkTributeSystem('tribute_system'),
88 'legion': () => this.legionHealth.checkLegionSystem('legion_system'),
89 'database': () => this.db.pingCheck('database'),
90 'memory': () => this.memory.checkHeap('memory', 100 * 1024 * 1024),
91 'navigation': () => this.legionariesHealth.checkNavigationSystem('navigation'),
92 'weather': () => this.legionariesHealth.checkWeatherSystem('weather')
93 };
94
95 const check = systemChecks[system];
96 if (!check) {
97 throw new NotFoundException(`System '${system}' not found. Available: ${Object.keys(systemChecks).join(', ')}`);
98 }
99
100 return this.health.check([check]);
101 }
102}

Custom Health Indicators

Tribute System Health Indicator

1// indicators/tribute-health.indicator.ts
2import { Injectable } from '@nestjs/common';
3import { HealthIndicator, HealthIndicatorResult, HealthCheckError } from '@nestjs/terminus';
4import { InjectRepository } from '@nestjs/typeorm';
5import { Repository } from 'typeorm';
6import { Tribute } from '../entities/tribute.entity';
7
8@Injectable()
9export class TributeHealthIndicator extends HealthIndicator {
10 constructor(
11 @InjectRepository(Tribute)
12 private tributeRepository: Repository<Tribute>
13 ) {
14 super();
15 }
16
17 async checkTributeSystem(key: string): Promise<HealthIndicatorResult> {
18 try {
19 const startTime = Date.now();
20 
21 // Check basic operations on tributes
22 const tributeCount = await this.tributeRepository.count();
23 const recentTributes = await this.tributeRepository.find({
24 take: 10,
25 order: { collectedAt: 'DESC' }
26 });
27 
28 const responseTime = Date.now() - startTime;
29 
30 // Check data integrity
31 const cursedTributes = await this.tributeRepository.count({ where: { isCursed: true } });
32 const cursedPercentage = tributeCount > 0 ? (cursedTributes / tributeCount) * 100 : 0;
33 
34 // Check if the system responds in reasonable time
35 if (responseTime > 5000) {
36 throw new HealthCheckError(
37 'Tribute system responding slowly',
38 this.getStatus(key, false, {
39 responseTime,
40 message: 'Tribute system responding too slowly!'
41 })
42 );
43 }
44 
45 // Check if we have too many cursed tributes
46 if (cursedPercentage > 50) {
47 throw new HealthCheckError(
48 'Too many cursed tributes detected',
49 this.getStatus(key, false, {
50 cursedPercentage,
51 message: 'Too many cursed tributes - possible threat!'
52 })
53 );
54 }
55 
56 return this.getStatus(key, true, {
57 tributeCount,
58 cursedPercentage: Math.round(cursedPercentage),
59 responseTime,
60 lastTribute: recentTributes[0]?.name || 'None',
61 status: 'Treasury operating correctly',
62 recommendation: responseTime > 1000 ? 'Consider query optimization' : 'System fully operational'
63 });
64 
65 } catch (error) {
66 throw new HealthCheckError(
67 'Tribute system check failed',
68 this.getStatus(key, false, {
69 error: error.message,
70 message: 'Error in the tribute system!',
71 action: 'Check database connection and table integrity'
72 })
73 );
74 }
75 }
76
77 async checkTributeIntegrity(key: string): Promise<HealthIndicatorResult> {
78 try {
79 // Check relationship integrity
80 const tributesWithoutChest = await this.tributeRepository
81 .createQueryBuilder('tribute')
82 .leftJoin('tribute.chest', 'chest')
83 .where('chest.id IS NULL')
84 .getCount();
85 
86 // Check duplicates
87 const duplicateSerialNumbers = await this.tributeRepository
88 .createQueryBuilder('tribute')
89 .select('tribute.serialNumber, COUNT(*) as count')
90 .groupBy('tribute.serialNumber')
91 .having('COUNT(*) > 1')
92 .getRawMany();
93 
94 // Check values
95 const tributesWithInvalidValue = await this.tributeRepository
96 .createQueryBuilder('tribute')
97 .where('tribute.value <= 0 OR tribute.value IS NULL')
98 .getCount();
99 
100 const issues = [];
101 if (tributesWithoutChest > 0) {
102 issues.push(`${tributesWithoutChest} tributes without an assigned chest`);
103 }
104 if (duplicateSerialNumbers.length > 0) {
105 issues.push(`${duplicateSerialNumbers.length} duplicate serial numbers`);
106 }
107 if (tributesWithInvalidValue > 0) {
108 issues.push(`${tributesWithInvalidValue} tributes with invalid value`);
109 }
110 
111 const isHealthy = issues.length === 0;
112 
113 return this.getStatus(key, isHealthy, {
114 issues: issues.length > 0 ? issues : ['No integrity issues'],
115 orphanedTributes: tributesWithoutChest,
116 duplicateSerials: duplicateSerialNumbers.length,
117 invalidValues: tributesWithInvalidValue,
118 status: isHealthy ? 'Data integrity OK' : 'Data issues detected',
119 recommendation: isHealthy ? 'Continue monitoring' : 'Requires immediate repair'
120 });
121 
122 } catch (error) {
123 throw new HealthCheckError(
124 'Tribute integrity check failed',
125 this.getStatus(key, false, { error: error.message })
126 );
127 }
128 }
129}

Legion System Health Indicator

1// indicators/legion-health.indicator.ts
2@Injectable()
3export class LegionHealthIndicator extends HealthIndicator {
4 constructor(
5 @InjectRepository(Legionary) private legionariesRepository: Repository<Legionary>
6 ) {
7 super();
8 }
9
10 async checkLegionSystem(key: string): Promise<HealthIndicatorResult> {
11 try {
12 const totalLegion = await this.legionariesRepository.count();
13 const activeLegion = await this.legionariesRepository.count({ where: { status: 'ACTIVE' } });
14 const centurions = await this.legionariesRepository.count({ where: { rank: 'CENTURION' } });
15 const officers = await this.legionariesRepository.count({ 
16 where: { rank: In(['OPTIO', 'QUAESTOR']) }
17 });
18 
19 // Check if we have sufficient cohort
20 if (totalLegion < 10) {
21 throw new HealthCheckError(
22 'Insufficient legion size',
23 this.getStatus(key, false, {
24 totalLegion,
25 message: 'Legion too small for safe marching!',
26 recommendation: 'Recruit more legionaries'
27 })
28 );
29 }
30 
31 // Check command structure
32 if (centurions === 0) {
33 throw new HealthCheckError(
34 'No centurion available',
35 this.getStatus(key, false, {
36 message: 'No centurion in the application!',
37 recommendation: 'Promote an experienced legionary to centurion'
38 })
39 );
40 }
41 
42 const activePercentage = (activeLegion / totalLegion) * 100;
43 const officerRatio = (officers / totalLegion) * 100;
44 
45 return this.getStatus(key, true, {
46 totalLegion,
47 activeLegion,
48 centurions,
49 officers,
50 activePercentage: Math.round(activePercentage),
51 officerRatio: Math.round(officerRatio),
52 status: 'Legion in good condition',
53 morale: activePercentage > 80 ? 'HIGH' : activePercentage > 60 ? 'MEDIUM' : 'LOW'
54 });
55 
56 } catch (error) {
57 throw new HealthCheckError(
58 'Legion system check failed',
59 this.getStatus(key, false, { error: error.message })
60 );
61 }
62 }
63
64 async checkLegionMorale(key: string): Promise<HealthIndicatorResult> {
65 try {
66 // Check legion activity (last 24h)
67 const yesterday = new Date();
68 yesterday.setDate(yesterday.getDate() - 1);
69 
70 const recentActivity = await this.legionariesRepository
71 .createQueryBuilder('legionaries')
72 .where('legionaries.lastActivity >= :yesterday', { yesterday })
73 .getCount();
74 
75 const totalLegion = await this.legionariesRepository.count();
76 const activityRate = (recentActivity / totalLegion) * 100;
77 
78 // Check for disciplinary issues
79 const desertions = await this.legionariesRepository.count({ 
80 where: { status: 'DESERTED', updatedAt: MoreThan(yesterday) } 
81 });
82 
83 const mutinyRisk = desertions > 2 ? 'HIGH' : desertions > 0 ? 'MEDIUM' : 'LOW';
84 const overallMorale = activityRate > 70 && mutinyRisk === 'LOW' ? 'HIGH' : 
85 activityRate > 50 && mutinyRisk !== 'HIGH' ? 'MEDIUM' : 'LOW';
86 
87 const isHealthy = overallMorale !== 'LOW';
88 
89 return this.getStatus(key, isHealthy, {
90 activityRate: Math.round(activityRate),
91 recentDesertions: desertions,
92 mutinyRisk,
93 overallMorale,
94 status: isHealthy ? 'Legion morale within normal range' : 'Legion morale issues',
95 recommendation: isHealthy ? 'Continue current management' : 'Consider improving working conditions'
96 });
97 
98 } catch (error) {
99 throw new HealthCheckError(
100 'Legion morale check failed',
101 this.getStatus(key, false, { error: error.message })
102 );
103 }
104 }
105}

Main Legionary Health Service

1// health/legionaries-health.service.ts
2@Injectable()
3export class LegionaryHealthService extends HealthIndicator {
4 constructor(
5 private httpService: HttpService,
6 private configService: ConfigService
7 ) {
8 super();
9 }
10
11 async checkLegionIntegrity(key: string): Promise<HealthIndicatorResult> {
12 try {
13 // Simulate fort integrity check
14 const systems = {
15 rampart: await this.checkRampartIntegrity(),
16 banners: await this.checkBannersCondition(),
17 ballistae: await this.checkBallistaeStatus(),
18 storage: await this.checkStorageCapacity()
19 };
20 
21 const failedSystems = Object.entries(systems)
22 .filter(([_, status]) => !status.healthy)
23 .map(([name]) => name);
24 
25 const isHealthy = failedSystems.length === 0;
26 
27 return this.getStatus(key, isHealthy, {
28 systems,
29 failedSystems,
30 overallCondition: isHealthy ? 'EXCELLENT' : failedSystems.length < 2 ? 'GOOD' : 'POOR',
31 combatReady: isHealthy || failedSystems.length < 2,
32 recommendation: isHealthy ? 'Cohort ready for campaign' : 'Requires repairs before marching'
33 });
34 
35 } catch (error) {
36 throw new HealthCheckError(
37 'Legion integrity check failed',
38 this.getStatus(key, false, { error: error.message })
39 );
40 }
41 }
42
43 async checkWeatherSystem(key: string): Promise<HealthIndicatorResult> {
44 try {
45 const weatherApiUrl = this.configService.get('WEATHER_API_URL');
46 
47 if (!weatherApiUrl) {
48 return this.getStatus(key, false, {
49 message: 'Weather API URL not configured',
50 recommendation: 'Configure WEATHER_API_URL environment variable'
51 });
52 }
53 
54 const startTime = Date.now();
55 const response = await this.httpService.get(weatherApiUrl + '/health').toPromise();
56 const responseTime = Date.now() - startTime;
57 
58 const isHealthy = response.status === 200 && responseTime < 3000;
59 
60 return this.getStatus(key, isHealthy, {
61 responseTime,
62 statusCode: response.status,
63 apiVersion: response.data?.version || 'unknown',
64 lastUpdate: response.data?.lastUpdate || 'unknown',
65 recommendation: isHealthy ? 'Weather system operational' : 'Check weather service'
66 });
67 
68 } catch (error) {
69 throw new HealthCheckError(
70 'Weather system check failed',
71 this.getStatus(key, false, {
72 error: error.message,
73 recommendation: 'Check weather service connectivity'
74 })
75 );
76 }
77 }
78
79 async checkBasicSystems(key: string): Promise<HealthIndicatorResult> {
80 try {
81 // Quick check of basic systems
82 const basicChecks = await Promise.all([
83 this.quickSystemCheck('engine'),
84 this.quickSystemCheck('power'),
85 this.quickSystemCheck('water'),
86 this.quickSystemCheck('food')
87 ]);
88 
89 const allHealthy = basicChecks.every(check => check.healthy);
90 const criticalIssues = basicChecks.filter(check => !check.healthy && check.critical);
91 
92 return this.getStatus(key, allHealthy && criticalIssues.length === 0, {
93 systems: basicChecks.reduce((acc, check) => {
94 acc[check.name] = { healthy: check.healthy, status: check.status };
95 return acc;
96 }, {}),
97 criticalIssues: criticalIssues.length,
98 overallStatus: allHealthy ? 'ALL_SYSTEMS_GO' : criticalIssues.length > 0 ? 'CRITICAL' : 'MINOR_ISSUES'
99 });
100 
101 } catch (error) {
102 throw new HealthCheckError(
103 'Basic systems check failed',
104 this.getStatus(key, false, { error: error.message })
105 );
106 }
107 }
108
109 // Helper methods
110 private async checkRampartIntegrity() {
111 return { healthy: true, condition: 'EXCELLENT', lastInspection: new Date() };
112 }
113
114 private async checkBannersCondition() {
115 return { healthy: true, condition: 'GOOD', efficiency: 95 };
116 }
117
118 private async checkBallistaeStatus() {
119 return { healthy: true, operational: 32, total: 32 };
120 }
121
122 private async checkStorageCapacity() {
123 return { healthy: true, used: 65, capacity: 100, unit: 'percent' };
124 }
125
126 private async quickSystemCheck(systemName: string) {
127 const systems = {
128 engine: { healthy: true, status: 'Running normally', critical: true },
129 power: { healthy: true, status: 'Full power available', critical: true },
130 water: { healthy: true, status: 'Fresh water reserves OK', critical: false },
131 food: { healthy: true, status: 'Provisions sufficient', critical: false }
132 };
133 
134 return { name: systemName, ...systems[systemName] };
135 }
136}

Health Checks are your early warning system - they allow you to detect problems before they become critical. A good diagnostic system is the difference between a safe campaign and a disaster in the field!

Remember: a wise centurion checks the fort status before every campaign, but the best centurion monitors it continuously!

Go to CodeWorlds