feat: add health check endpoint for Docker compatibility

This commit is contained in:
Tudor Stanciu 2025-10-05 01:43:58 +03:00
parent e53ccff95c
commit bb19edd7e3
5 changed files with 43 additions and 28 deletions

View File

@ -109,6 +109,7 @@ ENABLE_HTTPS_SECURITY=false
- Useful for reverse proxy scenarios or hosting multiple apps on same domain - Useful for reverse proxy scenarios or hosting multiple apps on same domain
- **Example**: `/geoip-ui` - **Example**: `/geoip-ui`
- **Note**: All API routes are prefixed with this path automatically - **Note**: All API routes are prefixed with this path automatically
- **Health Check**: The `/api/health` endpoint is ALWAYS available at root (`/api/health`) for Docker HEALTHCHECK compatibility, AND also at `{BASE_PATH}/api/health` for consistency
**`NODE_ENV`** **`NODE_ENV`**

View File

@ -0,0 +1,35 @@
import { Request, Response } from 'express';
import geoIPService from '../services/geoip.js';
import logger from '../services/logger.js';
export const healthCheckHandler = async (
_req: Request,
res: Response
): Promise<void> => {
try {
const isHealthy = await geoIPService.healthCheck();
if (isHealthy) {
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
service: 'Bitip GeoIP Service',
});
} else {
res.status(503).json({
status: 'unhealthy',
timestamp: new Date().toISOString(),
service: 'Bitip GeoIP Service',
error: 'GeoIP service health check failed',
});
}
} catch (error) {
logger.error('Health check failed', error as Error);
res.status(503).json({
status: 'unhealthy',
timestamp: new Date().toISOString(),
service: 'Bitip GeoIP Service',
error: 'Health check endpoint failed',
});
}
};

View File

@ -0,0 +1 @@
export * from './healthHandler.js';

View File

@ -12,6 +12,7 @@ import dynamicRateLimit from './middleware/rateLimit.js';
import config from './services/config.js'; import config from './services/config.js';
import logger from './services/logger.js'; import logger from './services/logger.js';
import { generateRuntimeConfig } from './services/runtimeConfig.js'; import { generateRuntimeConfig } from './services/runtimeConfig.js';
import { healthCheckHandler } from './handlers';
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename); const __dirname = dirname(__filename);
@ -76,6 +77,9 @@ const frontendPath = path.join(__dirname, '../frontend');
// Generate runtime configuration (env.js) at startup // Generate runtime configuration (env.js) at startup
generateRuntimeConfig(frontendPath, basePath); generateRuntimeConfig(frontendPath, basePath);
// Health check endpoint at root (always accessible for Docker HEALTHCHECK)
app.get('/api/health', healthCheckHandler);
// API routes with authentication and rate limiting // API routes with authentication and rate limiting
app.use(`${basePath}/api`, apiKeyAuth, dynamicRateLimit, apiRoutes); app.use(`${basePath}/api`, apiKeyAuth, dynamicRateLimit, apiRoutes);

View File

@ -6,6 +6,7 @@ import { dirname, join } from 'path';
import geoIPService from '../services/geoip.js'; import geoIPService from '../services/geoip.js';
import logger from '../services/logger.js'; import logger from '../services/logger.js';
import config from '../services/config.js'; import config from '../services/config.js';
import { healthCheckHandler } from '../handlers';
import { import {
BatchGeoIPRequest, BatchGeoIPRequest,
BatchGeoIPResponse, BatchGeoIPResponse,
@ -252,34 +253,7 @@ router.post('/lookup/batch', async (req: Request, res: Response) => {
}); });
// Health check // Health check
router.get('/health', async (req: Request, res: Response) => { router.get('/health', healthCheckHandler);
try {
const isHealthy = await geoIPService.healthCheck();
if (isHealthy) {
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
service: 'Bitip GeoIP Service',
});
} else {
res.status(503).json({
status: 'unhealthy',
timestamp: new Date().toISOString(),
service: 'Bitip GeoIP Service',
error: 'GeoIP service health check failed',
});
}
} catch (error) {
logger.error('Health check failed', error as Error);
res.status(503).json({
status: 'unhealthy',
timestamp: new Date().toISOString(),
service: 'Bitip GeoIP Service',
error: 'Health check endpoint failed',
});
}
});
// Get app version // Get app version
router.get('/version', (_req: Request, res: Response): void => { router.get('/version', (_req: Request, res: Response): void => {