Projektstart
This commit is contained in:
1138
backend/node_modules/fastify/lib/configValidator.js
generated
vendored
Normal file
1138
backend/node_modules/fastify/lib/configValidator.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
442
backend/node_modules/fastify/lib/contentTypeParser.js
generated
vendored
Normal file
442
backend/node_modules/fastify/lib/contentTypeParser.js
generated
vendored
Normal file
@@ -0,0 +1,442 @@
|
||||
'use strict'
|
||||
|
||||
const { AsyncResource } = require('node:async_hooks')
|
||||
const { FifoMap: Fifo } = require('toad-cache')
|
||||
const { safeParse: safeParseContentType, defaultContentType } = require('fast-content-type-parse')
|
||||
const secureJson = require('secure-json-parse')
|
||||
const {
|
||||
kDefaultJsonParse,
|
||||
kContentTypeParser,
|
||||
kBodyLimit,
|
||||
kRequestPayloadStream,
|
||||
kState,
|
||||
kTestInternals,
|
||||
kReplyIsError,
|
||||
kRouteContext
|
||||
} = require('./symbols')
|
||||
|
||||
const {
|
||||
FST_ERR_CTP_INVALID_TYPE,
|
||||
FST_ERR_CTP_EMPTY_TYPE,
|
||||
FST_ERR_CTP_ALREADY_PRESENT,
|
||||
FST_ERR_CTP_INVALID_HANDLER,
|
||||
FST_ERR_CTP_INVALID_PARSE_TYPE,
|
||||
FST_ERR_CTP_BODY_TOO_LARGE,
|
||||
FST_ERR_CTP_INVALID_MEDIA_TYPE,
|
||||
FST_ERR_CTP_INVALID_CONTENT_LENGTH,
|
||||
FST_ERR_CTP_EMPTY_JSON_BODY,
|
||||
FST_ERR_CTP_INSTANCE_ALREADY_STARTED
|
||||
} = require('./errors')
|
||||
|
||||
function ContentTypeParser (bodyLimit, onProtoPoisoning, onConstructorPoisoning) {
|
||||
this[kDefaultJsonParse] = getDefaultJsonParser(onProtoPoisoning, onConstructorPoisoning)
|
||||
// using a map instead of a plain object to avoid prototype hijack attacks
|
||||
this.customParsers = new Map()
|
||||
this.customParsers.set('application/json', new Parser(true, false, bodyLimit, this[kDefaultJsonParse]))
|
||||
this.customParsers.set('text/plain', new Parser(true, false, bodyLimit, defaultPlainTextParser))
|
||||
this.parserList = [new ParserListItem('application/json'), new ParserListItem('text/plain')]
|
||||
this.parserRegExpList = []
|
||||
this.cache = new Fifo(100)
|
||||
}
|
||||
|
||||
ContentTypeParser.prototype.add = function (contentType, opts, parserFn) {
|
||||
const contentTypeIsString = typeof contentType === 'string'
|
||||
|
||||
if (!contentTypeIsString && !(contentType instanceof RegExp)) throw new FST_ERR_CTP_INVALID_TYPE()
|
||||
if (contentTypeIsString && contentType.length === 0) throw new FST_ERR_CTP_EMPTY_TYPE()
|
||||
if (typeof parserFn !== 'function') throw new FST_ERR_CTP_INVALID_HANDLER()
|
||||
|
||||
if (this.existingParser(contentType)) {
|
||||
throw new FST_ERR_CTP_ALREADY_PRESENT(contentType)
|
||||
}
|
||||
|
||||
if (opts.parseAs !== undefined) {
|
||||
if (opts.parseAs !== 'string' && opts.parseAs !== 'buffer') {
|
||||
throw new FST_ERR_CTP_INVALID_PARSE_TYPE(opts.parseAs)
|
||||
}
|
||||
}
|
||||
|
||||
const parser = new Parser(
|
||||
opts.parseAs === 'string',
|
||||
opts.parseAs === 'buffer',
|
||||
opts.bodyLimit,
|
||||
parserFn
|
||||
)
|
||||
|
||||
if (contentTypeIsString && contentType === '*') {
|
||||
this.customParsers.set('', parser)
|
||||
} else {
|
||||
if (contentTypeIsString) {
|
||||
this.parserList.unshift(new ParserListItem(contentType))
|
||||
} else {
|
||||
contentType.isEssence = contentType.source.indexOf(';') === -1
|
||||
this.parserRegExpList.unshift(contentType)
|
||||
}
|
||||
this.customParsers.set(contentType.toString(), parser)
|
||||
}
|
||||
}
|
||||
|
||||
ContentTypeParser.prototype.hasParser = function (contentType) {
|
||||
return this.customParsers.has(typeof contentType === 'string' ? contentType : contentType.toString())
|
||||
}
|
||||
|
||||
ContentTypeParser.prototype.existingParser = function (contentType) {
|
||||
if (contentType === 'application/json' && this.customParsers.has(contentType)) {
|
||||
return this.customParsers.get(contentType).fn !== this[kDefaultJsonParse]
|
||||
}
|
||||
if (contentType === 'text/plain' && this.customParsers.has(contentType)) {
|
||||
return this.customParsers.get(contentType).fn !== defaultPlainTextParser
|
||||
}
|
||||
|
||||
return this.hasParser(contentType)
|
||||
}
|
||||
|
||||
ContentTypeParser.prototype.getParser = function (contentType) {
|
||||
if (this.hasParser(contentType)) {
|
||||
return this.customParsers.get(contentType)
|
||||
}
|
||||
|
||||
const parser = this.cache.get(contentType)
|
||||
if (parser !== undefined) return parser
|
||||
|
||||
const parsed = safeParseContentType(contentType)
|
||||
|
||||
// dummyContentType always the same object
|
||||
// we can use === for the comparison and return early
|
||||
if (parsed === defaultContentType) {
|
||||
return this.customParsers.get('')
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-var
|
||||
for (var i = 0; i !== this.parserList.length; ++i) {
|
||||
const parserListItem = this.parserList[i]
|
||||
if (compareContentType(parsed, parserListItem)) {
|
||||
const parser = this.customParsers.get(parserListItem.name)
|
||||
// we set request content-type in cache to reduce parsing of MIME type
|
||||
this.cache.set(contentType, parser)
|
||||
return parser
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-var
|
||||
for (var j = 0; j !== this.parserRegExpList.length; ++j) {
|
||||
const parserRegExp = this.parserRegExpList[j]
|
||||
if (compareRegExpContentType(contentType, parsed.type, parserRegExp)) {
|
||||
const parser = this.customParsers.get(parserRegExp.toString())
|
||||
// we set request content-type in cache to reduce parsing of MIME type
|
||||
this.cache.set(contentType, parser)
|
||||
return parser
|
||||
}
|
||||
}
|
||||
|
||||
return this.customParsers.get('')
|
||||
}
|
||||
|
||||
ContentTypeParser.prototype.removeAll = function () {
|
||||
this.customParsers = new Map()
|
||||
this.parserRegExpList = []
|
||||
this.parserList = []
|
||||
this.cache = new Fifo(100)
|
||||
}
|
||||
|
||||
ContentTypeParser.prototype.remove = function (contentType) {
|
||||
if (!(typeof contentType === 'string' || contentType instanceof RegExp)) throw new FST_ERR_CTP_INVALID_TYPE()
|
||||
|
||||
const removed = this.customParsers.delete(contentType.toString())
|
||||
|
||||
const parsers = typeof contentType === 'string' ? this.parserList : this.parserRegExpList
|
||||
|
||||
const idx = parsers.findIndex(ct => ct.toString() === contentType.toString())
|
||||
|
||||
if (idx > -1) {
|
||||
parsers.splice(idx, 1)
|
||||
}
|
||||
|
||||
return removed || idx > -1
|
||||
}
|
||||
|
||||
ContentTypeParser.prototype.run = function (contentType, handler, request, reply) {
|
||||
const parser = this.getParser(contentType)
|
||||
|
||||
if (parser === undefined) {
|
||||
if (request.is404) {
|
||||
handler(request, reply)
|
||||
} else {
|
||||
reply.send(new FST_ERR_CTP_INVALID_MEDIA_TYPE(contentType || undefined))
|
||||
}
|
||||
|
||||
// Early return to avoid allocating an AsyncResource if it's not needed
|
||||
return
|
||||
}
|
||||
|
||||
const resource = new AsyncResource('content-type-parser:run', request)
|
||||
|
||||
if (parser.asString === true || parser.asBuffer === true) {
|
||||
rawBody(
|
||||
request,
|
||||
reply,
|
||||
reply[kRouteContext]._parserOptions,
|
||||
parser,
|
||||
done
|
||||
)
|
||||
} else {
|
||||
const result = parser.fn(request, request[kRequestPayloadStream], done)
|
||||
|
||||
if (result && typeof result.then === 'function') {
|
||||
result.then(body => done(null, body), done)
|
||||
}
|
||||
}
|
||||
|
||||
function done (error, body) {
|
||||
// We cannot use resource.bind() because it is broken in node v12 and v14
|
||||
resource.runInAsyncScope(() => {
|
||||
resource.emitDestroy()
|
||||
if (error) {
|
||||
reply[kReplyIsError] = true
|
||||
reply.send(error)
|
||||
} else {
|
||||
request.body = body
|
||||
handler(request, reply)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function rawBody (request, reply, options, parser, done) {
|
||||
const asString = parser.asString
|
||||
const limit = options.limit === null ? parser.bodyLimit : options.limit
|
||||
const contentLength = request.headers['content-length'] === undefined
|
||||
? NaN
|
||||
: Number(request.headers['content-length'])
|
||||
|
||||
if (contentLength > limit) {
|
||||
// We must close the connection as the client is going
|
||||
// to send this data anyway
|
||||
reply.header('connection', 'close')
|
||||
reply.send(new FST_ERR_CTP_BODY_TOO_LARGE())
|
||||
return
|
||||
}
|
||||
|
||||
let receivedLength = 0
|
||||
let body = asString === true ? '' : []
|
||||
|
||||
const payload = request[kRequestPayloadStream] || request.raw
|
||||
|
||||
if (asString === true) {
|
||||
payload.setEncoding('utf8')
|
||||
}
|
||||
|
||||
payload.on('data', onData)
|
||||
payload.on('end', onEnd)
|
||||
payload.on('error', onEnd)
|
||||
payload.resume()
|
||||
|
||||
function onData (chunk) {
|
||||
receivedLength += chunk.length
|
||||
const { receivedEncodedLength = 0 } = payload
|
||||
// The resulting body length must not exceed bodyLimit (see "zip bomb").
|
||||
// The case when encoded length is larger than received length is rather theoretical,
|
||||
// unless the stream returned by preParsing hook is broken and reports wrong value.
|
||||
if (receivedLength > limit || receivedEncodedLength > limit) {
|
||||
payload.removeListener('data', onData)
|
||||
payload.removeListener('end', onEnd)
|
||||
payload.removeListener('error', onEnd)
|
||||
reply.send(new FST_ERR_CTP_BODY_TOO_LARGE())
|
||||
return
|
||||
}
|
||||
|
||||
if (asString === true) {
|
||||
body += chunk
|
||||
} else {
|
||||
body.push(chunk)
|
||||
}
|
||||
}
|
||||
|
||||
function onEnd (err) {
|
||||
payload.removeListener('data', onData)
|
||||
payload.removeListener('end', onEnd)
|
||||
payload.removeListener('error', onEnd)
|
||||
|
||||
if (err !== undefined) {
|
||||
if (!(typeof err.statusCode === 'number' && err.statusCode >= 400)) {
|
||||
err.statusCode = 400
|
||||
}
|
||||
reply[kReplyIsError] = true
|
||||
reply.code(err.statusCode).send(err)
|
||||
return
|
||||
}
|
||||
|
||||
if (asString === true) {
|
||||
receivedLength = Buffer.byteLength(body)
|
||||
}
|
||||
|
||||
if (!Number.isNaN(contentLength) && (payload.receivedEncodedLength || receivedLength) !== contentLength) {
|
||||
reply.header('connection', 'close')
|
||||
reply.send(new FST_ERR_CTP_INVALID_CONTENT_LENGTH())
|
||||
return
|
||||
}
|
||||
|
||||
if (asString === false) {
|
||||
body = Buffer.concat(body)
|
||||
}
|
||||
|
||||
const result = parser.fn(request, body, done)
|
||||
if (result && typeof result.then === 'function') {
|
||||
result.then(body => done(null, body), done)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getDefaultJsonParser (onProtoPoisoning, onConstructorPoisoning) {
|
||||
return defaultJsonParser
|
||||
|
||||
function defaultJsonParser (req, body, done) {
|
||||
if (body === '' || body == null || (Buffer.isBuffer(body) && body.length === 0)) {
|
||||
return done(new FST_ERR_CTP_EMPTY_JSON_BODY(), undefined)
|
||||
}
|
||||
let json
|
||||
try {
|
||||
json = secureJson.parse(body, { protoAction: onProtoPoisoning, constructorAction: onConstructorPoisoning })
|
||||
} catch (err) {
|
||||
err.statusCode = 400
|
||||
return done(err, undefined)
|
||||
}
|
||||
done(null, json)
|
||||
}
|
||||
}
|
||||
|
||||
function defaultPlainTextParser (req, body, done) {
|
||||
done(null, body)
|
||||
}
|
||||
|
||||
function Parser (asString, asBuffer, bodyLimit, fn) {
|
||||
this.asString = asString
|
||||
this.asBuffer = asBuffer
|
||||
this.bodyLimit = bodyLimit
|
||||
this.fn = fn
|
||||
}
|
||||
|
||||
function buildContentTypeParser (c) {
|
||||
const contentTypeParser = new ContentTypeParser()
|
||||
contentTypeParser[kDefaultJsonParse] = c[kDefaultJsonParse]
|
||||
contentTypeParser.customParsers = new Map(c.customParsers.entries())
|
||||
contentTypeParser.parserList = c.parserList.slice()
|
||||
contentTypeParser.parserRegExpList = c.parserRegExpList.slice()
|
||||
return contentTypeParser
|
||||
}
|
||||
|
||||
function addContentTypeParser (contentType, opts, parser) {
|
||||
if (this[kState].started) {
|
||||
throw new FST_ERR_CTP_INSTANCE_ALREADY_STARTED('addContentTypeParser')
|
||||
}
|
||||
|
||||
if (typeof opts === 'function') {
|
||||
parser = opts
|
||||
opts = {}
|
||||
}
|
||||
|
||||
if (!opts) opts = {}
|
||||
if (!opts.bodyLimit) opts.bodyLimit = this[kBodyLimit]
|
||||
|
||||
if (Array.isArray(contentType)) {
|
||||
contentType.forEach((type) => this[kContentTypeParser].add(type, opts, parser))
|
||||
} else {
|
||||
this[kContentTypeParser].add(contentType, opts, parser)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
function hasContentTypeParser (contentType) {
|
||||
return this[kContentTypeParser].hasParser(contentType)
|
||||
}
|
||||
|
||||
function removeContentTypeParser (contentType) {
|
||||
if (this[kState].started) {
|
||||
throw new FST_ERR_CTP_INSTANCE_ALREADY_STARTED('removeContentTypeParser')
|
||||
}
|
||||
|
||||
if (Array.isArray(contentType)) {
|
||||
for (const type of contentType) {
|
||||
this[kContentTypeParser].remove(type)
|
||||
}
|
||||
} else {
|
||||
this[kContentTypeParser].remove(contentType)
|
||||
}
|
||||
}
|
||||
|
||||
function removeAllContentTypeParsers () {
|
||||
if (this[kState].started) {
|
||||
throw new FST_ERR_CTP_INSTANCE_ALREADY_STARTED('removeAllContentTypeParsers')
|
||||
}
|
||||
|
||||
this[kContentTypeParser].removeAll()
|
||||
}
|
||||
|
||||
function compareContentType (contentType, parserListItem) {
|
||||
if (parserListItem.isEssence) {
|
||||
// we do essence check
|
||||
return contentType.type.indexOf(parserListItem) !== -1
|
||||
} else {
|
||||
// when the content-type includes parameters
|
||||
// we do a full-text search
|
||||
// reject essence content-type before checking parameters
|
||||
if (contentType.type.indexOf(parserListItem.type) === -1) return false
|
||||
for (const key of parserListItem.parameterKeys) {
|
||||
// reject when missing parameters
|
||||
if (!(key in contentType.parameters)) return false
|
||||
// reject when parameters do not match
|
||||
if (contentType.parameters[key] !== parserListItem.parameters[key]) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
function compareRegExpContentType (contentType, essenceMIMEType, regexp) {
|
||||
if (regexp.isEssence) {
|
||||
// we do essence check
|
||||
return regexp.test(essenceMIMEType)
|
||||
} else {
|
||||
// when the content-type includes parameters
|
||||
// we do a full-text match
|
||||
return regexp.test(contentType)
|
||||
}
|
||||
}
|
||||
|
||||
function ParserListItem (contentType) {
|
||||
this.name = contentType
|
||||
// we pre-calculate all the needed information
|
||||
// before content-type comparison
|
||||
const parsed = safeParseContentType(contentType)
|
||||
this.isEssence = contentType.indexOf(';') === -1
|
||||
// we should not allow empty string for parser list item
|
||||
// because it would become a match-all handler
|
||||
if (this.isEssence === false && parsed.type === '') {
|
||||
// handle semicolon or empty string
|
||||
const tmp = contentType.split(';', 1)[0]
|
||||
this.type = tmp === '' ? contentType : tmp
|
||||
} else {
|
||||
this.type = parsed.type
|
||||
}
|
||||
this.parameters = parsed.parameters
|
||||
this.parameterKeys = Object.keys(parsed.parameters)
|
||||
}
|
||||
|
||||
// used in ContentTypeParser.remove
|
||||
ParserListItem.prototype.toString = function () {
|
||||
return this.name
|
||||
}
|
||||
|
||||
module.exports = ContentTypeParser
|
||||
module.exports.helpers = {
|
||||
buildContentTypeParser,
|
||||
addContentTypeParser,
|
||||
hasContentTypeParser,
|
||||
removeContentTypeParser,
|
||||
removeAllContentTypeParsers
|
||||
}
|
||||
module.exports.defaultParsers = {
|
||||
getDefaultJsonParser,
|
||||
defaultTextParser: defaultPlainTextParser
|
||||
}
|
||||
module.exports[kTestInternals] = { rawBody }
|
||||
117
backend/node_modules/fastify/lib/context.js
generated
vendored
Normal file
117
backend/node_modules/fastify/lib/context.js
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
'use strict'
|
||||
|
||||
const {
|
||||
kFourOhFourContext,
|
||||
kReplySerializerDefault,
|
||||
kSchemaErrorFormatter,
|
||||
kErrorHandler,
|
||||
kChildLoggerFactory,
|
||||
kOptions,
|
||||
kReply,
|
||||
kRequest,
|
||||
kBodyLimit,
|
||||
kLogLevel,
|
||||
kContentTypeParser,
|
||||
kRouteByFastify,
|
||||
kRequestCacheValidateFns,
|
||||
kReplyCacheSerializeFns,
|
||||
kPublicRouteContext
|
||||
} = require('./symbols.js')
|
||||
|
||||
// Object that holds the context of every request
|
||||
// Every route holds an instance of this object.
|
||||
function Context ({
|
||||
schema,
|
||||
handler,
|
||||
config,
|
||||
requestIdLogLabel,
|
||||
childLoggerFactory,
|
||||
errorHandler,
|
||||
bodyLimit,
|
||||
logLevel,
|
||||
logSerializers,
|
||||
attachValidation,
|
||||
validatorCompiler,
|
||||
serializerCompiler,
|
||||
replySerializer,
|
||||
schemaErrorFormatter,
|
||||
exposeHeadRoute,
|
||||
prefixTrailingSlash,
|
||||
server,
|
||||
isFastify
|
||||
}) {
|
||||
this.schema = schema
|
||||
this.handler = handler
|
||||
this.Reply = server[kReply]
|
||||
this.Request = server[kRequest]
|
||||
this.contentTypeParser = server[kContentTypeParser]
|
||||
this.onRequest = null
|
||||
this.onSend = null
|
||||
this.onError = null
|
||||
this.onTimeout = null
|
||||
this.preHandler = null
|
||||
this.onResponse = null
|
||||
this.preSerialization = null
|
||||
this.onRequestAbort = null
|
||||
this.config = config
|
||||
this.errorHandler = errorHandler || server[kErrorHandler]
|
||||
this.requestIdLogLabel = requestIdLogLabel || server[kOptions].requestIdLogLabel
|
||||
this.childLoggerFactory = childLoggerFactory || server[kChildLoggerFactory]
|
||||
this._middie = null
|
||||
this._parserOptions = {
|
||||
limit: bodyLimit || server[kBodyLimit]
|
||||
}
|
||||
this.exposeHeadRoute = exposeHeadRoute
|
||||
this.prefixTrailingSlash = prefixTrailingSlash
|
||||
this.logLevel = logLevel || server[kLogLevel]
|
||||
this.logSerializers = logSerializers
|
||||
this[kFourOhFourContext] = null
|
||||
this.attachValidation = attachValidation
|
||||
this[kReplySerializerDefault] = replySerializer
|
||||
this.schemaErrorFormatter =
|
||||
schemaErrorFormatter ||
|
||||
server[kSchemaErrorFormatter] ||
|
||||
defaultSchemaErrorFormatter
|
||||
this[kRouteByFastify] = isFastify
|
||||
|
||||
this[kRequestCacheValidateFns] = null
|
||||
this[kReplyCacheSerializeFns] = null
|
||||
this.validatorCompiler = validatorCompiler || null
|
||||
this.serializerCompiler = serializerCompiler || null
|
||||
|
||||
// Route + Userland configurations for the route
|
||||
this[kPublicRouteContext] = getPublicRouteContext(this)
|
||||
|
||||
this.server = server
|
||||
}
|
||||
|
||||
function getPublicRouteContext (context) {
|
||||
return Object.create(null, {
|
||||
schema: {
|
||||
enumerable: true,
|
||||
get () {
|
||||
return context.schema
|
||||
}
|
||||
},
|
||||
config: {
|
||||
enumerable: true,
|
||||
get () {
|
||||
return context.config
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function defaultSchemaErrorFormatter (errors, dataVar) {
|
||||
let text = ''
|
||||
const separator = ', '
|
||||
|
||||
// eslint-disable-next-line no-var
|
||||
for (var i = 0; i !== errors.length; ++i) {
|
||||
const e = errors[i]
|
||||
text += dataVar + (e.instancePath || '') + ' ' + e.message + separator
|
||||
}
|
||||
return new Error(text.slice(0, -separator.length))
|
||||
}
|
||||
|
||||
module.exports = Context
|
||||
141
backend/node_modules/fastify/lib/decorate.js
generated
vendored
Normal file
141
backend/node_modules/fastify/lib/decorate.js
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
'use strict'
|
||||
|
||||
/* eslint no-prototype-builtins: 0 */
|
||||
|
||||
const {
|
||||
kReply,
|
||||
kRequest,
|
||||
kState,
|
||||
kHasBeenDecorated
|
||||
} = require('./symbols.js')
|
||||
|
||||
const {
|
||||
FST_ERR_DEC_ALREADY_PRESENT,
|
||||
FST_ERR_DEC_MISSING_DEPENDENCY,
|
||||
FST_ERR_DEC_AFTER_START,
|
||||
FST_ERR_DEC_DEPENDENCY_INVALID_TYPE
|
||||
} = require('./errors')
|
||||
|
||||
const { FSTDEP006 } = require('./warnings')
|
||||
|
||||
function decorate (instance, name, fn, dependencies) {
|
||||
if (Object.prototype.hasOwnProperty.call(instance, name)) {
|
||||
throw new FST_ERR_DEC_ALREADY_PRESENT(name)
|
||||
}
|
||||
|
||||
checkDependencies(instance, name, dependencies)
|
||||
|
||||
if (fn && (typeof fn.getter === 'function' || typeof fn.setter === 'function')) {
|
||||
Object.defineProperty(instance, name, {
|
||||
get: fn.getter,
|
||||
set: fn.setter
|
||||
})
|
||||
} else {
|
||||
instance[name] = fn
|
||||
}
|
||||
}
|
||||
|
||||
function decorateConstructor (konstructor, name, fn, dependencies) {
|
||||
const instance = konstructor.prototype
|
||||
if (Object.prototype.hasOwnProperty.call(instance, name) || hasKey(konstructor, name)) {
|
||||
throw new FST_ERR_DEC_ALREADY_PRESENT(name)
|
||||
}
|
||||
|
||||
konstructor[kHasBeenDecorated] = true
|
||||
checkDependencies(konstructor, name, dependencies)
|
||||
|
||||
if (fn && (typeof fn.getter === 'function' || typeof fn.setter === 'function')) {
|
||||
Object.defineProperty(instance, name, {
|
||||
get: fn.getter,
|
||||
set: fn.setter
|
||||
})
|
||||
} else if (typeof fn === 'function') {
|
||||
instance[name] = fn
|
||||
} else {
|
||||
konstructor.props.push({ key: name, value: fn })
|
||||
}
|
||||
}
|
||||
|
||||
function checkReferenceType (name, fn) {
|
||||
if (typeof fn === 'object' && fn && !(typeof fn.getter === 'function' || typeof fn.setter === 'function')) {
|
||||
FSTDEP006(name)
|
||||
}
|
||||
}
|
||||
|
||||
function decorateFastify (name, fn, dependencies) {
|
||||
assertNotStarted(this, name)
|
||||
decorate(this, name, fn, dependencies)
|
||||
return this
|
||||
}
|
||||
|
||||
function checkExistence (instance, name) {
|
||||
if (name) {
|
||||
return name in instance || (instance.prototype && name in instance.prototype) || hasKey(instance, name)
|
||||
}
|
||||
|
||||
return instance in this
|
||||
}
|
||||
|
||||
function hasKey (fn, name) {
|
||||
if (fn.props) {
|
||||
return fn.props.find(({ key }) => key === name)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function checkRequestExistence (name) {
|
||||
if (name && hasKey(this[kRequest], name)) return true
|
||||
return checkExistence(this[kRequest].prototype, name)
|
||||
}
|
||||
|
||||
function checkReplyExistence (name) {
|
||||
if (name && hasKey(this[kReply], name)) return true
|
||||
return checkExistence(this[kReply].prototype, name)
|
||||
}
|
||||
|
||||
function checkDependencies (instance, name, deps) {
|
||||
if (deps === undefined || deps === null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!Array.isArray(deps)) {
|
||||
throw new FST_ERR_DEC_DEPENDENCY_INVALID_TYPE(name)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-var
|
||||
for (var i = 0; i !== deps.length; ++i) {
|
||||
if (!checkExistence(instance, deps[i])) {
|
||||
throw new FST_ERR_DEC_MISSING_DEPENDENCY(deps[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function decorateReply (name, fn, dependencies) {
|
||||
assertNotStarted(this, name)
|
||||
checkReferenceType(name, fn)
|
||||
decorateConstructor(this[kReply], name, fn, dependencies)
|
||||
return this
|
||||
}
|
||||
|
||||
function decorateRequest (name, fn, dependencies) {
|
||||
assertNotStarted(this, name)
|
||||
checkReferenceType(name, fn)
|
||||
decorateConstructor(this[kRequest], name, fn, dependencies)
|
||||
return this
|
||||
}
|
||||
|
||||
function assertNotStarted (instance, name) {
|
||||
if (instance[kState].started) {
|
||||
throw new FST_ERR_DEC_AFTER_START(name)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
add: decorateFastify,
|
||||
exist: checkExistence,
|
||||
existRequest: checkRequestExistence,
|
||||
existReply: checkReplyExistence,
|
||||
dependencies: checkDependencies,
|
||||
decorateReply,
|
||||
decorateRequest
|
||||
}
|
||||
174
backend/node_modules/fastify/lib/error-handler.js
generated
vendored
Normal file
174
backend/node_modules/fastify/lib/error-handler.js
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
'use strict'
|
||||
|
||||
const statusCodes = require('node:http').STATUS_CODES
|
||||
const wrapThenable = require('./wrapThenable')
|
||||
const {
|
||||
kReplyHeaders,
|
||||
kReplyNextErrorHandler,
|
||||
kReplyIsRunningOnErrorHook,
|
||||
kReplyHasStatusCode,
|
||||
kRouteContext,
|
||||
kDisableRequestLogging
|
||||
} = require('./symbols.js')
|
||||
|
||||
const {
|
||||
FST_ERR_REP_INVALID_PAYLOAD_TYPE,
|
||||
FST_ERR_FAILED_ERROR_SERIALIZATION
|
||||
} = require('./errors')
|
||||
|
||||
const { getSchemaSerializer } = require('./schemas')
|
||||
|
||||
const serializeError = require('./error-serializer')
|
||||
|
||||
const rootErrorHandler = {
|
||||
func: defaultErrorHandler,
|
||||
toJSON () {
|
||||
return this.func.name.toString() + '()'
|
||||
}
|
||||
}
|
||||
|
||||
function handleError (reply, error, cb) {
|
||||
reply[kReplyIsRunningOnErrorHook] = false
|
||||
|
||||
const context = reply[kRouteContext]
|
||||
if (reply[kReplyNextErrorHandler] === false) {
|
||||
fallbackErrorHandler(error, reply, function (reply, payload) {
|
||||
try {
|
||||
reply.raw.writeHead(reply.raw.statusCode, reply[kReplyHeaders])
|
||||
} catch (error) {
|
||||
if (!reply.log[kDisableRequestLogging]) {
|
||||
reply.log.warn(
|
||||
{ req: reply.request, res: reply, err: error },
|
||||
error && error.message
|
||||
)
|
||||
}
|
||||
reply.raw.writeHead(reply.raw.statusCode)
|
||||
}
|
||||
reply.raw.end(payload)
|
||||
})
|
||||
return
|
||||
}
|
||||
const errorHandler = reply[kReplyNextErrorHandler] || context.errorHandler
|
||||
|
||||
// In case the error handler throws, we set the next errorHandler so we can error again
|
||||
reply[kReplyNextErrorHandler] = Object.getPrototypeOf(errorHandler)
|
||||
|
||||
// we need to remove content-type to allow content-type guessing for serialization
|
||||
delete reply[kReplyHeaders]['content-type']
|
||||
delete reply[kReplyHeaders]['content-length']
|
||||
|
||||
const func = errorHandler.func
|
||||
|
||||
if (!func) {
|
||||
reply[kReplyNextErrorHandler] = false
|
||||
fallbackErrorHandler(error, reply, cb)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const result = func(error, reply.request, reply)
|
||||
if (result !== undefined) {
|
||||
if (result !== null && typeof result.then === 'function') {
|
||||
wrapThenable(result, reply)
|
||||
} else {
|
||||
reply.send(result)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
reply.send(err)
|
||||
}
|
||||
}
|
||||
|
||||
function defaultErrorHandler (error, request, reply) {
|
||||
setErrorHeaders(error, reply)
|
||||
if (!reply[kReplyHasStatusCode] || reply.statusCode === 200) {
|
||||
const statusCode = error.statusCode || error.status
|
||||
reply.code(statusCode >= 400 ? statusCode : 500)
|
||||
}
|
||||
if (reply.statusCode < 500) {
|
||||
if (!reply.log[kDisableRequestLogging]) {
|
||||
reply.log.info(
|
||||
{ res: reply, err: error },
|
||||
error && error.message
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (!reply.log[kDisableRequestLogging]) {
|
||||
reply.log.error(
|
||||
{ req: request, res: reply, err: error },
|
||||
error && error.message
|
||||
)
|
||||
}
|
||||
}
|
||||
reply.send(error)
|
||||
}
|
||||
|
||||
function fallbackErrorHandler (error, reply, cb) {
|
||||
const res = reply.raw
|
||||
const statusCode = reply.statusCode
|
||||
reply[kReplyHeaders]['content-type'] = reply[kReplyHeaders]['content-type'] ?? 'application/json; charset=utf-8'
|
||||
let payload
|
||||
try {
|
||||
const serializerFn = getSchemaSerializer(reply[kRouteContext], statusCode, reply[kReplyHeaders]['content-type'])
|
||||
payload = (serializerFn === false)
|
||||
? serializeError({
|
||||
error: statusCodes[statusCode + ''],
|
||||
code: error.code,
|
||||
message: error.message,
|
||||
statusCode
|
||||
})
|
||||
: serializerFn(Object.create(error, {
|
||||
error: { value: statusCodes[statusCode + ''] },
|
||||
message: { value: error.message },
|
||||
statusCode: { value: statusCode }
|
||||
}))
|
||||
} catch (err) {
|
||||
if (!reply.log[kDisableRequestLogging]) {
|
||||
// error is always FST_ERR_SCH_SERIALIZATION_BUILD because this is called from route/compileSchemasForSerialization
|
||||
reply.log.error({ err, statusCode: res.statusCode }, 'The serializer for the given status code failed')
|
||||
}
|
||||
reply.code(500)
|
||||
payload = serializeError(new FST_ERR_FAILED_ERROR_SERIALIZATION(err.message, error.message))
|
||||
}
|
||||
|
||||
if (typeof payload !== 'string' && !Buffer.isBuffer(payload)) {
|
||||
payload = serializeError(new FST_ERR_REP_INVALID_PAYLOAD_TYPE(typeof payload))
|
||||
}
|
||||
|
||||
reply[kReplyHeaders]['content-length'] = '' + Buffer.byteLength(payload)
|
||||
|
||||
cb(reply, payload)
|
||||
}
|
||||
|
||||
function buildErrorHandler (parent = rootErrorHandler, func) {
|
||||
if (!func) {
|
||||
return parent
|
||||
}
|
||||
|
||||
const errorHandler = Object.create(parent)
|
||||
errorHandler.func = func
|
||||
return errorHandler
|
||||
}
|
||||
|
||||
function setErrorHeaders (error, reply) {
|
||||
const res = reply.raw
|
||||
let statusCode = res.statusCode
|
||||
statusCode = (statusCode >= 400) ? statusCode : 500
|
||||
// treat undefined and null as same
|
||||
if (error != null) {
|
||||
if (error.headers !== undefined) {
|
||||
reply.headers(error.headers)
|
||||
}
|
||||
if (error.status >= 400) {
|
||||
statusCode = error.status
|
||||
} else if (error.statusCode >= 400) {
|
||||
statusCode = error.statusCode
|
||||
}
|
||||
}
|
||||
res.statusCode = statusCode
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildErrorHandler,
|
||||
handleError
|
||||
}
|
||||
119
backend/node_modules/fastify/lib/error-serializer.js
generated
vendored
Normal file
119
backend/node_modules/fastify/lib/error-serializer.js
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
// This file is autogenerated by build/build-error-serializer.js, do not edit
|
||||
/* istanbul ignore file */
|
||||
|
||||
'use strict'
|
||||
|
||||
const Serializer = require('fast-json-stringify/lib/serializer')
|
||||
const serializerState = {"mode":"standalone"}
|
||||
const serializer = Serializer.restoreFromState(serializerState)
|
||||
|
||||
const validator = null
|
||||
|
||||
|
||||
module.exports = function anonymous(validator,serializer
|
||||
) {
|
||||
|
||||
const JSON_STR_BEGIN_OBJECT = '{'
|
||||
const JSON_STR_END_OBJECT = '}'
|
||||
const JSON_STR_BEGIN_ARRAY = '['
|
||||
const JSON_STR_END_ARRAY = ']'
|
||||
const JSON_STR_COMMA = ','
|
||||
const JSON_STR_COLONS = ':'
|
||||
const JSON_STR_QUOTE = '"'
|
||||
const JSON_STR_EMPTY_OBJECT = JSON_STR_BEGIN_OBJECT + JSON_STR_END_OBJECT
|
||||
const JSON_STR_EMPTY_ARRAY = JSON_STR_BEGIN_ARRAY + JSON_STR_END_ARRAY
|
||||
const JSON_STR_EMPTY_STRING = JSON_STR_QUOTE + JSON_STR_QUOTE
|
||||
const JSON_STR_NULL = 'null'
|
||||
|
||||
|
||||
|
||||
// #
|
||||
function anonymous0 (input) {
|
||||
const obj = (input && typeof input.toJSON === 'function')
|
||||
? input.toJSON()
|
||||
: input
|
||||
|
||||
if (obj === null) return JSON_STR_EMPTY_OBJECT
|
||||
|
||||
let value
|
||||
let json = JSON_STR_BEGIN_OBJECT
|
||||
let addComma = false
|
||||
|
||||
value = obj["statusCode"]
|
||||
if (value !== undefined) {
|
||||
!addComma && (addComma = true) || (json += JSON_STR_COMMA)
|
||||
json += "\"statusCode\":"
|
||||
json += serializer.asNumber(value)
|
||||
}
|
||||
|
||||
value = obj["code"]
|
||||
if (value !== undefined) {
|
||||
!addComma && (addComma = true) || (json += JSON_STR_COMMA)
|
||||
json += "\"code\":"
|
||||
|
||||
if (typeof value !== 'string') {
|
||||
if (value === null) {
|
||||
json += JSON_STR_EMPTY_STRING
|
||||
} else if (value instanceof Date) {
|
||||
json += JSON_STR_QUOTE + value.toISOString() + JSON_STR_QUOTE
|
||||
} else if (value instanceof RegExp) {
|
||||
json += serializer.asString(value.source)
|
||||
} else {
|
||||
json += serializer.asString(value.toString())
|
||||
}
|
||||
} else {
|
||||
json += serializer.asString(value)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
value = obj["error"]
|
||||
if (value !== undefined) {
|
||||
!addComma && (addComma = true) || (json += JSON_STR_COMMA)
|
||||
json += "\"error\":"
|
||||
|
||||
if (typeof value !== 'string') {
|
||||
if (value === null) {
|
||||
json += JSON_STR_EMPTY_STRING
|
||||
} else if (value instanceof Date) {
|
||||
json += JSON_STR_QUOTE + value.toISOString() + JSON_STR_QUOTE
|
||||
} else if (value instanceof RegExp) {
|
||||
json += serializer.asString(value.source)
|
||||
} else {
|
||||
json += serializer.asString(value.toString())
|
||||
}
|
||||
} else {
|
||||
json += serializer.asString(value)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
value = obj["message"]
|
||||
if (value !== undefined) {
|
||||
!addComma && (addComma = true) || (json += JSON_STR_COMMA)
|
||||
json += "\"message\":"
|
||||
|
||||
if (typeof value !== 'string') {
|
||||
if (value === null) {
|
||||
json += JSON_STR_EMPTY_STRING
|
||||
} else if (value instanceof Date) {
|
||||
json += JSON_STR_QUOTE + value.toISOString() + JSON_STR_QUOTE
|
||||
} else if (value instanceof RegExp) {
|
||||
json += serializer.asString(value.source)
|
||||
} else {
|
||||
json += serializer.asString(value.toString())
|
||||
}
|
||||
} else {
|
||||
json += serializer.asString(value)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return json + JSON_STR_END_OBJECT
|
||||
|
||||
}
|
||||
|
||||
const main = anonymous0
|
||||
return main
|
||||
|
||||
}(validator, serializer)
|
||||
475
backend/node_modules/fastify/lib/errors.js
generated
vendored
Normal file
475
backend/node_modules/fastify/lib/errors.js
generated
vendored
Normal file
@@ -0,0 +1,475 @@
|
||||
'use strict'
|
||||
|
||||
const createError = require('@fastify/error')
|
||||
|
||||
const codes = {
|
||||
/**
|
||||
* Basic
|
||||
*/
|
||||
FST_ERR_NOT_FOUND: createError(
|
||||
'FST_ERR_NOT_FOUND',
|
||||
'Not Found',
|
||||
404
|
||||
),
|
||||
FST_ERR_OPTIONS_NOT_OBJ: createError(
|
||||
'FST_ERR_OPTIONS_NOT_OBJ',
|
||||
'Options must be an object',
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_QSP_NOT_FN: createError(
|
||||
'FST_ERR_QSP_NOT_FN',
|
||||
"querystringParser option should be a function, instead got '%s'",
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN: createError(
|
||||
'FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN',
|
||||
"schemaController.bucket option should be a function, instead got '%s'",
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN: createError(
|
||||
'FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN',
|
||||
"schemaErrorFormatter option should be a non async function. Instead got '%s'.",
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_OBJ: createError(
|
||||
'FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_OBJ',
|
||||
"ajv.customOptions option should be an object, instead got '%s'",
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_ARR: createError(
|
||||
'FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_ARR',
|
||||
"ajv.plugins option should be an array, instead got '%s'",
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_VERSION_CONSTRAINT_NOT_STR: createError(
|
||||
'FST_ERR_VERSION_CONSTRAINT_NOT_STR',
|
||||
'Version constraint should be a string.',
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_VALIDATION: createError(
|
||||
'FST_ERR_VALIDATION',
|
||||
'%s',
|
||||
400
|
||||
),
|
||||
FST_ERR_LISTEN_OPTIONS_INVALID: createError(
|
||||
'FST_ERR_LISTEN_OPTIONS_INVALID',
|
||||
"Invalid listen options: '%s'",
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_ERROR_HANDLER_NOT_FN: createError(
|
||||
'FST_ERR_ERROR_HANDLER_NOT_FN',
|
||||
'Error Handler must be a function',
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
|
||||
/**
|
||||
* ContentTypeParser
|
||||
*/
|
||||
FST_ERR_CTP_ALREADY_PRESENT: createError(
|
||||
'FST_ERR_CTP_ALREADY_PRESENT',
|
||||
"Content type parser '%s' already present."
|
||||
),
|
||||
FST_ERR_CTP_INVALID_TYPE: createError(
|
||||
'FST_ERR_CTP_INVALID_TYPE',
|
||||
'The content type should be a string or a RegExp',
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_CTP_EMPTY_TYPE: createError(
|
||||
'FST_ERR_CTP_EMPTY_TYPE',
|
||||
'The content type cannot be an empty string',
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_CTP_INVALID_HANDLER: createError(
|
||||
'FST_ERR_CTP_INVALID_HANDLER',
|
||||
'The content type handler should be a function',
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_CTP_INVALID_PARSE_TYPE: createError(
|
||||
'FST_ERR_CTP_INVALID_PARSE_TYPE',
|
||||
"The body parser can only parse your data as 'string' or 'buffer', you asked '%s' which is not supported.",
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_CTP_BODY_TOO_LARGE: createError(
|
||||
'FST_ERR_CTP_BODY_TOO_LARGE',
|
||||
'Request body is too large',
|
||||
413,
|
||||
RangeError
|
||||
),
|
||||
FST_ERR_CTP_INVALID_MEDIA_TYPE: createError(
|
||||
'FST_ERR_CTP_INVALID_MEDIA_TYPE',
|
||||
'Unsupported Media Type: %s',
|
||||
415
|
||||
),
|
||||
FST_ERR_CTP_INVALID_CONTENT_LENGTH: createError(
|
||||
'FST_ERR_CTP_INVALID_CONTENT_LENGTH',
|
||||
'Request body size did not match Content-Length',
|
||||
400,
|
||||
RangeError
|
||||
),
|
||||
FST_ERR_CTP_EMPTY_JSON_BODY: createError(
|
||||
'FST_ERR_CTP_EMPTY_JSON_BODY',
|
||||
"Body cannot be empty when content-type is set to 'application/json'",
|
||||
400
|
||||
),
|
||||
FST_ERR_CTP_INSTANCE_ALREADY_STARTED: createError(
|
||||
'FST_ERR_CTP_INSTANCE_ALREADY_STARTED',
|
||||
'Cannot call "%s" when fastify instance is already started!',
|
||||
400
|
||||
),
|
||||
|
||||
/**
|
||||
* decorate
|
||||
*/
|
||||
FST_ERR_DEC_ALREADY_PRESENT: createError(
|
||||
'FST_ERR_DEC_ALREADY_PRESENT',
|
||||
"The decorator '%s' has already been added!"
|
||||
),
|
||||
FST_ERR_DEC_DEPENDENCY_INVALID_TYPE: createError(
|
||||
'FST_ERR_DEC_DEPENDENCY_INVALID_TYPE',
|
||||
"The dependencies of decorator '%s' must be of type Array.",
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_DEC_MISSING_DEPENDENCY: createError(
|
||||
'FST_ERR_DEC_MISSING_DEPENDENCY',
|
||||
"The decorator is missing dependency '%s'."
|
||||
),
|
||||
FST_ERR_DEC_AFTER_START: createError(
|
||||
'FST_ERR_DEC_AFTER_START',
|
||||
"The decorator '%s' has been added after start!"
|
||||
),
|
||||
|
||||
/**
|
||||
* hooks
|
||||
*/
|
||||
FST_ERR_HOOK_INVALID_TYPE: createError(
|
||||
'FST_ERR_HOOK_INVALID_TYPE',
|
||||
'The hook name must be a string',
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_HOOK_INVALID_HANDLER: createError(
|
||||
'FST_ERR_HOOK_INVALID_HANDLER',
|
||||
'%s hook should be a function, instead got %s',
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_HOOK_INVALID_ASYNC_HANDLER: createError(
|
||||
'FST_ERR_HOOK_INVALID_ASYNC_HANDLER',
|
||||
'Async function has too many arguments. Async hooks should not use the \'done\' argument.',
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_HOOK_NOT_SUPPORTED: createError(
|
||||
'FST_ERR_HOOK_NOT_SUPPORTED',
|
||||
'%s hook not supported!',
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
|
||||
/**
|
||||
* Middlewares
|
||||
*/
|
||||
FST_ERR_MISSING_MIDDLEWARE: createError(
|
||||
'FST_ERR_MISSING_MIDDLEWARE',
|
||||
'You must register a plugin for handling middlewares, visit fastify.dev/docs/latest/Reference/Middleware/ for more info.',
|
||||
500
|
||||
),
|
||||
|
||||
FST_ERR_HOOK_TIMEOUT: createError(
|
||||
'FST_ERR_HOOK_TIMEOUT',
|
||||
"A callback for '%s' hook timed out. You may have forgotten to call 'done' function or to resolve a Promise"
|
||||
),
|
||||
|
||||
/**
|
||||
* logger
|
||||
*/
|
||||
FST_ERR_LOG_INVALID_DESTINATION: createError(
|
||||
'FST_ERR_LOG_INVALID_DESTINATION',
|
||||
'Cannot specify both logger.stream and logger.file options'
|
||||
),
|
||||
|
||||
FST_ERR_LOG_INVALID_LOGGER: createError(
|
||||
'FST_ERR_LOG_INVALID_LOGGER',
|
||||
"Invalid logger object provided. The logger instance should have these functions(s): '%s'.",
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
|
||||
/**
|
||||
* reply
|
||||
*/
|
||||
FST_ERR_REP_INVALID_PAYLOAD_TYPE: createError(
|
||||
'FST_ERR_REP_INVALID_PAYLOAD_TYPE',
|
||||
"Attempted to send payload of invalid type '%s'. Expected a string or Buffer.",
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_REP_RESPONSE_BODY_CONSUMED: createError(
|
||||
'FST_ERR_REP_RESPONSE_BODY_CONSUMED',
|
||||
'Response.body is already consumed.'
|
||||
),
|
||||
FST_ERR_REP_ALREADY_SENT: createError(
|
||||
'FST_ERR_REP_ALREADY_SENT',
|
||||
'Reply was already sent, did you forget to "return reply" in "%s" (%s)?'
|
||||
),
|
||||
FST_ERR_REP_SENT_VALUE: createError(
|
||||
'FST_ERR_REP_SENT_VALUE',
|
||||
'The only possible value for reply.sent is true.',
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_SEND_INSIDE_ONERR: createError(
|
||||
'FST_ERR_SEND_INSIDE_ONERR',
|
||||
'You cannot use `send` inside the `onError` hook'
|
||||
),
|
||||
FST_ERR_SEND_UNDEFINED_ERR: createError(
|
||||
'FST_ERR_SEND_UNDEFINED_ERR',
|
||||
'Undefined error has occurred'
|
||||
),
|
||||
FST_ERR_BAD_STATUS_CODE: createError(
|
||||
'FST_ERR_BAD_STATUS_CODE',
|
||||
'Called reply with an invalid status code: %s'
|
||||
),
|
||||
FST_ERR_BAD_TRAILER_NAME: createError(
|
||||
'FST_ERR_BAD_TRAILER_NAME',
|
||||
'Called reply.trailer with an invalid header name: %s'
|
||||
),
|
||||
FST_ERR_BAD_TRAILER_VALUE: createError(
|
||||
'FST_ERR_BAD_TRAILER_VALUE',
|
||||
"Called reply.trailer('%s', fn) with an invalid type: %s. Expected a function."
|
||||
),
|
||||
FST_ERR_FAILED_ERROR_SERIALIZATION: createError(
|
||||
'FST_ERR_FAILED_ERROR_SERIALIZATION',
|
||||
'Failed to serialize an error. Error: %s. Original error: %s'
|
||||
),
|
||||
FST_ERR_MISSING_SERIALIZATION_FN: createError(
|
||||
'FST_ERR_MISSING_SERIALIZATION_FN',
|
||||
'Missing serialization function. Key "%s"'
|
||||
),
|
||||
FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN: createError(
|
||||
'FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN',
|
||||
'Missing serialization function. Key "%s:%s"'
|
||||
),
|
||||
FST_ERR_REQ_INVALID_VALIDATION_INVOCATION: createError(
|
||||
'FST_ERR_REQ_INVALID_VALIDATION_INVOCATION',
|
||||
'Invalid validation invocation. Missing validation function for HTTP part "%s" nor schema provided.'
|
||||
),
|
||||
|
||||
/**
|
||||
* schemas
|
||||
*/
|
||||
FST_ERR_SCH_MISSING_ID: createError(
|
||||
'FST_ERR_SCH_MISSING_ID',
|
||||
'Missing schema $id property'
|
||||
),
|
||||
FST_ERR_SCH_ALREADY_PRESENT: createError(
|
||||
'FST_ERR_SCH_ALREADY_PRESENT',
|
||||
"Schema with id '%s' already declared!"
|
||||
),
|
||||
FST_ERR_SCH_CONTENT_MISSING_SCHEMA: createError(
|
||||
'FST_ERR_SCH_CONTENT_MISSING_SCHEMA',
|
||||
"Schema is missing for the content type '%s'"
|
||||
),
|
||||
FST_ERR_SCH_DUPLICATE: createError(
|
||||
'FST_ERR_SCH_DUPLICATE',
|
||||
"Schema with '%s' already present!"
|
||||
),
|
||||
FST_ERR_SCH_VALIDATION_BUILD: createError(
|
||||
'FST_ERR_SCH_VALIDATION_BUILD',
|
||||
'Failed building the validation schema for %s: %s, due to error %s'
|
||||
),
|
||||
FST_ERR_SCH_SERIALIZATION_BUILD: createError(
|
||||
'FST_ERR_SCH_SERIALIZATION_BUILD',
|
||||
'Failed building the serialization schema for %s: %s, due to error %s'
|
||||
),
|
||||
FST_ERR_SCH_RESPONSE_SCHEMA_NOT_NESTED_2XX: createError(
|
||||
'FST_ERR_SCH_RESPONSE_SCHEMA_NOT_NESTED_2XX',
|
||||
'response schemas should be nested under a valid status code, e.g { 2xx: { type: "object" } }'
|
||||
),
|
||||
|
||||
/**
|
||||
* http2
|
||||
*/
|
||||
FST_ERR_HTTP2_INVALID_VERSION: createError(
|
||||
'FST_ERR_HTTP2_INVALID_VERSION',
|
||||
'HTTP2 is available only from node >= 8.8.1'
|
||||
),
|
||||
|
||||
/**
|
||||
* initialConfig
|
||||
*/
|
||||
FST_ERR_INIT_OPTS_INVALID: createError(
|
||||
'FST_ERR_INIT_OPTS_INVALID',
|
||||
"Invalid initialization options: '%s'"
|
||||
),
|
||||
FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE: createError(
|
||||
'FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE',
|
||||
"Cannot set forceCloseConnections to 'idle' as your HTTP server does not support closeIdleConnections method"
|
||||
),
|
||||
|
||||
/**
|
||||
* router
|
||||
*/
|
||||
FST_ERR_DUPLICATED_ROUTE: createError(
|
||||
'FST_ERR_DUPLICATED_ROUTE',
|
||||
"Method '%s' already declared for route '%s'"
|
||||
),
|
||||
FST_ERR_BAD_URL: createError(
|
||||
'FST_ERR_BAD_URL',
|
||||
"'%s' is not a valid url component",
|
||||
400,
|
||||
URIError
|
||||
),
|
||||
FST_ERR_ASYNC_CONSTRAINT: createError(
|
||||
'FST_ERR_ASYNC_CONSTRAINT',
|
||||
'Unexpected error from async constraint',
|
||||
500
|
||||
),
|
||||
FST_ERR_DEFAULT_ROUTE_INVALID_TYPE: createError(
|
||||
'FST_ERR_DEFAULT_ROUTE_INVALID_TYPE',
|
||||
'The defaultRoute type should be a function',
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_INVALID_URL: createError(
|
||||
'FST_ERR_INVALID_URL',
|
||||
"URL must be a string. Received '%s'",
|
||||
400,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_ROUTE_OPTIONS_NOT_OBJ: createError(
|
||||
'FST_ERR_ROUTE_OPTIONS_NOT_OBJ',
|
||||
'Options for "%s:%s" route must be an object',
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_ROUTE_DUPLICATED_HANDLER: createError(
|
||||
'FST_ERR_ROUTE_DUPLICATED_HANDLER',
|
||||
'Duplicate handler for "%s:%s" route is not allowed!',
|
||||
500
|
||||
),
|
||||
FST_ERR_ROUTE_HANDLER_NOT_FN: createError(
|
||||
'FST_ERR_ROUTE_HANDLER_NOT_FN',
|
||||
'Error Handler for %s:%s route, if defined, must be a function',
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_ROUTE_MISSING_HANDLER: createError(
|
||||
'FST_ERR_ROUTE_MISSING_HANDLER',
|
||||
'Missing handler function for "%s:%s" route.',
|
||||
500
|
||||
),
|
||||
FST_ERR_ROUTE_METHOD_INVALID: createError(
|
||||
'FST_ERR_ROUTE_METHOD_INVALID',
|
||||
'Provided method is invalid!',
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_ROUTE_METHOD_NOT_SUPPORTED: createError(
|
||||
'FST_ERR_ROUTE_METHOD_NOT_SUPPORTED',
|
||||
'%s method is not supported.',
|
||||
500
|
||||
),
|
||||
FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED: createError(
|
||||
'FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED',
|
||||
'Body validation schema for %s:%s route is not supported!',
|
||||
500
|
||||
),
|
||||
FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT: createError(
|
||||
'FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT',
|
||||
"'bodyLimit' option must be an integer > 0. Got '%s'",
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_ROUTE_REWRITE_NOT_STR: createError(
|
||||
'FST_ERR_ROUTE_REWRITE_NOT_STR',
|
||||
'Rewrite url for "%s" needs to be of type "string" but received "%s"',
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
|
||||
/**
|
||||
* again listen when close server
|
||||
*/
|
||||
FST_ERR_REOPENED_CLOSE_SERVER: createError(
|
||||
'FST_ERR_REOPENED_CLOSE_SERVER',
|
||||
'Fastify has already been closed and cannot be reopened'
|
||||
),
|
||||
FST_ERR_REOPENED_SERVER: createError(
|
||||
'FST_ERR_REOPENED_SERVER',
|
||||
'Fastify is already listening'
|
||||
),
|
||||
FST_ERR_INSTANCE_ALREADY_LISTENING: createError(
|
||||
'FST_ERR_INSTANCE_ALREADY_LISTENING',
|
||||
'Fastify instance is already listening. %s'
|
||||
),
|
||||
|
||||
/**
|
||||
* plugin
|
||||
*/
|
||||
FST_ERR_PLUGIN_VERSION_MISMATCH: createError(
|
||||
'FST_ERR_PLUGIN_VERSION_MISMATCH',
|
||||
"fastify-plugin: %s - expected '%s' fastify version, '%s' is installed"
|
||||
),
|
||||
FST_ERR_PLUGIN_NOT_PRESENT_IN_INSTANCE: createError(
|
||||
'FST_ERR_PLUGIN_NOT_PRESENT_IN_INSTANCE',
|
||||
"The decorator '%s'%s is not present in %s"
|
||||
),
|
||||
|
||||
/**
|
||||
* Avvio Errors
|
||||
*/
|
||||
FST_ERR_PLUGIN_CALLBACK_NOT_FN: createError(
|
||||
'FST_ERR_PLUGIN_CALLBACK_NOT_FN',
|
||||
'fastify-plugin: %s',
|
||||
500,
|
||||
TypeError
|
||||
),
|
||||
FST_ERR_PLUGIN_NOT_VALID: createError(
|
||||
'FST_ERR_PLUGIN_NOT_VALID',
|
||||
'fastify-plugin: %s'
|
||||
),
|
||||
FST_ERR_ROOT_PLG_BOOTED: createError(
|
||||
'FST_ERR_ROOT_PLG_BOOTED',
|
||||
'fastify-plugin: %s'
|
||||
),
|
||||
FST_ERR_PARENT_PLUGIN_BOOTED: createError(
|
||||
'FST_ERR_PARENT_PLUGIN_BOOTED',
|
||||
'fastify-plugin: %s'
|
||||
),
|
||||
FST_ERR_PLUGIN_TIMEOUT: createError(
|
||||
'FST_ERR_PLUGIN_TIMEOUT',
|
||||
'fastify-plugin: %s'
|
||||
)
|
||||
}
|
||||
|
||||
function appendStackTrace (oldErr, newErr) {
|
||||
newErr.cause = oldErr
|
||||
|
||||
return newErr
|
||||
}
|
||||
|
||||
module.exports = codes
|
||||
module.exports.appendStackTrace = appendStackTrace
|
||||
module.exports.AVVIO_ERRORS_MAP = {
|
||||
AVV_ERR_CALLBACK_NOT_FN: codes.FST_ERR_PLUGIN_CALLBACK_NOT_FN,
|
||||
AVV_ERR_PLUGIN_NOT_VALID: codes.FST_ERR_PLUGIN_NOT_VALID,
|
||||
AVV_ERR_ROOT_PLG_BOOTED: codes.FST_ERR_ROOT_PLG_BOOTED,
|
||||
AVV_ERR_PARENT_PLG_LOADED: codes.FST_ERR_PARENT_PLUGIN_BOOTED,
|
||||
AVV_ERR_READY_TIMEOUT: codes.FST_ERR_PLUGIN_TIMEOUT,
|
||||
AVV_ERR_PLUGIN_EXEC_TIMEOUT: codes.FST_ERR_PLUGIN_TIMEOUT
|
||||
}
|
||||
187
backend/node_modules/fastify/lib/fourOhFour.js
generated
vendored
Normal file
187
backend/node_modules/fastify/lib/fourOhFour.js
generated
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
'use strict'
|
||||
|
||||
const FindMyWay = require('find-my-way')
|
||||
|
||||
const Reply = require('./reply')
|
||||
const Request = require('./request')
|
||||
const Context = require('./context')
|
||||
const {
|
||||
kRoutePrefix,
|
||||
kCanSetNotFoundHandler,
|
||||
kFourOhFourLevelInstance,
|
||||
kFourOhFourContext,
|
||||
kHooks,
|
||||
kErrorHandler
|
||||
} = require('./symbols.js')
|
||||
const { lifecycleHooks } = require('./hooks')
|
||||
const { buildErrorHandler } = require('./error-handler.js')
|
||||
const {
|
||||
FST_ERR_NOT_FOUND
|
||||
} = require('./errors')
|
||||
const { createChildLogger } = require('./logger')
|
||||
const { getGenReqId } = require('./reqIdGenFactory.js')
|
||||
|
||||
/**
|
||||
* Each fastify instance have a:
|
||||
* kFourOhFourLevelInstance: point to a fastify instance that has the 404 handler set
|
||||
* kCanSetNotFoundHandler: bool to track if the 404 handler has already been set
|
||||
* kFourOhFour: the singleton instance of this 404 module
|
||||
* kFourOhFourContext: the context in the reply object where the handler will be executed
|
||||
*/
|
||||
function fourOhFour (options) {
|
||||
const { logger, disableRequestLogging } = options
|
||||
|
||||
// 404 router, used for handling encapsulated 404 handlers
|
||||
const router = FindMyWay({ onBadUrl: createOnBadUrl(), defaultRoute: fourOhFourFallBack })
|
||||
let _onBadUrlHandler = null
|
||||
|
||||
return { router, setNotFoundHandler, setContext, arrange404 }
|
||||
|
||||
function arrange404 (instance) {
|
||||
// Change the pointer of the fastify instance to itself, so register + prefix can add new 404 handler
|
||||
instance[kFourOhFourLevelInstance] = instance
|
||||
instance[kCanSetNotFoundHandler] = true
|
||||
// we need to bind instance for the context
|
||||
router.onBadUrl = router.onBadUrl.bind(instance)
|
||||
router.defaultRoute = router.defaultRoute.bind(instance)
|
||||
}
|
||||
|
||||
function basic404 (request, reply) {
|
||||
const { url, method } = request.raw
|
||||
const message = `Route ${method}:${url} not found`
|
||||
if (!disableRequestLogging) {
|
||||
request.log.info(message)
|
||||
}
|
||||
reply.code(404).send({
|
||||
message,
|
||||
error: 'Not Found',
|
||||
statusCode: 404
|
||||
})
|
||||
}
|
||||
|
||||
function createOnBadUrl () {
|
||||
return function onBadUrl (path, req, res) {
|
||||
const fourOhFourContext = this[kFourOhFourLevelInstance][kFourOhFourContext]
|
||||
const id = getGenReqId(fourOhFourContext.server, req)
|
||||
const childLogger = createChildLogger(fourOhFourContext, logger, req, id)
|
||||
const request = new Request(id, null, req, null, childLogger, fourOhFourContext)
|
||||
const reply = new Reply(res, request, childLogger)
|
||||
|
||||
_onBadUrlHandler(request, reply)
|
||||
}
|
||||
}
|
||||
|
||||
function setContext (instance, context) {
|
||||
const _404Context = Object.assign({}, instance[kFourOhFourContext])
|
||||
_404Context.onSend = context.onSend
|
||||
context[kFourOhFourContext] = _404Context
|
||||
}
|
||||
|
||||
function setNotFoundHandler (opts, handler, avvio, routeHandler) {
|
||||
// First initialization of the fastify root instance
|
||||
if (this[kCanSetNotFoundHandler] === undefined) {
|
||||
this[kCanSetNotFoundHandler] = true
|
||||
}
|
||||
if (this[kFourOhFourContext] === undefined) {
|
||||
this[kFourOhFourContext] = null
|
||||
}
|
||||
|
||||
const _fastify = this
|
||||
const prefix = this[kRoutePrefix] || '/'
|
||||
|
||||
if (this[kCanSetNotFoundHandler] === false) {
|
||||
throw new Error(`Not found handler already set for Fastify instance with prefix: '${prefix}'`)
|
||||
}
|
||||
|
||||
if (typeof opts === 'object') {
|
||||
if (opts.preHandler) {
|
||||
if (Array.isArray(opts.preHandler)) {
|
||||
opts.preHandler = opts.preHandler.map(hook => hook.bind(_fastify))
|
||||
} else {
|
||||
opts.preHandler = opts.preHandler.bind(_fastify)
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.preValidation) {
|
||||
if (Array.isArray(opts.preValidation)) {
|
||||
opts.preValidation = opts.preValidation.map(hook => hook.bind(_fastify))
|
||||
} else {
|
||||
opts.preValidation = opts.preValidation.bind(_fastify)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof opts === 'function') {
|
||||
handler = opts
|
||||
opts = undefined
|
||||
}
|
||||
opts = opts || {}
|
||||
|
||||
if (handler) {
|
||||
this[kFourOhFourLevelInstance][kCanSetNotFoundHandler] = false
|
||||
handler = handler.bind(this)
|
||||
// update onBadUrl handler
|
||||
_onBadUrlHandler = handler
|
||||
} else {
|
||||
handler = basic404
|
||||
// update onBadUrl handler
|
||||
_onBadUrlHandler = basic404
|
||||
}
|
||||
|
||||
this.after((notHandledErr, done) => {
|
||||
_setNotFoundHandler.call(this, prefix, opts, handler, avvio, routeHandler)
|
||||
done(notHandledErr)
|
||||
})
|
||||
}
|
||||
|
||||
function _setNotFoundHandler (prefix, opts, handler, avvio, routeHandler) {
|
||||
const context = new Context({
|
||||
schema: opts.schema,
|
||||
handler,
|
||||
config: opts.config || {},
|
||||
server: this
|
||||
})
|
||||
|
||||
avvio.once('preReady', () => {
|
||||
const context = this[kFourOhFourContext]
|
||||
for (const hook of lifecycleHooks) {
|
||||
const toSet = this[kHooks][hook]
|
||||
.concat(opts[hook] || [])
|
||||
.map(h => h.bind(this))
|
||||
context[hook] = toSet.length ? toSet : null
|
||||
}
|
||||
context.errorHandler = opts.errorHandler ? buildErrorHandler(this[kErrorHandler], opts.errorHandler) : this[kErrorHandler]
|
||||
})
|
||||
|
||||
if (this[kFourOhFourContext] !== null && prefix === '/') {
|
||||
Object.assign(this[kFourOhFourContext], context) // Replace the default 404 handler
|
||||
return
|
||||
}
|
||||
|
||||
this[kFourOhFourLevelInstance][kFourOhFourContext] = context
|
||||
|
||||
router.all(prefix + (prefix.endsWith('/') ? '*' : '/*'), routeHandler, context)
|
||||
router.all(prefix, routeHandler, context)
|
||||
}
|
||||
|
||||
function fourOhFourFallBack (req, res) {
|
||||
// if this happen, we have a very bad bug
|
||||
// we might want to do some hard debugging
|
||||
// here, let's print out as much info as
|
||||
// we can
|
||||
const fourOhFourContext = this[kFourOhFourLevelInstance][kFourOhFourContext]
|
||||
const id = getGenReqId(fourOhFourContext.server, req)
|
||||
const childLogger = createChildLogger(fourOhFourContext, logger, req, id)
|
||||
|
||||
childLogger.info({ req }, 'incoming request')
|
||||
|
||||
const request = new Request(id, null, req, null, childLogger, fourOhFourContext)
|
||||
const reply = new Reply(res, request, childLogger)
|
||||
|
||||
request.log.warn('the default handler for 404 did not catch this, this is likely a fastify bug, please report it')
|
||||
request.log.warn(router.prettyPrint())
|
||||
reply.code(404).send(new FST_ERR_NOT_FOUND())
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = fourOhFour
|
||||
156
backend/node_modules/fastify/lib/handleRequest.js
generated
vendored
Normal file
156
backend/node_modules/fastify/lib/handleRequest.js
generated
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
'use strict'
|
||||
|
||||
const { validate: validateSchema } = require('./validation')
|
||||
const { preValidationHookRunner, preHandlerHookRunner } = require('./hooks')
|
||||
const wrapThenable = require('./wrapThenable')
|
||||
const {
|
||||
kReplyIsError,
|
||||
kRouteContext
|
||||
} = require('./symbols')
|
||||
|
||||
function handleRequest (err, request, reply) {
|
||||
if (reply.sent === true) return
|
||||
if (err != null) {
|
||||
reply[kReplyIsError] = true
|
||||
reply.send(err)
|
||||
return
|
||||
}
|
||||
|
||||
const method = request.raw.method
|
||||
const headers = request.headers
|
||||
const context = request[kRouteContext]
|
||||
|
||||
if (method === 'GET' || method === 'HEAD') {
|
||||
handler(request, reply)
|
||||
return
|
||||
}
|
||||
|
||||
const contentType = headers['content-type']
|
||||
|
||||
if (method === 'POST' || method === 'PUT' || method === 'PATCH' || method === 'TRACE' || method === 'SEARCH' ||
|
||||
method === 'PROPFIND' || method === 'PROPPATCH' || method === 'LOCK' || method === 'COPY' || method === 'MOVE' ||
|
||||
method === 'MKCOL' || method === 'REPORT' || method === 'MKCALENDAR') {
|
||||
if (contentType === undefined) {
|
||||
if (
|
||||
headers['transfer-encoding'] === undefined &&
|
||||
(headers['content-length'] === '0' || headers['content-length'] === undefined)
|
||||
) { // Request has no body to parse
|
||||
handler(request, reply)
|
||||
} else {
|
||||
context.contentTypeParser.run('', handler, request, reply)
|
||||
}
|
||||
} else {
|
||||
context.contentTypeParser.run(contentType, handler, request, reply)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (method === 'OPTIONS' || method === 'DELETE') {
|
||||
if (
|
||||
contentType !== undefined &&
|
||||
(
|
||||
headers['transfer-encoding'] !== undefined ||
|
||||
headers['content-length'] !== undefined
|
||||
)
|
||||
) {
|
||||
context.contentTypeParser.run(contentType, handler, request, reply)
|
||||
} else {
|
||||
handler(request, reply)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Return 404 instead of 405 see https://github.com/fastify/fastify/pull/862 for discussion
|
||||
handler(request, reply)
|
||||
}
|
||||
|
||||
function handler (request, reply) {
|
||||
try {
|
||||
if (request[kRouteContext].preValidation !== null) {
|
||||
preValidationHookRunner(
|
||||
request[kRouteContext].preValidation,
|
||||
request,
|
||||
reply,
|
||||
preValidationCallback
|
||||
)
|
||||
} else {
|
||||
preValidationCallback(null, request, reply)
|
||||
}
|
||||
} catch (err) {
|
||||
preValidationCallback(err, request, reply)
|
||||
}
|
||||
}
|
||||
|
||||
function preValidationCallback (err, request, reply) {
|
||||
if (reply.sent === true) return
|
||||
|
||||
if (err != null) {
|
||||
reply[kReplyIsError] = true
|
||||
reply.send(err)
|
||||
return
|
||||
}
|
||||
|
||||
const validationErr = validateSchema(reply[kRouteContext], request)
|
||||
const isAsync = (validationErr && typeof validationErr.then === 'function') || false
|
||||
|
||||
if (isAsync) {
|
||||
const cb = validationCompleted.bind(null, request, reply)
|
||||
validationErr.then(cb, cb)
|
||||
} else {
|
||||
validationCompleted(request, reply, validationErr)
|
||||
}
|
||||
}
|
||||
|
||||
function validationCompleted (request, reply, validationErr) {
|
||||
if (validationErr) {
|
||||
if (reply[kRouteContext].attachValidation === false) {
|
||||
reply.send(validationErr)
|
||||
return
|
||||
}
|
||||
|
||||
reply.request.validationError = validationErr
|
||||
}
|
||||
|
||||
// preHandler hook
|
||||
if (request[kRouteContext].preHandler !== null) {
|
||||
preHandlerHookRunner(
|
||||
request[kRouteContext].preHandler,
|
||||
request,
|
||||
reply,
|
||||
preHandlerCallback
|
||||
)
|
||||
} else {
|
||||
preHandlerCallback(null, request, reply)
|
||||
}
|
||||
}
|
||||
|
||||
function preHandlerCallback (err, request, reply) {
|
||||
if (reply.sent) return
|
||||
|
||||
if (err != null) {
|
||||
reply[kReplyIsError] = true
|
||||
reply.send(err)
|
||||
return
|
||||
}
|
||||
|
||||
let result
|
||||
|
||||
try {
|
||||
result = request[kRouteContext].handler(request, reply)
|
||||
} catch (err) {
|
||||
reply[kReplyIsError] = true
|
||||
reply.send(err)
|
||||
return
|
||||
}
|
||||
|
||||
if (result !== undefined) {
|
||||
if (result !== null && typeof result.then === 'function') {
|
||||
wrapThenable(result, reply)
|
||||
} else {
|
||||
reply.send(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = handleRequest
|
||||
module.exports[Symbol.for('internals')] = { handler, preHandlerCallback }
|
||||
31
backend/node_modules/fastify/lib/headRoute.js
generated
vendored
Normal file
31
backend/node_modules/fastify/lib/headRoute.js
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
'use strict'
|
||||
function headRouteOnSendHandler (req, reply, payload, done) {
|
||||
// If payload is undefined
|
||||
if (payload === undefined) {
|
||||
reply.header('content-length', '0')
|
||||
return done(null, null)
|
||||
}
|
||||
|
||||
if (typeof payload.resume === 'function') {
|
||||
payload.on('error', (err) => {
|
||||
reply.log.error({ err }, 'Error on Stream found for HEAD route')
|
||||
})
|
||||
payload.resume()
|
||||
return done(null, null)
|
||||
}
|
||||
|
||||
const size = '' + Buffer.byteLength(payload)
|
||||
|
||||
reply.header('content-length', size)
|
||||
|
||||
done(null, null)
|
||||
}
|
||||
|
||||
function parseHeadOnSendHandlers (onSendHandlers) {
|
||||
if (onSendHandlers == null) return headRouteOnSendHandler
|
||||
return Array.isArray(onSendHandlers) ? [...onSendHandlers, headRouteOnSendHandler] : [onSendHandlers, headRouteOnSendHandler]
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
parseHeadOnSendHandlers
|
||||
}
|
||||
426
backend/node_modules/fastify/lib/hooks.js
generated
vendored
Normal file
426
backend/node_modules/fastify/lib/hooks.js
generated
vendored
Normal file
@@ -0,0 +1,426 @@
|
||||
'use strict'
|
||||
|
||||
const applicationHooks = [
|
||||
'onRoute',
|
||||
'onRegister',
|
||||
'onReady',
|
||||
'onListen',
|
||||
'preClose',
|
||||
'onClose'
|
||||
]
|
||||
const lifecycleHooks = [
|
||||
'onTimeout',
|
||||
'onRequest',
|
||||
'preParsing',
|
||||
'preValidation',
|
||||
'preSerialization',
|
||||
'preHandler',
|
||||
'onSend',
|
||||
'onResponse',
|
||||
'onError',
|
||||
'onRequestAbort'
|
||||
]
|
||||
const supportedHooks = lifecycleHooks.concat(applicationHooks)
|
||||
const {
|
||||
FST_ERR_HOOK_INVALID_TYPE,
|
||||
FST_ERR_HOOK_INVALID_HANDLER,
|
||||
FST_ERR_SEND_UNDEFINED_ERR,
|
||||
FST_ERR_HOOK_TIMEOUT,
|
||||
FST_ERR_HOOK_NOT_SUPPORTED,
|
||||
AVVIO_ERRORS_MAP,
|
||||
appendStackTrace
|
||||
} = require('./errors')
|
||||
|
||||
const {
|
||||
kChildren,
|
||||
kHooks,
|
||||
kRequestPayloadStream
|
||||
} = require('./symbols')
|
||||
|
||||
function Hooks () {
|
||||
this.onRequest = []
|
||||
this.preParsing = []
|
||||
this.preValidation = []
|
||||
this.preSerialization = []
|
||||
this.preHandler = []
|
||||
this.onResponse = []
|
||||
this.onSend = []
|
||||
this.onError = []
|
||||
this.onRoute = []
|
||||
this.onRegister = []
|
||||
this.onReady = []
|
||||
this.onListen = []
|
||||
this.onTimeout = []
|
||||
this.onRequestAbort = []
|
||||
this.preClose = []
|
||||
}
|
||||
|
||||
Hooks.prototype = Object.create(null)
|
||||
|
||||
Hooks.prototype.validate = function (hook, fn) {
|
||||
if (typeof hook !== 'string') throw new FST_ERR_HOOK_INVALID_TYPE()
|
||||
if (Array.isArray(this[hook]) === false) {
|
||||
throw new FST_ERR_HOOK_NOT_SUPPORTED(hook)
|
||||
}
|
||||
if (typeof fn !== 'function') throw new FST_ERR_HOOK_INVALID_HANDLER(hook, Object.prototype.toString.call(fn))
|
||||
}
|
||||
|
||||
Hooks.prototype.add = function (hook, fn) {
|
||||
this.validate(hook, fn)
|
||||
this[hook].push(fn)
|
||||
}
|
||||
|
||||
function buildHooks (h) {
|
||||
const hooks = new Hooks()
|
||||
hooks.onRequest = h.onRequest.slice()
|
||||
hooks.preParsing = h.preParsing.slice()
|
||||
hooks.preValidation = h.preValidation.slice()
|
||||
hooks.preSerialization = h.preSerialization.slice()
|
||||
hooks.preHandler = h.preHandler.slice()
|
||||
hooks.onSend = h.onSend.slice()
|
||||
hooks.onResponse = h.onResponse.slice()
|
||||
hooks.onError = h.onError.slice()
|
||||
hooks.onRoute = h.onRoute.slice()
|
||||
hooks.onRegister = h.onRegister.slice()
|
||||
hooks.onTimeout = h.onTimeout.slice()
|
||||
hooks.onRequestAbort = h.onRequestAbort.slice()
|
||||
hooks.onReady = []
|
||||
hooks.onListen = []
|
||||
hooks.preClose = []
|
||||
return hooks
|
||||
}
|
||||
|
||||
function hookRunnerApplication (hookName, boot, server, cb) {
|
||||
const hooks = server[kHooks][hookName]
|
||||
let i = 0
|
||||
let c = 0
|
||||
|
||||
next()
|
||||
|
||||
function exit (err) {
|
||||
if (err) {
|
||||
if (err.code === 'AVV_ERR_READY_TIMEOUT') {
|
||||
err = appendStackTrace(err, new FST_ERR_HOOK_TIMEOUT(hookName))
|
||||
} else {
|
||||
err = AVVIO_ERRORS_MAP[err.code] != null
|
||||
? appendStackTrace(err, new AVVIO_ERRORS_MAP[err.code](err.message))
|
||||
: err
|
||||
}
|
||||
|
||||
cb(err)
|
||||
return
|
||||
}
|
||||
cb()
|
||||
}
|
||||
|
||||
function next (err) {
|
||||
if (err) {
|
||||
exit(err)
|
||||
return
|
||||
}
|
||||
|
||||
if (i === hooks.length && c === server[kChildren].length) {
|
||||
if (i === 0 && c === 0) { // speed up start
|
||||
exit()
|
||||
} else {
|
||||
// This is the last function executed for every fastify instance
|
||||
boot(function manageTimeout (err, done) {
|
||||
// this callback is needed by fastify to provide an hook interface without the error
|
||||
// as first parameter and managing it on behalf the user
|
||||
exit(err)
|
||||
|
||||
// this callback is needed by avvio to continue the loading of the next `register` plugins
|
||||
done(err)
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (i === hooks.length && c < server[kChildren].length) {
|
||||
const child = server[kChildren][c++]
|
||||
hookRunnerApplication(hookName, boot, child, next)
|
||||
return
|
||||
}
|
||||
|
||||
boot(wrap(hooks[i++], server))
|
||||
next()
|
||||
}
|
||||
|
||||
function wrap (fn, server) {
|
||||
return function (err, done) {
|
||||
if (err) {
|
||||
done(err)
|
||||
return
|
||||
}
|
||||
|
||||
if (fn.length === 1) {
|
||||
try {
|
||||
fn.call(server, done)
|
||||
} catch (error) {
|
||||
done(error)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const ret = fn.call(server)
|
||||
if (ret && typeof ret.then === 'function') {
|
||||
ret.then(done, done)
|
||||
return
|
||||
}
|
||||
} catch (error) {
|
||||
err = error
|
||||
}
|
||||
|
||||
done(err) // auto done
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onListenHookRunner (server) {
|
||||
const hooks = server[kHooks].onListen
|
||||
const hooksLen = hooks.length
|
||||
|
||||
let i = 0
|
||||
let c = 0
|
||||
|
||||
next()
|
||||
|
||||
function next (err) {
|
||||
err && server.log.error(err)
|
||||
|
||||
if (
|
||||
i === hooksLen
|
||||
) {
|
||||
while (c < server[kChildren].length) {
|
||||
const child = server[kChildren][c++]
|
||||
onListenHookRunner(child)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
wrap(hooks[i++], server, next)
|
||||
}
|
||||
|
||||
async function wrap (fn, server, done) {
|
||||
if (fn.length === 1) {
|
||||
try {
|
||||
fn.call(server, done)
|
||||
} catch (e) {
|
||||
done(e)
|
||||
}
|
||||
return
|
||||
}
|
||||
try {
|
||||
const ret = fn.call(server)
|
||||
if (ret && typeof ret.then === 'function') {
|
||||
ret.then(done, done)
|
||||
return
|
||||
}
|
||||
done()
|
||||
} catch (error) {
|
||||
done(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hookRunnerGenerator (iterator) {
|
||||
return function hookRunner (functions, request, reply, cb) {
|
||||
let i = 0
|
||||
|
||||
function next (err) {
|
||||
if (err || i === functions.length) {
|
||||
cb(err, request, reply)
|
||||
return
|
||||
}
|
||||
|
||||
let result
|
||||
try {
|
||||
result = iterator(functions[i++], request, reply, next)
|
||||
} catch (error) {
|
||||
cb(error, request, reply)
|
||||
return
|
||||
}
|
||||
if (result && typeof result.then === 'function') {
|
||||
result.then(handleResolve, handleReject)
|
||||
}
|
||||
}
|
||||
|
||||
function handleResolve () {
|
||||
next()
|
||||
}
|
||||
|
||||
function handleReject (err) {
|
||||
if (!err) {
|
||||
err = new FST_ERR_SEND_UNDEFINED_ERR()
|
||||
}
|
||||
|
||||
cb(err, request, reply)
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
function onResponseHookIterator (fn, request, reply, next) {
|
||||
return fn(request, reply, next)
|
||||
}
|
||||
|
||||
const onResponseHookRunner = hookRunnerGenerator(onResponseHookIterator)
|
||||
const preValidationHookRunner = hookRunnerGenerator(hookIterator)
|
||||
const preHandlerHookRunner = hookRunnerGenerator(hookIterator)
|
||||
const onTimeoutHookRunner = hookRunnerGenerator(hookIterator)
|
||||
const onRequestHookRunner = hookRunnerGenerator(hookIterator)
|
||||
|
||||
function onSendHookRunner (functions, request, reply, payload, cb) {
|
||||
let i = 0
|
||||
|
||||
function next (err, newPayload) {
|
||||
if (err) {
|
||||
cb(err, request, reply, payload)
|
||||
return
|
||||
}
|
||||
|
||||
if (newPayload !== undefined) {
|
||||
payload = newPayload
|
||||
}
|
||||
|
||||
if (i === functions.length) {
|
||||
cb(null, request, reply, payload)
|
||||
return
|
||||
}
|
||||
|
||||
let result
|
||||
try {
|
||||
result = functions[i++](request, reply, payload, next)
|
||||
} catch (error) {
|
||||
cb(error, request, reply)
|
||||
return
|
||||
}
|
||||
if (result && typeof result.then === 'function') {
|
||||
result.then(handleResolve, handleReject)
|
||||
}
|
||||
}
|
||||
|
||||
function handleResolve (newPayload) {
|
||||
next(null, newPayload)
|
||||
}
|
||||
|
||||
function handleReject (err) {
|
||||
if (!err) {
|
||||
err = new FST_ERR_SEND_UNDEFINED_ERR()
|
||||
}
|
||||
|
||||
cb(err, request, reply, payload)
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
||||
|
||||
const preSerializationHookRunner = onSendHookRunner
|
||||
|
||||
function preParsingHookRunner (functions, request, reply, cb) {
|
||||
let i = 0
|
||||
|
||||
function next (err, newPayload) {
|
||||
if (reply.sent) {
|
||||
return
|
||||
}
|
||||
|
||||
if (newPayload !== undefined) {
|
||||
request[kRequestPayloadStream] = newPayload
|
||||
}
|
||||
|
||||
if (err || i === functions.length) {
|
||||
cb(err, request, reply)
|
||||
return
|
||||
}
|
||||
|
||||
let result
|
||||
try {
|
||||
result = functions[i++](request, reply, request[kRequestPayloadStream], next)
|
||||
} catch (error) {
|
||||
cb(error, request, reply)
|
||||
return
|
||||
}
|
||||
|
||||
if (result && typeof result.then === 'function') {
|
||||
result.then(handleResolve, handleReject)
|
||||
}
|
||||
}
|
||||
|
||||
function handleResolve (newPayload) {
|
||||
next(null, newPayload)
|
||||
}
|
||||
|
||||
function handleReject (err) {
|
||||
if (!err) {
|
||||
err = new FST_ERR_SEND_UNDEFINED_ERR()
|
||||
}
|
||||
|
||||
cb(err, request, reply)
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
||||
|
||||
function onRequestAbortHookRunner (functions, request, cb) {
|
||||
let i = 0
|
||||
|
||||
function next (err) {
|
||||
if (err || i === functions.length) {
|
||||
cb(err, request)
|
||||
return
|
||||
}
|
||||
|
||||
let result
|
||||
try {
|
||||
result = functions[i++](request, next)
|
||||
} catch (error) {
|
||||
cb(error, request)
|
||||
return
|
||||
}
|
||||
if (result && typeof result.then === 'function') {
|
||||
result.then(handleResolve, handleReject)
|
||||
}
|
||||
}
|
||||
|
||||
function handleResolve () {
|
||||
next()
|
||||
}
|
||||
|
||||
function handleReject (err) {
|
||||
if (!err) {
|
||||
err = new FST_ERR_SEND_UNDEFINED_ERR()
|
||||
}
|
||||
|
||||
cb(err, request)
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
||||
|
||||
function hookIterator (fn, request, reply, next) {
|
||||
if (reply.sent === true) return undefined
|
||||
return fn(request, reply, next)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Hooks,
|
||||
buildHooks,
|
||||
hookRunnerGenerator,
|
||||
preParsingHookRunner,
|
||||
onResponseHookRunner,
|
||||
onSendHookRunner,
|
||||
preSerializationHookRunner,
|
||||
onRequestAbortHookRunner,
|
||||
hookIterator,
|
||||
hookRunnerApplication,
|
||||
onListenHookRunner,
|
||||
preHandlerHookRunner,
|
||||
preValidationHookRunner,
|
||||
onRequestHookRunner,
|
||||
onTimeoutHookRunner,
|
||||
lifecycleHooks,
|
||||
supportedHooks
|
||||
}
|
||||
24
backend/node_modules/fastify/lib/httpMethods.js
generated
vendored
Normal file
24
backend/node_modules/fastify/lib/httpMethods.js
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
supportedMethods: [
|
||||
'DELETE',
|
||||
'GET',
|
||||
'HEAD',
|
||||
'PATCH',
|
||||
'POST',
|
||||
'PUT',
|
||||
'OPTIONS',
|
||||
'PROPFIND',
|
||||
'PROPPATCH',
|
||||
'MKCOL',
|
||||
'COPY',
|
||||
'MOVE',
|
||||
'LOCK',
|
||||
'UNLOCK',
|
||||
'TRACE',
|
||||
'SEARCH',
|
||||
'REPORT',
|
||||
'MKCALENDAR'
|
||||
]
|
||||
}
|
||||
37
backend/node_modules/fastify/lib/initialConfigValidation.js
generated
vendored
Normal file
37
backend/node_modules/fastify/lib/initialConfigValidation.js
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
'use strict'
|
||||
|
||||
const validate = require('./configValidator')
|
||||
const deepClone = require('rfdc')({ circles: true, proto: false })
|
||||
const { FST_ERR_INIT_OPTS_INVALID } = require('./errors')
|
||||
|
||||
function validateInitialConfig (options) {
|
||||
const opts = deepClone(options)
|
||||
|
||||
if (!validate(opts)) {
|
||||
const error = new FST_ERR_INIT_OPTS_INVALID(JSON.stringify(validate.errors.map(e => e.message)))
|
||||
error.errors = validate.errors
|
||||
throw error
|
||||
}
|
||||
|
||||
return deepFreezeObject(opts)
|
||||
}
|
||||
|
||||
function deepFreezeObject (object) {
|
||||
const properties = Object.getOwnPropertyNames(object)
|
||||
|
||||
for (const name of properties) {
|
||||
const value = object[name]
|
||||
|
||||
if (ArrayBuffer.isView(value) && !(value instanceof DataView)) {
|
||||
continue
|
||||
}
|
||||
|
||||
object[name] = value && typeof value === 'object' ? deepFreezeObject(value) : value
|
||||
}
|
||||
|
||||
return Object.freeze(object)
|
||||
}
|
||||
|
||||
module.exports = validateInitialConfig
|
||||
module.exports.defaultInitOptions = validate.defaultInitOptions
|
||||
module.exports.utils = { deepFreezeObject }
|
||||
170
backend/node_modules/fastify/lib/logger.js
generated
vendored
Normal file
170
backend/node_modules/fastify/lib/logger.js
generated
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Code imported from `pino-http`
|
||||
* Repo: https://github.com/pinojs/pino-http
|
||||
* License: MIT (https://raw.githubusercontent.com/pinojs/pino-http/master/LICENSE)
|
||||
*/
|
||||
|
||||
const nullLogger = require('abstract-logging')
|
||||
const pino = require('pino')
|
||||
const { serializersSym } = pino.symbols
|
||||
const {
|
||||
FST_ERR_LOG_INVALID_DESTINATION,
|
||||
FST_ERR_LOG_INVALID_LOGGER
|
||||
} = require('./errors')
|
||||
|
||||
function createPinoLogger (opts) {
|
||||
if (opts.stream && opts.file) {
|
||||
throw new FST_ERR_LOG_INVALID_DESTINATION()
|
||||
} else if (opts.file) {
|
||||
// we do not have stream
|
||||
opts.stream = pino.destination(opts.file)
|
||||
delete opts.file
|
||||
}
|
||||
|
||||
const prevLogger = opts.logger
|
||||
const prevGenReqId = opts.genReqId
|
||||
let logger = null
|
||||
|
||||
if (prevLogger) {
|
||||
opts.logger = undefined
|
||||
opts.genReqId = undefined
|
||||
// we need to tap into pino internals because in v5 it supports
|
||||
// adding serializers in child loggers
|
||||
if (prevLogger[serializersSym]) {
|
||||
opts.serializers = Object.assign({}, opts.serializers, prevLogger[serializersSym])
|
||||
}
|
||||
logger = prevLogger.child({}, opts)
|
||||
opts.logger = prevLogger
|
||||
opts.genReqId = prevGenReqId
|
||||
} else {
|
||||
logger = pino(opts, opts.stream)
|
||||
}
|
||||
|
||||
return logger
|
||||
}
|
||||
|
||||
const serializers = {
|
||||
req: function asReqValue (req) {
|
||||
return {
|
||||
method: req.method,
|
||||
url: req.url,
|
||||
version: req.headers && req.headers['accept-version'],
|
||||
hostname: req.hostname,
|
||||
remoteAddress: req.ip,
|
||||
remotePort: req.socket ? req.socket.remotePort : undefined
|
||||
}
|
||||
},
|
||||
err: pino.stdSerializers.err,
|
||||
res: function asResValue (reply) {
|
||||
return {
|
||||
statusCode: reply.statusCode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function now () {
|
||||
const ts = process.hrtime()
|
||||
return (ts[0] * 1e3) + (ts[1] / 1e6)
|
||||
}
|
||||
|
||||
function createLogger (options) {
|
||||
if (!options.logger) {
|
||||
const logger = nullLogger
|
||||
logger.child = () => logger
|
||||
return { logger, hasLogger: false }
|
||||
}
|
||||
|
||||
if (validateLogger(options.logger)) {
|
||||
const logger = createPinoLogger({
|
||||
logger: options.logger,
|
||||
serializers: Object.assign({}, serializers, options.logger.serializers)
|
||||
})
|
||||
return { logger, hasLogger: true }
|
||||
}
|
||||
|
||||
const localLoggerOptions = {}
|
||||
if (Object.prototype.toString.call(options.logger) === '[object Object]') {
|
||||
Reflect.ownKeys(options.logger).forEach(prop => {
|
||||
Object.defineProperty(localLoggerOptions, prop, {
|
||||
value: options.logger[prop],
|
||||
writable: true,
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
})
|
||||
})
|
||||
}
|
||||
localLoggerOptions.level = localLoggerOptions.level || 'info'
|
||||
localLoggerOptions.serializers = Object.assign({}, serializers, localLoggerOptions.serializers)
|
||||
options.logger = localLoggerOptions
|
||||
const logger = createPinoLogger(options.logger)
|
||||
return { logger, hasLogger: true }
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a provided logger object meets the requirements
|
||||
* of a Fastify compatible logger.
|
||||
*
|
||||
* @param {object} logger Object to validate.
|
||||
* @param {boolean?} strict `true` if the object must be a logger (always throw if any methods missing)
|
||||
*
|
||||
* @returns {boolean} `true` when the logger meets the requirements.
|
||||
*
|
||||
* @throws {FST_ERR_LOG_INVALID_LOGGER} When the logger object is
|
||||
* missing required methods.
|
||||
*/
|
||||
function validateLogger (logger, strict) {
|
||||
const methods = ['info', 'error', 'debug', 'fatal', 'warn', 'trace', 'child']
|
||||
const missingMethods = logger
|
||||
? methods.filter(method => !logger[method] || typeof logger[method] !== 'function')
|
||||
: methods
|
||||
|
||||
if (!missingMethods.length) {
|
||||
return true
|
||||
} else if ((missingMethods.length === methods.length) && !strict) {
|
||||
return false
|
||||
} else {
|
||||
throw FST_ERR_LOG_INVALID_LOGGER(missingMethods.join(','))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility for creating a child logger with the appropriate bindings, logger factory
|
||||
* and validation.
|
||||
* @param {object} context
|
||||
* @param {import('../fastify').FastifyBaseLogger} logger
|
||||
* @param {import('../fastify').RawRequestDefaultExpression<any>} req
|
||||
* @param {string} reqId
|
||||
* @param {import('../types/logger.js').ChildLoggerOptions?} loggerOpts
|
||||
*/
|
||||
function createChildLogger (context, logger, req, reqId, loggerOpts) {
|
||||
const loggerBindings = {
|
||||
[context.requestIdLogLabel]: reqId
|
||||
}
|
||||
const child = context.childLoggerFactory.call(context.server, logger, loggerBindings, loggerOpts || {}, req)
|
||||
|
||||
// Optimization: bypass validation if the factory is our own default factory
|
||||
if (context.childLoggerFactory !== defaultChildLoggerFactory) {
|
||||
validateLogger(child, true) // throw if the child is not a valid logger
|
||||
}
|
||||
|
||||
return child
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('../fastify.js').FastifyBaseLogger} logger
|
||||
* @param {import('../types/logger.js').Bindings} bindings
|
||||
* @param {import('../types/logger.js').ChildLoggerOptions} opts
|
||||
*/
|
||||
function defaultChildLoggerFactory (logger, bindings, opts) {
|
||||
return logger.child(bindings, opts)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createLogger,
|
||||
createChildLogger,
|
||||
defaultChildLoggerFactory,
|
||||
serializers,
|
||||
now
|
||||
}
|
||||
10
backend/node_modules/fastify/lib/noop-set.js
generated
vendored
Normal file
10
backend/node_modules/fastify/lib/noop-set.js
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = function noopSet () {
|
||||
return {
|
||||
[Symbol.iterator]: function * () {},
|
||||
add () {},
|
||||
delete () {},
|
||||
has () { return true }
|
||||
}
|
||||
}
|
||||
88
backend/node_modules/fastify/lib/pluginOverride.js
generated
vendored
Normal file
88
backend/node_modules/fastify/lib/pluginOverride.js
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
'use strict'
|
||||
|
||||
const {
|
||||
kAvvioBoot,
|
||||
kChildren,
|
||||
kRoutePrefix,
|
||||
kLogLevel,
|
||||
kLogSerializers,
|
||||
kHooks,
|
||||
kSchemaController,
|
||||
kContentTypeParser,
|
||||
kReply,
|
||||
kRequest,
|
||||
kFourOhFour,
|
||||
kPluginNameChain
|
||||
} = require('./symbols.js')
|
||||
|
||||
const Reply = require('./reply')
|
||||
const Request = require('./request')
|
||||
const SchemaController = require('./schema-controller')
|
||||
const ContentTypeParser = require('./contentTypeParser')
|
||||
const { buildHooks } = require('./hooks')
|
||||
const pluginUtils = require('./pluginUtils')
|
||||
|
||||
// Function that runs the encapsulation magic.
|
||||
// Everything that need to be encapsulated must be handled in this function.
|
||||
module.exports = function override (old, fn, opts) {
|
||||
const shouldSkipOverride = pluginUtils.registerPlugin.call(old, fn)
|
||||
|
||||
const fnName = pluginUtils.getPluginName(fn) || pluginUtils.getFuncPreview(fn)
|
||||
if (shouldSkipOverride) {
|
||||
// after every plugin registration we will enter a new name
|
||||
old[kPluginNameChain].push(fnName)
|
||||
return old
|
||||
}
|
||||
|
||||
const instance = Object.create(old)
|
||||
old[kChildren].push(instance)
|
||||
instance.ready = old[kAvvioBoot].bind(instance)
|
||||
instance[kChildren] = []
|
||||
|
||||
instance[kReply] = Reply.buildReply(instance[kReply])
|
||||
instance[kRequest] = Request.buildRequest(instance[kRequest])
|
||||
|
||||
instance[kContentTypeParser] = ContentTypeParser.helpers.buildContentTypeParser(instance[kContentTypeParser])
|
||||
instance[kHooks] = buildHooks(instance[kHooks])
|
||||
instance[kRoutePrefix] = buildRoutePrefix(instance[kRoutePrefix], opts.prefix)
|
||||
instance[kLogLevel] = opts.logLevel || instance[kLogLevel]
|
||||
instance[kSchemaController] = SchemaController.buildSchemaController(old[kSchemaController])
|
||||
instance.getSchema = instance[kSchemaController].getSchema.bind(instance[kSchemaController])
|
||||
instance.getSchemas = instance[kSchemaController].getSchemas.bind(instance[kSchemaController])
|
||||
|
||||
// Track the registered and loaded plugins since the root instance.
|
||||
// It does not track the current encapsulated plugin.
|
||||
instance[pluginUtils.kRegisteredPlugins] = Object.create(instance[pluginUtils.kRegisteredPlugins])
|
||||
|
||||
// Track the plugin chain since the root instance.
|
||||
// When an non-encapsulated plugin is added, the chain will be updated.
|
||||
instance[kPluginNameChain] = [fnName]
|
||||
|
||||
if (instance[kLogSerializers] || opts.logSerializers) {
|
||||
instance[kLogSerializers] = Object.assign(Object.create(instance[kLogSerializers]), opts.logSerializers)
|
||||
}
|
||||
|
||||
if (opts.prefix) {
|
||||
instance[kFourOhFour].arrange404(instance)
|
||||
}
|
||||
|
||||
for (const hook of instance[kHooks].onRegister) hook.call(this, instance, opts)
|
||||
|
||||
return instance
|
||||
}
|
||||
|
||||
function buildRoutePrefix (instancePrefix, pluginPrefix) {
|
||||
if (!pluginPrefix) {
|
||||
return instancePrefix
|
||||
}
|
||||
|
||||
// Ensure that there is a '/' between the prefixes
|
||||
if (instancePrefix.endsWith('/') && pluginPrefix[0] === '/') {
|
||||
// Remove the extra '/' to avoid: '/first//second'
|
||||
pluginPrefix = pluginPrefix.slice(1)
|
||||
} else if (pluginPrefix[0] !== '/') {
|
||||
pluginPrefix = '/' + pluginPrefix
|
||||
}
|
||||
|
||||
return instancePrefix + pluginPrefix
|
||||
}
|
||||
167
backend/node_modules/fastify/lib/pluginUtils.js
generated
vendored
Normal file
167
backend/node_modules/fastify/lib/pluginUtils.js
generated
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
'use strict'
|
||||
|
||||
const semver = require('semver')
|
||||
const assert = require('node:assert')
|
||||
const kRegisteredPlugins = Symbol.for('registered-plugin')
|
||||
const {
|
||||
kTestInternals
|
||||
} = require('./symbols.js')
|
||||
const { exist, existReply, existRequest } = require('./decorate')
|
||||
const {
|
||||
FST_ERR_PLUGIN_VERSION_MISMATCH,
|
||||
FST_ERR_PLUGIN_NOT_PRESENT_IN_INSTANCE
|
||||
} = require('./errors')
|
||||
const { FSTWRN002 } = require('./warnings.js')
|
||||
|
||||
function getMeta (fn) {
|
||||
return fn[Symbol.for('plugin-meta')]
|
||||
}
|
||||
|
||||
function getPluginName (func) {
|
||||
const display = getDisplayName(func)
|
||||
if (display) {
|
||||
return display
|
||||
}
|
||||
|
||||
// let's see if this is a file, and in that case use that
|
||||
// this is common for plugins
|
||||
const cache = require.cache
|
||||
// cache is undefined inside SEA
|
||||
if (cache) {
|
||||
const keys = Object.keys(cache)
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i]
|
||||
if (cache[key].exports === func) {
|
||||
return key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if not maybe it's a named function, so use that
|
||||
if (func.name) {
|
||||
return func.name
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function getFuncPreview (func) {
|
||||
// takes the first two lines of the function if nothing else works
|
||||
return func.toString().split('\n').slice(0, 2).map(s => s.trim()).join(' -- ')
|
||||
}
|
||||
|
||||
function getDisplayName (fn) {
|
||||
return fn[Symbol.for('fastify.display-name')]
|
||||
}
|
||||
|
||||
function shouldSkipOverride (fn) {
|
||||
return !!fn[Symbol.for('skip-override')]
|
||||
}
|
||||
|
||||
function checkDependencies (fn) {
|
||||
const meta = getMeta(fn)
|
||||
if (!meta) return
|
||||
|
||||
const dependencies = meta.dependencies
|
||||
if (!dependencies) return
|
||||
assert(Array.isArray(dependencies), 'The dependencies should be an array of strings')
|
||||
|
||||
dependencies.forEach(dependency => {
|
||||
assert(
|
||||
this[kRegisteredPlugins].indexOf(dependency) > -1,
|
||||
`The dependency '${dependency}' of plugin '${meta.name}' is not registered`
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
function checkDecorators (fn) {
|
||||
const meta = getMeta(fn)
|
||||
if (!meta) return
|
||||
|
||||
const { decorators, name } = meta
|
||||
if (!decorators) return
|
||||
|
||||
if (decorators.fastify) _checkDecorators(this, 'Fastify', decorators.fastify, name)
|
||||
if (decorators.reply) _checkDecorators(this, 'Reply', decorators.reply, name)
|
||||
if (decorators.request) _checkDecorators(this, 'Request', decorators.request, name)
|
||||
}
|
||||
|
||||
const checks = {
|
||||
Fastify: exist,
|
||||
Request: existRequest,
|
||||
Reply: existReply
|
||||
}
|
||||
|
||||
function _checkDecorators (that, instance, decorators, name) {
|
||||
assert(Array.isArray(decorators), 'The decorators should be an array of strings')
|
||||
|
||||
decorators.forEach(decorator => {
|
||||
const withPluginName = typeof name === 'string' ? ` required by '${name}'` : ''
|
||||
if (!checks[instance].call(that, decorator)) {
|
||||
throw new FST_ERR_PLUGIN_NOT_PRESENT_IN_INSTANCE(decorator, withPluginName, instance)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function checkVersion (fn) {
|
||||
const meta = getMeta(fn)
|
||||
if (!meta) return
|
||||
|
||||
const requiredVersion = meta.fastify
|
||||
|
||||
const fastifyRc = /-rc.+$/.test(this.version)
|
||||
if (fastifyRc === true && semver.gt(this.version, semver.coerce(requiredVersion)) === true) {
|
||||
// A Fastify release candidate phase is taking place. In order to reduce
|
||||
// the effort needed to test plugins with the RC, we allow plugins targeting
|
||||
// the prior Fastify release to be loaded.
|
||||
return
|
||||
}
|
||||
if (requiredVersion && semver.satisfies(this.version, requiredVersion, { includePrerelease: fastifyRc }) === false) {
|
||||
// We are not in a release candidate phase. Thus, we must honor the semver
|
||||
// ranges defined by the plugin's metadata. Which is to say, if the plugin
|
||||
// expects an older version of Fastify than the _current_ version, we will
|
||||
// throw an error.
|
||||
throw new FST_ERR_PLUGIN_VERSION_MISMATCH(meta.name, requiredVersion, this.version)
|
||||
}
|
||||
}
|
||||
|
||||
function registerPluginName (fn) {
|
||||
const meta = getMeta(fn)
|
||||
if (!meta) return
|
||||
|
||||
const name = meta.name
|
||||
if (!name) return
|
||||
this[kRegisteredPlugins].push(name)
|
||||
return name
|
||||
}
|
||||
|
||||
function checkPluginHealthiness (fn, pluginName) {
|
||||
if (fn.constructor.name === 'AsyncFunction' && fn.length === 3) {
|
||||
FSTWRN002(pluginName || 'anonymous')
|
||||
}
|
||||
}
|
||||
|
||||
function registerPlugin (fn) {
|
||||
const pluginName = registerPluginName.call(this, fn) || getPluginName(fn)
|
||||
checkPluginHealthiness.call(this, fn, pluginName)
|
||||
checkVersion.call(this, fn)
|
||||
checkDecorators.call(this, fn)
|
||||
checkDependencies.call(this, fn)
|
||||
return shouldSkipOverride(fn)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getPluginName,
|
||||
getFuncPreview,
|
||||
kRegisteredPlugins,
|
||||
getDisplayName,
|
||||
registerPlugin
|
||||
}
|
||||
|
||||
module.exports[kTestInternals] = {
|
||||
shouldSkipOverride,
|
||||
getMeta,
|
||||
checkDecorators,
|
||||
checkDependencies
|
||||
}
|
||||
969
backend/node_modules/fastify/lib/reply.js
generated
vendored
Normal file
969
backend/node_modules/fastify/lib/reply.js
generated
vendored
Normal file
@@ -0,0 +1,969 @@
|
||||
'use strict'
|
||||
|
||||
const eos = require('node:stream').finished
|
||||
const Readable = require('node:stream').Readable
|
||||
|
||||
const {
|
||||
kFourOhFourContext,
|
||||
kPublicRouteContext,
|
||||
kReplyErrorHandlerCalled,
|
||||
kReplyHijacked,
|
||||
kReplyStartTime,
|
||||
kReplyEndTime,
|
||||
kReplySerializer,
|
||||
kReplySerializerDefault,
|
||||
kReplyIsError,
|
||||
kReplyHeaders,
|
||||
kReplyTrailers,
|
||||
kReplyHasStatusCode,
|
||||
kReplyIsRunningOnErrorHook,
|
||||
kReplyNextErrorHandler,
|
||||
kDisableRequestLogging,
|
||||
kSchemaResponse,
|
||||
kReplyCacheSerializeFns,
|
||||
kSchemaController,
|
||||
kOptions,
|
||||
kRouteContext
|
||||
} = require('./symbols.js')
|
||||
const {
|
||||
onSendHookRunner,
|
||||
onResponseHookRunner,
|
||||
preHandlerHookRunner,
|
||||
preSerializationHookRunner
|
||||
} = require('./hooks')
|
||||
|
||||
const internals = require('./handleRequest')[Symbol.for('internals')]
|
||||
const loggerUtils = require('./logger')
|
||||
const now = loggerUtils.now
|
||||
const { handleError } = require('./error-handler')
|
||||
const { getSchemaSerializer } = require('./schemas')
|
||||
|
||||
const CONTENT_TYPE = {
|
||||
JSON: 'application/json; charset=utf-8',
|
||||
PLAIN: 'text/plain; charset=utf-8',
|
||||
OCTET: 'application/octet-stream'
|
||||
}
|
||||
const {
|
||||
FST_ERR_REP_INVALID_PAYLOAD_TYPE,
|
||||
FST_ERR_REP_RESPONSE_BODY_CONSUMED,
|
||||
FST_ERR_REP_ALREADY_SENT,
|
||||
FST_ERR_REP_SENT_VALUE,
|
||||
FST_ERR_SEND_INSIDE_ONERR,
|
||||
FST_ERR_BAD_STATUS_CODE,
|
||||
FST_ERR_BAD_TRAILER_NAME,
|
||||
FST_ERR_BAD_TRAILER_VALUE,
|
||||
FST_ERR_MISSING_SERIALIZATION_FN,
|
||||
FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN
|
||||
} = require('./errors')
|
||||
const { FSTDEP010, FSTDEP013, FSTDEP019, FSTDEP020, FSTDEP021 } = require('./warnings')
|
||||
|
||||
const toString = Object.prototype.toString
|
||||
|
||||
function Reply (res, request, log) {
|
||||
this.raw = res
|
||||
this[kReplySerializer] = null
|
||||
this[kReplyErrorHandlerCalled] = false
|
||||
this[kReplyIsError] = false
|
||||
this[kReplyIsRunningOnErrorHook] = false
|
||||
this.request = request
|
||||
this[kReplyHeaders] = {}
|
||||
this[kReplyTrailers] = null
|
||||
this[kReplyHasStatusCode] = false
|
||||
this[kReplyStartTime] = undefined
|
||||
this.log = log
|
||||
}
|
||||
Reply.props = []
|
||||
|
||||
Object.defineProperties(Reply.prototype, {
|
||||
[kRouteContext]: {
|
||||
get () {
|
||||
return this.request[kRouteContext]
|
||||
}
|
||||
},
|
||||
// TODO: remove once v5 is done
|
||||
// Is temporary to avoid constant conflicts between `next` and `main`
|
||||
context: {
|
||||
get () {
|
||||
FSTDEP019()
|
||||
return this.request[kRouteContext]
|
||||
}
|
||||
},
|
||||
elapsedTime: {
|
||||
get () {
|
||||
if (this[kReplyStartTime] === undefined) {
|
||||
return 0
|
||||
}
|
||||
return (this[kReplyEndTime] || now()) - this[kReplyStartTime]
|
||||
}
|
||||
},
|
||||
server: {
|
||||
get () {
|
||||
return this.request[kRouteContext].server
|
||||
}
|
||||
},
|
||||
sent: {
|
||||
enumerable: true,
|
||||
get () {
|
||||
// We are checking whether reply was hijacked or the response has ended.
|
||||
return (this[kReplyHijacked] || this.raw.writableEnded) === true
|
||||
},
|
||||
set (value) {
|
||||
FSTDEP010()
|
||||
|
||||
if (value !== true) {
|
||||
throw new FST_ERR_REP_SENT_VALUE()
|
||||
}
|
||||
|
||||
// We throw only if sent was overwritten from Fastify
|
||||
if (this.sent && this[kReplyHijacked]) {
|
||||
throw new FST_ERR_REP_ALREADY_SENT(this.request.url, this.request.method)
|
||||
}
|
||||
|
||||
this[kReplyHijacked] = true
|
||||
}
|
||||
},
|
||||
statusCode: {
|
||||
get () {
|
||||
return this.raw.statusCode
|
||||
},
|
||||
set (value) {
|
||||
this.code(value)
|
||||
}
|
||||
},
|
||||
[kPublicRouteContext]: {
|
||||
get () {
|
||||
return this.request[kPublicRouteContext]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Reply.prototype.hijack = function () {
|
||||
this[kReplyHijacked] = true
|
||||
return this
|
||||
}
|
||||
|
||||
Reply.prototype.send = function (payload) {
|
||||
if (this[kReplyIsRunningOnErrorHook] === true) {
|
||||
throw new FST_ERR_SEND_INSIDE_ONERR()
|
||||
}
|
||||
|
||||
if (this.sent) {
|
||||
this.log.warn({ err: new FST_ERR_REP_ALREADY_SENT(this.request.url, this.request.method) })
|
||||
return this
|
||||
}
|
||||
|
||||
if (payload instanceof Error || this[kReplyIsError] === true) {
|
||||
this[kReplyIsError] = false
|
||||
onErrorHook(this, payload, onSendHook)
|
||||
return this
|
||||
}
|
||||
|
||||
if (payload === undefined) {
|
||||
onSendHook(this, payload)
|
||||
return this
|
||||
}
|
||||
|
||||
const contentType = this.getHeader('content-type')
|
||||
const hasContentType = contentType !== undefined
|
||||
|
||||
if (payload !== null) {
|
||||
if (
|
||||
// node:stream
|
||||
typeof payload.pipe === 'function' ||
|
||||
// node:stream/web
|
||||
typeof payload.getReader === 'function' ||
|
||||
// Response
|
||||
toString.call(payload) === '[object Response]'
|
||||
) {
|
||||
onSendHook(this, payload)
|
||||
return this
|
||||
}
|
||||
|
||||
if (payload?.buffer instanceof ArrayBuffer) {
|
||||
if (hasContentType === false) {
|
||||
this[kReplyHeaders]['content-type'] = CONTENT_TYPE.OCTET
|
||||
}
|
||||
const payloadToSend = Buffer.isBuffer(payload) ? payload : Buffer.from(payload.buffer, payload.byteOffset, payload.byteLength)
|
||||
onSendHook(this, payloadToSend)
|
||||
return this
|
||||
}
|
||||
|
||||
if (hasContentType === false && typeof payload === 'string') {
|
||||
this[kReplyHeaders]['content-type'] = CONTENT_TYPE.PLAIN
|
||||
onSendHook(this, payload)
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
if (this[kReplySerializer] !== null) {
|
||||
if (typeof payload !== 'string') {
|
||||
preSerializationHook(this, payload)
|
||||
return this
|
||||
} else {
|
||||
payload = this[kReplySerializer](payload)
|
||||
}
|
||||
|
||||
// The indexOf below also matches custom json mimetypes such as 'application/hal+json' or 'application/ld+json'
|
||||
} else if (hasContentType === false || contentType.indexOf('json') > -1) {
|
||||
if (hasContentType === false) {
|
||||
this[kReplyHeaders]['content-type'] = CONTENT_TYPE.JSON
|
||||
} else {
|
||||
// If user doesn't set charset, we will set charset to utf-8
|
||||
if (contentType.indexOf('charset') === -1) {
|
||||
const customContentType = contentType.trim()
|
||||
if (customContentType.endsWith(';')) {
|
||||
// custom content-type is ended with ';'
|
||||
this[kReplyHeaders]['content-type'] = `${customContentType} charset=utf-8`
|
||||
} else {
|
||||
this[kReplyHeaders]['content-type'] = `${customContentType}; charset=utf-8`
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typeof payload !== 'string') {
|
||||
preSerializationHook(this, payload)
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
onSendHook(this, payload)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
Reply.prototype.getHeader = function (key) {
|
||||
key = key.toLowerCase()
|
||||
const res = this.raw
|
||||
let value = this[kReplyHeaders][key]
|
||||
if (value === undefined && res.hasHeader(key)) {
|
||||
value = res.getHeader(key)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
Reply.prototype.getHeaders = function () {
|
||||
return {
|
||||
...this.raw.getHeaders(),
|
||||
...this[kReplyHeaders]
|
||||
}
|
||||
}
|
||||
|
||||
Reply.prototype.hasHeader = function (key) {
|
||||
key = key.toLowerCase()
|
||||
|
||||
return this[kReplyHeaders][key] !== undefined || this.raw.hasHeader(key)
|
||||
}
|
||||
|
||||
Reply.prototype.removeHeader = function (key) {
|
||||
// Node.js does not like headers with keys set to undefined,
|
||||
// so we have to delete the key.
|
||||
delete this[kReplyHeaders][key.toLowerCase()]
|
||||
return this
|
||||
}
|
||||
|
||||
Reply.prototype.header = function (key, value = '') {
|
||||
key = key.toLowerCase()
|
||||
|
||||
if (this[kReplyHeaders][key] && key === 'set-cookie') {
|
||||
// https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.2
|
||||
if (typeof this[kReplyHeaders][key] === 'string') {
|
||||
this[kReplyHeaders][key] = [this[kReplyHeaders][key]]
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
Array.prototype.push.apply(this[kReplyHeaders][key], value)
|
||||
} else {
|
||||
this[kReplyHeaders][key].push(value)
|
||||
}
|
||||
} else {
|
||||
this[kReplyHeaders][key] = value
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
Reply.prototype.headers = function (headers) {
|
||||
const keys = Object.keys(headers)
|
||||
/* eslint-disable no-var */
|
||||
for (var i = 0; i !== keys.length; ++i) {
|
||||
const key = keys[i]
|
||||
this.header(key, headers[key])
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Trailer#directives
|
||||
// https://datatracker.ietf.org/doc/html/rfc7230.html#chunked.trailer.part
|
||||
const INVALID_TRAILERS = new Set([
|
||||
'transfer-encoding',
|
||||
'content-length',
|
||||
'host',
|
||||
'cache-control',
|
||||
'max-forwards',
|
||||
'te',
|
||||
'authorization',
|
||||
'set-cookie',
|
||||
'content-encoding',
|
||||
'content-type',
|
||||
'content-range',
|
||||
'trailer'
|
||||
])
|
||||
|
||||
Reply.prototype.trailer = function (key, fn) {
|
||||
key = key.toLowerCase()
|
||||
if (INVALID_TRAILERS.has(key)) {
|
||||
throw new FST_ERR_BAD_TRAILER_NAME(key)
|
||||
}
|
||||
if (typeof fn !== 'function') {
|
||||
throw new FST_ERR_BAD_TRAILER_VALUE(key, typeof fn)
|
||||
}
|
||||
if (this[kReplyTrailers] === null) this[kReplyTrailers] = {}
|
||||
this[kReplyTrailers][key] = fn
|
||||
return this
|
||||
}
|
||||
|
||||
Reply.prototype.hasTrailer = function (key) {
|
||||
return this[kReplyTrailers]?.[key.toLowerCase()] !== undefined
|
||||
}
|
||||
|
||||
Reply.prototype.removeTrailer = function (key) {
|
||||
if (this[kReplyTrailers] === null) return this
|
||||
this[kReplyTrailers][key.toLowerCase()] = undefined
|
||||
return this
|
||||
}
|
||||
|
||||
Reply.prototype.code = function (code) {
|
||||
const intValue = Number(code)
|
||||
if (isNaN(intValue) || intValue < 100 || intValue > 599) {
|
||||
throw new FST_ERR_BAD_STATUS_CODE(code || String(code))
|
||||
}
|
||||
|
||||
this.raw.statusCode = intValue
|
||||
this[kReplyHasStatusCode] = true
|
||||
return this
|
||||
}
|
||||
|
||||
Reply.prototype.status = Reply.prototype.code
|
||||
|
||||
Reply.prototype.getSerializationFunction = function (schemaOrStatus, contentType) {
|
||||
let serialize
|
||||
|
||||
if (typeof schemaOrStatus === 'string' || typeof schemaOrStatus === 'number') {
|
||||
if (typeof contentType === 'string') {
|
||||
serialize = this[kRouteContext][kSchemaResponse]?.[schemaOrStatus]?.[contentType]
|
||||
} else {
|
||||
serialize = this[kRouteContext][kSchemaResponse]?.[schemaOrStatus]
|
||||
}
|
||||
} else if (typeof schemaOrStatus === 'object') {
|
||||
serialize = this[kRouteContext][kReplyCacheSerializeFns]?.get(schemaOrStatus)
|
||||
}
|
||||
|
||||
return serialize
|
||||
}
|
||||
|
||||
Reply.prototype.compileSerializationSchema = function (schema, httpStatus = null, contentType = null) {
|
||||
const { request } = this
|
||||
const { method, url } = request
|
||||
|
||||
// Check if serialize function already compiled
|
||||
if (this[kRouteContext][kReplyCacheSerializeFns]?.has(schema)) {
|
||||
return this[kRouteContext][kReplyCacheSerializeFns].get(schema)
|
||||
}
|
||||
|
||||
const serializerCompiler = this[kRouteContext].serializerCompiler ||
|
||||
this.server[kSchemaController].serializerCompiler ||
|
||||
(
|
||||
// We compile the schemas if no custom serializerCompiler is provided
|
||||
// nor set
|
||||
this.server[kSchemaController].setupSerializer(this.server[kOptions]) ||
|
||||
this.server[kSchemaController].serializerCompiler
|
||||
)
|
||||
|
||||
const serializeFn = serializerCompiler({
|
||||
schema,
|
||||
method,
|
||||
url,
|
||||
httpStatus,
|
||||
contentType
|
||||
})
|
||||
|
||||
// We create a WeakMap to compile the schema only once
|
||||
// Its done lazily to avoid add overhead by creating the WeakMap
|
||||
// if it is not used
|
||||
// TODO: Explore a central cache for all the schemas shared across
|
||||
// encapsulated contexts
|
||||
if (this[kRouteContext][kReplyCacheSerializeFns] == null) {
|
||||
this[kRouteContext][kReplyCacheSerializeFns] = new WeakMap()
|
||||
}
|
||||
|
||||
this[kRouteContext][kReplyCacheSerializeFns].set(schema, serializeFn)
|
||||
|
||||
return serializeFn
|
||||
}
|
||||
|
||||
Reply.prototype.serializeInput = function (input, schema, httpStatus, contentType) {
|
||||
const possibleContentType = httpStatus
|
||||
let serialize
|
||||
httpStatus = typeof schema === 'string' || typeof schema === 'number'
|
||||
? schema
|
||||
: httpStatus
|
||||
|
||||
contentType = httpStatus && possibleContentType !== httpStatus
|
||||
? possibleContentType
|
||||
: contentType
|
||||
|
||||
if (httpStatus != null) {
|
||||
if (contentType != null) {
|
||||
serialize = this[kRouteContext][kSchemaResponse]?.[httpStatus]?.[contentType]
|
||||
} else {
|
||||
serialize = this[kRouteContext][kSchemaResponse]?.[httpStatus]
|
||||
}
|
||||
|
||||
if (serialize == null) {
|
||||
if (contentType) throw new FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN(httpStatus, contentType)
|
||||
throw new FST_ERR_MISSING_SERIALIZATION_FN(httpStatus)
|
||||
}
|
||||
} else {
|
||||
// Check if serialize function already compiled
|
||||
if (this[kRouteContext][kReplyCacheSerializeFns]?.has(schema)) {
|
||||
serialize = this[kRouteContext][kReplyCacheSerializeFns].get(schema)
|
||||
} else {
|
||||
serialize = this.compileSerializationSchema(schema, httpStatus, contentType)
|
||||
}
|
||||
}
|
||||
|
||||
return serialize(input)
|
||||
}
|
||||
|
||||
Reply.prototype.serialize = function (payload) {
|
||||
if (this[kReplySerializer] !== null) {
|
||||
return this[kReplySerializer](payload)
|
||||
} else {
|
||||
if (this[kRouteContext] && this[kRouteContext][kReplySerializerDefault]) {
|
||||
return this[kRouteContext][kReplySerializerDefault](payload, this.raw.statusCode)
|
||||
} else {
|
||||
return serialize(this[kRouteContext], payload, this.raw.statusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reply.prototype.serializer = function (fn) {
|
||||
this[kReplySerializer] = fn
|
||||
return this
|
||||
}
|
||||
|
||||
Reply.prototype.type = function (type) {
|
||||
this[kReplyHeaders]['content-type'] = type
|
||||
return this
|
||||
}
|
||||
|
||||
Reply.prototype.redirect = function (url, code) {
|
||||
if (typeof url === 'number') {
|
||||
FSTDEP021()
|
||||
const temp = code
|
||||
code = url
|
||||
url = temp
|
||||
}
|
||||
|
||||
if (!code) {
|
||||
code = this[kReplyHasStatusCode] ? this.raw.statusCode : 302
|
||||
}
|
||||
|
||||
return this.header('location', url).code(code).send()
|
||||
}
|
||||
|
||||
Reply.prototype.callNotFound = function () {
|
||||
notFound(this)
|
||||
return this
|
||||
}
|
||||
|
||||
// TODO: should be removed in fastify@5
|
||||
Reply.prototype.getResponseTime = function () {
|
||||
FSTDEP020()
|
||||
|
||||
return this.elapsedTime
|
||||
}
|
||||
|
||||
// Make reply a thenable, so it could be used with async/await.
|
||||
// See
|
||||
// - https://github.com/fastify/fastify/issues/1864 for the discussions
|
||||
// - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then for the signature
|
||||
Reply.prototype.then = function (fulfilled, rejected) {
|
||||
if (this.sent) {
|
||||
fulfilled()
|
||||
return
|
||||
}
|
||||
|
||||
eos(this.raw, (err) => {
|
||||
// We must not treat ERR_STREAM_PREMATURE_CLOSE as
|
||||
// an error because it is created by eos, not by the stream.
|
||||
if (err && err.code !== 'ERR_STREAM_PREMATURE_CLOSE') {
|
||||
if (rejected) {
|
||||
rejected(err)
|
||||
} else {
|
||||
this.log && this.log.warn('unhandled rejection on reply.then')
|
||||
}
|
||||
} else {
|
||||
fulfilled()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function preSerializationHook (reply, payload) {
|
||||
if (reply[kRouteContext].preSerialization !== null) {
|
||||
preSerializationHookRunner(
|
||||
reply[kRouteContext].preSerialization,
|
||||
reply.request,
|
||||
reply,
|
||||
payload,
|
||||
preSerializationHookEnd
|
||||
)
|
||||
} else {
|
||||
preSerializationHookEnd(null, reply.request, reply, payload)
|
||||
}
|
||||
}
|
||||
|
||||
function preSerializationHookEnd (err, request, reply, payload) {
|
||||
if (err != null) {
|
||||
onErrorHook(reply, err)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
if (reply[kReplySerializer] !== null) {
|
||||
payload = reply[kReplySerializer](payload)
|
||||
} else if (reply[kRouteContext] && reply[kRouteContext][kReplySerializerDefault]) {
|
||||
payload = reply[kRouteContext][kReplySerializerDefault](payload, reply.raw.statusCode)
|
||||
} else {
|
||||
payload = serialize(reply[kRouteContext], payload, reply.raw.statusCode, reply[kReplyHeaders]['content-type'])
|
||||
}
|
||||
} catch (e) {
|
||||
wrapSerializationError(e, reply)
|
||||
onErrorHook(reply, e)
|
||||
return
|
||||
}
|
||||
|
||||
onSendHook(reply, payload)
|
||||
}
|
||||
|
||||
function wrapSerializationError (error, reply) {
|
||||
error.serialization = reply[kRouteContext].config
|
||||
}
|
||||
|
||||
function onSendHook (reply, payload) {
|
||||
if (reply[kRouteContext].onSend !== null) {
|
||||
onSendHookRunner(
|
||||
reply[kRouteContext].onSend,
|
||||
reply.request,
|
||||
reply,
|
||||
payload,
|
||||
wrapOnSendEnd
|
||||
)
|
||||
} else {
|
||||
onSendEnd(reply, payload)
|
||||
}
|
||||
}
|
||||
|
||||
function wrapOnSendEnd (err, request, reply, payload) {
|
||||
if (err != null) {
|
||||
onErrorHook(reply, err)
|
||||
} else {
|
||||
onSendEnd(reply, payload)
|
||||
}
|
||||
}
|
||||
|
||||
function safeWriteHead (reply, statusCode) {
|
||||
const res = reply.raw
|
||||
try {
|
||||
res.writeHead(statusCode, reply[kReplyHeaders])
|
||||
} catch (err) {
|
||||
if (err.code === 'ERR_HTTP_HEADERS_SENT') {
|
||||
reply.log.warn(`Reply was already sent, did you forget to "return reply" in the "${reply.request.raw.url}" (${reply.request.raw.method}) route?`)
|
||||
}
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
function onSendEnd (reply, payload) {
|
||||
const res = reply.raw
|
||||
const req = reply.request
|
||||
|
||||
// we check if we need to update the trailers header and set it
|
||||
if (reply[kReplyTrailers] !== null) {
|
||||
const trailerHeaders = Object.keys(reply[kReplyTrailers])
|
||||
let header = ''
|
||||
for (const trailerName of trailerHeaders) {
|
||||
if (typeof reply[kReplyTrailers][trailerName] !== 'function') continue
|
||||
header += ' '
|
||||
header += trailerName
|
||||
}
|
||||
// it must be chunked for trailer to work
|
||||
reply.header('Transfer-Encoding', 'chunked')
|
||||
reply.header('Trailer', header.trim())
|
||||
}
|
||||
|
||||
// since Response contain status code, headers and body,
|
||||
// we need to update the status, add the headers and use it's body as payload
|
||||
// before continuing
|
||||
if (toString.call(payload) === '[object Response]') {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Response/status
|
||||
if (typeof payload.status === 'number') {
|
||||
reply.code(payload.status)
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Response/headers
|
||||
if (typeof payload.headers === 'object' && typeof payload.headers.forEach === 'function') {
|
||||
for (const [headerName, headerValue] of payload.headers) {
|
||||
reply.header(headerName, headerValue)
|
||||
}
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Response/body
|
||||
if (payload.body !== null) {
|
||||
if (payload.bodyUsed) {
|
||||
throw new FST_ERR_REP_RESPONSE_BODY_CONSUMED()
|
||||
}
|
||||
}
|
||||
// Keep going, body is either null or ReadableStream
|
||||
payload = payload.body
|
||||
}
|
||||
const statusCode = res.statusCode
|
||||
|
||||
if (payload === undefined || payload === null) {
|
||||
// according to https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
|
||||
// we cannot send a content-length for 304 and 204, and all status code
|
||||
// < 200
|
||||
// A sender MUST NOT send a Content-Length header field in any message
|
||||
// that contains a Transfer-Encoding header field.
|
||||
// For HEAD we don't overwrite the `content-length`
|
||||
if (statusCode >= 200 && statusCode !== 204 && statusCode !== 304 && req.method !== 'HEAD' && reply[kReplyTrailers] === null) {
|
||||
reply[kReplyHeaders]['content-length'] = '0'
|
||||
}
|
||||
|
||||
safeWriteHead(reply, statusCode)
|
||||
sendTrailer(payload, res, reply)
|
||||
return
|
||||
}
|
||||
|
||||
if ((statusCode >= 100 && statusCode < 200) || statusCode === 204) {
|
||||
// Responses without a content body must not send content-type
|
||||
// or content-length headers.
|
||||
// See https://www.rfc-editor.org/rfc/rfc9110.html#section-8.6.
|
||||
reply.removeHeader('content-type')
|
||||
reply.removeHeader('content-length')
|
||||
safeWriteHead(reply, statusCode)
|
||||
sendTrailer(undefined, res, reply)
|
||||
if (typeof payload.resume === 'function') {
|
||||
payload.on('error', noop)
|
||||
payload.resume()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// node:stream
|
||||
if (typeof payload.pipe === 'function') {
|
||||
sendStream(payload, res, reply)
|
||||
return
|
||||
}
|
||||
|
||||
// node:stream/web
|
||||
if (typeof payload.getReader === 'function') {
|
||||
sendWebStream(payload, res, reply)
|
||||
return
|
||||
}
|
||||
|
||||
if (typeof payload !== 'string' && !Buffer.isBuffer(payload)) {
|
||||
throw new FST_ERR_REP_INVALID_PAYLOAD_TYPE(typeof payload)
|
||||
}
|
||||
|
||||
if (reply[kReplyTrailers] === null) {
|
||||
const contentLength = reply[kReplyHeaders]['content-length']
|
||||
if (!contentLength ||
|
||||
(req.raw.method !== 'HEAD' &&
|
||||
Number(contentLength) !== Buffer.byteLength(payload)
|
||||
)
|
||||
) {
|
||||
reply[kReplyHeaders]['content-length'] = '' + Buffer.byteLength(payload)
|
||||
}
|
||||
}
|
||||
|
||||
safeWriteHead(reply, statusCode)
|
||||
// write payload first
|
||||
res.write(payload)
|
||||
// then send trailers
|
||||
sendTrailer(payload, res, reply)
|
||||
}
|
||||
|
||||
function logStreamError (logger, err, res) {
|
||||
if (err.code === 'ERR_STREAM_PREMATURE_CLOSE') {
|
||||
if (!logger[kDisableRequestLogging]) {
|
||||
logger.info({ res }, 'stream closed prematurely')
|
||||
}
|
||||
} else {
|
||||
logger.warn({ err }, 'response terminated with an error with headers already sent')
|
||||
}
|
||||
}
|
||||
|
||||
function sendWebStream (payload, res, reply) {
|
||||
const nodeStream = Readable.fromWeb(payload)
|
||||
sendStream(nodeStream, res, reply)
|
||||
}
|
||||
|
||||
function sendStream (payload, res, reply) {
|
||||
let sourceOpen = true
|
||||
let errorLogged = false
|
||||
|
||||
// set trailer when stream ended
|
||||
sendStreamTrailer(payload, res, reply)
|
||||
|
||||
eos(payload, { readable: true, writable: false }, function (err) {
|
||||
sourceOpen = false
|
||||
if (err != null) {
|
||||
if (res.headersSent || reply.request.raw.aborted === true) {
|
||||
if (!errorLogged) {
|
||||
errorLogged = true
|
||||
logStreamError(reply.log, err, reply)
|
||||
}
|
||||
res.destroy()
|
||||
} else {
|
||||
onErrorHook(reply, err)
|
||||
}
|
||||
}
|
||||
// there is nothing to do if there is not an error
|
||||
})
|
||||
|
||||
eos(res, function (err) {
|
||||
if (sourceOpen) {
|
||||
if (err != null && res.headersSent && !errorLogged) {
|
||||
errorLogged = true
|
||||
logStreamError(reply.log, err, res)
|
||||
}
|
||||
if (typeof payload.destroy === 'function') {
|
||||
payload.destroy()
|
||||
} else if (typeof payload.close === 'function') {
|
||||
payload.close(noop)
|
||||
} else if (typeof payload.abort === 'function') {
|
||||
payload.abort()
|
||||
} else {
|
||||
reply.log.warn('stream payload does not end properly')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// streams will error asynchronously, and we want to handle that error
|
||||
// appropriately, e.g. a 404 for a missing file. So we cannot use
|
||||
// writeHead, and we need to resort to setHeader, which will trigger
|
||||
// a writeHead when there is data to send.
|
||||
if (!res.headersSent) {
|
||||
for (const key in reply[kReplyHeaders]) {
|
||||
res.setHeader(key, reply[kReplyHeaders][key])
|
||||
}
|
||||
} else {
|
||||
reply.log.warn('response will send, but you shouldn\'t use res.writeHead in stream mode')
|
||||
}
|
||||
payload.pipe(res)
|
||||
}
|
||||
|
||||
function sendTrailer (payload, res, reply) {
|
||||
if (reply[kReplyTrailers] === null) {
|
||||
// when no trailer, we close the stream
|
||||
res.end(null, null, null) // avoid ArgumentsAdaptorTrampoline from V8
|
||||
return
|
||||
}
|
||||
const trailerHeaders = Object.keys(reply[kReplyTrailers])
|
||||
const trailers = {}
|
||||
let handled = 0
|
||||
let skipped = true
|
||||
function send () {
|
||||
// add trailers when all handler handled
|
||||
/* istanbul ignore else */
|
||||
if (handled === 0) {
|
||||
res.addTrailers(trailers)
|
||||
// we need to properly close the stream
|
||||
// after trailers sent
|
||||
res.end(null, null, null) // avoid ArgumentsAdaptorTrampoline from V8
|
||||
}
|
||||
}
|
||||
|
||||
for (const trailerName of trailerHeaders) {
|
||||
if (typeof reply[kReplyTrailers][trailerName] !== 'function') continue
|
||||
skipped = false
|
||||
handled--
|
||||
|
||||
function cb (err, value) {
|
||||
// TODO: we may protect multiple callback calls
|
||||
// or mixing async-await with callback
|
||||
handled++
|
||||
|
||||
// we can safely ignore error for trailer
|
||||
// since it does affect the client
|
||||
// we log in here only for debug usage
|
||||
if (err) reply.log.debug(err)
|
||||
else trailers[trailerName] = value
|
||||
|
||||
// we push the check to the end of event
|
||||
// loop, so the registration continue to
|
||||
// process.
|
||||
process.nextTick(send)
|
||||
}
|
||||
|
||||
const result = reply[kReplyTrailers][trailerName](reply, payload, cb)
|
||||
if (typeof result === 'object' && typeof result.then === 'function') {
|
||||
result.then((v) => cb(null, v), cb)
|
||||
} else if (result !== null && result !== undefined) {
|
||||
// TODO: should be removed in fastify@5
|
||||
FSTDEP013()
|
||||
cb(null, result)
|
||||
}
|
||||
}
|
||||
|
||||
// when all trailers are skipped
|
||||
// we need to close the stream
|
||||
if (skipped) res.end(null, null, null) // avoid ArgumentsAdaptorTrampoline from V8
|
||||
}
|
||||
|
||||
function sendStreamTrailer (payload, res, reply) {
|
||||
if (reply[kReplyTrailers] === null) return
|
||||
payload.on('end', () => sendTrailer(null, res, reply))
|
||||
}
|
||||
|
||||
function onErrorHook (reply, error, cb) {
|
||||
if (reply[kRouteContext].onError !== null && !reply[kReplyNextErrorHandler]) {
|
||||
reply[kReplyIsRunningOnErrorHook] = true
|
||||
onSendHookRunner(
|
||||
reply[kRouteContext].onError,
|
||||
reply.request,
|
||||
reply,
|
||||
error,
|
||||
() => handleError(reply, error, cb)
|
||||
)
|
||||
} else {
|
||||
handleError(reply, error, cb)
|
||||
}
|
||||
}
|
||||
|
||||
function setupResponseListeners (reply) {
|
||||
reply[kReplyStartTime] = now()
|
||||
|
||||
const onResFinished = err => {
|
||||
reply[kReplyEndTime] = now()
|
||||
reply.raw.removeListener('finish', onResFinished)
|
||||
reply.raw.removeListener('error', onResFinished)
|
||||
|
||||
const ctx = reply[kRouteContext]
|
||||
|
||||
if (ctx && ctx.onResponse !== null) {
|
||||
onResponseHookRunner(
|
||||
ctx.onResponse,
|
||||
reply.request,
|
||||
reply,
|
||||
onResponseCallback
|
||||
)
|
||||
} else {
|
||||
onResponseCallback(err, reply.request, reply)
|
||||
}
|
||||
}
|
||||
|
||||
reply.raw.on('finish', onResFinished)
|
||||
reply.raw.on('error', onResFinished)
|
||||
}
|
||||
|
||||
function onResponseCallback (err, request, reply) {
|
||||
if (reply.log[kDisableRequestLogging]) {
|
||||
return
|
||||
}
|
||||
|
||||
const responseTime = reply.elapsedTime
|
||||
|
||||
if (err != null) {
|
||||
reply.log.error({
|
||||
res: reply,
|
||||
err,
|
||||
responseTime
|
||||
}, 'request errored')
|
||||
return
|
||||
}
|
||||
|
||||
reply.log.info({
|
||||
res: reply,
|
||||
responseTime
|
||||
}, 'request completed')
|
||||
}
|
||||
|
||||
function buildReply (R) {
|
||||
const props = R.props.slice()
|
||||
|
||||
function _Reply (res, request, log) {
|
||||
this.raw = res
|
||||
this[kReplyIsError] = false
|
||||
this[kReplyErrorHandlerCalled] = false
|
||||
this[kReplyHijacked] = false
|
||||
this[kReplySerializer] = null
|
||||
this.request = request
|
||||
this[kReplyHeaders] = {}
|
||||
this[kReplyTrailers] = null
|
||||
this[kReplyStartTime] = undefined
|
||||
this[kReplyEndTime] = undefined
|
||||
this.log = log
|
||||
|
||||
// eslint-disable-next-line no-var
|
||||
var prop
|
||||
// eslint-disable-next-line no-var
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
prop = props[i]
|
||||
this[prop.key] = prop.value
|
||||
}
|
||||
}
|
||||
Object.setPrototypeOf(_Reply.prototype, R.prototype)
|
||||
Object.setPrototypeOf(_Reply, R)
|
||||
_Reply.parent = R
|
||||
_Reply.props = props
|
||||
return _Reply
|
||||
}
|
||||
|
||||
function notFound (reply) {
|
||||
if (reply[kRouteContext][kFourOhFourContext] === null) {
|
||||
reply.log.warn('Trying to send a NotFound error inside a 404 handler. Sending basic 404 response.')
|
||||
reply.code(404).send('404 Not Found')
|
||||
return
|
||||
}
|
||||
|
||||
reply.request[kRouteContext] = reply[kRouteContext][kFourOhFourContext]
|
||||
|
||||
// preHandler hook
|
||||
if (reply[kRouteContext].preHandler !== null) {
|
||||
preHandlerHookRunner(
|
||||
reply[kRouteContext].preHandler,
|
||||
reply.request,
|
||||
reply,
|
||||
internals.preHandlerCallback
|
||||
)
|
||||
} else {
|
||||
internals.preHandlerCallback(null, reply.request, reply)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function runs when a payload that is not a string|buffer|stream or null
|
||||
* should be serialized to be streamed to the response.
|
||||
* This is the default serializer that can be customized by the user using the replySerializer
|
||||
*
|
||||
* @param {object} context the request context
|
||||
* @param {object} data the JSON payload to serialize
|
||||
* @param {number} statusCode the http status code
|
||||
* @param {string} [contentType] the reply content type
|
||||
* @returns {string} the serialized payload
|
||||
*/
|
||||
function serialize (context, data, statusCode, contentType) {
|
||||
const fnSerialize = getSchemaSerializer(context, statusCode, contentType)
|
||||
if (fnSerialize) {
|
||||
return fnSerialize(data)
|
||||
}
|
||||
return JSON.stringify(data)
|
||||
}
|
||||
|
||||
function noop () { }
|
||||
|
||||
module.exports = Reply
|
||||
module.exports.buildReply = buildReply
|
||||
module.exports.setupResponseListeners = setupResponseListeners
|
||||
52
backend/node_modules/fastify/lib/reqIdGenFactory.js
generated
vendored
Normal file
52
backend/node_modules/fastify/lib/reqIdGenFactory.js
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* @callback GenerateRequestId
|
||||
* @param {Object} req
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} [requestIdHeader]
|
||||
* @param {GenerateRequestId} [optGenReqId]
|
||||
* @returns {GenerateRequestId}
|
||||
*/
|
||||
function reqIdGenFactory (requestIdHeader, optGenReqId) {
|
||||
const genReqId = optGenReqId || buildDefaultGenReqId()
|
||||
|
||||
if (requestIdHeader) {
|
||||
return buildOptionalHeaderReqId(requestIdHeader, genReqId)
|
||||
}
|
||||
|
||||
return genReqId
|
||||
}
|
||||
|
||||
function getGenReqId (contextServer, req) {
|
||||
return contextServer.genReqId(req)
|
||||
}
|
||||
|
||||
function buildDefaultGenReqId () {
|
||||
// 2,147,483,647 (2^31 − 1) stands for max SMI value (an internal optimization of V8).
|
||||
// With this upper bound, if you'll be generating 1k ids/sec, you're going to hit it in ~25 days.
|
||||
// This is very likely to happen in real-world applications, hence the limit is enforced.
|
||||
// Growing beyond this value will make the id generation slower and cause a deopt.
|
||||
// In the worst cases, it will become a float, losing accuracy.
|
||||
const maxInt = 2147483647
|
||||
|
||||
let nextReqId = 0
|
||||
return function defaultGenReqId () {
|
||||
nextReqId = (nextReqId + 1) & maxInt
|
||||
return `req-${nextReqId.toString(36)}`
|
||||
}
|
||||
}
|
||||
|
||||
function buildOptionalHeaderReqId (requestIdHeader, genReqId) {
|
||||
return function (req) {
|
||||
return req.headers[requestIdHeader] || genReqId(req)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getGenReqId,
|
||||
reqIdGenFactory
|
||||
}
|
||||
366
backend/node_modules/fastify/lib/request.js
generated
vendored
Normal file
366
backend/node_modules/fastify/lib/request.js
generated
vendored
Normal file
@@ -0,0 +1,366 @@
|
||||
'use strict'
|
||||
|
||||
const proxyAddr = require('proxy-addr')
|
||||
const semver = require('semver')
|
||||
const {
|
||||
FSTDEP005,
|
||||
FSTDEP012,
|
||||
FSTDEP015,
|
||||
FSTDEP016,
|
||||
FSTDEP017,
|
||||
FSTDEP018
|
||||
} = require('./warnings')
|
||||
const {
|
||||
kHasBeenDecorated,
|
||||
kSchemaBody,
|
||||
kSchemaHeaders,
|
||||
kSchemaParams,
|
||||
kSchemaQuerystring,
|
||||
kSchemaController,
|
||||
kOptions,
|
||||
kRequestCacheValidateFns,
|
||||
kRouteContext,
|
||||
kPublicRouteContext,
|
||||
kRequestOriginalUrl
|
||||
} = require('./symbols')
|
||||
const { FST_ERR_REQ_INVALID_VALIDATION_INVOCATION } = require('./errors')
|
||||
|
||||
const HTTP_PART_SYMBOL_MAP = {
|
||||
body: kSchemaBody,
|
||||
headers: kSchemaHeaders,
|
||||
params: kSchemaParams,
|
||||
querystring: kSchemaQuerystring,
|
||||
query: kSchemaQuerystring
|
||||
}
|
||||
|
||||
function Request (id, params, req, query, log, context) {
|
||||
this.id = id
|
||||
this[kRouteContext] = context
|
||||
this.params = params
|
||||
this.raw = req
|
||||
this.query = query
|
||||
this.log = log
|
||||
this.body = undefined
|
||||
}
|
||||
Request.props = []
|
||||
|
||||
function getTrustProxyFn (tp) {
|
||||
if (typeof tp === 'function') {
|
||||
return tp
|
||||
}
|
||||
if (tp === true) {
|
||||
// Support plain true/false
|
||||
return function () { return true }
|
||||
}
|
||||
if (typeof tp === 'number') {
|
||||
// Support trusting hop count
|
||||
return function (a, i) { return i < tp }
|
||||
}
|
||||
if (typeof tp === 'string') {
|
||||
// Support comma-separated tps
|
||||
const values = tp.split(',').map(it => it.trim())
|
||||
return proxyAddr.compile(values)
|
||||
}
|
||||
return proxyAddr.compile(tp)
|
||||
}
|
||||
|
||||
function buildRequest (R, trustProxy) {
|
||||
if (trustProxy) {
|
||||
return buildRequestWithTrustProxy(R, trustProxy)
|
||||
}
|
||||
|
||||
return buildRegularRequest(R)
|
||||
}
|
||||
|
||||
function buildRegularRequest (R) {
|
||||
const props = R.props.slice()
|
||||
function _Request (id, params, req, query, log, context) {
|
||||
this.id = id
|
||||
this[kRouteContext] = context
|
||||
this.params = params
|
||||
this.raw = req
|
||||
this.query = query
|
||||
this.log = log
|
||||
this.body = undefined
|
||||
|
||||
// eslint-disable-next-line no-var
|
||||
var prop
|
||||
// eslint-disable-next-line no-var
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
prop = props[i]
|
||||
this[prop.key] = prop.value
|
||||
}
|
||||
}
|
||||
Object.setPrototypeOf(_Request.prototype, R.prototype)
|
||||
Object.setPrototypeOf(_Request, R)
|
||||
_Request.props = props
|
||||
_Request.parent = R
|
||||
|
||||
return _Request
|
||||
}
|
||||
|
||||
function getLastEntryInMultiHeaderValue (headerValue) {
|
||||
// we use the last one if the header is set more than once
|
||||
const lastIndex = headerValue.lastIndexOf(',')
|
||||
return lastIndex === -1 ? headerValue.trim() : headerValue.slice(lastIndex + 1).trim()
|
||||
}
|
||||
|
||||
function buildRequestWithTrustProxy (R, trustProxy) {
|
||||
const _Request = buildRegularRequest(R)
|
||||
const proxyFn = getTrustProxyFn(trustProxy)
|
||||
|
||||
// This is a more optimized version of decoration
|
||||
_Request[kHasBeenDecorated] = true
|
||||
|
||||
Object.defineProperties(_Request.prototype, {
|
||||
ip: {
|
||||
get () {
|
||||
return proxyAddr(this.raw, proxyFn)
|
||||
}
|
||||
},
|
||||
ips: {
|
||||
get () {
|
||||
return proxyAddr.all(this.raw, proxyFn)
|
||||
}
|
||||
},
|
||||
hostname: {
|
||||
get () {
|
||||
if (this.ip !== undefined && this.headers['x-forwarded-host']) {
|
||||
return getLastEntryInMultiHeaderValue(this.headers['x-forwarded-host'])
|
||||
}
|
||||
return this.headers.host || this.headers[':authority']
|
||||
}
|
||||
},
|
||||
protocol: {
|
||||
get () {
|
||||
if (this.headers['x-forwarded-proto']) {
|
||||
return getLastEntryInMultiHeaderValue(this.headers['x-forwarded-proto'])
|
||||
}
|
||||
if (this.socket) {
|
||||
return this.socket.encrypted ? 'https' : 'http'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return _Request
|
||||
}
|
||||
|
||||
Object.defineProperties(Request.prototype, {
|
||||
server: {
|
||||
get () {
|
||||
return this[kRouteContext].server
|
||||
}
|
||||
},
|
||||
url: {
|
||||
get () {
|
||||
return this.raw.url
|
||||
}
|
||||
},
|
||||
originalUrl: {
|
||||
get () {
|
||||
/* istanbul ignore else */
|
||||
if (!this[kRequestOriginalUrl]) {
|
||||
this[kRequestOriginalUrl] = this.raw.originalUrl || this.raw.url
|
||||
}
|
||||
return this[kRequestOriginalUrl]
|
||||
}
|
||||
},
|
||||
method: {
|
||||
get () {
|
||||
return this.raw.method
|
||||
}
|
||||
},
|
||||
context: {
|
||||
get () {
|
||||
FSTDEP012()
|
||||
return this[kRouteContext]
|
||||
}
|
||||
},
|
||||
routerPath: {
|
||||
get () {
|
||||
FSTDEP017()
|
||||
return this[kRouteContext].config?.url
|
||||
}
|
||||
},
|
||||
routeOptions: {
|
||||
get () {
|
||||
const context = this[kRouteContext]
|
||||
const routeLimit = context._parserOptions.limit
|
||||
const serverLimit = context.server.initialConfig.bodyLimit
|
||||
const version = context.server.hasConstraintStrategy('version') ? this.raw.headers['accept-version'] : undefined
|
||||
const options = {
|
||||
method: context.config?.method,
|
||||
url: context.config?.url,
|
||||
bodyLimit: (routeLimit || serverLimit),
|
||||
attachValidation: context.attachValidation,
|
||||
logLevel: context.logLevel,
|
||||
exposeHeadRoute: context.exposeHeadRoute,
|
||||
prefixTrailingSlash: context.prefixTrailingSlash,
|
||||
handler: context.handler,
|
||||
version
|
||||
}
|
||||
|
||||
Object.defineProperties(options, {
|
||||
config: {
|
||||
get: () => context.config
|
||||
},
|
||||
schema: {
|
||||
get: () => context.schema
|
||||
}
|
||||
})
|
||||
|
||||
return Object.freeze(options)
|
||||
}
|
||||
},
|
||||
routerMethod: {
|
||||
get () {
|
||||
FSTDEP018()
|
||||
return this[kRouteContext].config?.method
|
||||
}
|
||||
},
|
||||
routeConfig: {
|
||||
get () {
|
||||
FSTDEP016()
|
||||
return this[kRouteContext][kPublicRouteContext]?.config
|
||||
}
|
||||
},
|
||||
routeSchema: {
|
||||
get () {
|
||||
FSTDEP015()
|
||||
return this[kRouteContext][kPublicRouteContext].schema
|
||||
}
|
||||
},
|
||||
is404: {
|
||||
get () {
|
||||
return this[kRouteContext].config?.url === undefined
|
||||
}
|
||||
},
|
||||
connection: {
|
||||
get () {
|
||||
/* istanbul ignore next */
|
||||
if (semver.gte(process.versions.node, '13.0.0')) {
|
||||
FSTDEP005()
|
||||
}
|
||||
return this.raw.connection
|
||||
}
|
||||
},
|
||||
socket: {
|
||||
get () {
|
||||
return this.raw.socket
|
||||
}
|
||||
},
|
||||
ip: {
|
||||
get () {
|
||||
if (this.socket) {
|
||||
return this.socket.remoteAddress
|
||||
}
|
||||
}
|
||||
},
|
||||
hostname: {
|
||||
get () {
|
||||
return this.raw.headers.host || this.raw.headers[':authority']
|
||||
}
|
||||
},
|
||||
protocol: {
|
||||
get () {
|
||||
if (this.socket) {
|
||||
return this.socket.encrypted ? 'https' : 'http'
|
||||
}
|
||||
}
|
||||
},
|
||||
headers: {
|
||||
get () {
|
||||
if (this.additionalHeaders) {
|
||||
return Object.assign({}, this.raw.headers, this.additionalHeaders)
|
||||
}
|
||||
return this.raw.headers
|
||||
},
|
||||
set (headers) {
|
||||
this.additionalHeaders = headers
|
||||
}
|
||||
},
|
||||
getValidationFunction: {
|
||||
value: function (httpPartOrSchema) {
|
||||
if (typeof httpPartOrSchema === 'string') {
|
||||
const symbol = HTTP_PART_SYMBOL_MAP[httpPartOrSchema]
|
||||
return this[kRouteContext][symbol]
|
||||
} else if (typeof httpPartOrSchema === 'object') {
|
||||
return this[kRouteContext][kRequestCacheValidateFns]?.get(httpPartOrSchema)
|
||||
}
|
||||
}
|
||||
},
|
||||
compileValidationSchema: {
|
||||
value: function (schema, httpPart = null) {
|
||||
const { method, url } = this
|
||||
|
||||
if (this[kRouteContext][kRequestCacheValidateFns]?.has(schema)) {
|
||||
return this[kRouteContext][kRequestCacheValidateFns].get(schema)
|
||||
}
|
||||
|
||||
const validatorCompiler = this[kRouteContext].validatorCompiler ||
|
||||
this.server[kSchemaController].validatorCompiler ||
|
||||
(
|
||||
// We compile the schemas if no custom validatorCompiler is provided
|
||||
// nor set
|
||||
this.server[kSchemaController].setupValidator(this.server[kOptions]) ||
|
||||
this.server[kSchemaController].validatorCompiler
|
||||
)
|
||||
|
||||
const validateFn = validatorCompiler({
|
||||
schema,
|
||||
method,
|
||||
url,
|
||||
httpPart
|
||||
})
|
||||
|
||||
// We create a WeakMap to compile the schema only once
|
||||
// Its done lazily to avoid add overhead by creating the WeakMap
|
||||
// if it is not used
|
||||
// TODO: Explore a central cache for all the schemas shared across
|
||||
// encapsulated contexts
|
||||
if (this[kRouteContext][kRequestCacheValidateFns] == null) {
|
||||
this[kRouteContext][kRequestCacheValidateFns] = new WeakMap()
|
||||
}
|
||||
|
||||
this[kRouteContext][kRequestCacheValidateFns].set(schema, validateFn)
|
||||
|
||||
return validateFn
|
||||
}
|
||||
},
|
||||
validateInput: {
|
||||
value: function (input, schema, httpPart) {
|
||||
httpPart = typeof schema === 'string' ? schema : httpPart
|
||||
|
||||
const symbol = (httpPart != null && typeof httpPart === 'string') && HTTP_PART_SYMBOL_MAP[httpPart]
|
||||
let validate
|
||||
|
||||
if (symbol) {
|
||||
// Validate using the HTTP Request Part schema
|
||||
validate = this[kRouteContext][symbol]
|
||||
}
|
||||
|
||||
// We cannot compile if the schema is missed
|
||||
if (validate == null && (schema == null ||
|
||||
typeof schema !== 'object' ||
|
||||
Array.isArray(schema))
|
||||
) {
|
||||
throw new FST_ERR_REQ_INVALID_VALIDATION_INVOCATION(httpPart)
|
||||
}
|
||||
|
||||
if (validate == null) {
|
||||
if (this[kRouteContext][kRequestCacheValidateFns]?.has(schema)) {
|
||||
validate = this[kRouteContext][kRequestCacheValidateFns].get(schema)
|
||||
} else {
|
||||
// We proceed to compile if there's no validate function yet
|
||||
validate = this.compileValidationSchema(schema, httpPart)
|
||||
}
|
||||
}
|
||||
|
||||
return validate(input)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = Request
|
||||
module.exports.buildRequest = buildRequest
|
||||
628
backend/node_modules/fastify/lib/route.js
generated
vendored
Normal file
628
backend/node_modules/fastify/lib/route.js
generated
vendored
Normal file
@@ -0,0 +1,628 @@
|
||||
'use strict'
|
||||
|
||||
const FindMyWay = require('find-my-way')
|
||||
const Context = require('./context')
|
||||
const handleRequest = require('./handleRequest')
|
||||
const { onRequestAbortHookRunner, lifecycleHooks, preParsingHookRunner, onTimeoutHookRunner, onRequestHookRunner } = require('./hooks')
|
||||
const { supportedMethods } = require('./httpMethods')
|
||||
const { normalizeSchema } = require('./schemas')
|
||||
const { parseHeadOnSendHandlers } = require('./headRoute')
|
||||
const {
|
||||
FSTDEP007,
|
||||
FSTDEP008,
|
||||
FSTDEP014
|
||||
} = require('./warnings')
|
||||
|
||||
const {
|
||||
compileSchemasForValidation,
|
||||
compileSchemasForSerialization
|
||||
} = require('./validation')
|
||||
|
||||
const {
|
||||
FST_ERR_SCH_VALIDATION_BUILD,
|
||||
FST_ERR_SCH_SERIALIZATION_BUILD,
|
||||
FST_ERR_DEFAULT_ROUTE_INVALID_TYPE,
|
||||
FST_ERR_DUPLICATED_ROUTE,
|
||||
FST_ERR_INVALID_URL,
|
||||
FST_ERR_HOOK_INVALID_HANDLER,
|
||||
FST_ERR_ROUTE_OPTIONS_NOT_OBJ,
|
||||
FST_ERR_ROUTE_DUPLICATED_HANDLER,
|
||||
FST_ERR_ROUTE_HANDLER_NOT_FN,
|
||||
FST_ERR_ROUTE_MISSING_HANDLER,
|
||||
FST_ERR_ROUTE_METHOD_NOT_SUPPORTED,
|
||||
FST_ERR_ROUTE_METHOD_INVALID,
|
||||
FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED,
|
||||
FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT,
|
||||
FST_ERR_HOOK_INVALID_ASYNC_HANDLER
|
||||
} = require('./errors')
|
||||
|
||||
const {
|
||||
kRoutePrefix,
|
||||
kLogLevel,
|
||||
kLogSerializers,
|
||||
kHooks,
|
||||
kSchemaController,
|
||||
kOptions,
|
||||
kReplySerializerDefault,
|
||||
kReplyIsError,
|
||||
kRequestPayloadStream,
|
||||
kDisableRequestLogging,
|
||||
kSchemaErrorFormatter,
|
||||
kErrorHandler,
|
||||
kHasBeenDecorated,
|
||||
kRequestAcceptVersion,
|
||||
kRouteByFastify,
|
||||
kRouteContext
|
||||
} = require('./symbols.js')
|
||||
const { buildErrorHandler } = require('./error-handler')
|
||||
const { createChildLogger } = require('./logger')
|
||||
const { getGenReqId } = require('./reqIdGenFactory.js')
|
||||
|
||||
function buildRouting (options) {
|
||||
const router = FindMyWay(options.config)
|
||||
|
||||
let avvio
|
||||
let fourOhFour
|
||||
let logger
|
||||
let hasLogger
|
||||
let setupResponseListeners
|
||||
let throwIfAlreadyStarted
|
||||
let disableRequestLogging
|
||||
let ignoreTrailingSlash
|
||||
let ignoreDuplicateSlashes
|
||||
let return503OnClosing
|
||||
let globalExposeHeadRoutes
|
||||
let validateHTTPVersion
|
||||
let keepAliveConnections
|
||||
|
||||
let closing = false
|
||||
|
||||
return {
|
||||
/**
|
||||
* @param {import('../fastify').FastifyServerOptions} options
|
||||
* @param {*} fastifyArgs
|
||||
*/
|
||||
setup (options, fastifyArgs) {
|
||||
avvio = fastifyArgs.avvio
|
||||
fourOhFour = fastifyArgs.fourOhFour
|
||||
logger = fastifyArgs.logger
|
||||
hasLogger = fastifyArgs.hasLogger
|
||||
setupResponseListeners = fastifyArgs.setupResponseListeners
|
||||
throwIfAlreadyStarted = fastifyArgs.throwIfAlreadyStarted
|
||||
validateHTTPVersion = fastifyArgs.validateHTTPVersion
|
||||
|
||||
globalExposeHeadRoutes = options.exposeHeadRoutes
|
||||
disableRequestLogging = options.disableRequestLogging
|
||||
ignoreTrailingSlash = options.ignoreTrailingSlash
|
||||
ignoreDuplicateSlashes = options.ignoreDuplicateSlashes
|
||||
return503OnClosing = Object.prototype.hasOwnProperty.call(options, 'return503OnClosing') ? options.return503OnClosing : true
|
||||
keepAliveConnections = fastifyArgs.keepAliveConnections
|
||||
},
|
||||
routing: router.lookup.bind(router), // router func to find the right handler to call
|
||||
route, // configure a route in the fastify instance
|
||||
hasRoute,
|
||||
prepareRoute,
|
||||
getDefaultRoute: function () {
|
||||
FSTDEP014()
|
||||
return router.defaultRoute
|
||||
},
|
||||
setDefaultRoute: function (defaultRoute) {
|
||||
FSTDEP014()
|
||||
if (typeof defaultRoute !== 'function') {
|
||||
throw new FST_ERR_DEFAULT_ROUTE_INVALID_TYPE()
|
||||
}
|
||||
|
||||
router.defaultRoute = defaultRoute
|
||||
},
|
||||
routeHandler,
|
||||
closeRoutes: () => { closing = true },
|
||||
printRoutes: router.prettyPrint.bind(router),
|
||||
addConstraintStrategy,
|
||||
hasConstraintStrategy,
|
||||
isAsyncConstraint,
|
||||
findRoute
|
||||
}
|
||||
|
||||
function addConstraintStrategy (strategy) {
|
||||
throwIfAlreadyStarted('Cannot add constraint strategy!')
|
||||
return router.addConstraintStrategy(strategy)
|
||||
}
|
||||
|
||||
function hasConstraintStrategy (strategyName) {
|
||||
return router.hasConstraintStrategy(strategyName)
|
||||
}
|
||||
|
||||
function isAsyncConstraint () {
|
||||
return router.constrainer.asyncStrategiesInUse.size > 0
|
||||
}
|
||||
|
||||
// Convert shorthand to extended route declaration
|
||||
function prepareRoute ({ method, url, options, handler, isFastify }) {
|
||||
if (typeof url !== 'string') {
|
||||
throw new FST_ERR_INVALID_URL(typeof url)
|
||||
}
|
||||
|
||||
if (!handler && typeof options === 'function') {
|
||||
handler = options // for support over direct function calls such as fastify.get() options are reused as the handler
|
||||
options = {}
|
||||
} else if (handler && typeof handler === 'function') {
|
||||
if (Object.prototype.toString.call(options) !== '[object Object]') {
|
||||
throw new FST_ERR_ROUTE_OPTIONS_NOT_OBJ(method, url)
|
||||
} else if (options.handler) {
|
||||
if (typeof options.handler === 'function') {
|
||||
throw new FST_ERR_ROUTE_DUPLICATED_HANDLER(method, url)
|
||||
} else {
|
||||
throw new FST_ERR_ROUTE_HANDLER_NOT_FN(method, url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
options = Object.assign({}, options, {
|
||||
method,
|
||||
url,
|
||||
path: url,
|
||||
handler: handler || (options && options.handler)
|
||||
})
|
||||
|
||||
return route.call(this, { options, isFastify })
|
||||
}
|
||||
|
||||
function hasRoute ({ options }) {
|
||||
const normalizedMethod = options.method?.toUpperCase() ?? ''
|
||||
return findRoute({
|
||||
...options,
|
||||
method: normalizedMethod
|
||||
}) !== null
|
||||
}
|
||||
|
||||
function findRoute (options) {
|
||||
const route = router.find(
|
||||
options.method,
|
||||
options.url || '',
|
||||
options.constraints
|
||||
)
|
||||
if (route) {
|
||||
// we must reduce the expose surface, otherwise
|
||||
// we provide the ability for the user to modify
|
||||
// all the route and server information in runtime
|
||||
return {
|
||||
handler: route.handler,
|
||||
params: route.params,
|
||||
searchParams: route.searchParams
|
||||
}
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Route management
|
||||
* @param {{ options: import('../fastify').RouteOptions, isFastify: boolean }}
|
||||
*/
|
||||
function route ({ options, isFastify }) {
|
||||
// Since we are mutating/assigning only top level props, it is fine to have a shallow copy using the spread operator
|
||||
const opts = { ...options }
|
||||
|
||||
const { exposeHeadRoute } = opts
|
||||
const hasRouteExposeHeadRouteFlag = exposeHeadRoute != null
|
||||
const shouldExposeHead = hasRouteExposeHeadRouteFlag ? exposeHeadRoute : globalExposeHeadRoutes
|
||||
|
||||
const isGetRoute = opts.method === 'GET' ||
|
||||
(Array.isArray(opts.method) && opts.method.includes('GET'))
|
||||
const isHeadRoute = opts.method === 'HEAD' ||
|
||||
(Array.isArray(opts.method) && opts.method.includes('HEAD'))
|
||||
|
||||
// we need to clone a set of initial options for HEAD route
|
||||
const headOpts = shouldExposeHead && isGetRoute ? { ...options } : null
|
||||
|
||||
throwIfAlreadyStarted('Cannot add route!')
|
||||
|
||||
const path = opts.url || opts.path || ''
|
||||
|
||||
if (Array.isArray(opts.method)) {
|
||||
// eslint-disable-next-line no-var
|
||||
for (var i = 0; i < opts.method.length; ++i) {
|
||||
opts.method[i] = normalizeAndValidateMethod(opts.method[i])
|
||||
validateSchemaBodyOption(opts.method[i], path, opts.schema)
|
||||
}
|
||||
} else {
|
||||
opts.method = normalizeAndValidateMethod(opts.method)
|
||||
validateSchemaBodyOption(opts.method, path, opts.schema)
|
||||
}
|
||||
|
||||
if (!opts.handler) {
|
||||
throw new FST_ERR_ROUTE_MISSING_HANDLER(opts.method, path)
|
||||
}
|
||||
|
||||
if (opts.errorHandler !== undefined && typeof opts.errorHandler !== 'function') {
|
||||
throw new FST_ERR_ROUTE_HANDLER_NOT_FN(opts.method, path)
|
||||
}
|
||||
|
||||
validateBodyLimitOption(opts.bodyLimit)
|
||||
|
||||
const prefix = this[kRoutePrefix]
|
||||
|
||||
if (path === '/' && prefix.length > 0 && opts.method !== 'HEAD') {
|
||||
switch (opts.prefixTrailingSlash) {
|
||||
case 'slash':
|
||||
addNewRoute.call(this, { path, isFastify })
|
||||
break
|
||||
case 'no-slash':
|
||||
addNewRoute.call(this, { path: '', isFastify })
|
||||
break
|
||||
case 'both':
|
||||
default:
|
||||
addNewRoute.call(this, { path: '', isFastify })
|
||||
// If ignoreTrailingSlash is set to true we need to add only the '' route to prevent adding an incomplete one.
|
||||
if (ignoreTrailingSlash !== true && (ignoreDuplicateSlashes !== true || !prefix.endsWith('/'))) {
|
||||
addNewRoute.call(this, { path, prefixing: true, isFastify })
|
||||
}
|
||||
}
|
||||
} else if (path[0] === '/' && prefix.endsWith('/')) {
|
||||
// Ensure that '/prefix/' + '/route' gets registered as '/prefix/route'
|
||||
addNewRoute.call(this, { path: path.slice(1), isFastify })
|
||||
} else {
|
||||
addNewRoute.call(this, { path, isFastify })
|
||||
}
|
||||
|
||||
// chainable api
|
||||
return this
|
||||
|
||||
function addNewRoute ({ path, prefixing = false, isFastify = false }) {
|
||||
const url = prefix + path
|
||||
|
||||
opts.url = url
|
||||
opts.path = url
|
||||
opts.routePath = path
|
||||
opts.prefix = prefix
|
||||
opts.logLevel = opts.logLevel || this[kLogLevel]
|
||||
|
||||
if (this[kLogSerializers] || opts.logSerializers) {
|
||||
opts.logSerializers = Object.assign(Object.create(this[kLogSerializers]), opts.logSerializers)
|
||||
}
|
||||
|
||||
if (opts.attachValidation == null) {
|
||||
opts.attachValidation = false
|
||||
}
|
||||
|
||||
if (prefixing === false) {
|
||||
// run 'onRoute' hooks
|
||||
for (const hook of this[kHooks].onRoute) {
|
||||
hook.call(this, opts)
|
||||
}
|
||||
}
|
||||
|
||||
for (const hook of lifecycleHooks) {
|
||||
if (opts && hook in opts) {
|
||||
if (Array.isArray(opts[hook])) {
|
||||
for (const func of opts[hook]) {
|
||||
if (typeof func !== 'function') {
|
||||
throw new FST_ERR_HOOK_INVALID_HANDLER(hook, Object.prototype.toString.call(func))
|
||||
}
|
||||
|
||||
if (hook === 'onSend' || hook === 'preSerialization' || hook === 'onError' || hook === 'preParsing') {
|
||||
if (func.constructor.name === 'AsyncFunction' && func.length === 4) {
|
||||
throw new FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
|
||||
}
|
||||
} else if (hook === 'onRequestAbort') {
|
||||
if (func.constructor.name === 'AsyncFunction' && func.length !== 1) {
|
||||
throw new FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
|
||||
}
|
||||
} else {
|
||||
if (func.constructor.name === 'AsyncFunction' && func.length === 3) {
|
||||
throw new FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (opts[hook] !== undefined && typeof opts[hook] !== 'function') {
|
||||
throw new FST_ERR_HOOK_INVALID_HANDLER(hook, Object.prototype.toString.call(opts[hook]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const constraints = opts.constraints || {}
|
||||
const config = {
|
||||
...opts.config,
|
||||
url,
|
||||
method: opts.method
|
||||
}
|
||||
|
||||
const context = new Context({
|
||||
schema: opts.schema,
|
||||
handler: opts.handler.bind(this),
|
||||
config,
|
||||
errorHandler: opts.errorHandler,
|
||||
childLoggerFactory: opts.childLoggerFactory,
|
||||
bodyLimit: opts.bodyLimit,
|
||||
logLevel: opts.logLevel,
|
||||
logSerializers: opts.logSerializers,
|
||||
attachValidation: opts.attachValidation,
|
||||
schemaErrorFormatter: opts.schemaErrorFormatter,
|
||||
replySerializer: this[kReplySerializerDefault],
|
||||
validatorCompiler: opts.validatorCompiler,
|
||||
serializerCompiler: opts.serializerCompiler,
|
||||
exposeHeadRoute: shouldExposeHead,
|
||||
prefixTrailingSlash: (opts.prefixTrailingSlash || 'both'),
|
||||
server: this,
|
||||
isFastify
|
||||
})
|
||||
|
||||
if (opts.version) {
|
||||
FSTDEP008()
|
||||
constraints.version = opts.version
|
||||
}
|
||||
|
||||
const headHandler = router.findRoute('HEAD', opts.url, constraints)
|
||||
const hasHEADHandler = headHandler !== null
|
||||
|
||||
// remove the head route created by fastify
|
||||
if (isHeadRoute && hasHEADHandler && !context[kRouteByFastify] && headHandler.store[kRouteByFastify]) {
|
||||
router.off('HEAD', opts.url, constraints)
|
||||
}
|
||||
|
||||
try {
|
||||
router.on(opts.method, opts.url, { constraints }, routeHandler, context)
|
||||
} catch (error) {
|
||||
// any route insertion error created by fastify can be safely ignore
|
||||
// because it only duplicate route for head
|
||||
if (!context[kRouteByFastify]) {
|
||||
const isDuplicatedRoute = error.message.includes(`Method '${opts.method}' already declared for route`)
|
||||
if (isDuplicatedRoute) {
|
||||
throw new FST_ERR_DUPLICATED_ROUTE(opts.method, opts.url)
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
this.after((notHandledErr, done) => {
|
||||
// Send context async
|
||||
context.errorHandler = opts.errorHandler ? buildErrorHandler(this[kErrorHandler], opts.errorHandler) : this[kErrorHandler]
|
||||
context._parserOptions.limit = opts.bodyLimit || null
|
||||
context.logLevel = opts.logLevel
|
||||
context.logSerializers = opts.logSerializers
|
||||
context.attachValidation = opts.attachValidation
|
||||
context[kReplySerializerDefault] = this[kReplySerializerDefault]
|
||||
context.schemaErrorFormatter = opts.schemaErrorFormatter || this[kSchemaErrorFormatter] || context.schemaErrorFormatter
|
||||
|
||||
// Run hooks and more
|
||||
avvio.once('preReady', () => {
|
||||
for (const hook of lifecycleHooks) {
|
||||
const toSet = this[kHooks][hook]
|
||||
.concat(opts[hook] || [])
|
||||
.map(h => h.bind(this))
|
||||
context[hook] = toSet.length ? toSet : null
|
||||
}
|
||||
|
||||
// Optimization: avoid encapsulation if no decoration has been done.
|
||||
while (!context.Request[kHasBeenDecorated] && context.Request.parent) {
|
||||
context.Request = context.Request.parent
|
||||
}
|
||||
while (!context.Reply[kHasBeenDecorated] && context.Reply.parent) {
|
||||
context.Reply = context.Reply.parent
|
||||
}
|
||||
|
||||
// Must store the 404 Context in 'preReady' because it is only guaranteed to
|
||||
// be available after all of the plugins and routes have been loaded.
|
||||
fourOhFour.setContext(this, context)
|
||||
|
||||
if (opts.schema) {
|
||||
context.schema = normalizeSchema(opts, context.schema, this.initialConfig)
|
||||
|
||||
const schemaController = this[kSchemaController]
|
||||
if (!opts.validatorCompiler && (opts.schema.body || opts.schema.headers || opts.schema.querystring || opts.schema.params)) {
|
||||
schemaController.setupValidator(this[kOptions])
|
||||
}
|
||||
try {
|
||||
const isCustom = typeof opts?.validatorCompiler === 'function' || schemaController.isCustomValidatorCompiler
|
||||
compileSchemasForValidation(context, opts.validatorCompiler || schemaController.validatorCompiler, isCustom)
|
||||
} catch (error) {
|
||||
throw new FST_ERR_SCH_VALIDATION_BUILD(opts.method, url, error.message)
|
||||
}
|
||||
|
||||
if (opts.schema.response && !opts.serializerCompiler) {
|
||||
schemaController.setupSerializer(this[kOptions])
|
||||
}
|
||||
try {
|
||||
compileSchemasForSerialization(context, opts.serializerCompiler || schemaController.serializerCompiler)
|
||||
} catch (error) {
|
||||
throw new FST_ERR_SCH_SERIALIZATION_BUILD(opts.method, url, error.message)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
done(notHandledErr)
|
||||
})
|
||||
|
||||
// register head route in sync
|
||||
// we must place it after the `this.after`
|
||||
|
||||
if (shouldExposeHead && isGetRoute && !isHeadRoute && !hasHEADHandler) {
|
||||
const onSendHandlers = parseHeadOnSendHandlers(headOpts.onSend)
|
||||
prepareRoute.call(this, { method: 'HEAD', url: path, options: { ...headOpts, onSend: onSendHandlers }, isFastify: true })
|
||||
} else if (hasHEADHandler && exposeHeadRoute) {
|
||||
FSTDEP007()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HTTP request entry point, the routing has already been executed
|
||||
function routeHandler (req, res, params, context, query) {
|
||||
const id = getGenReqId(context.server, req)
|
||||
|
||||
const loggerOpts = {
|
||||
level: context.logLevel
|
||||
}
|
||||
|
||||
if (context.logSerializers) {
|
||||
loggerOpts.serializers = context.logSerializers
|
||||
}
|
||||
const childLogger = createChildLogger(context, logger, req, id, loggerOpts)
|
||||
childLogger[kDisableRequestLogging] = disableRequestLogging
|
||||
|
||||
// TODO: The check here should be removed once https://github.com/nodejs/node/issues/43115 resolve in core.
|
||||
if (!validateHTTPVersion(req.httpVersion)) {
|
||||
childLogger.info({ res: { statusCode: 505 } }, 'request aborted - invalid HTTP version')
|
||||
const message = '{"error":"HTTP Version Not Supported","message":"HTTP Version Not Supported","statusCode":505}'
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': message.length
|
||||
}
|
||||
res.writeHead(505, headers)
|
||||
res.end(message)
|
||||
return
|
||||
}
|
||||
|
||||
if (closing === true) {
|
||||
/* istanbul ignore next mac, windows */
|
||||
if (req.httpVersionMajor !== 2) {
|
||||
res.setHeader('Connection', 'close')
|
||||
}
|
||||
|
||||
// TODO remove return503OnClosing after Node v18 goes EOL
|
||||
/* istanbul ignore else */
|
||||
if (return503OnClosing) {
|
||||
// On Node v19 we cannot test this behavior as it won't be necessary
|
||||
// anymore. It will close all the idle connections before they reach this
|
||||
// stage.
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': '80'
|
||||
}
|
||||
res.writeHead(503, headers)
|
||||
res.end('{"error":"Service Unavailable","message":"Service Unavailable","statusCode":503}')
|
||||
childLogger.info({ res: { statusCode: 503 } }, 'request aborted - refusing to accept new requests as server is closing')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// When server.forceCloseConnections is true, we will collect any requests
|
||||
// that have indicated they want persistence so that they can be reaped
|
||||
// on server close. Otherwise, the container is a noop container.
|
||||
const connHeader = String.prototype.toLowerCase.call(req.headers.connection || '')
|
||||
if (connHeader === 'keep-alive') {
|
||||
if (keepAliveConnections.has(req.socket) === false) {
|
||||
keepAliveConnections.add(req.socket)
|
||||
req.socket.on('close', removeTrackedSocket.bind({ keepAliveConnections, socket: req.socket }))
|
||||
}
|
||||
}
|
||||
|
||||
// we revert the changes in defaultRoute
|
||||
if (req.headers[kRequestAcceptVersion] !== undefined) {
|
||||
req.headers['accept-version'] = req.headers[kRequestAcceptVersion]
|
||||
req.headers[kRequestAcceptVersion] = undefined
|
||||
}
|
||||
|
||||
const request = new context.Request(id, params, req, query, childLogger, context)
|
||||
const reply = new context.Reply(res, request, childLogger)
|
||||
if (disableRequestLogging === false) {
|
||||
childLogger.info({ req: request }, 'incoming request')
|
||||
}
|
||||
|
||||
if (hasLogger === true || context.onResponse !== null) {
|
||||
setupResponseListeners(reply)
|
||||
}
|
||||
|
||||
if (context.onRequest !== null) {
|
||||
onRequestHookRunner(
|
||||
context.onRequest,
|
||||
request,
|
||||
reply,
|
||||
runPreParsing
|
||||
)
|
||||
} else {
|
||||
runPreParsing(null, request, reply)
|
||||
}
|
||||
|
||||
if (context.onRequestAbort !== null) {
|
||||
req.on('close', () => {
|
||||
/* istanbul ignore else */
|
||||
if (req.aborted) {
|
||||
onRequestAbortHookRunner(
|
||||
context.onRequestAbort,
|
||||
request,
|
||||
handleOnRequestAbortHooksErrors.bind(null, reply)
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (context.onTimeout !== null) {
|
||||
if (!request.raw.socket._meta) {
|
||||
request.raw.socket.on('timeout', handleTimeout)
|
||||
}
|
||||
request.raw.socket._meta = { context, request, reply }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleOnRequestAbortHooksErrors (reply, err) {
|
||||
if (err) {
|
||||
reply.log.error({ err }, 'onRequestAborted hook failed')
|
||||
}
|
||||
}
|
||||
|
||||
function handleTimeout () {
|
||||
const { context, request, reply } = this._meta
|
||||
onTimeoutHookRunner(
|
||||
context.onTimeout,
|
||||
request,
|
||||
reply,
|
||||
noop
|
||||
)
|
||||
}
|
||||
|
||||
function normalizeAndValidateMethod (method) {
|
||||
if (typeof method !== 'string') {
|
||||
throw new FST_ERR_ROUTE_METHOD_INVALID()
|
||||
}
|
||||
method = method.toUpperCase()
|
||||
if (supportedMethods.indexOf(method) === -1) {
|
||||
throw new FST_ERR_ROUTE_METHOD_NOT_SUPPORTED(method)
|
||||
}
|
||||
|
||||
return method
|
||||
}
|
||||
|
||||
function validateSchemaBodyOption (method, path, schema) {
|
||||
if ((method === 'GET' || method === 'HEAD') && schema && schema.body) {
|
||||
throw new FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED(method, path)
|
||||
}
|
||||
}
|
||||
|
||||
function validateBodyLimitOption (bodyLimit) {
|
||||
if (bodyLimit === undefined) return
|
||||
if (!Number.isInteger(bodyLimit) || bodyLimit <= 0) {
|
||||
throw new FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT(bodyLimit)
|
||||
}
|
||||
}
|
||||
|
||||
function runPreParsing (err, request, reply) {
|
||||
if (reply.sent === true) return
|
||||
if (err != null) {
|
||||
reply[kReplyIsError] = true
|
||||
reply.send(err)
|
||||
return
|
||||
}
|
||||
|
||||
request[kRequestPayloadStream] = request.raw
|
||||
|
||||
if (request[kRouteContext].preParsing !== null) {
|
||||
preParsingHookRunner(request[kRouteContext].preParsing, request, reply, handleRequest)
|
||||
} else {
|
||||
handleRequest(null, request, reply)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used within the route handler as a `net.Socket.close` event handler.
|
||||
* The purpose is to remove a socket from the tracked sockets collection when
|
||||
* the socket has naturally timed out.
|
||||
*/
|
||||
function removeTrackedSocket () {
|
||||
this.keepAliveConnections.delete(this.socket)
|
||||
}
|
||||
|
||||
function noop () { }
|
||||
|
||||
module.exports = { buildRouting, validateBodyLimitOption }
|
||||
164
backend/node_modules/fastify/lib/schema-controller.js
generated
vendored
Normal file
164
backend/node_modules/fastify/lib/schema-controller.js
generated
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
'use strict'
|
||||
|
||||
const { buildSchemas } = require('./schemas')
|
||||
const SerializerSelector = require('@fastify/fast-json-stringify-compiler')
|
||||
const ValidatorSelector = require('@fastify/ajv-compiler')
|
||||
|
||||
/**
|
||||
* Called at every fastify context that is being created.
|
||||
* @param {object} parentSchemaCtrl: the SchemaController instance of the Fastify parent context
|
||||
* @param {object} opts: the `schemaController` server option. It can be undefined when a parentSchemaCtrl is set
|
||||
* @return {object}:a new SchemaController
|
||||
*/
|
||||
function buildSchemaController (parentSchemaCtrl, opts) {
|
||||
if (parentSchemaCtrl) {
|
||||
return new SchemaController(parentSchemaCtrl, opts)
|
||||
}
|
||||
|
||||
const compilersFactory = Object.assign({
|
||||
buildValidator: null,
|
||||
buildSerializer: null
|
||||
}, opts?.compilersFactory)
|
||||
|
||||
if (!compilersFactory.buildValidator) {
|
||||
compilersFactory.buildValidator = ValidatorSelector()
|
||||
}
|
||||
if (!compilersFactory.buildSerializer) {
|
||||
compilersFactory.buildSerializer = SerializerSelector()
|
||||
}
|
||||
|
||||
const option = {
|
||||
bucket: (opts && opts.bucket) || buildSchemas,
|
||||
compilersFactory,
|
||||
isCustomValidatorCompiler: typeof opts?.compilersFactory?.buildValidator === 'function',
|
||||
isCustomSerializerCompiler: typeof opts?.compilersFactory?.buildValidator === 'function'
|
||||
}
|
||||
|
||||
return new SchemaController(undefined, option)
|
||||
}
|
||||
|
||||
class SchemaController {
|
||||
constructor (parent, options) {
|
||||
this.opts = options || parent?.opts
|
||||
this.addedSchemas = false
|
||||
|
||||
this.compilersFactory = this.opts.compilersFactory
|
||||
|
||||
if (parent) {
|
||||
this.schemaBucket = this.opts.bucket(parent.getSchemas())
|
||||
this.validatorCompiler = parent.getValidatorCompiler()
|
||||
this.serializerCompiler = parent.getSerializerCompiler()
|
||||
this.isCustomValidatorCompiler = parent.isCustomValidatorCompiler
|
||||
this.isCustomSerializerCompiler = parent.isCustomSerializerCompiler
|
||||
this.parent = parent
|
||||
} else {
|
||||
this.schemaBucket = this.opts.bucket()
|
||||
this.isCustomValidatorCompiler = this.opts.isCustomValidatorCompiler || false
|
||||
this.isCustomSerializerCompiler = this.opts.isCustomSerializerCompiler || false
|
||||
}
|
||||
}
|
||||
|
||||
// Bucket interface
|
||||
add (schema) {
|
||||
this.addedSchemas = true
|
||||
return this.schemaBucket.add(schema)
|
||||
}
|
||||
|
||||
getSchema (schemaId) {
|
||||
return this.schemaBucket.getSchema(schemaId)
|
||||
}
|
||||
|
||||
getSchemas () {
|
||||
return this.schemaBucket.getSchemas()
|
||||
}
|
||||
|
||||
setValidatorCompiler (validatorCompiler) {
|
||||
// Set up as if the fixed validator compiler had been provided
|
||||
// by a custom 'options.compilersFactory.buildValidator' that
|
||||
// always returns the same compiler object. This is required because:
|
||||
//
|
||||
// - setValidatorCompiler must immediately install a compiler to preserve
|
||||
// legacy behavior
|
||||
// - setupValidator will recreate compilers from builders in some
|
||||
// circumstances, so we have to install this adapter to make it
|
||||
// behave the same if the legacy API is used
|
||||
//
|
||||
// The cloning of the compilersFactory object is necessary because
|
||||
// we are aliasing the parent compilersFactory if none was provided
|
||||
// to us (see constructor.)
|
||||
this.compilersFactory = Object.assign(
|
||||
{},
|
||||
this.compilersFactory,
|
||||
{ buildValidator: () => validatorCompiler })
|
||||
this.validatorCompiler = validatorCompiler
|
||||
this.isCustomValidatorCompiler = true
|
||||
}
|
||||
|
||||
setSerializerCompiler (serializerCompiler) {
|
||||
// Set up as if the fixed serializer compiler had been provided
|
||||
// by a custom 'options.compilersFactory.buildSerializer' that
|
||||
// always returns the same compiler object. This is required because:
|
||||
//
|
||||
// - setSerializerCompiler must immediately install a compiler to preserve
|
||||
// legacy behavior
|
||||
// - setupSerializer will recreate compilers from builders in some
|
||||
// circumstances, so we have to install this adapter to make it
|
||||
// behave the same if the legacy API is used
|
||||
//
|
||||
// The cloning of the compilersFactory object is necessary because
|
||||
// we are aliasing the parent compilersFactory if none was provided
|
||||
// to us (see constructor.)
|
||||
this.compilersFactory = Object.assign(
|
||||
{},
|
||||
this.compilersFactory,
|
||||
{ buildSerializer: () => serializerCompiler })
|
||||
this.serializerCompiler = serializerCompiler
|
||||
this.isCustomSerializerCompiler = true
|
||||
}
|
||||
|
||||
getValidatorCompiler () {
|
||||
return this.validatorCompiler || (this.parent && this.parent.getValidatorCompiler())
|
||||
}
|
||||
|
||||
getSerializerCompiler () {
|
||||
return this.serializerCompiler || (this.parent && this.parent.getSerializerCompiler())
|
||||
}
|
||||
|
||||
getSerializerBuilder () {
|
||||
return this.compilersFactory.buildSerializer || (this.parent && this.parent.getSerializerBuilder())
|
||||
}
|
||||
|
||||
getValidatorBuilder () {
|
||||
return this.compilersFactory.buildValidator || (this.parent && this.parent.getValidatorBuilder())
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will be called when a validator must be setup.
|
||||
* Do not setup the compiler more than once
|
||||
* @param {object} serverOptions the fastify server options
|
||||
*/
|
||||
setupValidator (serverOptions) {
|
||||
const isReady = this.validatorCompiler !== undefined && !this.addedSchemas
|
||||
if (isReady) {
|
||||
return
|
||||
}
|
||||
this.validatorCompiler = this.getValidatorBuilder()(this.schemaBucket.getSchemas(), serverOptions.ajv)
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will be called when a serializer must be setup.
|
||||
* Do not setup the compiler more than once
|
||||
* @param {object} serverOptions the fastify server options
|
||||
*/
|
||||
setupSerializer (serverOptions) {
|
||||
const isReady = this.serializerCompiler !== undefined && !this.addedSchemas
|
||||
if (isReady) {
|
||||
return
|
||||
}
|
||||
|
||||
this.serializerCompiler = this.getSerializerBuilder()(this.schemaBucket.getSchemas(), serverOptions.serializerOpts)
|
||||
}
|
||||
}
|
||||
|
||||
SchemaController.buildSchemaController = buildSchemaController
|
||||
module.exports = SchemaController
|
||||
219
backend/node_modules/fastify/lib/schemas.js
generated
vendored
Normal file
219
backend/node_modules/fastify/lib/schemas.js
generated
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
'use strict'
|
||||
|
||||
const fastClone = require('rfdc')({ circles: false, proto: true })
|
||||
const { kSchemaVisited, kSchemaResponse } = require('./symbols')
|
||||
const kFluentSchema = Symbol.for('fluent-schema-object')
|
||||
|
||||
const {
|
||||
FSTDEP022
|
||||
} = require('./warnings')
|
||||
const {
|
||||
FST_ERR_SCH_MISSING_ID,
|
||||
FST_ERR_SCH_ALREADY_PRESENT,
|
||||
FST_ERR_SCH_DUPLICATE,
|
||||
FST_ERR_SCH_CONTENT_MISSING_SCHEMA
|
||||
} = require('./errors')
|
||||
|
||||
const SCHEMAS_SOURCE = ['params', 'body', 'querystring', 'query', 'headers']
|
||||
|
||||
function Schemas (initStore) {
|
||||
this.store = initStore || {}
|
||||
}
|
||||
|
||||
Schemas.prototype.add = function (inputSchema) {
|
||||
const schema = fastClone((inputSchema.isFluentSchema || inputSchema.isFluentJSONSchema || inputSchema[kFluentSchema])
|
||||
? inputSchema.valueOf()
|
||||
: inputSchema
|
||||
)
|
||||
|
||||
// developers can add schemas without $id, but with $def instead
|
||||
const id = schema.$id
|
||||
if (!id) {
|
||||
throw new FST_ERR_SCH_MISSING_ID()
|
||||
}
|
||||
|
||||
if (this.store[id]) {
|
||||
throw new FST_ERR_SCH_ALREADY_PRESENT(id)
|
||||
}
|
||||
|
||||
this.store[id] = schema
|
||||
}
|
||||
|
||||
Schemas.prototype.getSchemas = function () {
|
||||
return Object.assign({}, this.store)
|
||||
}
|
||||
|
||||
Schemas.prototype.getSchema = function (schemaId) {
|
||||
return this.store[schemaId]
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a schema is a non-plain object.
|
||||
*
|
||||
* @param {*} schema the schema to check
|
||||
* @returns {boolean} true if schema has a custom prototype
|
||||
*/
|
||||
function isCustomSchemaPrototype (schema) {
|
||||
return typeof schema === 'object' && Object.getPrototypeOf(schema) !== Object.prototype
|
||||
}
|
||||
|
||||
function normalizeSchema (opts, routeSchemas, serverOptions) {
|
||||
if (routeSchemas[kSchemaVisited]) {
|
||||
return routeSchemas
|
||||
}
|
||||
|
||||
// alias query to querystring schema
|
||||
if (routeSchemas.query) {
|
||||
// check if our schema has both querystring and query
|
||||
if (routeSchemas.querystring) {
|
||||
throw new FST_ERR_SCH_DUPLICATE('querystring')
|
||||
}
|
||||
routeSchemas.querystring = routeSchemas.query
|
||||
}
|
||||
|
||||
generateFluentSchema(routeSchemas)
|
||||
|
||||
for (const key of SCHEMAS_SOURCE) {
|
||||
const schema = routeSchemas[key]
|
||||
if (schema && !isCustomSchemaPrototype(schema)) {
|
||||
if (key === 'body' && schema.content) {
|
||||
const contentProperty = schema.content
|
||||
const keys = Object.keys(contentProperty)
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const contentType = keys[i]
|
||||
const contentSchema = contentProperty[contentType].schema
|
||||
if (!contentSchema) {
|
||||
throw new FST_ERR_SCH_CONTENT_MISSING_SCHEMA(contentType)
|
||||
}
|
||||
routeSchemas.body.content[contentType].schema = getSchemaAnyway(opts.url, contentSchema, serverOptions.jsonShorthand)
|
||||
}
|
||||
continue
|
||||
}
|
||||
routeSchemas[key] = getSchemaAnyway(opts.url, schema, serverOptions.jsonShorthand)
|
||||
}
|
||||
}
|
||||
|
||||
if (routeSchemas.response) {
|
||||
const httpCodes = Object.keys(routeSchemas.response)
|
||||
for (const code of httpCodes) {
|
||||
if (isCustomSchemaPrototype(routeSchemas.response[code])) {
|
||||
continue
|
||||
}
|
||||
|
||||
const contentProperty = routeSchemas.response[code].content
|
||||
|
||||
let hasContentMultipleContentTypes = false
|
||||
if (contentProperty) {
|
||||
const keys = Object.keys(contentProperty)
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const mediaName = keys[i]
|
||||
if (!contentProperty[mediaName].schema) {
|
||||
if (keys.length === 1) { break }
|
||||
throw new FST_ERR_SCH_CONTENT_MISSING_SCHEMA(mediaName)
|
||||
}
|
||||
routeSchemas.response[code].content[mediaName].schema = getSchemaAnyway(opts.url, contentProperty[mediaName].schema, serverOptions.jsonShorthand)
|
||||
if (i === keys.length - 1) {
|
||||
hasContentMultipleContentTypes = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasContentMultipleContentTypes) {
|
||||
routeSchemas.response[code] = getSchemaAnyway(opts.url, routeSchemas.response[code], serverOptions.jsonShorthand)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
routeSchemas[kSchemaVisited] = true
|
||||
return routeSchemas
|
||||
}
|
||||
|
||||
function generateFluentSchema (schema) {
|
||||
for (const key of SCHEMAS_SOURCE) {
|
||||
if (schema[key] && (schema[key].isFluentSchema || schema[key][kFluentSchema])) {
|
||||
schema[key] = schema[key].valueOf()
|
||||
}
|
||||
}
|
||||
|
||||
if (schema.response) {
|
||||
const httpCodes = Object.keys(schema.response)
|
||||
for (const code of httpCodes) {
|
||||
if (schema.response[code].isFluentSchema || schema.response[code][kFluentSchema]) {
|
||||
schema.response[code] = schema.response[code].valueOf()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getSchemaAnyway (url, schema, jsonShorthand) {
|
||||
if (!jsonShorthand || schema.$ref || schema.oneOf || schema.allOf || schema.anyOf || schema.$merge || schema.$patch) return schema
|
||||
if (!schema.type && !schema.properties) {
|
||||
FSTDEP022(url)
|
||||
return {
|
||||
type: 'object',
|
||||
properties: schema
|
||||
}
|
||||
}
|
||||
return schema
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the right JSON schema compiled function in the request context
|
||||
* setup by the route configuration `schema.response`.
|
||||
* It will look for the exact match (eg 200) or generic (eg 2xx)
|
||||
*
|
||||
* @param {object} context the request context
|
||||
* @param {number} statusCode the http status code
|
||||
* @param {string} [contentType] the reply content type
|
||||
* @returns {function|false} the right JSON Schema function to serialize
|
||||
* the reply or false if it is not set
|
||||
*/
|
||||
function getSchemaSerializer (context, statusCode, contentType) {
|
||||
const responseSchemaDef = context[kSchemaResponse]
|
||||
if (!responseSchemaDef) {
|
||||
return false
|
||||
}
|
||||
if (responseSchemaDef[statusCode]) {
|
||||
if (responseSchemaDef[statusCode].constructor === Object && contentType) {
|
||||
const mediaName = contentType.split(';', 1)[0]
|
||||
if (responseSchemaDef[statusCode][mediaName]) {
|
||||
return responseSchemaDef[statusCode][mediaName]
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
return responseSchemaDef[statusCode]
|
||||
}
|
||||
const fallbackStatusCode = (statusCode + '')[0] + 'xx'
|
||||
if (responseSchemaDef[fallbackStatusCode]) {
|
||||
if (responseSchemaDef[fallbackStatusCode].constructor === Object && contentType) {
|
||||
const mediaName = contentType.split(';', 1)[0]
|
||||
if (responseSchemaDef[fallbackStatusCode][mediaName]) {
|
||||
return responseSchemaDef[fallbackStatusCode][mediaName]
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return responseSchemaDef[fallbackStatusCode]
|
||||
}
|
||||
if (responseSchemaDef.default) {
|
||||
if (responseSchemaDef.default.constructor === Object && contentType) {
|
||||
const mediaName = contentType.split(';', 1)[0]
|
||||
if (responseSchemaDef.default[mediaName]) {
|
||||
return responseSchemaDef.default[mediaName]
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return responseSchemaDef.default
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildSchemas (initStore) { return new Schemas(initStore) },
|
||||
getSchemaSerializer,
|
||||
normalizeSchema
|
||||
}
|
||||
447
backend/node_modules/fastify/lib/server.js
generated
vendored
Normal file
447
backend/node_modules/fastify/lib/server.js
generated
vendored
Normal file
@@ -0,0 +1,447 @@
|
||||
'use strict'
|
||||
|
||||
const http = require('node:http')
|
||||
const https = require('node:https')
|
||||
const dns = require('node:dns')
|
||||
|
||||
const { FSTDEP011 } = require('./warnings')
|
||||
const { kState, kOptions, kServerBindings } = require('./symbols')
|
||||
const { onListenHookRunner } = require('./hooks')
|
||||
const {
|
||||
FST_ERR_HTTP2_INVALID_VERSION,
|
||||
FST_ERR_REOPENED_CLOSE_SERVER,
|
||||
FST_ERR_REOPENED_SERVER,
|
||||
FST_ERR_LISTEN_OPTIONS_INVALID
|
||||
} = require('./errors')
|
||||
|
||||
module.exports.createServer = createServer
|
||||
module.exports.compileValidateHTTPVersion = compileValidateHTTPVersion
|
||||
|
||||
function defaultResolveServerListeningText (address) {
|
||||
return `Server listening at ${address}`
|
||||
}
|
||||
|
||||
function createServer (options, httpHandler) {
|
||||
const server = getServerInstance(options, httpHandler)
|
||||
|
||||
// `this` is the Fastify object
|
||||
function listen (listenOptions, ...args) {
|
||||
let cb = args.slice(-1).pop()
|
||||
// When the variadic signature deprecation is complete, the function
|
||||
// declaration should become:
|
||||
// function listen (listenOptions = { port: 0, host: 'localhost' }, cb = undefined)
|
||||
// Upon doing so, the `normalizeListenArgs` function is no longer needed,
|
||||
// and all of this preamble to feed it correctly also no longer needed.
|
||||
const firstArgType = Object.prototype.toString.call(arguments[0])
|
||||
if (arguments.length === 0) {
|
||||
listenOptions = normalizeListenArgs([])
|
||||
} else if (arguments.length > 0 && (firstArgType !== '[object Object]' && firstArgType !== '[object Function]')) {
|
||||
FSTDEP011()
|
||||
listenOptions = normalizeListenArgs(Array.from(arguments))
|
||||
cb = listenOptions.cb
|
||||
} else if (args.length > 1) {
|
||||
// `.listen(obj, a, ..., n, callback )`
|
||||
FSTDEP011()
|
||||
// Deal with `.listen(port, host, backlog, [cb])`
|
||||
const hostPath = listenOptions.path ? [listenOptions.path] : [listenOptions.port ?? 0, listenOptions.host ?? 'localhost']
|
||||
Object.assign(listenOptions, normalizeListenArgs([...hostPath, ...args]))
|
||||
} else {
|
||||
listenOptions.cb = cb
|
||||
}
|
||||
if (listenOptions.signal) {
|
||||
if (typeof listenOptions.signal.on !== 'function' && typeof listenOptions.signal.addEventListener !== 'function') {
|
||||
throw new FST_ERR_LISTEN_OPTIONS_INVALID('Invalid options.signal')
|
||||
}
|
||||
|
||||
if (listenOptions.signal.aborted) {
|
||||
this.close()
|
||||
} else {
|
||||
const onAborted = () => {
|
||||
this.close()
|
||||
}
|
||||
listenOptions.signal.addEventListener('abort', onAborted, { once: true })
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a path specified, don't default host to 'localhost' so we don't end up listening
|
||||
// on both path and host
|
||||
// See https://github.com/fastify/fastify/issues/4007
|
||||
let host
|
||||
if (listenOptions.path == null) {
|
||||
host = listenOptions.host ?? 'localhost'
|
||||
} else {
|
||||
host = listenOptions.host
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(listenOptions, 'host') === false ||
|
||||
listenOptions.host == null) {
|
||||
listenOptions.host = host
|
||||
}
|
||||
if (host === 'localhost') {
|
||||
listenOptions.cb = (err, address) => {
|
||||
if (err) {
|
||||
// the server did not start
|
||||
cb(err, address)
|
||||
return
|
||||
}
|
||||
|
||||
multipleBindings.call(this, server, httpHandler, options, listenOptions, () => {
|
||||
this[kState].listening = true
|
||||
cb(null, address)
|
||||
onListenHookRunner(this)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
listenOptions.cb = (err, address) => {
|
||||
// the server did not start
|
||||
if (err) {
|
||||
cb(err, address)
|
||||
return
|
||||
}
|
||||
this[kState].listening = true
|
||||
cb(null, address)
|
||||
onListenHookRunner(this)
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/nodejs/node/issues/9390
|
||||
// If listening to 'localhost', listen to both 127.0.0.1 or ::1 if they are available.
|
||||
// If listening to 127.0.0.1, only listen to 127.0.0.1.
|
||||
// If listening to ::1, only listen to ::1.
|
||||
|
||||
if (cb === undefined) {
|
||||
const listening = listenPromise.call(this, server, listenOptions)
|
||||
/* istanbul ignore else */
|
||||
return listening.then(address => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (host === 'localhost') {
|
||||
multipleBindings.call(this, server, httpHandler, options, listenOptions, () => {
|
||||
this[kState].listening = true
|
||||
resolve(address)
|
||||
onListenHookRunner(this)
|
||||
})
|
||||
} else {
|
||||
resolve(address)
|
||||
onListenHookRunner(this)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
this.ready(listenCallback.call(this, server, listenOptions))
|
||||
}
|
||||
|
||||
return { server, listen }
|
||||
}
|
||||
|
||||
function multipleBindings (mainServer, httpHandler, serverOpts, listenOptions, onListen) {
|
||||
// the main server is started, we need to start the secondary servers
|
||||
this[kState].listening = false
|
||||
|
||||
// let's check if we need to bind additional addresses
|
||||
dns.lookup(listenOptions.host, { all: true }, (dnsErr, addresses) => {
|
||||
if (dnsErr) {
|
||||
// not blocking the main server listening
|
||||
// this.log.warn('dns.lookup error:', dnsErr)
|
||||
onListen()
|
||||
return
|
||||
}
|
||||
|
||||
const isMainServerListening = mainServer.listening && serverOpts.serverFactory
|
||||
|
||||
let binding = 0
|
||||
let bound = 0
|
||||
if (!isMainServerListening) {
|
||||
const primaryAddress = mainServer.address()
|
||||
for (const adr of addresses) {
|
||||
if (adr.address !== primaryAddress.address) {
|
||||
binding++
|
||||
const secondaryOpts = Object.assign({}, listenOptions, {
|
||||
host: adr.address,
|
||||
port: primaryAddress.port,
|
||||
cb: (_ignoreErr) => {
|
||||
bound++
|
||||
|
||||
/* istanbul ignore next: the else won't be taken unless listening fails */
|
||||
if (!_ignoreErr) {
|
||||
this[kServerBindings].push(secondaryServer)
|
||||
}
|
||||
|
||||
if (bound === binding) {
|
||||
// regardless of the error, we are done
|
||||
onListen()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const secondaryServer = getServerInstance(serverOpts, httpHandler)
|
||||
const closeSecondary = () => {
|
||||
// To avoid fall into situations where the close of the
|
||||
// secondary server is triggered before the preClose hook
|
||||
// is done running, we better wait until the main server
|
||||
// is closed.
|
||||
// No new TCP connections are accepted
|
||||
// We swallow any error from the secondary
|
||||
// server
|
||||
secondaryServer.close(() => {})
|
||||
if (serverOpts.forceCloseConnections === 'idle') {
|
||||
// Not needed in Node 19
|
||||
secondaryServer.closeIdleConnections()
|
||||
} else if (typeof secondaryServer.closeAllConnections === 'function' && serverOpts.forceCloseConnections) {
|
||||
secondaryServer.closeAllConnections()
|
||||
}
|
||||
}
|
||||
|
||||
secondaryServer.on('upgrade', mainServer.emit.bind(mainServer, 'upgrade'))
|
||||
mainServer.on('unref', closeSecondary)
|
||||
mainServer.on('close', closeSecondary)
|
||||
mainServer.on('error', closeSecondary)
|
||||
this[kState].listening = false
|
||||
listenCallback.call(this, secondaryServer, secondaryOpts)()
|
||||
}
|
||||
}
|
||||
}
|
||||
// no extra bindings are necessary
|
||||
if (binding === 0) {
|
||||
onListen()
|
||||
return
|
||||
}
|
||||
|
||||
// in test files we are using unref so we need to propagate the unref event
|
||||
// to the secondary servers. It is valid only when the user is
|
||||
// listening on localhost
|
||||
const originUnref = mainServer.unref
|
||||
/* c8 ignore next 4 */
|
||||
mainServer.unref = function () {
|
||||
originUnref.call(mainServer)
|
||||
mainServer.emit('unref')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function listenCallback (server, listenOptions) {
|
||||
const wrap = (err) => {
|
||||
server.removeListener('error', wrap)
|
||||
server.removeListener('listening', wrap)
|
||||
if (!err) {
|
||||
const address = logServerAddress.call(this, server, listenOptions.listenTextResolver || defaultResolveServerListeningText)
|
||||
listenOptions.cb(null, address)
|
||||
} else {
|
||||
this[kState].listening = false
|
||||
listenOptions.cb(err, null)
|
||||
}
|
||||
}
|
||||
|
||||
return (err) => {
|
||||
if (err != null) return listenOptions.cb(err)
|
||||
|
||||
if (this[kState].listening && this[kState].closing) {
|
||||
return listenOptions.cb(new FST_ERR_REOPENED_CLOSE_SERVER(), null)
|
||||
} else if (this[kState].listening) {
|
||||
return listenOptions.cb(new FST_ERR_REOPENED_SERVER(), null)
|
||||
}
|
||||
|
||||
server.once('error', wrap)
|
||||
if (!this[kState].closing) {
|
||||
server.once('listening', wrap)
|
||||
server.listen(listenOptions)
|
||||
this[kState].listening = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function listenPromise (server, listenOptions) {
|
||||
if (this[kState].listening && this[kState].closing) {
|
||||
return Promise.reject(new FST_ERR_REOPENED_CLOSE_SERVER())
|
||||
} else if (this[kState].listening) {
|
||||
return Promise.reject(new FST_ERR_REOPENED_SERVER())
|
||||
}
|
||||
|
||||
return this.ready().then(() => {
|
||||
let errEventHandler
|
||||
let listeningEventHandler
|
||||
function cleanup () {
|
||||
server.removeListener('error', errEventHandler)
|
||||
server.removeListener('listening', listeningEventHandler)
|
||||
}
|
||||
const errEvent = new Promise((resolve, reject) => {
|
||||
errEventHandler = (err) => {
|
||||
cleanup()
|
||||
this[kState].listening = false
|
||||
reject(err)
|
||||
}
|
||||
server.once('error', errEventHandler)
|
||||
})
|
||||
const listeningEvent = new Promise((resolve, reject) => {
|
||||
listeningEventHandler = () => {
|
||||
cleanup()
|
||||
this[kState].listening = true
|
||||
resolve(logServerAddress.call(this, server, listenOptions.listenTextResolver || defaultResolveServerListeningText))
|
||||
}
|
||||
server.once('listening', listeningEventHandler)
|
||||
})
|
||||
|
||||
server.listen(listenOptions)
|
||||
|
||||
return Promise.race([
|
||||
errEvent, // e.g invalid port range error is always emitted before the server listening
|
||||
listeningEvent
|
||||
])
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a function that, based upon initial configuration, will
|
||||
* verify that every incoming request conforms to allowed
|
||||
* HTTP versions for the Fastify instance, e.g. a Fastify HTTP/1.1
|
||||
* server will not serve HTTP/2 requests upon the result of the
|
||||
* verification function.
|
||||
*
|
||||
* @param {object} options fastify option
|
||||
* @param {function} [options.serverFactory] If present, the
|
||||
* validator function will skip all checks.
|
||||
* @param {boolean} [options.http2 = false] If true, the validator
|
||||
* function will allow HTTP/2 requests.
|
||||
* @param {object} [options.https = null] https server options
|
||||
* @param {boolean} [options.https.allowHTTP1] If true and use
|
||||
* with options.http2 the validator function will allow HTTP/1
|
||||
* request to http2 server.
|
||||
*
|
||||
* @returns {function} HTTP version validator function.
|
||||
*/
|
||||
function compileValidateHTTPVersion (options) {
|
||||
let bypass = false
|
||||
// key-value map to store valid http version
|
||||
const map = new Map()
|
||||
if (options.serverFactory) {
|
||||
// When serverFactory is passed, we cannot identify how to check http version reliably
|
||||
// So, we should skip the http version check
|
||||
bypass = true
|
||||
}
|
||||
if (options.http2) {
|
||||
// HTTP2 must serve HTTP/2.0
|
||||
map.set('2.0', true)
|
||||
if (options.https && options.https.allowHTTP1 === true) {
|
||||
// HTTP2 with HTTPS.allowHTTP1 allow fallback to HTTP/1.1 and HTTP/1.0
|
||||
map.set('1.1', true)
|
||||
map.set('1.0', true)
|
||||
}
|
||||
} else {
|
||||
// HTTP must server HTTP/1.1 and HTTP/1.0
|
||||
map.set('1.1', true)
|
||||
map.set('1.0', true)
|
||||
}
|
||||
// The compiled function here placed in one of the hottest path inside fastify
|
||||
// the implementation here must be as performant as possible
|
||||
return function validateHTTPVersion (httpVersion) {
|
||||
// `bypass` skip the check when custom server factory provided
|
||||
// `httpVersion in obj` check for the valid http version we should support
|
||||
return bypass || map.has(httpVersion)
|
||||
}
|
||||
}
|
||||
|
||||
function getServerInstance (options, httpHandler) {
|
||||
let server = null
|
||||
// node@20 do not accepts options as boolean
|
||||
// we need to provide proper https option
|
||||
const httpsOptions = options.https === true ? {} : options.https
|
||||
if (options.serverFactory) {
|
||||
server = options.serverFactory(httpHandler, options)
|
||||
} else if (options.http2) {
|
||||
if (typeof httpsOptions === 'object') {
|
||||
server = http2().createSecureServer(httpsOptions, httpHandler)
|
||||
} else {
|
||||
server = http2().createServer(httpHandler)
|
||||
}
|
||||
server.on('session', sessionTimeout(options.http2SessionTimeout))
|
||||
} else {
|
||||
// this is http1
|
||||
if (httpsOptions) {
|
||||
server = https.createServer(httpsOptions, httpHandler)
|
||||
} else {
|
||||
server = http.createServer(options.http, httpHandler)
|
||||
}
|
||||
server.keepAliveTimeout = options.keepAliveTimeout
|
||||
server.requestTimeout = options.requestTimeout
|
||||
// we treat zero as null
|
||||
// and null is the default setting from nodejs
|
||||
// so we do not pass the option to server
|
||||
if (options.maxRequestsPerSocket > 0) {
|
||||
server.maxRequestsPerSocket = options.maxRequestsPerSocket
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.serverFactory) {
|
||||
server.setTimeout(options.connectionTimeout)
|
||||
}
|
||||
return server
|
||||
}
|
||||
|
||||
function normalizeListenArgs (args) {
|
||||
if (args.length === 0) {
|
||||
return { port: 0, host: 'localhost' }
|
||||
}
|
||||
|
||||
const cb = typeof args[args.length - 1] === 'function' ? args.pop() : undefined
|
||||
const options = { cb }
|
||||
|
||||
const firstArg = args[0]
|
||||
const argsLength = args.length
|
||||
const lastArg = args[argsLength - 1]
|
||||
if (typeof firstArg === 'string' && isNaN(firstArg)) {
|
||||
/* Deal with listen (pipe[, backlog]) */
|
||||
options.path = firstArg
|
||||
options.backlog = argsLength > 1 ? lastArg : undefined
|
||||
} else {
|
||||
/* Deal with listen ([port[, host[, backlog]]]) */
|
||||
options.port = argsLength >= 1 && Number.isInteger(firstArg) ? firstArg : normalizePort(firstArg)
|
||||
// This will listen to what localhost is.
|
||||
// It can be 127.0.0.1 or ::1, depending on the operating system.
|
||||
// Fixes https://github.com/fastify/fastify/issues/1022.
|
||||
options.host = argsLength >= 2 && args[1] ? args[1] : 'localhost'
|
||||
options.backlog = argsLength >= 3 ? args[2] : undefined
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
function normalizePort (firstArg) {
|
||||
const port = Number(firstArg)
|
||||
return port >= 0 && !Number.isNaN(port) && Number.isInteger(port) ? port : 0
|
||||
}
|
||||
|
||||
function logServerAddress (server, listenTextResolver) {
|
||||
let address = server.address()
|
||||
const isUnixSocket = typeof address === 'string'
|
||||
/* istanbul ignore next */
|
||||
if (!isUnixSocket) {
|
||||
if (address.address.indexOf(':') === -1) {
|
||||
address = address.address + ':' + address.port
|
||||
} else {
|
||||
address = '[' + address.address + ']:' + address.port
|
||||
}
|
||||
}
|
||||
/* istanbul ignore next */
|
||||
address = (isUnixSocket ? '' : ('http' + (this[kOptions].https ? 's' : '') + '://')) + address
|
||||
|
||||
const serverListeningText = listenTextResolver(address)
|
||||
this.log.info(serverListeningText)
|
||||
return address
|
||||
}
|
||||
|
||||
function http2 () {
|
||||
try {
|
||||
return require('node:http2')
|
||||
} catch (err) {
|
||||
throw new FST_ERR_HTTP2_INVALID_VERSION()
|
||||
}
|
||||
}
|
||||
|
||||
function sessionTimeout (timeout) {
|
||||
return function (session) {
|
||||
session.setTimeout(timeout, close)
|
||||
}
|
||||
}
|
||||
|
||||
function close () {
|
||||
this.close()
|
||||
}
|
||||
65
backend/node_modules/fastify/lib/symbols.js
generated
vendored
Normal file
65
backend/node_modules/fastify/lib/symbols.js
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
'use strict'
|
||||
|
||||
const keys = {
|
||||
kAvvioBoot: Symbol('fastify.avvioBoot'),
|
||||
kChildren: Symbol('fastify.children'),
|
||||
kServerBindings: Symbol('fastify.serverBindings'),
|
||||
kBodyLimit: Symbol('fastify.bodyLimit'),
|
||||
kRoutePrefix: Symbol('fastify.routePrefix'),
|
||||
kLogLevel: Symbol('fastify.logLevel'),
|
||||
kLogSerializers: Symbol('fastify.logSerializers'),
|
||||
kHooks: Symbol('fastify.hooks'),
|
||||
kContentTypeParser: Symbol('fastify.contentTypeParser'),
|
||||
kState: Symbol('fastify.state'),
|
||||
kOptions: Symbol('fastify.options'),
|
||||
kDisableRequestLogging: Symbol('fastify.disableRequestLogging'),
|
||||
kPluginNameChain: Symbol('fastify.pluginNameChain'),
|
||||
kRouteContext: Symbol('fastify.context'),
|
||||
kPublicRouteContext: Symbol('fastify.routeOptions'),
|
||||
kGenReqId: Symbol('fastify.genReqId'),
|
||||
// Schema
|
||||
kSchemaController: Symbol('fastify.schemaController'),
|
||||
kSchemaHeaders: Symbol('headers-schema'),
|
||||
kSchemaParams: Symbol('params-schema'),
|
||||
kSchemaQuerystring: Symbol('querystring-schema'),
|
||||
kSchemaBody: Symbol('body-schema'),
|
||||
kSchemaResponse: Symbol('response-schema'),
|
||||
kSchemaErrorFormatter: Symbol('fastify.schemaErrorFormatter'),
|
||||
kSchemaVisited: Symbol('fastify.schemas.visited'),
|
||||
// Request
|
||||
kRequest: Symbol('fastify.Request'),
|
||||
kRequestPayloadStream: Symbol('fastify.RequestPayloadStream'),
|
||||
kRequestAcceptVersion: Symbol('fastify.RequestAcceptVersion'),
|
||||
kRequestCacheValidateFns: Symbol('fastify.request.cache.validateFns'),
|
||||
kRequestOriginalUrl: Symbol('fastify.request.originalUrl'),
|
||||
// 404
|
||||
kFourOhFour: Symbol('fastify.404'),
|
||||
kCanSetNotFoundHandler: Symbol('fastify.canSetNotFoundHandler'),
|
||||
kFourOhFourLevelInstance: Symbol('fastify.404LogLevelInstance'),
|
||||
kFourOhFourContext: Symbol('fastify.404ContextKey'),
|
||||
kDefaultJsonParse: Symbol('fastify.defaultJSONParse'),
|
||||
// Reply
|
||||
kReply: Symbol('fastify.Reply'),
|
||||
kReplySerializer: Symbol('fastify.reply.serializer'),
|
||||
kReplyIsError: Symbol('fastify.reply.isError'),
|
||||
kReplyHeaders: Symbol('fastify.reply.headers'),
|
||||
kReplyTrailers: Symbol('fastify.reply.trailers'),
|
||||
kReplyHasStatusCode: Symbol('fastify.reply.hasStatusCode'),
|
||||
kReplyHijacked: Symbol('fastify.reply.hijacked'),
|
||||
kReplyStartTime: Symbol('fastify.reply.startTime'),
|
||||
kReplyNextErrorHandler: Symbol('fastify.reply.nextErrorHandler'),
|
||||
kReplyEndTime: Symbol('fastify.reply.endTime'),
|
||||
kReplyErrorHandlerCalled: Symbol('fastify.reply.errorHandlerCalled'),
|
||||
kReplyIsRunningOnErrorHook: Symbol('fastify.reply.isRunningOnErrorHook'),
|
||||
kReplySerializerDefault: Symbol('fastify.replySerializerDefault'),
|
||||
kReplyCacheSerializeFns: Symbol('fastify.reply.cache.serializeFns'),
|
||||
// This symbol is only meant to be used for fastify tests and should not be used for any other purpose
|
||||
kTestInternals: Symbol('fastify.testInternals'),
|
||||
kErrorHandler: Symbol('fastify.errorHandler'),
|
||||
kChildLoggerFactory: Symbol('fastify.childLoggerFactory'),
|
||||
kHasBeenDecorated: Symbol('fastify.hasBeenDecorated'),
|
||||
kKeepAliveConnections: Symbol('fastify.keepAliveConnections'),
|
||||
kRouteByFastify: Symbol('fastify.routeByFastify')
|
||||
}
|
||||
|
||||
module.exports = keys
|
||||
272
backend/node_modules/fastify/lib/validation.js
generated
vendored
Normal file
272
backend/node_modules/fastify/lib/validation.js
generated
vendored
Normal file
@@ -0,0 +1,272 @@
|
||||
'use strict'
|
||||
|
||||
const {
|
||||
kSchemaHeaders: headersSchema,
|
||||
kSchemaParams: paramsSchema,
|
||||
kSchemaQuerystring: querystringSchema,
|
||||
kSchemaBody: bodySchema,
|
||||
kSchemaResponse: responseSchema
|
||||
} = require('./symbols')
|
||||
const scChecker = /^[1-5]{1}[0-9]{2}$|^[1-5]xx$|^default$/
|
||||
|
||||
const {
|
||||
FST_ERR_SCH_RESPONSE_SCHEMA_NOT_NESTED_2XX
|
||||
} = require('./errors')
|
||||
|
||||
const { FSTWRN001 } = require('./warnings')
|
||||
|
||||
function compileSchemasForSerialization (context, compile) {
|
||||
if (!context.schema || !context.schema.response) {
|
||||
return
|
||||
}
|
||||
const { method, url } = context.config || {}
|
||||
context[responseSchema] = Object.keys(context.schema.response)
|
||||
.reduce(function (acc, statusCode) {
|
||||
const schema = context.schema.response[statusCode]
|
||||
statusCode = statusCode.toLowerCase()
|
||||
if (!scChecker.exec(statusCode)) {
|
||||
throw new FST_ERR_SCH_RESPONSE_SCHEMA_NOT_NESTED_2XX()
|
||||
}
|
||||
|
||||
if (schema.content) {
|
||||
const contentTypesSchemas = {}
|
||||
for (const mediaName of Object.keys(schema.content)) {
|
||||
const contentSchema = schema.content[mediaName].schema
|
||||
contentTypesSchemas[mediaName] = compile({
|
||||
schema: contentSchema,
|
||||
url,
|
||||
method,
|
||||
httpStatus: statusCode,
|
||||
contentType: mediaName
|
||||
})
|
||||
}
|
||||
acc[statusCode] = contentTypesSchemas
|
||||
} else {
|
||||
acc[statusCode] = compile({
|
||||
schema,
|
||||
url,
|
||||
method,
|
||||
httpStatus: statusCode
|
||||
})
|
||||
}
|
||||
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
||||
function compileSchemasForValidation (context, compile, isCustom) {
|
||||
const { schema } = context
|
||||
if (!schema) {
|
||||
return
|
||||
}
|
||||
|
||||
const { method, url } = context.config || {}
|
||||
|
||||
const headers = schema.headers
|
||||
// the or part is used for backward compatibility
|
||||
if (headers && (isCustom || Object.getPrototypeOf(headers) !== Object.prototype)) {
|
||||
// do not mess with schema when custom validator applied, e.g. Joi, Typebox
|
||||
context[headersSchema] = compile({ schema: headers, method, url, httpPart: 'headers' })
|
||||
} else if (headers) {
|
||||
// The header keys are case insensitive
|
||||
// https://datatracker.ietf.org/doc/html/rfc2616#section-4.2
|
||||
const headersSchemaLowerCase = {}
|
||||
Object.keys(headers).forEach(k => { headersSchemaLowerCase[k] = headers[k] })
|
||||
if (headersSchemaLowerCase.required instanceof Array) {
|
||||
headersSchemaLowerCase.required = headersSchemaLowerCase.required.map(h => h.toLowerCase())
|
||||
}
|
||||
if (headers.properties) {
|
||||
headersSchemaLowerCase.properties = {}
|
||||
Object.keys(headers.properties).forEach(k => {
|
||||
headersSchemaLowerCase.properties[k.toLowerCase()] = headers.properties[k]
|
||||
})
|
||||
}
|
||||
context[headersSchema] = compile({ schema: headersSchemaLowerCase, method, url, httpPart: 'headers' })
|
||||
} else if (Object.prototype.hasOwnProperty.call(schema, 'headers')) {
|
||||
FSTWRN001('headers', method, url)
|
||||
}
|
||||
|
||||
if (schema.body) {
|
||||
const contentProperty = schema.body.content
|
||||
if (contentProperty) {
|
||||
const contentTypeSchemas = {}
|
||||
for (const contentType of Object.keys(contentProperty)) {
|
||||
const contentSchema = contentProperty[contentType].schema
|
||||
contentTypeSchemas[contentType] = compile({ schema: contentSchema, method, url, httpPart: 'body', contentType })
|
||||
}
|
||||
context[bodySchema] = contentTypeSchemas
|
||||
} else {
|
||||
context[bodySchema] = compile({ schema: schema.body, method, url, httpPart: 'body' })
|
||||
}
|
||||
} else if (Object.prototype.hasOwnProperty.call(schema, 'body')) {
|
||||
FSTWRN001('body', method, url)
|
||||
}
|
||||
|
||||
if (schema.querystring) {
|
||||
context[querystringSchema] = compile({ schema: schema.querystring, method, url, httpPart: 'querystring' })
|
||||
} else if (Object.prototype.hasOwnProperty.call(schema, 'querystring')) {
|
||||
FSTWRN001('querystring', method, url)
|
||||
}
|
||||
|
||||
if (schema.params) {
|
||||
context[paramsSchema] = compile({ schema: schema.params, method, url, httpPart: 'params' })
|
||||
} else if (Object.prototype.hasOwnProperty.call(schema, 'params')) {
|
||||
FSTWRN001('params', method, url)
|
||||
}
|
||||
}
|
||||
|
||||
function validateParam (validatorFunction, request, paramName) {
|
||||
const isUndefined = request[paramName] === undefined
|
||||
const ret = validatorFunction && validatorFunction(isUndefined ? null : request[paramName])
|
||||
|
||||
if (ret?.then) {
|
||||
return ret
|
||||
.then((res) => { return answer(res) })
|
||||
.catch(err => { return err }) // return as simple error (not throw)
|
||||
}
|
||||
|
||||
return answer(ret)
|
||||
|
||||
function answer (ret) {
|
||||
if (ret === false) return validatorFunction.errors
|
||||
if (ret && ret.error) return ret.error
|
||||
if (ret && ret.value) request[paramName] = ret.value
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function validate (context, request, execution) {
|
||||
const runExecution = execution === undefined
|
||||
|
||||
if (runExecution || !execution.skipParams) {
|
||||
const params = validateParam(context[paramsSchema], request, 'params')
|
||||
if (params) {
|
||||
if (typeof params.then !== 'function') {
|
||||
return wrapValidationError(params, 'params', context.schemaErrorFormatter)
|
||||
} else {
|
||||
return validateAsyncParams(params, context, request)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (runExecution || !execution.skipBody) {
|
||||
let validatorFunction = null
|
||||
if (typeof context[bodySchema] === 'function') {
|
||||
validatorFunction = context[bodySchema]
|
||||
} else if (context[bodySchema]) {
|
||||
// TODO: add request.contentType and reuse it here
|
||||
const contentType = getEssenceMediaType(request.headers['content-type'])
|
||||
const contentSchema = context[bodySchema][contentType]
|
||||
if (contentSchema) {
|
||||
validatorFunction = contentSchema
|
||||
}
|
||||
}
|
||||
const body = validateParam(validatorFunction, request, 'body')
|
||||
if (body) {
|
||||
if (typeof body.then !== 'function') {
|
||||
return wrapValidationError(body, 'body', context.schemaErrorFormatter)
|
||||
} else {
|
||||
return validateAsyncBody(body, context, request)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (runExecution || !execution.skipQuery) {
|
||||
const query = validateParam(context[querystringSchema], request, 'query')
|
||||
if (query) {
|
||||
if (typeof query.then !== 'function') {
|
||||
return wrapValidationError(query, 'querystring', context.schemaErrorFormatter)
|
||||
} else {
|
||||
return validateAsyncQuery(query, context, request)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const headers = validateParam(context[headersSchema], request, 'headers')
|
||||
if (headers) {
|
||||
if (typeof headers.then !== 'function') {
|
||||
return wrapValidationError(headers, 'headers', context.schemaErrorFormatter)
|
||||
} else {
|
||||
return validateAsyncHeaders(headers, context, request)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function validateAsyncParams (validatePromise, context, request) {
|
||||
return validatePromise
|
||||
.then((paramsResult) => {
|
||||
if (paramsResult) {
|
||||
return wrapValidationError(paramsResult, 'params', context.schemaErrorFormatter)
|
||||
}
|
||||
|
||||
return validate(context, request, { skipParams: true })
|
||||
})
|
||||
}
|
||||
|
||||
function validateAsyncBody (validatePromise, context, request) {
|
||||
return validatePromise
|
||||
.then((bodyResult) => {
|
||||
if (bodyResult) {
|
||||
return wrapValidationError(bodyResult, 'body', context.schemaErrorFormatter)
|
||||
}
|
||||
|
||||
return validate(context, request, { skipParams: true, skipBody: true })
|
||||
})
|
||||
}
|
||||
|
||||
function validateAsyncQuery (validatePromise, context, request) {
|
||||
return validatePromise
|
||||
.then((queryResult) => {
|
||||
if (queryResult) {
|
||||
return wrapValidationError(queryResult, 'querystring', context.schemaErrorFormatter)
|
||||
}
|
||||
|
||||
return validate(context, request, { skipParams: true, skipBody: true, skipQuery: true })
|
||||
})
|
||||
}
|
||||
|
||||
function validateAsyncHeaders (validatePromise, context, request) {
|
||||
return validatePromise
|
||||
.then((headersResult) => {
|
||||
if (headersResult) {
|
||||
return wrapValidationError(headersResult, 'headers', context.schemaErrorFormatter)
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
function wrapValidationError (result, dataVar, schemaErrorFormatter) {
|
||||
if (result instanceof Error) {
|
||||
result.statusCode = result.statusCode || 400
|
||||
result.code = result.code || 'FST_ERR_VALIDATION'
|
||||
result.validationContext = result.validationContext || dataVar
|
||||
return result
|
||||
}
|
||||
|
||||
const error = schemaErrorFormatter(result, dataVar)
|
||||
error.statusCode = error.statusCode || 400
|
||||
error.code = error.code || 'FST_ERR_VALIDATION'
|
||||
error.validation = result
|
||||
error.validationContext = dataVar
|
||||
return error
|
||||
}
|
||||
|
||||
/**
|
||||
* simple function to retrieve the essence media type
|
||||
* @param {string} header
|
||||
* @returns {string} Mimetype string.
|
||||
*/
|
||||
function getEssenceMediaType (header) {
|
||||
if (!header) return ''
|
||||
return header.split(/[ ;]/, 1)[0].trim().toLowerCase()
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
symbols: { bodySchema, querystringSchema, responseSchema, paramsSchema, headersSchema },
|
||||
compileSchemasForValidation,
|
||||
compileSchemasForSerialization,
|
||||
validate
|
||||
}
|
||||
130
backend/node_modules/fastify/lib/warnings.js
generated
vendored
Normal file
130
backend/node_modules/fastify/lib/warnings.js
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
'use strict'
|
||||
|
||||
const { createDeprecation, createWarning } = require('process-warning')
|
||||
|
||||
const FSTDEP005 = createDeprecation({
|
||||
code: 'FSTDEP005',
|
||||
message: 'You are accessing the deprecated "request.connection" property. Use "request.socket" instead.'
|
||||
})
|
||||
|
||||
const FSTDEP006 = createDeprecation({
|
||||
code: 'FSTDEP006',
|
||||
message: 'You are decorating Request/Reply with a reference type. This reference is shared amongst all requests. Use onRequest hook instead. Property: %s'
|
||||
})
|
||||
|
||||
const FSTDEP007 = createDeprecation({
|
||||
code: 'FSTDEP007',
|
||||
message: 'You are trying to set a HEAD route using "exposeHeadRoute" route flag when a sibling route is already set. See documentation for more info.'
|
||||
})
|
||||
|
||||
const FSTDEP008 = createDeprecation({
|
||||
code: 'FSTDEP008',
|
||||
message: 'You are using route constraints via the route { version: "..." } option, use { constraints: { version: "..." } } option instead.'
|
||||
})
|
||||
|
||||
const FSTDEP009 = createDeprecation({
|
||||
code: 'FSTDEP009',
|
||||
message: 'You are using a custom route versioning strategy via the server { versioning: "..." } option, use { constraints: { version: "..." } } option instead.'
|
||||
})
|
||||
|
||||
const FSTDEP010 = createDeprecation({
|
||||
code: 'FSTDEP010',
|
||||
message: 'Modifying the "reply.sent" property is deprecated. Use the "reply.hijack()" method instead.'
|
||||
})
|
||||
|
||||
const FSTDEP011 = createDeprecation({
|
||||
code: 'FSTDEP011',
|
||||
message: 'Variadic listen method is deprecated. Please use ".listen(optionsObject)" instead. The variadic signature will be removed in `fastify@5`.'
|
||||
})
|
||||
|
||||
const FSTDEP012 = createDeprecation({
|
||||
code: 'FSTDEP012',
|
||||
message: 'request.context property access is deprecated. Please use "request.routeOptions.config" or "request.routeOptions.schema" instead for accessing Route settings. The "request.context" will be removed in `fastify@5`.'
|
||||
})
|
||||
|
||||
const FSTDEP013 = createDeprecation({
|
||||
code: 'FSTDEP013',
|
||||
message: 'Direct return of "trailers" function is deprecated. Please use "callback" or "async-await" for return value. The support of direct return will removed in `fastify@5`.'
|
||||
})
|
||||
|
||||
const FSTDEP014 = createDeprecation({
|
||||
code: 'FSTDEP014',
|
||||
message: 'You are trying to set/access the default route. This property is deprecated. Please, use setNotFoundHandler if you want to custom a 404 handler or the wildcard (*) to match all routes.'
|
||||
})
|
||||
|
||||
const FSTDEP015 = createDeprecation({
|
||||
code: 'FSTDEP015',
|
||||
message: 'You are accessing the deprecated "request.routeSchema" property. Use "request.routeOptions.schema" instead. Property "req.routeSchema" will be removed in `fastify@5`.'
|
||||
})
|
||||
|
||||
const FSTDEP016 = createDeprecation({
|
||||
code: 'FSTDEP016',
|
||||
message: 'You are accessing the deprecated "request.routeConfig" property. Use "request.routeOptions.config" instead. Property "req.routeConfig" will be removed in `fastify@5`.'
|
||||
})
|
||||
|
||||
const FSTDEP017 = createDeprecation({
|
||||
code: 'FSTDEP017',
|
||||
message: 'You are accessing the deprecated "request.routerPath" property. Use "request.routeOptions.url" instead. Property "req.routerPath" will be removed in `fastify@5`.'
|
||||
})
|
||||
|
||||
const FSTDEP018 = createDeprecation({
|
||||
code: 'FSTDEP018',
|
||||
message: 'You are accessing the deprecated "request.routerMethod" property. Use "request.routeOptions.method" instead. Property "req.routerMethod" will be removed in `fastify@5`.'
|
||||
})
|
||||
|
||||
const FSTDEP019 = createDeprecation({
|
||||
code: 'FSTDEP019',
|
||||
message: 'reply.context property access is deprecated. Please use "request.routeOptions.config" or "request.routeOptions.schema" instead for accessing Route settings. The "reply.context" will be removed in `fastify@5`.'
|
||||
})
|
||||
|
||||
const FSTDEP020 = createDeprecation({
|
||||
code: 'FSTDEP020',
|
||||
message: 'You are using the deprecated "reply.getResponseTime()" method. Use the "reply.elapsedTime" property instead. Method "reply.getResponseTime()" will be removed in `fastify@5`.'
|
||||
})
|
||||
|
||||
const FSTDEP021 = createDeprecation({
|
||||
code: 'FSTDEP021',
|
||||
message: 'The `reply.redirect()` method has a new signature: `reply.redirect(url: string, code?: number)`. It will be enforced in `fastify@v5`'
|
||||
})
|
||||
|
||||
const FSTDEP022 = createDeprecation({
|
||||
code: 'FSTDEP021',
|
||||
message: 'You are using the deprecated json shorthand schema on route %s. Specify full object schema instead. It will be removed in `fastify@v5`'
|
||||
})
|
||||
|
||||
const FSTWRN001 = createWarning({
|
||||
name: 'FastifyWarning',
|
||||
code: 'FSTWRN001',
|
||||
message: 'The %s schema for %s: %s is missing. This may indicate the schema is not well specified.',
|
||||
unlimited: true
|
||||
})
|
||||
|
||||
const FSTWRN002 = createWarning({
|
||||
name: 'FastifyWarning',
|
||||
code: 'FSTWRN002',
|
||||
message: 'The %s plugin being registered mixes async and callback styles, which will result in an error in `fastify@5`',
|
||||
unlimited: true
|
||||
})
|
||||
|
||||
module.exports = {
|
||||
FSTDEP005,
|
||||
FSTDEP006,
|
||||
FSTDEP007,
|
||||
FSTDEP008,
|
||||
FSTDEP009,
|
||||
FSTDEP010,
|
||||
FSTDEP011,
|
||||
FSTDEP012,
|
||||
FSTDEP013,
|
||||
FSTDEP014,
|
||||
FSTDEP015,
|
||||
FSTDEP016,
|
||||
FSTDEP017,
|
||||
FSTDEP018,
|
||||
FSTDEP019,
|
||||
FSTDEP020,
|
||||
FSTDEP021,
|
||||
FSTDEP022,
|
||||
FSTWRN001,
|
||||
FSTWRN002
|
||||
}
|
||||
50
backend/node_modules/fastify/lib/wrapThenable.js
generated
vendored
Normal file
50
backend/node_modules/fastify/lib/wrapThenable.js
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
'use strict'
|
||||
|
||||
const {
|
||||
kReplyIsError,
|
||||
kReplyHijacked
|
||||
} = require('./symbols')
|
||||
|
||||
function wrapThenable (thenable, reply) {
|
||||
thenable.then(function (payload) {
|
||||
if (reply[kReplyHijacked] === true) {
|
||||
return
|
||||
}
|
||||
|
||||
// this is for async functions that are using reply.send directly
|
||||
//
|
||||
// since wrap-thenable will be called when using reply.send directly
|
||||
// without actual return. the response can be sent already or
|
||||
// the request may be terminated during the reply. in this situation,
|
||||
// it require an extra checking of request.aborted to see whether
|
||||
// the request is killed by client.
|
||||
if (payload !== undefined || (reply.sent === false && reply.raw.headersSent === false && reply.request.raw.aborted === false)) {
|
||||
// we use a try-catch internally to avoid adding a catch to another
|
||||
// promise, increase promise perf by 10%
|
||||
try {
|
||||
reply.send(payload)
|
||||
} catch (err) {
|
||||
reply[kReplyIsError] = true
|
||||
reply.send(err)
|
||||
}
|
||||
}
|
||||
}, function (err) {
|
||||
if (reply.sent === true) {
|
||||
reply.log.error({ err }, 'Promise errored, but reply.sent = true was set')
|
||||
return
|
||||
}
|
||||
|
||||
reply[kReplyIsError] = true
|
||||
|
||||
// try-catch allow to re-throw error in error handler for async handler
|
||||
try {
|
||||
reply.send(err)
|
||||
// The following should not happen
|
||||
/* c8 ignore next 3 */
|
||||
} catch (err) {
|
||||
reply.send(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = wrapThenable
|
||||
Reference in New Issue
Block a user