feat: implement comprehensive health check endpoints for API, Auth, and Realtime services to monitor database, Redis, MinIO, and InfluxDB connectivity.

This commit is contained in:
Giuseppe Raffa
2026-04-04 12:10:00 +02:00
parent ba6941fd2a
commit b31a04b1a7
10 changed files with 106 additions and 8 deletions

View File

@@ -16,10 +16,19 @@ app.get('/', (req, res) => {
res.redirect('/health'); res.redirect('/health');
}); });
app.get('/health', (req, res) => { app.get('/health', async (req, res) => {
const postgres = await require('./storage/postgres').checkPostgres();
const influx = await require('./storage/influx').checkInflux();
const minio = await require('./storage/minio').checkMinio();
const allOk = Object.values(postgres).every(s => s === 'connected') && influx && minio;
res.json({ res.json({
status: "ok", status: allOk ? "ok" : "degraded",
service: "api", service: "api",
databases: postgres,
influx: influx ? 'connected' : 'disconnected',
minio: minio ? 'connected' : 'disconnected',
version: version, version: version,
build_number: vBuild, build_number: vBuild,
version_state: vState version_state: vState

View File

@@ -59,9 +59,19 @@ async function query(bucket, relativeTime, measurement, sensor, field) {
} }
async function checkInflux() {
try {
await querying.rows(`from(bucket: "boat") |> range(start: -1s) |> limit(n:1)`).next();
return true;
} catch (error) {
return false;
}
}
module.exports = { module.exports = {
write:append, write:append,
writeBatch, writeBatch,
query query,
checkInflux
} }

View File

@@ -123,6 +123,15 @@ async function getFileStream(bucket, objectName) {
return dataStream; return dataStream;
} }
async function checkMinio() {
try {
await client.listBuckets();
return true;
} catch (error) {
return false;
}
}
module.exports = { module.exports = {
bucketExists, bucketExists,
getBuckets, getBuckets,
@@ -132,5 +141,6 @@ module.exports = {
removeObject, removeObject,
upload, upload,
download, download,
getFileStream getFileStream,
checkMinio
} }

View File

@@ -70,10 +70,24 @@ async function remove(table, condition, params, type = 'users') {
const sql = `DELETE FROM ${table} WHERE ${condition}`; const sql = `DELETE FROM ${table} WHERE ${condition}`;
return await query(sql, params, type); return await query(sql, params, type);
} }
async function checkPostgres() {
const status = {};
for (const [name, pool] of Object.entries(pools)) {
try {
await pool.query('SELECT NOW()');
status[name] = 'connected';
} catch (error) {
status[name] = 'disconnected';
}
}
return status;
}
module.exports = { module.exports = {
query, query,
append, append,
remove, remove,
getClient, getClient,
checkPostgres,
pools pools
}; };

View File

@@ -35,6 +35,22 @@ app.set('view engine', 'html');
const authRoutes = require('./routes/auth'); const authRoutes = require('./routes/auth');
app.use('/', authRoutes); app.use('/', authRoutes);
app.get('/health', async (req, res) => {
const dbConnected = await database.checkPostgres();
const redisHelper = require('./storage/redis');
const redisConnected = await redisHelper.checkRedis();
res.json({
status: dbConnected && redisConnected ? "ok" : "degraded",
service: "auth",
database: dbConnected ? "connected" : "disconnected",
redis: redisConnected ? "connected" : "disconnected",
version: version,
build_number: vBuild,
version_state: vState
});
});
// Startup // Startup
async function start() { async function start() {
await database.initDb(); await database.initDb();

View File

@@ -97,9 +97,19 @@ async function initDb() {
`); `);
} }
async function checkPostgres() {
try {
await pool.query('SELECT NOW()');
return true;
} catch (error) {
return false;
}
}
module.exports = { module.exports = {
pool, pool,
query, query,
getClient, getClient,
initDb initDb,
checkPostgres
}; };

View File

@@ -33,4 +33,16 @@ function connected() {
return redis.status === 'ready'; return redis.status === 'ready';
} }
module.exports = { redis, configure, connected }; async function checkRedis() {
try {
if (redis.status !== 'ready') {
await redis.connect().catch(() => {});
}
await redis.ping();
return true;
} catch (err) {
return false;
}
}
module.exports = { redis, configure, connected, checkRedis };

View File

@@ -12,8 +12,10 @@ const pool = new Pool({
async function checkDB() { async function checkDB() {
try { try {
await pool.query('SELECT NOW()'); await pool.query('SELECT NOW()');
return true;
} catch (error) { } catch (error) {
console.error('Database connection failed:', error); console.error('Database connection failed:', error);
return false;
} }
} }

View File

@@ -65,6 +65,15 @@ async function getWatcherCount(sensorId) {
return parseInt(count) || 0; return parseInt(count) || 0;
} }
async function checkRedis() {
try {
await redis.ping();
return true;
} catch (error) {
return false;
}
}
module.exports = { module.exports = {
setSession, setSession,
getSession, getSession,
@@ -74,6 +83,7 @@ module.exports = {
addWatcher, addWatcher,
removeWatcher, removeWatcher,
getWatcherCount, getWatcherCount,
checkRedis,
redis, redis,
redisSub redisSub
}; };

View File

@@ -22,9 +22,14 @@ app.get('/', (req, res) => {
res.redirect('/health'); res.redirect('/health');
}); });
app.get('/health', (req, res) => { app.get('/health', async (req, res) => {
const dbConnected = await require('./helper/authdb').checkDB();
const redisConnected = await redisHelper.checkRedis();
res.status(200).send({ res.status(200).send({
status: 'OK', status: dbConnected && redisConnected ? 'OK' : 'DEGRADED',
database: dbConnected ? 'connected' : 'disconnected',
redis: redisConnected ? 'connected' : 'disconnected',
service: 'realtime', service: 'realtime',
version: process.env.VERSION, version: process.env.VERSION,
build: process.env.VERSION_BUILD, build: process.env.VERSION_BUILD,