From b31a04b1a766159a47eb9f959cf9b08c49019357 Mon Sep 17 00:00:00 2001 From: Giuseppe Raffa <77052701+sesee3@users.noreply.github.com> Date: Sat, 4 Apr 2026 12:10:00 +0200 Subject: [PATCH] feat: implement comprehensive health check endpoints for API, Auth, and Realtime services to monitor database, Redis, MinIO, and InfluxDB connectivity. --- api/src/index.js | 13 +++++++++++-- api/src/storage/influx.js | 12 +++++++++++- api/src/storage/minio.js | 12 +++++++++++- api/src/storage/postgres.js | 14 ++++++++++++++ auth/src/index.js | 16 ++++++++++++++++ auth/src/storage/database.js | 12 +++++++++++- auth/src/storage/redis.js | 14 +++++++++++++- realtime/src/helper/authdb.js | 2 ++ realtime/src/helper/redis.js | 10 ++++++++++ realtime/src/index.js | 9 +++++++-- 10 files changed, 106 insertions(+), 8 deletions(-) diff --git a/api/src/index.js b/api/src/index.js index 74867e0..94af1b4 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -16,10 +16,19 @@ app.get('/', (req, res) => { 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({ - status: "ok", + status: allOk ? "ok" : "degraded", service: "api", + databases: postgres, + influx: influx ? 'connected' : 'disconnected', + minio: minio ? 'connected' : 'disconnected', version: version, build_number: vBuild, version_state: vState diff --git a/api/src/storage/influx.js b/api/src/storage/influx.js index eca99d8..1101ecf 100644 --- a/api/src/storage/influx.js +++ b/api/src/storage/influx.js @@ -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 = { write:append, writeBatch, - query + query, + checkInflux } diff --git a/api/src/storage/minio.js b/api/src/storage/minio.js index 75c012b..fc016b8 100644 --- a/api/src/storage/minio.js +++ b/api/src/storage/minio.js @@ -123,6 +123,15 @@ async function getFileStream(bucket, objectName) { return dataStream; } +async function checkMinio() { + try { + await client.listBuckets(); + return true; + } catch (error) { + return false; + } +} + module.exports = { bucketExists, getBuckets, @@ -132,5 +141,6 @@ module.exports = { removeObject, upload, download, - getFileStream + getFileStream, + checkMinio } \ No newline at end of file diff --git a/api/src/storage/postgres.js b/api/src/storage/postgres.js index da3c7f2..c4c1f10 100644 --- a/api/src/storage/postgres.js +++ b/api/src/storage/postgres.js @@ -70,10 +70,24 @@ async function remove(table, condition, params, type = 'users') { const sql = `DELETE FROM ${table} WHERE ${condition}`; 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 = { query, append, remove, getClient, + checkPostgres, pools }; \ No newline at end of file diff --git a/auth/src/index.js b/auth/src/index.js index 43ccd2e..93af68a 100644 --- a/auth/src/index.js +++ b/auth/src/index.js @@ -35,6 +35,22 @@ app.set('view engine', 'html'); const authRoutes = require('./routes/auth'); 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 async function start() { await database.initDb(); diff --git a/auth/src/storage/database.js b/auth/src/storage/database.js index 422d514..681a2e6 100644 --- a/auth/src/storage/database.js +++ b/auth/src/storage/database.js @@ -97,9 +97,19 @@ async function initDb() { `); } +async function checkPostgres() { + try { + await pool.query('SELECT NOW()'); + return true; + } catch (error) { + return false; + } +} + module.exports = { pool, query, getClient, - initDb + initDb, + checkPostgres }; diff --git a/auth/src/storage/redis.js b/auth/src/storage/redis.js index b0680b9..86a1095 100644 --- a/auth/src/storage/redis.js +++ b/auth/src/storage/redis.js @@ -32,5 +32,17 @@ async function configure() { function connected() { return redis.status === 'ready'; } + +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 }; \ No newline at end of file +module.exports = { redis, configure, connected, checkRedis }; \ No newline at end of file diff --git a/realtime/src/helper/authdb.js b/realtime/src/helper/authdb.js index aa2dd61..2de4512 100644 --- a/realtime/src/helper/authdb.js +++ b/realtime/src/helper/authdb.js @@ -12,8 +12,10 @@ const pool = new Pool({ async function checkDB() { try { await pool.query('SELECT NOW()'); + return true; } catch (error) { console.error('Database connection failed:', error); + return false; } } diff --git a/realtime/src/helper/redis.js b/realtime/src/helper/redis.js index 80fecbe..955c325 100644 --- a/realtime/src/helper/redis.js +++ b/realtime/src/helper/redis.js @@ -65,6 +65,15 @@ async function getWatcherCount(sensorId) { return parseInt(count) || 0; } +async function checkRedis() { + try { + await redis.ping(); + return true; + } catch (error) { + return false; + } +} + module.exports = { setSession, getSession, @@ -74,6 +83,7 @@ module.exports = { addWatcher, removeWatcher, getWatcherCount, + checkRedis, redis, redisSub }; \ No newline at end of file diff --git a/realtime/src/index.js b/realtime/src/index.js index 956c3d3..bb80eb6 100644 --- a/realtime/src/index.js +++ b/realtime/src/index.js @@ -22,9 +22,14 @@ app.get('/', (req, res) => { 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({ - status: 'OK', + status: dbConnected && redisConnected ? 'OK' : 'DEGRADED', + database: dbConnected ? 'connected' : 'disconnected', + redis: redisConnected ? 'connected' : 'disconnected', service: 'realtime', version: process.env.VERSION, build: process.env.VERSION_BUILD,