import { Injectable } from '@nestjs/common';
import { DatabaseService } from '../../infrastructure/database/database.service';
import { ProductionConfigService } from '../../common/config/production-config.service';
@Injectable()
export class HealthService {
constructor(
private readonly database:DatabaseService,
private readonly config:ProductionConfigService,
) {}
live() {
return {
status:'ok',
service:'gastro-os-backend',
version:process.env.APP_VERSION??'development',
commit:process.env.GIT_SHA??null,
};
}
async ready() {
await this.database.ping();
const [outbox]=await Promise.all([
this.database.query<any>(
`select count(*)::int as pending,
extract(epoch from (now()-min(created_at)))::int as oldest_seconds
from outbox_events
where processed_at is null`,
).catch(()=>[{pending:0,oldest_seconds:null}]),
]);
const db=this.database.poolStats();
const config=this.config.readiness();
const reasons:string[]=[];
if(db.waiting>Number(process.env.READINESS_DB_WAITING_MAX??10)) reasons.push('database_pool_saturated');
if(Number(outbox[0]?.pending??0)>Number(process.env.READINESS_OUTBOX_PENDING_MAX??50000)) reasons.push('outbox_backlog');
if(config.production && (!config.oidcConfigured || config.localAuthEnabled)) reasons.push('production_auth_invalid');
return {
status:reasons.length?'degraded':'ok',
database:{status:'ok',pool:db},
outbox:{
pending:Number(outbox[0]?.pending??0),
oldestSeconds:outbox[0]?.oldest_seconds==null?null:Number(outbox[0].oldest_seconds),
},
configuration:config,
reasons,
};
}
}
