From 0e5a44d0325e51b54afa9cddba7fe00c17ed96d8 Mon Sep 17 00:00:00 2001 From: James Chartrand Date: Wed, 8 May 2024 18:40:15 -0400 Subject: [PATCH] add healthcheck --- healthcheck.js | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/app.js | 21 +++++++++++++- src/config.js | 2 +- 3 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 healthcheck.js diff --git a/healthcheck.js b/healthcheck.js new file mode 100644 index 0000000..e179299 --- /dev/null +++ b/healthcheck.js @@ -0,0 +1,79 @@ +import nodemailer from 'nodemailer' +import axios from 'axios' + +const serviceURL = process.env.HEALTH_CHECK_SERVICE_URL +const serviceName = process.env.HEALTH_CHECK_SERVICE_NAME +const shouldPostToWebHook = process.env.HEALTH_CHECK_WEB_HOOK +const shouldSendEmail = + process.env.HEALTH_CHECK_SMTP_HOST && + process.env.HEALTH_CHECK_SMTP_USER && + process.env.HEALTH_CHECK_SMTP_PASS && + process.env.HEALTH_CHECK_EMAIL_FROM && + process.env.HEALTH_CHECK_EMAIL_RECIPIENT + +axios + .get(serviceURL) + .then(async function (response) { + try { + const body = response.data + if (body.healthy === true) { + process.exit(0) + } + await notify(`${serviceName} is unhealthy: ${body.error}`) + process.exit(1) + } catch (error) { + await notify( + `${serviceName} is potentially unhealthy - error: ${JSON.stringify(error)}` + ) + process.exit(1) + } + }) + .catch(async (error) => { + await notify( + `${serviceName} is unhealthy and will restart after 3 tries. Error: ${error.message}` + ) + process.exit(1) + }) + +async function notify(message) { + console.log(message) + if (shouldSendEmail) await sendMail(message) + if (shouldPostToWebHook) await postToWebHook(message) +} + +async function postToWebHook(text) { + await axios + .post(process.env.HEALTH_CHECK_WEB_HOOK, { text }) + .catch((error) => { + console.error(error) + }) +} + +async function sendMail(message) { + const messageParams = { + from: process.env.HEALTH_CHECK_EMAIL_FROM, + to: process.env.HEALTH_CHECK_EMAIL_RECIPIENT, + subject: process.env.HEALTH_CHECK_EMAIL_SUBJECT, + text: message + } + + const mailTransport = { + host: process.env.HEALTH_CHECK_SMTP_HOST, + auth: { + user: process.env.HEALTH_CHECK_SMTP_USER, + pass: process.env.HEALTH_CHECK_SMTP_PASS + }, + ...(process.env.HEALTH_CHECK_SMTP_PORT && { + port: process.env.HEALTH_CHECK_SMTP_PORT + }) + } + + const transporter = nodemailer.createTransport(mailTransport) + + try { + await transporter.sendMail(messageParams) + } catch (error) { + console.log('the email send error: ') + console.log(error) + } +} diff --git a/src/app.js b/src/app.js index c145fc4..6b7899d 100644 --- a/src/app.js +++ b/src/app.js @@ -6,7 +6,8 @@ import errorHandler from './middleware/errorHandler.js' import errorLogger from './middleware/errorLogger.js' import invalidPathHandler from './middleware/invalidPathHandler.js' import verifyAuthHeader from './verifyAuthHeader.js' -import { getConfig } from './config.js' +import { getConfig, defaultTenantName } from './config.js' +import { getUnsignedVC } from './test-fixtures/vc.js' function IssuingException (code, message, error = null) { this.code = code @@ -27,6 +28,24 @@ export async function build (opts = {}) { app.use(express.urlencoded({ extended: false })) app.use(cors()) + app.get('/healthz', async function (req, res) { + try { + const { data } = await axios.post( + `${req.protocol}://${req.headers.host}/instance/${defaultTenantName}/credentials/issue`, + getUnsignedVC() + ) + if (!data.proof) + throw new SigningException(503, 'issuer-coordinator healthz failed') + } catch (e) { + console.log(`exception in healthz: ${JSON.stringify(e)}`) + return res.status(503).json({ + error: `issuer-coordinator healthz check failed with error: ${e}`, + healthy: false + }) + } + res.send({ message: 'issuer-coordinator server status: ok.', healthy: true }) + }) + app.get('/', async function (req, res, next) { if (enableStatusService) { try { diff --git a/src/config.js b/src/config.js index 62fe589..bc77880 100644 --- a/src/config.js +++ b/src/config.js @@ -1,6 +1,6 @@ let CONFIG const defaultPort = 4005 -const defaultTenantName = 'test' +export const defaultTenantName = 'test' const demoTenantName = 'testing' const randomTenantName = 'random' const randtomTenantToken = 'UNPROTECTED'