-
anne.matthes authoredanne.matthes authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
createApp.js 2.91 KiB
// Ignore paths for logging, metrics and docs
const ignorePaths = ['/ready', '/healthy']
// Fast, minimalist web framework for node.
const express = require('express')
// Helmet helps you secure your Express app by setting various HTTP headers
const helmet = require('helmet')
// Very low overhead Node.js logger. Logs in json use pino-pretty for dev.
const logger = require('pino')()
// Fastest HTTP logger for Node.js in town
const httpLogger = require('pino-http')({ logger, autoLogging: { ignorePaths } })
// Readiness and liveness checks middleware
const health = require('@cloudnative/health-connect')
// Prometheus middleware for standard api metrics
const apiMetrics = require('prometheus-api-metrics')
// Swagger UI for api-docs
const swaggerUi = require('swagger-ui-express')
const yaml = require('js-yaml')
const fs = require('fs')
const fetch = require('node-fetch')
const swaggerDocument = yaml.load(fs.readFileSync('./swagger.yaml', 'utf8'))
module.exports = () => {
// The app returned by express() is in fact a JavaScript Function, designed to be passed to Node’s HTTP servers as a callback to handle requests.
const app = express()
const healthCheck = new health.HealthChecker()
// Application-level middleware
app.use(httpLogger)
app.use(helmet())
app.use('/healthy', health.LivenessEndpoint(healthCheck))
app.use('/ready', health.ReadinessEndpoint(healthCheck))
app.use(apiMetrics({ excludeRoutes: ignorePaths }))
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument))
app.use('/swagger.json', (req, res) => res.json(swaggerDocument))
const isReady = async () => {
await fetchManifest()
if (manifestCache.length === 0) throw new Error('No manifests found.')
}
const readinessCheck = new health.ReadinessCheck('readinessCheck', isReady)
healthCheck.registerReadinessCheck(readinessCheck)
const livenessCheck = new health.LivenessCheck('livenessCheck', isReady)
healthCheck.registerLivenessCheck(livenessCheck)
const urls = yaml.load(fs.readFileSync('./config/manifests/urls.yaml', 'utf8')).manifests
let manifestCache = []
let lastCached
app.timeout = 30000
const fetchManifest = async () => {
if (+new Date() < lastCached + (app.timeout || 30000)) return
const results = urls.map(url => fetch(url).then(result => {
if (!result.ok) throw new Error(`Failed to load manifest for url ${result.url} (Status: ${result.status}: ${result.statusText})`)
return result.json().catch(err => { throw new Error(`Failed to load manifest for url ${result.url}: ${err}`) })
}))
manifestCache = (await Promise.all(results)).flat()
lastCached = +new Date()
}
app.get('/api/manifest.json', async (req, res, next) => {
try {
await fetchManifest()
res.json(manifestCache || [])
} catch (err) {
next(err)
}
})
app.use(function (err, req, res, next) {
logger.error(err)
res.status(500).end()
})
return app
}