From 305f91c6bd957991e7d2e31b797516ddf6ea8bda Mon Sep 17 00:00:00 2001 From: Steve Korshakov Date: Tue, 26 Aug 2025 20:23:00 -0700 Subject: [PATCH] feat: healthcheck --- deploy/handy.yaml | 20 +++++++++++++++++++- sources/app/api.ts | 23 ++++++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/deploy/handy.yaml b/deploy/handy.yaml index 49b78c9..d4537db 100644 --- a/deploy/handy.yaml +++ b/deploy/handy.yaml @@ -25,15 +25,33 @@ spec: - name: handy image: docker.korshakov.com/handy-server:{version} ports: - - containerPort: 3001 + - containerPort: 3005 env: - name: NODE_ENV value: production + - name: PORT + value: "3005" - name: REDIS_URL value: redis://happy-redis:6379 envFrom: - secretRef: name: handy-secrets + livenessProbe: + httpGet: + path: /health + port: 3005 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /health + port: 3005 + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 --- apiVersion: policy/v1 kind: PodDisruptionBudget diff --git a/sources/app/api.ts b/sources/app/api.ts index abcf8f1..9467a1c 100644 --- a/sources/app/api.ts +++ b/sources/app/api.ts @@ -64,7 +64,7 @@ export async function startApi(): Promise<{ app: FastifyInstance; io: Server }> logger: true, bodyLimit: 1024 * 1024 * 100, // 100MB }); - app.register(require('@fastify/cors'), { + app.register(import('@fastify/cors'), { origin: '*', allowedHeaders: '*', methods: ['GET', 'POST'] @@ -72,6 +72,27 @@ export async function startApi(): Promise<{ app: FastifyInstance; io: Server }> app.get('/', function (request, reply) { reply.send('Welcome to Happy Server!'); }); + + // Health check endpoint + app.get('/health', async (request, reply) => { + try { + // Test database connectivity + await db.$queryRaw`SELECT 1`; + reply.send({ + status: 'ok', + timestamp: new Date().toISOString(), + service: 'happy-server' + }); + } catch (error) { + log({ module: 'health', level: 'error' }, `Health check failed: ${error}`); + reply.code(503).send({ + status: 'error', + timestamp: new Date().toISOString(), + service: 'happy-server', + error: 'Database connectivity failed' + }); + } + }); app.setValidatorCompiler(validatorCompiler); app.setSerializerCompiler(serializerCompiler); const typed = app.withTypeProvider();