2022-11-13 13:34:09 +01:00
|
|
|
import Fastify, { type FastifyReply, type FastifyRequest, type HookHandlerDoneFunction } from 'fastify'
|
2022-11-16 22:19:08 +01:00
|
|
|
import { readFileSync } from 'fs'
|
|
|
|
import { isAuthenticated } from '../../security'
|
2022-11-14 01:20:53 +01:00
|
|
|
import type { HttpAdapter, HttpResponse, HttpServer } from './api'
|
2022-11-16 22:19:08 +01:00
|
|
|
import fastifyAuth from '@fastify/auth'
|
|
|
|
import fastifyBasicAuth from '@fastify/basic-auth'
|
2022-11-07 22:31:17 +01:00
|
|
|
|
|
|
|
function adaptResponseToReply(response: HttpResponse, reply: FastifyReply): void {
|
|
|
|
if (response.statusCode) {
|
|
|
|
void reply.code(response.statusCode)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (response.headers) {
|
|
|
|
void reply.headers(response.headers)
|
|
|
|
}
|
2022-11-09 19:08:24 +01:00
|
|
|
|
|
|
|
if (response.body) {
|
|
|
|
void reply.send(response.body)
|
|
|
|
}
|
2022-11-07 22:31:17 +01:00
|
|
|
}
|
|
|
|
|
2022-11-13 13:34:33 +01:00
|
|
|
function formatCombinedLog(request: FastifyRequest, reply: FastifyReply): string {
|
|
|
|
const remoteAddress = [request.socket.remoteAddress, request.socket.remotePort].filter((v) => v != null).join(':')
|
|
|
|
const userAgent = request.headers['user-agent'] || ''
|
|
|
|
const contentType = request.headers['content-type'] || ''
|
|
|
|
return `${remoteAddress} - "${request.method} ${request.url} HTTP/${request.raw.httpVersion}" ${reply.statusCode} "${request.protocol}://${request.hostname}" "${userAgent}" "${contentType}"`
|
|
|
|
}
|
|
|
|
|
2022-11-16 22:19:08 +01:00
|
|
|
function createFastify(server: HttpServer): ReturnType<typeof Fastify> {
|
|
|
|
const config = { logger: false }
|
|
|
|
|
|
|
|
if (server.config.tls_cert_file && server.config.tls_key_file) {
|
|
|
|
server.log?.debug('Running with TLS enabled')
|
|
|
|
return Fastify({
|
|
|
|
...config,
|
|
|
|
https: {
|
|
|
|
key: readFileSync(server.config.tls_key_file),
|
|
|
|
cert: readFileSync(server.config.tls_cert_file),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return Fastify({
|
|
|
|
...config,
|
2022-11-09 19:08:24 +01:00
|
|
|
serverFactory: server.serverFactory,
|
2022-11-07 22:31:17 +01:00
|
|
|
})
|
2022-11-16 22:19:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export const fastifyServe: HttpAdapter = async (server: HttpServer) => {
|
|
|
|
const fastify = createFastify(server)
|
|
|
|
|
|
|
|
if (server.config.basic_auth && Object.keys(server.config.basic_auth).length > 0) {
|
|
|
|
const users = server.config.basic_auth
|
|
|
|
|
|
|
|
const validate = async (username: string, password: string) => {
|
|
|
|
if (!(await isAuthenticated(username, password, users))) {
|
|
|
|
throw new Error('Unauthorized')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
await fastify.register(fastifyAuth)
|
|
|
|
await fastify.register(fastifyBasicAuth, { validate, authenticate: true })
|
|
|
|
|
|
|
|
fastify.after(() => {
|
|
|
|
fastify.addHook('preHandler', fastify.auth([fastify.basicAuth]))
|
|
|
|
})
|
|
|
|
}
|
2022-11-07 22:31:17 +01:00
|
|
|
|
2022-11-13 13:34:33 +01:00
|
|
|
fastify.addHook('onResponse', (request: FastifyRequest, reply: FastifyReply) => {
|
|
|
|
if (reply.statusCode >= 400) {
|
|
|
|
server.log?.warn(formatCombinedLog(request, reply))
|
2022-11-14 01:20:53 +01:00
|
|
|
} else if (server.config.debug) {
|
2022-11-13 13:34:33 +01:00
|
|
|
server.log?.debug(formatCombinedLog(request, reply))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2022-11-07 22:31:17 +01:00
|
|
|
fastify.addHook('onRequest', (request: FastifyRequest, reply: FastifyReply, next: HookHandlerDoneFunction) => {
|
|
|
|
const response = server.onRequest()
|
|
|
|
|
|
|
|
if (response) {
|
|
|
|
adaptResponseToReply(response, reply)
|
|
|
|
}
|
|
|
|
|
|
|
|
next()
|
|
|
|
})
|
|
|
|
|
|
|
|
fastify.setErrorHandler(async (error, request: FastifyRequest, reply: FastifyReply) => {
|
|
|
|
adaptResponseToReply(server.onError(error), reply)
|
|
|
|
})
|
|
|
|
|
|
|
|
fastify.setNotFoundHandler(async (request: FastifyRequest, reply: FastifyReply) => {
|
|
|
|
adaptResponseToReply(server.onNotFound(), reply)
|
|
|
|
})
|
|
|
|
|
|
|
|
fastify.get('/metrics', async (request: FastifyRequest, reply: FastifyReply) => {
|
|
|
|
adaptResponseToReply(server.onMetrics(), reply)
|
|
|
|
})
|
|
|
|
|
2022-11-14 01:20:53 +01:00
|
|
|
await fastify.listen({ port: server.config.port, host: '::' })
|
2022-11-07 22:31:17 +01:00
|
|
|
|
|
|
|
return {
|
|
|
|
shutdown() {
|
|
|
|
void fastify.close()
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|