Source: plugin.js

/*
 * Copyright 2018-2024 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict'

/**
 * Plugin:
 * this module exports the plugin as a function.
 * @module plugin
 */

const payloadOK = { statusCode: 200, status: 'ok' }
const payloadKO = { statusCode: 500, status: 'ko' }

/**
 * Plugin implementation.
 *
 * @param {!object} fastify Fastify instance
 * @param {!object} options plugin configuration options
 * <ul>
 *     <li>healthcheckUrl (string, default `/health`) for the route of the health check,</li>
 *     <li>healthcheckUrlDisable (boolean, default false) flag to disable health check route,</li>
 *     <li>healthcheckUrlAlwaysFail (boolean, default false) flag to always return failure from health check route (mainly for testing purposes),</li>
 *     <li>exposeUptime (boolean, default false) flag to show even process uptime in health check results,</li>
 *     <li>underPressureOptions (object, default empty) for options to send directly to under-pressure,</li>
 *     <li>schemaOptions (object, default empty) for options to use for route schema,</li>
 *     <li>configOptions (object, default empty) for options to use for route config,</li>
 * </ul>
 * @param {!function} done callback, to call as last step
 *
 * @namespace
 */
function fastifyHealthcheck (fastify, options, done) {
  const {
    healthcheckUrl = '/health',
    healthcheckUrlDisable = false,
    healthcheckUrlAlwaysFail = false,
    exposeUptime = false,
    underPressureOptions = { },
    schemaOptions,
    configOptions
  } = options

  ensureIsString(healthcheckUrl, 'healthcheckUrl')
  ensureIsBoolean(healthcheckUrlDisable, 'healthcheckUrlDisable')
  ensureIsBoolean(healthcheckUrlAlwaysFail, 'healthcheckUrlAlwaysFail')
  ensureIsBoolean(exposeUptime, 'exposeUptime')
  ensureIsObject(underPressureOptions, 'underPressureOptions')
  if (schemaOptions) {
    ensureIsObject(schemaOptions, 'schemaOptions')
  }
  if (configOptions) {
    ensureIsObject(configOptions, 'configOptions')
  }

  // execute plugin code

  // register under-pressure plugin
  // note that it will trigger automatic failure responses
  // in all routes defined here when current values are higher
  // that threshold values set
  fastify.register(require('@fastify/under-pressure'), underPressureOptions)

  let healthcheckHandler = normalHandler
  if (exposeUptime === true) {
    healthcheckHandler = normalHandlerWithUptime
  }
  if (healthcheckUrlAlwaysFail !== null && healthcheckUrlAlwaysFail === true) {
    healthcheckHandler = failHandler
  }

  if (healthcheckUrlDisable === null || healthcheckUrlDisable === false) {
    fastify.route({
      method: 'GET',
      url: healthcheckUrl,
      handler: healthcheckHandler,
      schema: schemaOptions,
      config: configOptions
    })
  }

  done()
}

/**
 * Handler that generates a failure response.
 *
 * @param {!req} req the request
 * @param {!reply} reply the response
 *
 * @private
 */
function failHandler (req, reply) {
  reply.code(500).send(payloadKO)
}

/**
 * Handler that generates a success response.
 *
 * @param {!req} req the request
 * @param {!reply} reply the response
 *
 * @private
 */
function normalHandler (req, reply) {
  reply.code(200).send(payloadOK)
}

/**
 * Handler that generates a success response, and show even process uptime.
 *
 * @param {!req} req the request
 * @param {!reply} reply the response
 *
 * @private
 */
function normalHandlerWithUptime (req, reply) {
  reply.code(200).send({ ...payloadOK, uptime: process.uptime() })
}

// utility functions

function ensureIsString (arg, name) {
  if (arg !== null && typeof arg !== 'string') {
    throw new TypeError(`The argument '${name}' must be a string, instead got a '${typeof arg}'`)
  }
}

function ensureIsBoolean (arg, name) {
  if (arg !== null && typeof arg !== 'boolean') {
    throw new TypeError(`The argument '${name}' must be a boolean, instead got a '${typeof arg}'`)
  }
}

function ensureIsObject (arg, name) {
  if (arg !== null && typeof arg !== 'object') {
    throw new TypeError(`The argument '${name}' must be a object, instead got a '${typeof arg}'`)
  }
}

// not using fastify-plugin, to fully encapsulate under-pressure plugin
module.exports = fastifyHealthcheck