mirror of
https://github.com/Shadowfita/docmost.git
synced 2025-11-13 00:02:30 +10:00
refactor health module
This commit is contained in:
@ -43,6 +43,7 @@
|
||||
"@nestjs/passport": "^10.0.3",
|
||||
"@nestjs/platform-fastify": "^10.3.9",
|
||||
"@nestjs/platform-socket.io": "^10.3.9",
|
||||
"@nestjs/terminus": "^10.2.3",
|
||||
"@nestjs/websockets": "^10.3.9",
|
||||
"@react-email/components": "0.0.19",
|
||||
"@react-email/render": "^0.0.15",
|
||||
|
||||
@ -16,7 +16,15 @@ export class TransformHttpResponseInterceptor<T>
|
||||
intercept(
|
||||
context: ExecutionContext,
|
||||
next: CallHandler<T>,
|
||||
): Observable<Response<T>> {
|
||||
): Observable<Response<T> | any> {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const path = request.url;
|
||||
|
||||
// Skip interceptor for the /api/health path
|
||||
if (path === '/api/health') {
|
||||
return next.handle();
|
||||
}
|
||||
|
||||
return next.handle().pipe(
|
||||
map((data) => {
|
||||
const status = context.switchToHttp().getResponse().statusCode;
|
||||
|
||||
@ -34,7 +34,10 @@ export class CoreModule implements NestModule {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply(DomainMiddleware)
|
||||
.exclude({ path: 'auth/setup', method: RequestMethod.POST })
|
||||
.exclude(
|
||||
{ path: 'auth/setup', method: RequestMethod.POST },
|
||||
{ path: 'health', method: RequestMethod.GET },
|
||||
)
|
||||
.forRoutes('*');
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,37 +1,22 @@
|
||||
import { KyselyDB } from '@docmost/db/types/kysely.types';
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
InternalServerErrorException,
|
||||
Logger,
|
||||
} from '@nestjs/common';
|
||||
import { sql } from 'kysely';
|
||||
import { InjectKysely } from 'nestjs-kysely';
|
||||
import { Redis } from 'ioredis';
|
||||
import { EnvironmentService } from '../environment/environment.service';
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { HealthCheck, HealthCheckService } from '@nestjs/terminus';
|
||||
import { PostgresHealthIndicator } from './postgres.health';
|
||||
import { RedisHealthIndicator } from './redis.health';
|
||||
|
||||
@Controller()
|
||||
@Controller('health')
|
||||
export class HealthController {
|
||||
constructor(
|
||||
@InjectKysely() private readonly db: KyselyDB,
|
||||
private environmentService: EnvironmentService,
|
||||
private health: HealthCheckService,
|
||||
private postgres: PostgresHealthIndicator,
|
||||
private redis: RedisHealthIndicator,
|
||||
) {}
|
||||
|
||||
private readonly logger = new Logger(HealthController.name);
|
||||
|
||||
@Get('health')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
async health() {
|
||||
try {
|
||||
const redis = new Redis(this.environmentService.getRedisUrl());
|
||||
|
||||
await sql`SELECT 1=1`.execute(this.db);
|
||||
await redis.ping();
|
||||
} catch (error) {
|
||||
this.logger.error('Health check failed');
|
||||
throw new InternalServerErrorException();
|
||||
}
|
||||
@Get()
|
||||
@HealthCheck()
|
||||
async check() {
|
||||
return this.health.check([
|
||||
() => this.postgres.pingCheck('database'),
|
||||
() => this.redis.pingCheck('redis'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,13 @@
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { HealthController } from './health.controller';
|
||||
import { TerminusModule } from '@nestjs/terminus';
|
||||
import { PostgresHealthIndicator } from './postgres.health';
|
||||
import { RedisHealthIndicator } from './redis.health';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
controllers: [HealthController],
|
||||
providers: [PostgresHealthIndicator, RedisHealthIndicator],
|
||||
imports: [TerminusModule],
|
||||
})
|
||||
export class HealthModule {}
|
||||
|
||||
38
apps/server/src/integrations/health/postgres.health.ts
Normal file
38
apps/server/src/integrations/health/postgres.health.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { InjectKysely } from 'nestjs-kysely';
|
||||
import {
|
||||
HealthCheckError,
|
||||
HealthIndicator,
|
||||
HealthIndicatorResult,
|
||||
} from '@nestjs/terminus';
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { sql } from 'kysely';
|
||||
import { KyselyDB } from '@docmost/db/types/kysely.types';
|
||||
|
||||
@Injectable()
|
||||
export class PostgresHealthIndicator extends HealthIndicator {
|
||||
private readonly logger = new Logger(PostgresHealthIndicator.name);
|
||||
|
||||
constructor(@InjectKysely() private readonly db: KyselyDB) {
|
||||
super();
|
||||
}
|
||||
|
||||
async pingCheck(key: string): Promise<HealthIndicatorResult> {
|
||||
let isHealthy = false;
|
||||
|
||||
try {
|
||||
await sql`SELECT 1=1`.execute(this.db);
|
||||
isHealthy = true;
|
||||
} catch (e) {
|
||||
this.logger.error(JSON.stringify(e));
|
||||
}
|
||||
|
||||
if (isHealthy) {
|
||||
return this.getStatus(key, isHealthy);
|
||||
} else {
|
||||
throw new HealthCheckError(
|
||||
`${key} is not available`,
|
||||
this.getStatus(key, isHealthy),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
41
apps/server/src/integrations/health/redis.health.ts
Normal file
41
apps/server/src/integrations/health/redis.health.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import {
|
||||
HealthCheckError,
|
||||
HealthIndicator,
|
||||
HealthIndicatorResult,
|
||||
} from '@nestjs/terminus';
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { EnvironmentService } from '../environment/environment.service';
|
||||
import { Redis } from 'ioredis';
|
||||
|
||||
@Injectable()
|
||||
export class RedisHealthIndicator extends HealthIndicator {
|
||||
private readonly logger = new Logger(RedisHealthIndicator.name);
|
||||
|
||||
constructor(private environmentService: EnvironmentService) {
|
||||
super();
|
||||
}
|
||||
|
||||
async pingCheck(key: string): Promise<HealthIndicatorResult> {
|
||||
let isHealthy = false;
|
||||
|
||||
try {
|
||||
const redis = new Redis(this.environmentService.getRedisUrl(), {
|
||||
maxRetriesPerRequest: 15,
|
||||
});
|
||||
|
||||
await redis.ping();
|
||||
isHealthy = true;
|
||||
} catch (e) {
|
||||
this.logger.error(e);
|
||||
}
|
||||
|
||||
if (isHealthy) {
|
||||
return this.getStatus(key, isHealthy);
|
||||
} else {
|
||||
throw new HealthCheckError(
|
||||
`${key} is not available`,
|
||||
this.getStatus(key, isHealthy),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -38,7 +38,8 @@ async function bootstrap() {
|
||||
.addHook('preHandler', function (req, reply, done) {
|
||||
if (
|
||||
req.originalUrl.startsWith('/api') &&
|
||||
!req.originalUrl.startsWith('/api/auth/setup')
|
||||
!req.originalUrl.startsWith('/api/auth/setup') &&
|
||||
!req.originalUrl.startsWith('/api/health')
|
||||
) {
|
||||
if (!req.raw?.['workspaceId']) {
|
||||
throw new NotFoundException('Workspace not found');
|
||||
|
||||
Reference in New Issue
Block a user