From 06d7e51f6330fc01cdf0a3716a3e0d8e31316f69 Mon Sep 17 00:00:00 2001 From: Francesco Spilla Date: Mon, 10 Feb 2025 14:35:46 +0100 Subject: [PATCH] chore(repo) init AppHealth module --- apps/ebitemp-api/src/app/app.module.ts | 2 + .../app/modules/health/health.controller.ts | 28 +++++ .../src/app/modules/health/health.module.ts | 11 ++ apps/ebitemp-api/src/main.ts | 9 +- package.json | 1 + pnpm-lock.yaml | 104 +++++++++++++++++- 6 files changed, 151 insertions(+), 4 deletions(-) create mode 100644 apps/ebitemp-api/src/app/modules/health/health.controller.ts create mode 100644 apps/ebitemp-api/src/app/modules/health/health.module.ts diff --git a/apps/ebitemp-api/src/app/app.module.ts b/apps/ebitemp-api/src/app/app.module.ts index 363f9f0..e765a5f 100644 --- a/apps/ebitemp-api/src/app/app.module.ts +++ b/apps/ebitemp-api/src/app/app.module.ts @@ -6,12 +6,14 @@ import { AppConfigModule } from './modules/config/config.module'; import { AppDatabaseModule } from './modules/database/database.module'; import { AppFileTransactionsModule } from './modules/file-transactions/file-transactions.module'; import { AppLoggerModule } from './modules/logger/logger.module'; +import { AppHealthModule } from './modules/health/health.module'; @Module({ imports: [ AppConfigModule, AppLoggerModule, AppDatabaseModule, + AppHealthModule, AppFileTransactionsModule, ], controllers: [AppController], diff --git a/apps/ebitemp-api/src/app/modules/health/health.controller.ts b/apps/ebitemp-api/src/app/modules/health/health.controller.ts new file mode 100644 index 0000000..732b77b --- /dev/null +++ b/apps/ebitemp-api/src/app/modules/health/health.controller.ts @@ -0,0 +1,28 @@ +import { Controller, Get } from '@nestjs/common'; +import { + DiskHealthIndicator, + HealthCheck, + HealthCheckService, + MemoryHealthIndicator, + TypeOrmHealthIndicator, +} from '@nestjs/terminus'; + +@Controller('health') +export class HealthController { + constructor( + private readonly health: HealthCheckService, + private readonly db: TypeOrmHealthIndicator, + private readonly disk: DiskHealthIndicator, + private readonly memory: MemoryHealthIndicator + ) {} + + @Get() + @HealthCheck() + check() { + return this.health.check([ + () => this.db.pingCheck('database'), + () => this.disk.checkStorage('storage', { path: '/', thresholdPercent: 0.5 }), // Threshold at 50% space occupied in path, + () => this.memory.checkHeap('memory', 150 * 1024 * 1024), // Threshold at 150MB memory occupied by process + ]); + } +} diff --git a/apps/ebitemp-api/src/app/modules/health/health.module.ts b/apps/ebitemp-api/src/app/modules/health/health.module.ts new file mode 100644 index 0000000..edc6d3d --- /dev/null +++ b/apps/ebitemp-api/src/app/modules/health/health.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { TerminusModule } from '@nestjs/terminus'; +import { HealthController } from './health.controller'; + +@Module({ + imports: [TerminusModule.forRoot({ + errorLogStyle: 'pretty', + })], + controllers: [HealthController], +}) +export class AppHealthModule {} diff --git a/apps/ebitemp-api/src/main.ts b/apps/ebitemp-api/src/main.ts index 177d862..ddea903 100644 --- a/apps/ebitemp-api/src/main.ts +++ b/apps/ebitemp-api/src/main.ts @@ -1,4 +1,4 @@ -import { Logger } from '@nestjs/common'; +import { Logger, RequestMethod } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { FastifyAdapter, @@ -24,8 +24,11 @@ async function bootstrap() { AppModule, new FastifyAdapter(appOpts) ); - const globalPrefix = 'api'; - app.setGlobalPrefix(globalPrefix); + const globalPrefix = 'v1'; + app.setGlobalPrefix(globalPrefix, { + exclude: [{ path: 'health', method: RequestMethod.GET }], + }); + app.enableShutdownHooks(); const config = app.get(localConfig.KEY); const port = config.port || 3000; diff --git a/package.json b/package.json index 1f72c9e..eab73b7 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@nestjs/core": "^11.0.8", "@nestjs/platform-express": "^11.0.8", "@nestjs/platform-fastify": "^11.0.8", + "@nestjs/terminus": "^11.0.0", "@nestjs/typeorm": "^11.0.0", "@nx/devkit": "20.4.2", "axios": "^1.7.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a177843..2bf33d1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ dependencies: '@nestjs/platform-fastify': specifier: ^11.0.8 version: 11.0.8(@nestjs/common@11.0.8)(@nestjs/core@11.0.8) + '@nestjs/terminus': + specifier: ^11.0.0 + version: 11.0.0(@nestjs/common@11.0.8)(@nestjs/core@11.0.8)(@nestjs/typeorm@11.0.0)(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20) '@nestjs/typeorm': specifier: ^11.0.0 version: 11.0.0(@nestjs/common@11.0.8)(@nestjs/core@11.0.8)(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20) @@ -2547,6 +2550,64 @@ packages: - chokidar dev: true + /@nestjs/terminus@11.0.0(@nestjs/common@11.0.8)(@nestjs/core@11.0.8)(@nestjs/typeorm@11.0.0)(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20): + resolution: {integrity: sha512-c55LOo9YGovmQHtFUMa/vDaxGZ2cglMTZejqgHREaApt/GArTfgYYGwhRXPLq8ZwiQQlLuYB+79e9iA8mlDSLA==} + peerDependencies: + '@grpc/grpc-js': '*' + '@grpc/proto-loader': '*' + '@mikro-orm/core': '*' + '@mikro-orm/nestjs': '*' + '@nestjs/axios': ^2.0.0 || ^3.0.0 || ^4.0.0 + '@nestjs/common': ^10.0.0 || ^11.0.0 + '@nestjs/core': ^10.0.0 || ^11.0.0 + '@nestjs/microservices': ^10.0.0 || ^11.0.0 + '@nestjs/mongoose': ^11.0.0 + '@nestjs/sequelize': ^10.0.0 || ^11.0.0 + '@nestjs/typeorm': ^10.0.0 || ^11.0.0 + '@prisma/client': '*' + mongoose: '*' + reflect-metadata: 0.1.x || 0.2.x + rxjs: 7.x + sequelize: '*' + typeorm: '*' + peerDependenciesMeta: + '@grpc/grpc-js': + optional: true + '@grpc/proto-loader': + optional: true + '@mikro-orm/core': + optional: true + '@mikro-orm/nestjs': + optional: true + '@nestjs/axios': + optional: true + '@nestjs/microservices': + optional: true + '@nestjs/mongoose': + optional: true + '@nestjs/sequelize': + optional: true + '@nestjs/typeorm': + optional: true + '@prisma/client': + optional: true + mongoose: + optional: true + sequelize: + optional: true + typeorm: + optional: true + dependencies: + '@nestjs/common': 11.0.8(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/core': 11.0.8(@nestjs/common@11.0.8)(@nestjs/platform-express@11.0.8)(reflect-metadata@0.2.2)(rxjs@7.8.1) + '@nestjs/typeorm': 11.0.0(@nestjs/common@11.0.8)(@nestjs/core@11.0.8)(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20) + boxen: 5.1.2 + check-disk-space: 3.4.0 + reflect-metadata: 0.2.2 + rxjs: 7.8.1 + typeorm: 0.3.20(mssql@11.0.1)(ts-node@10.9.2) + dev: false + /@nestjs/testing@11.0.8(@nestjs/common@11.0.8)(@nestjs/core@11.0.8)(@nestjs/platform-express@11.0.8): resolution: {integrity: sha512-5Reqec4MQSm4nFasKE5Pd799cAx3MmjkweF17Wgj/EJNWhFgVdv6N9OUIWXbU8nc8Pjso1fJmv0KJyN6h51qOA==} peerDependencies: @@ -4233,6 +4294,12 @@ packages: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 + /ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + dependencies: + string-width: 4.2.3 + dev: false + /ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} @@ -4654,6 +4721,20 @@ packages: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} dev: true + /boxen@5.1.2: + resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==} + engines: {node: '>=10'} + dependencies: + ansi-align: 3.0.1 + camelcase: 6.3.0 + chalk: 4.1.2 + cli-boxes: 2.2.1 + string-width: 4.2.3 + type-fest: 0.20.2 + widest-line: 3.1.0 + wrap-ansi: 7.0.0 + dev: false + /brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: @@ -4799,7 +4880,6 @@ packages: /camelcase@6.3.0: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - dev: true /caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} @@ -4855,6 +4935,11 @@ packages: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} dev: true + /check-disk-space@3.4.0: + resolution: {integrity: sha512-drVkSqfwA+TvuEhFipiR1OC9boEGZL5RrWvVsOthdcvQNXyCCuKkEiTOTXZ7qxSf/GLwq4GvzfrQD/Wz325hgw==} + engines: {node: '>=16'} + dev: false + /chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -4890,6 +4975,11 @@ packages: resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} dev: true + /cli-boxes@2.2.1: + resolution: {integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==} + engines: {node: '>=6'} + dev: false + /cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -10704,6 +10794,11 @@ packages: engines: {node: '>=4'} dev: true + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: false + /type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} @@ -11313,6 +11408,13 @@ packages: dependencies: isexe: 2.0.0 + /widest-line@3.1.0: + resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==} + engines: {node: '>=8'} + dependencies: + string-width: 4.2.3 + dev: false + /wildcard@2.0.1: resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} dev: true