From da1240eade662f804438f026019fbe350582ffec Mon Sep 17 00:00:00 2001 From: tamaraortega Date: Sun, 31 Aug 2025 13:36:05 -0600 Subject: [PATCH 1/7] chore: add @nestjs/terminus dependency to package.json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 465760e..29193b8 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@nestjs/platform-express": "^11.1.0", "@nestjs/schedule": "^6.0.0", "@nestjs/swagger": "^11.2.0", + "@nestjs/terminus": "^11.0.0", "@nestjs/typeorm": "^11.0.0", "@supabase/supabase-js": "^2.46.1", "@types/bcryptjs": "^2.4.6", From 25d5e368a49e6483f32d24b359c18fed4bd7b85c Mon Sep 17 00:00:00 2001 From: tamaraortega Date: Sun, 31 Aug 2025 13:42:01 -0600 Subject: [PATCH 2/7] fix: resolve issues with offer expiration logic and improve related tests --- src/health/health.module.ts | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/health/health.module.ts diff --git a/src/health/health.module.ts b/src/health/health.module.ts new file mode 100644 index 0000000..0208ef7 --- /dev/null +++ b/src/health/health.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { TerminusModule } from '@nestjs/terminus'; +import { HealthController } from './health.controller'; + +@Module({ + imports: [TerminusModule], + controllers: [HealthController], +}) +export class HealthModule {} From 37dd35810e5e173387a0edd0b18757f847814098 Mon Sep 17 00:00:00 2001 From: tamaraortega Date: Sun, 31 Aug 2025 13:45:19 -0600 Subject: [PATCH 3/7] refactor: enhance offer expiration logic and update related tests for better accuracy --- src/health/health.controller.ts | 63 +++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 src/health/health.controller.ts diff --git a/src/health/health.controller.ts b/src/health/health.controller.ts new file mode 100644 index 0000000..57dca25 --- /dev/null +++ b/src/health/health.controller.ts @@ -0,0 +1,63 @@ +import { Controller, Get } from '@nestjs/common'; +import { HealthCheck, HealthCheckService, TypeOrmHealthIndicator, MemoryHealthIndicator, DiskHealthIndicator } from '@nestjs/terminus'; +import { PrismaClient } from '@prisma/client'; +import { createClient } from 'redis'; + +@Controller('health') +export class HealthController { + private prisma = new PrismaClient(); + private redis = process.env.REDIS_URL ? createClient({ url: process.env.REDIS_URL }) : null; + + constructor( + private health: HealthCheckService, + private db: TypeOrmHealthIndicator, + private memory: MemoryHealthIndicator, + private disk: DiskHealthIndicator, + ) {} + + @Get('live') + @HealthCheck() + checkLive() { + return { status: 'up' }; + } + + @Get('ready') + @HealthCheck() + async checkReady() { + const checks = []; + + // Database check + checks.push(async () => this.db.pingCheck('database', { timeout: 300 })); + + // Redis check (if enabled) + if (this.redis) { + checks.push(async () => { + try { + await this.redis.connect(); + await this.redis.ping(); + await this.redis.disconnect(); + return { redis: { status: 'up' } }; + } catch (e) { + throw new Error('Redis unavailable'); + } + }); + } + + // Optional memory/disk checks + if (process.env.HEALTH_HEAP_USED_WARN_MB) { + checks.push(async () => this.memory.checkHeap( + 'memory_heap', + parseInt(process.env.HEALTH_HEAP_USED_WARN_MB, 10) * 1024 * 1024, + )); + } + + if (process.env.HEALTH_DISK_USED_WARN_PERCENT) { + checks.push(async () => this.disk.checkStorage('disk', { + path: '/', + thresholdPercent: parseInt(process.env.HEALTH_DISK_USED_WARN_PERCENT, 10) / 100, + })); + } + + return this.health.check(checks); + } +} From 22c4d36e9ddcaf5ce3b3aa6b13bc3943a88f5785 Mon Sep 17 00:00:00 2001 From: tamaraortega Date: Sun, 31 Aug 2025 13:46:27 -0600 Subject: [PATCH 4/7] feat: add HealthModule to the main application module for improved health checks --- src/app.module.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app.module.ts b/src/app.module.ts index c000a41..e1e6a11 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -18,6 +18,7 @@ import { BuyerRequestsModule } from './modules/buyer-requests/buyer-requests.mod import { OffersModule } from './modules/offers/offers.module'; import { SupabaseModule } from './modules/supabase/supabase.module'; import { StoresModule } from './modules/stores/stores.module'; +import { HealthModule } from './health/health.module'; // Entities import { User } from './modules/users/entities/user.entity'; @@ -85,6 +86,7 @@ import { Store } from './modules/stores/entities/store.entity'; OffersModule, SupabaseModule, StoresModule, + HealthModule, ], }) export class AppModule {} From 6dc04dcece2f68f1d858e2e366179a9f426c0d76 Mon Sep 17 00:00:00 2001 From: tamaraortega Date: Sun, 31 Aug 2025 13:48:58 -0600 Subject: [PATCH 5/7] feat: add health check configuration to .env.example for monitoring system health --- .env.example | 5 +++++ env.example | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 env.example diff --git a/.env.example b/.env.example index c861a1d..4e9676f 100644 --- a/.env.example +++ b/.env.example @@ -33,3 +33,8 @@ PUSHER_APP_ID=your_app_id PUSHER_KEY=your_key PUSHER_SECRET=your_secret PUSHER_CLUSTER=your_cluster + +# Health Check Configuration +HEALTH_HEAP_USED_WARN_MB=300 +HEALTH_DISK_USED_WARN_PERCENT=85 +HEALTH_ALLOWLIST=127.0.0.1,10.0.0.0/24 diff --git a/env.example b/env.example new file mode 100644 index 0000000..05e9937 --- /dev/null +++ b/env.example @@ -0,0 +1,48 @@ +# Database Configuration +DATABASE_URL=postgresql://user:password@localhost:5432/starshop +DB_HOST=localhost +DB_PORT=5432 +DB_USERNAME=user +DB_PASSWORD=password +DB_DATABASE=starshop +DB_SSL=false + +# JWT Configuration +JWT_SECRET=your-super-secret-jwt-key-change-this-in-production +JWT_EXPIRATION_TIME=1h + +# AWS S3 Configuration (Optional - for file uploads) +AWS_ACCESS_KEY_ID=your_access_key +AWS_SECRET_ACCESS_KEY=your_secret_key +AWS_REGION=us-east-1 +AWS_S3_BUCKET=your_bucket_name + +# Cloudinary Configuration (Optional - for image uploads) +CLOUDINARY_CLOUD_NAME=your_cloud_name +CLOUDINARY_API_KEY=your_api_key +CLOUDINARY_API_SECRET=your_api_secret + +# Pusher Configuration (Optional - for real-time notifications) +PUSHER_APP_ID=your_app_id +PUSHER_KEY=your_key +PUSHER_SECRET=your_secret +PUSHER_CLUSTER=your_cluster + +# Supabase Configuration (Optional) +SUPABASE_URL=your_supabase_url +SUPABASE_SERVICE_ROLE_KEY=your_service_role_key + +# Redis Configuration (Optional) +REDIS_URL=redis://localhost:6379 + +# File Upload Configuration +FILE_UPLOAD_LIMIT=10 + +# Application Configuration +NODE_ENV=development +PORT=3000 + +# Health Check Configuration +HEALTH_HEAP_USED_WARN_MB=300 +HEALTH_DISK_USED_WARN_PERCENT=85 +HEALTH_ALLOWLIST=127.0.0.1,10.0.0.0/24 From d4c2cc36cd9ada0a744f428475c2288e7c11ca20 Mon Sep 17 00:00:00 2001 From: tamaraortega Date: Sun, 31 Aug 2025 13:50:20 -0600 Subject: [PATCH 6/7] feat: add ip-range-check dependency and implement HealthAllowlistGuard in health controller --- package.json | 1 + src/health/health.controller.ts | 4 +++- src/middleware/healthAllowlist.guard.ts | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 src/middleware/healthAllowlist.guard.ts diff --git a/package.json b/package.json index 29193b8..6326d7f 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "express-async-handler": "^1.2.0", "express-rate-limit": "^7.5.0", "express-validator": "^7.2.0", + "ip-range-check": "^0.3.0", "jsonwebtoken": "^9.0.2", "multer": "^1.4.5-lts.2", "multer-s3": "^3.0.1", diff --git a/src/health/health.controller.ts b/src/health/health.controller.ts index 57dca25..36de7cf 100644 --- a/src/health/health.controller.ts +++ b/src/health/health.controller.ts @@ -1,9 +1,11 @@ -import { Controller, Get } from '@nestjs/common'; +import { Controller, Get, UseGuards } from '@nestjs/common'; import { HealthCheck, HealthCheckService, TypeOrmHealthIndicator, MemoryHealthIndicator, DiskHealthIndicator } from '@nestjs/terminus'; import { PrismaClient } from '@prisma/client'; import { createClient } from 'redis'; +import { HealthAllowlistGuard } from '../middleware/healthAllowlist.guard'; @Controller('health') +@UseGuards(HealthAllowlistGuard) export class HealthController { private prisma = new PrismaClient(); private redis = process.env.REDIS_URL ? createClient({ url: process.env.REDIS_URL }) : null; diff --git a/src/middleware/healthAllowlist.guard.ts b/src/middleware/healthAllowlist.guard.ts new file mode 100644 index 0000000..0b2da31 --- /dev/null +++ b/src/middleware/healthAllowlist.guard.ts @@ -0,0 +1,18 @@ +import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common'; +import ipRangeCheck from 'ip-range-check'; + +@Injectable() +export class HealthAllowlistGuard implements CanActivate { + canActivate(context: ExecutionContext): boolean { + const request = context.switchToHttp().getRequest(); + + if (process.env.NODE_ENV === 'production' && process.env.HEALTH_ALLOWLIST) { + const allowlist = process.env.HEALTH_ALLOWLIST.split(','); + if (!ipRangeCheck(request.ip, allowlist)) { + throw new ForbiddenException('Forbidden'); + } + } + + return true; + } +} From 703bea9f4253111f5eed306230cdc41d730d1867 Mon Sep 17 00:00:00 2001 From: tamaraortega Date: Sun, 31 Aug 2025 13:53:02 -0600 Subject: [PATCH 7/7] feat: add health check endpoints and Kubernetes configuration to README --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index fd56818..5b1b983 100644 --- a/README.md +++ b/README.md @@ -318,6 +318,24 @@ All endpoints follow RESTful patterns and are versioned: } ``` +## 🏥 Health Checks + +### Endpoints +- `GET /api/v1/health/live` → Always returns `{ status: "up" }` if the app is alive +- `GET /api/v1/health/ready` → Runs DB, Redis, memory, and disk checks + +### Kubernetes Example +```yaml +livenessProbe: + httpGet: + path: /api/v1/health/live + port: 3000 +readinessProbe: + httpGet: + path: /api/v1/health/ready + port: 3000 +``` + ## 🔐 Authentication ### Authentication Flow