- POST /auth/login, /auth/refresh, /auth/logout, /auth/select-tenant, /auth/change-password, GET /auth/tenants — wired to packages/auth - JwtAuthGuard + DomainExceptionFilter (401/403 without leaking internals) - LoginRateLimitGuard: Redis-backed 5/min per IP and per email (agente.md secao 149), safe across multiple API instances - helmet + restrictive cors (deny-by-default) + global rate limit - GET /health, /health/live, /health/ready checking Postgres and Redis - changePassword() added to packages/auth for the mustChangePassword flow - fixed REDIS_HOST/POSTGRES_HOST docker-compose-only hostnames not resolving from the host process; added REDIS_URL for host-side use - verified end-to-end with curl: login, wrong password / unknown email (same generic error), authenticated route, missing token, refresh rotation, logout revocation, and the 429 rate limit kicking in after 5 attempts
41 lines
912 B
TypeScript
41 lines
912 B
TypeScript
import { Controller, Get, HttpException, HttpStatus } from "@nestjs/common";
|
|
import { getPrismaClient } from "@b2bcall/database";
|
|
import { getRedisClient } from "../common/redis";
|
|
|
|
@Controller("health")
|
|
export class HealthController {
|
|
@Get()
|
|
root() {
|
|
return { status: "ok" };
|
|
}
|
|
|
|
@Get("live")
|
|
live() {
|
|
return { status: "ok" };
|
|
}
|
|
|
|
@Get("ready")
|
|
async ready() {
|
|
const checks: Record<string, "ok" | "fail"> = { postgres: "ok", redis: "ok" };
|
|
|
|
try {
|
|
await getPrismaClient().$queryRaw`SELECT 1`;
|
|
} catch {
|
|
checks.postgres = "fail";
|
|
}
|
|
|
|
try {
|
|
await getRedisClient().ping();
|
|
} catch {
|
|
checks.redis = "fail";
|
|
}
|
|
|
|
const healthy = Object.values(checks).every((v) => v === "ok");
|
|
if (!healthy) {
|
|
throw new HttpException({ status: "unhealthy", checks }, HttpStatus.SERVICE_UNAVAILABLE);
|
|
}
|
|
|
|
return { status: "ok", checks };
|
|
}
|
|
}
|