mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 07:10:57 +00:00
fix: normalized the region and logged the otp properly
This commit is contained in:
108
apps/edr-freight-api/src/modules/health/health.controller.ts
Normal file
108
apps/edr-freight-api/src/modules/health/health.controller.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
// health.controller.ts
|
||||
|
||||
import { Controller, Get, HttpStatus, Res } from "@nestjs/common";
|
||||
import { ApiOperation, ApiTags } from "@nestjs/swagger";
|
||||
import { InjectDataSource } from "@nestjs/typeorm";
|
||||
import { Public } from "@edr/api-common";
|
||||
import { Response } from "express";
|
||||
import { DataSource } from "typeorm";
|
||||
|
||||
import { EmailClientService } from "../notifications/email-client.service";
|
||||
import { SmsClientService } from "../notifications/sms-client.service";
|
||||
|
||||
type CheckStatus = "ok" | "error" | "unknown";
|
||||
|
||||
/**
|
||||
* Readiness normally stays green when only the broker is down.
|
||||
*
|
||||
* A 503 pulls the pod out of the load balancer, which would take booking,
|
||||
* tracking and billing offline because SMS is unreachable — a strictly worse
|
||||
* outcome than degraded notifications. The broker check is therefore reported,
|
||||
* not enforced, and `READINESS_REQUIRES_BROKER=true` opts into hard-failing for
|
||||
* deployments where a silent OTP black hole is the greater risk.
|
||||
*/
|
||||
const READINESS_REQUIRES_BROKER =
|
||||
process.env.READINESS_REQUIRES_BROKER === "true";
|
||||
|
||||
@ApiTags("Health")
|
||||
@Controller("health")
|
||||
export class HealthController {
|
||||
constructor(
|
||||
@InjectDataSource()
|
||||
private readonly dataSource: DataSource,
|
||||
private readonly smsClient: SmsClientService,
|
||||
private readonly emailClient: EmailClientService,
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
@Public()
|
||||
@ApiOperation({ summary: "Liveness probe" })
|
||||
liveness() {
|
||||
return { status: "ok", timestamp: new Date().toISOString() };
|
||||
}
|
||||
|
||||
@Get("ready")
|
||||
@Public()
|
||||
@ApiOperation({
|
||||
summary:
|
||||
"Readiness probe — database plus SMS/email broker connectivity. Broker failures report as degraded unless READINESS_REQUIRES_BROKER=true.",
|
||||
})
|
||||
async readiness(@Res() res: Response) {
|
||||
const startedAt = Date.now();
|
||||
|
||||
let database: { status: CheckStatus; latencyMs: number; error?: string };
|
||||
try {
|
||||
await this.dataSource.query("SELECT 1");
|
||||
database = { status: "ok", latencyMs: Date.now() - startedAt };
|
||||
} catch (error) {
|
||||
database = {
|
||||
status: "error",
|
||||
latencyMs: Date.now() - startedAt,
|
||||
error: error instanceof Error ? error.message : "Unknown error",
|
||||
};
|
||||
}
|
||||
|
||||
// `null` from the client means the connection manager was not reachable
|
||||
// through Nest's internals — surfaced as "unknown" so a shape change in
|
||||
// @nestjs/microservices degrades to honest ignorance, not a false "ok".
|
||||
const toStatus = (connected: boolean | null): CheckStatus =>
|
||||
connected === null ? "unknown" : connected ? "ok" : "error";
|
||||
|
||||
const broker = {
|
||||
sms: { status: toStatus(this.smsClient.brokerConnected) },
|
||||
email: { status: toStatus(this.emailClient.brokerConnected) },
|
||||
// Every OTP, and every booking/billing notification, publishes through
|
||||
// these. `error` here means codes are being generated and silently dropped.
|
||||
enabled: process.env.RABBITMQ_ENABLED !== "false",
|
||||
};
|
||||
|
||||
const brokerDown =
|
||||
broker.sms.status === "error" || broker.email.status === "error";
|
||||
const failed =
|
||||
database.status === "error" ||
|
||||
(READINESS_REQUIRES_BROKER && brokerDown);
|
||||
|
||||
const status = failed ? "error" : brokerDown ? "degraded" : "ok";
|
||||
|
||||
return res
|
||||
.status(failed ? HttpStatus.SERVICE_UNAVAILABLE : HttpStatus.OK)
|
||||
.json({
|
||||
status,
|
||||
timestamp: new Date().toISOString(),
|
||||
checks: { database, broker },
|
||||
});
|
||||
}
|
||||
|
||||
@Get("info")
|
||||
@Public()
|
||||
@ApiOperation({ summary: "App info — version, environment, uptime" })
|
||||
info() {
|
||||
return {
|
||||
name: "edr-freight-api",
|
||||
version: process.env.npm_package_version ?? "1.0.0",
|
||||
environment: process.env.NODE_ENV ?? "development",
|
||||
uptimeSeconds: Math.floor(process.uptime()),
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
14
apps/edr-freight-api/src/modules/health/health.module.ts
Normal file
14
apps/edr-freight-api/src/modules/health/health.module.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
// health.module.ts
|
||||
|
||||
import { Module } from "@nestjs/common";
|
||||
|
||||
import { HealthController } from "./health.controller";
|
||||
import { NotificationsModule } from "../notifications/notifications.module";
|
||||
|
||||
@Module({
|
||||
// NotificationsModule exports the SMS/email clients; the readiness probe reads
|
||||
// their broker connection state rather than opening a second connection.
|
||||
imports: [NotificationsModule],
|
||||
controllers: [HealthController],
|
||||
})
|
||||
export class HealthModule {}
|
||||
Reference in New Issue
Block a user