- Docker: Multi-stage Dockerfile with security hardening, docker-compose for production and development environments - Environment: Comprehensive .env.example with all config options, lib/config/env.ts for typed environment validation - Logging: Structured JSON logging with request/response middleware - Monitoring: Prometheus metrics endpoint, Grafana dashboard, health checks (liveness/readiness probes) - Security: Security headers, rate limiting, CORS middleware - CI/CD: GitHub Actions workflows for CI, production deploy, and preview deployments - Error tracking: Sentry integration foundation Files created: - Docker: Dockerfile, docker-compose.yml, docker-compose.dev.yml, .dockerignore - Config: lib/config/env.ts, lib/config/index.ts - Logging: lib/logging/logger.ts, lib/logging/middleware.ts - Monitoring: lib/monitoring/sentry.ts, lib/monitoring/metrics.ts, lib/monitoring/health.ts - Security: lib/security/headers.ts, lib/security/rateLimit.ts, lib/security/cors.ts - API: pages/api/health/*, pages/api/metrics.ts - Infra: infra/prometheus/prometheus.yml, infra/grafana/*
65 lines
1.7 KiB
TypeScript
65 lines
1.7 KiB
TypeScript
/**
|
|
* Readiness Probe Endpoint
|
|
* Agent 4: Production Deployment
|
|
*
|
|
* GET /api/health/ready
|
|
* Returns readiness status for Kubernetes/container orchestration.
|
|
* Used to determine if the application is ready to receive traffic.
|
|
*/
|
|
|
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
import { healthChecks } from '../../../lib/monitoring';
|
|
|
|
interface ReadyResponse {
|
|
ready: boolean;
|
|
checks: Array<{
|
|
name: string;
|
|
status: string;
|
|
}>;
|
|
timestamp: string;
|
|
}
|
|
|
|
interface ErrorResponse {
|
|
ready: boolean;
|
|
error: string;
|
|
timestamp: string;
|
|
}
|
|
|
|
export default async function handler(
|
|
req: NextApiRequest,
|
|
res: NextApiResponse<ReadyResponse | ErrorResponse>
|
|
) {
|
|
if (req.method !== 'GET') {
|
|
res.setHeader('Allow', ['GET']);
|
|
return res.status(405).json({
|
|
ready: false,
|
|
error: `Method ${req.method} is not allowed`,
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
}
|
|
|
|
try {
|
|
const health = await healthChecks.checkReadiness();
|
|
const isReady = health.status === 'healthy' || health.status === 'degraded';
|
|
|
|
// Add cache headers - don't cache readiness checks
|
|
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
|
|
res.setHeader('Pragma', 'no-cache');
|
|
res.setHeader('Expires', '0');
|
|
|
|
return res.status(isReady ? 200 : 503).json({
|
|
ready: isReady,
|
|
checks: health.checks.map((c) => ({
|
|
name: c.name,
|
|
status: c.status,
|
|
})),
|
|
timestamp: health.timestamp,
|
|
});
|
|
} catch (error) {
|
|
return res.status(503).json({
|
|
ready: false,
|
|
error: error instanceof Error ? error.message : 'Unknown error',
|
|
timestamp: new Date().toISOString(),
|
|
});
|
|
}
|
|
}
|