 142b83cc13
			
		
	
	142b83cc13
	
	
	
		
			
			New features: - Automatic error reporting (enabled by default) - Increase build times by leveraging docker build caches - Fixes: - Fix error handling - Fix vue autodetect - Custom dockerfile is not the default Others: - Cleanup `logs-servers` collection, because old errors are not standardized - New Traefik proxy version - Standardized directory configurations
		
			
				
	
	
		
			240 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			240 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* eslint-disable */
 | |
| "use strict";
 | |
| var __importDefault = (this && this.__importDefault) || function (mod) {
 | |
|     return (mod && mod.__esModule) ? mod : { "default": mod };
 | |
| };
 | |
| Object.defineProperty(exports, "__esModule", { value: true });
 | |
| exports.compileResponseValidationSchema = exports.addResponseValidation = exports.convertValidationErrors = exports.validationMessagesFormatters = exports.niceJoin = void 0;
 | |
| const ajv_1 = __importDefault(require("ajv"));
 | |
| const http_errors_enhanced_1 = require("http-errors-enhanced");
 | |
| const interfaces_1 = require("./interfaces");
 | |
| const utils_1 = require("./utils");
 | |
| function niceJoin(array, lastSeparator = ' and ', separator = ', ') {
 | |
|     switch (array.length) {
 | |
|         case 0:
 | |
|             return '';
 | |
|         case 1:
 | |
|             return array[0];
 | |
|         case 2:
 | |
|             return array.join(lastSeparator);
 | |
|         default:
 | |
|             return array.slice(0, array.length - 1).join(separator) + lastSeparator + array[array.length - 1];
 | |
|     }
 | |
| }
 | |
| exports.niceJoin = niceJoin;
 | |
| exports.validationMessagesFormatters = {
 | |
|     contentType: () => 'only JSON payloads are accepted. Please set the "Content-Type" header to start with "application/json"',
 | |
|     json: () => 'the body payload is not a valid JSON',
 | |
|     jsonEmpty: () => 'the JSON body payload cannot be empty if the "Content-Type" header is set',
 | |
|     missing: () => 'must be present',
 | |
|     unknown: () => 'is not a valid property',
 | |
|     uuid: () => 'must be a valid GUID (UUID v4)',
 | |
|     timestamp: () => 'must be a valid ISO 8601 / RFC 3339 timestamp (example: 2018-07-06T12:34:56Z)',
 | |
|     date: () => 'must be a valid ISO 8601 / RFC 3339 date (example: 2018-07-06)',
 | |
|     time: () => 'must be a valid ISO 8601 / RFC 3339 time (example: 12:34:56)',
 | |
|     uri: () => 'must be a valid URI',
 | |
|     hostname: () => 'must be a valid hostname',
 | |
|     ipv4: () => 'must be a valid IPv4',
 | |
|     ipv6: () => 'must be a valid IPv6',
 | |
|     paramType: (type) => {
 | |
|         switch (type) {
 | |
|             case 'integer':
 | |
|                 return 'must be a valid integer number';
 | |
|             case 'number':
 | |
|                 return 'must be a valid number';
 | |
|             case 'boolean':
 | |
|                 return 'must be a valid boolean (true or false)';
 | |
|             case 'object':
 | |
|                 return 'must be a object';
 | |
|             case 'array':
 | |
|                 return 'must be an array';
 | |
|             default:
 | |
|                 return 'must be a string';
 | |
|         }
 | |
|     },
 | |
|     presentString: () => 'must be a non empty string',
 | |
|     minimum: (min) => `must be a number greater than or equal to ${min}`,
 | |
|     maximum: (max) => `must be a number less than or equal to ${max}`,
 | |
|     minimumProperties(min) {
 | |
|         return min === 1 ? 'cannot be a empty object' : `must be a object with at least ${min} properties`;
 | |
|     },
 | |
|     maximumProperties(max) {
 | |
|         return max === 0 ? 'must be a empty object' : `must be a object with at most ${max} properties`;
 | |
|     },
 | |
|     minimumItems(min) {
 | |
|         return min === 1 ? 'cannot be a empty array' : `must be an array with at least ${min} items`;
 | |
|     },
 | |
|     maximumItems(max) {
 | |
|         return max === 0 ? 'must be a empty array' : `must be an array with at most ${max} items`;
 | |
|     },
 | |
|     enum: (values) => `must be one of the following values: ${niceJoin(values.map((f) => `"${f}"`), ' or ')}`,
 | |
|     pattern: (pattern) => `must match pattern "${pattern.replace(/\(\?:/g, '(')}"`,
 | |
|     invalidResponseCode: (code) => `This endpoint cannot respond with HTTP status ${code}.`,
 | |
|     invalidResponse: (code) => `The response returned from the endpoint violates its specification for the HTTP status ${code}.`,
 | |
|     invalidFormat: (format) => `must match format "${format}" (format)`
 | |
| };
 | |
| function convertValidationErrors(section, data, validationErrors) {
 | |
|     const errors = {};
 | |
|     if (section === 'querystring') {
 | |
|         section = 'query';
 | |
|     }
 | |
|     // For each error
 | |
|     for (const e of validationErrors) {
 | |
|         let message = '';
 | |
|         let pattern;
 | |
|         let value;
 | |
|         let reason;
 | |
|         // Normalize the key
 | |
|         let key = e.dataPath;
 | |
|         if (key.startsWith('.')) {
 | |
|             key = key.substring(1);
 | |
|         }
 | |
|         // Remove useless quotes
 | |
|         /* istanbul ignore next */
 | |
|         if (key.startsWith('[') && key.endsWith(']')) {
 | |
|             key = key.substring(1, key.length - 1);
 | |
|         }
 | |
|         // Depending on the type
 | |
|         switch (e.keyword) {
 | |
|             case 'required':
 | |
|             case 'dependencies':
 | |
|                 key = e.params.missingProperty;
 | |
|                 message = exports.validationMessagesFormatters.missing();
 | |
|                 break;
 | |
|             case 'additionalProperties':
 | |
|                 key = e.params.additionalProperty;
 | |
|                 message = exports.validationMessagesFormatters.unknown();
 | |
|                 break;
 | |
|             case 'type':
 | |
|                 message = exports.validationMessagesFormatters.paramType(e.params.type);
 | |
|                 break;
 | |
|             case 'minProperties':
 | |
|                 message = exports.validationMessagesFormatters.minimumProperties(e.params.limit);
 | |
|                 break;
 | |
|             case 'maxProperties':
 | |
|                 message = exports.validationMessagesFormatters.maximumProperties(e.params.limit);
 | |
|                 break;
 | |
|             case 'minItems':
 | |
|                 message = exports.validationMessagesFormatters.minimumItems(e.params.limit);
 | |
|                 break;
 | |
|             case 'maxItems':
 | |
|                 message = exports.validationMessagesFormatters.maximumItems(e.params.limit);
 | |
|                 break;
 | |
|             case 'minimum':
 | |
|                 message = exports.validationMessagesFormatters.minimum(e.params.limit);
 | |
|                 break;
 | |
|             case 'maximum':
 | |
|                 message = exports.validationMessagesFormatters.maximum(e.params.limit);
 | |
|                 break;
 | |
|             case 'enum':
 | |
|                 message = exports.validationMessagesFormatters.enum(e.params.allowedValues);
 | |
|                 break;
 | |
|             case 'pattern':
 | |
|                 pattern = e.params.pattern;
 | |
|                 value = utils_1.get(data, key);
 | |
|                 if (pattern === '.+' && !value) {
 | |
|                     message = exports.validationMessagesFormatters.presentString();
 | |
|                 }
 | |
|                 else {
 | |
|                     message = exports.validationMessagesFormatters.pattern(e.params.pattern);
 | |
|                 }
 | |
|                 break;
 | |
|             case 'format':
 | |
|                 reason = e.params.format;
 | |
|                 // Normalize the key
 | |
|                 if (reason === 'date-time') {
 | |
|                     reason = 'timestamp';
 | |
|                 }
 | |
|                 message = (exports.validationMessagesFormatters[reason] || exports.validationMessagesFormatters.invalidFormat)(reason);
 | |
|                 break;
 | |
|         }
 | |
|         // No custom message was found, default to input one replacing the starting verb and adding some path info
 | |
|         if (!message.length) {
 | |
|             message = `${e.message.replace(/^should/, 'must')} (${e.keyword})`;
 | |
|         }
 | |
|         // Remove useless quotes
 | |
|         /* istanbul ignore next */
 | |
|         if (key.match(/(?:^['"])(?:[^.]+)(?:['"]$)/)) {
 | |
|             key = key.substring(1, key.length - 1);
 | |
|         }
 | |
|         // Fix empty properties
 | |
|         if (!key) {
 | |
|             key = '$root';
 | |
|         }
 | |
|         key = key.replace(/^\//, '');
 | |
|         errors[key] = message;
 | |
|     }
 | |
|     return { [section]: errors };
 | |
| }
 | |
| exports.convertValidationErrors = convertValidationErrors;
 | |
| function addResponseValidation(route) {
 | |
|     var _a;
 | |
|     if (!((_a = route.schema) === null || _a === void 0 ? void 0 : _a.response)) {
 | |
|         return;
 | |
|     }
 | |
|     const validators = {};
 | |
|     /*
 | |
|       Add these validators to the list of the one to compile once the server is started.
 | |
|       This makes possible to handle shared schemas.
 | |
|     */
 | |
|     this[interfaces_1.kHttpErrorsEnhancedResponseValidations].push([
 | |
|         this,
 | |
|         validators,
 | |
|         Object.entries(route.schema.response)
 | |
|     ]);
 | |
|     // Note that this hook is not called for non JSON payloads therefore validation is not possible in such cases
 | |
|     route.preSerialization = async function (request, reply, payload) {
 | |
|         const statusCode = reply.raw.statusCode;
 | |
|         // Never validate error 500
 | |
|         if (statusCode === http_errors_enhanced_1.INTERNAL_SERVER_ERROR) {
 | |
|             return payload;
 | |
|         }
 | |
|         // No validator, it means the HTTP status is not allowed
 | |
|         const validator = validators[statusCode];
 | |
|         if (!validator) {
 | |
|             if (request[interfaces_1.kHttpErrorsEnhancedConfiguration].allowUndeclaredResponses) {
 | |
|                 return payload;
 | |
|             }
 | |
|             throw new http_errors_enhanced_1.InternalServerError(exports.validationMessagesFormatters.invalidResponseCode(statusCode));
 | |
|         }
 | |
|         // Now validate the payload
 | |
|         const valid = validator(payload);
 | |
|         if (!valid) {
 | |
|             throw new http_errors_enhanced_1.InternalServerError(exports.validationMessagesFormatters.invalidResponse(statusCode), {
 | |
|                 failedValidations: convertValidationErrors('response', payload, validator.errors)
 | |
|             });
 | |
|         }
 | |
|         return payload;
 | |
|     };
 | |
| }
 | |
| exports.addResponseValidation = addResponseValidation;
 | |
| function compileResponseValidationSchema(configuration) {
 | |
|     // Fix CJS/ESM interoperability
 | |
|     // @ts-expect-error
 | |
|     let AjvConstructor = ajv_1.default;
 | |
|     /* istanbul ignore next */
 | |
|     if (AjvConstructor.default) {
 | |
|         AjvConstructor = AjvConstructor.default;
 | |
|     }
 | |
|     const hasCustomizer = typeof configuration.responseValidatorCustomizer === 'function';
 | |
|     for (const [instance, validators, schemas] of this[interfaces_1.kHttpErrorsEnhancedResponseValidations]) {
 | |
|         // @ts-expect-error
 | |
|         const compiler = new AjvConstructor({
 | |
|             // The fastify defaults, with the exception of removeAdditional and coerceTypes, which have been reversed
 | |
|             removeAdditional: false,
 | |
|             useDefaults: true,
 | |
|             coerceTypes: false,
 | |
|             allErrors: true
 | |
|         });
 | |
|         compiler.addSchema(Object.values(instance.getSchemas()));
 | |
|         compiler.addKeyword('example');
 | |
|         if (hasCustomizer) {
 | |
|             configuration.responseValidatorCustomizer(compiler);
 | |
|         }
 | |
|         for (const [code, schema] of schemas) {
 | |
|             validators[code] = compiler.compile(schema);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| exports.compileResponseValidationSchema = compileResponseValidationSchema;
 |