
- Complete NestJS TypeScript implementation with WebSocket support - Direct messaging (DM) and group chat functionality - End-to-end encryption with AES encryption and key pairs - Media file support (images, videos, audio, documents) up to 100MB - Push notifications with Firebase Cloud Messaging integration - Mention alerts and real-time typing indicators - User authentication with JWT and Passport - SQLite database with TypeORM entities and relationships - Comprehensive API documentation with Swagger/OpenAPI - File upload handling with secure access control - Online/offline status tracking and presence management - Message editing, deletion, and reply functionality - Notification management with automatic cleanup - Health check endpoint for monitoring - CORS configuration for cross-origin requests - Environment-based configuration management - Structured for Flutter SDK integration Features implemented: ✅ Real-time messaging with Socket.IO ✅ User registration and authentication ✅ Direct messages and group chats ✅ Media file uploads and management ✅ End-to-end encryption ✅ Push notifications ✅ Mention alerts ✅ Typing indicators ✅ Message read receipts ✅ Online status tracking ✅ File access control ✅ Comprehensive API documentation Ready for Flutter SDK development and production deployment.
351 lines
19 KiB
JavaScript
351 lines
19 KiB
JavaScript
var __read = (this && this.__read) || function (o, n) {
|
|
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
if (!m) return o;
|
|
var i = m.call(o), r, ar = [], e;
|
|
try {
|
|
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
}
|
|
catch (error) { e = { error: error }; }
|
|
finally {
|
|
try {
|
|
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
}
|
|
finally { if (e) throw e.error; }
|
|
}
|
|
return ar;
|
|
};
|
|
import { ValidationError } from './ValidationError';
|
|
import { ValidationTypes } from './ValidationTypes';
|
|
import { ValidationUtils } from './ValidationUtils';
|
|
import { isPromise, convertToArray } from '../utils';
|
|
import { getMetadataStorage } from '../metadata/MetadataStorage';
|
|
/**
|
|
* Executes validation over given object.
|
|
*/
|
|
var ValidationExecutor = /** @class */ (function () {
|
|
// -------------------------------------------------------------------------
|
|
// Constructor
|
|
// -------------------------------------------------------------------------
|
|
function ValidationExecutor(validator, validatorOptions) {
|
|
this.validator = validator;
|
|
this.validatorOptions = validatorOptions;
|
|
// -------------------------------------------------------------------------
|
|
// Properties
|
|
// -------------------------------------------------------------------------
|
|
this.awaitingPromises = [];
|
|
this.ignoreAsyncValidations = false;
|
|
// -------------------------------------------------------------------------
|
|
// Private Properties
|
|
// -------------------------------------------------------------------------
|
|
this.metadataStorage = getMetadataStorage();
|
|
}
|
|
// -------------------------------------------------------------------------
|
|
// Public Methods
|
|
// -------------------------------------------------------------------------
|
|
ValidationExecutor.prototype.execute = function (object, targetSchema, validationErrors) {
|
|
var _this = this;
|
|
var _a, _b;
|
|
/**
|
|
* If there is no metadata registered it means possibly the dependencies are not flatterned and
|
|
* more than one instance is used.
|
|
*
|
|
* TODO: This needs proper handling, forcing to use the same container or some other proper solution.
|
|
*/
|
|
if (!this.metadataStorage.hasValidationMetaData && ((_a = this.validatorOptions) === null || _a === void 0 ? void 0 : _a.enableDebugMessages) === true) {
|
|
console.warn("No validation metadata found. No validation will be performed. There are multiple possible reasons:\n" +
|
|
" - There may be multiple class-validator versions installed. You will need to flatten your dependencies to fix the issue.\n" +
|
|
" - This validation runs before any file with validation decorator was parsed by NodeJS.");
|
|
}
|
|
var groups = this.validatorOptions ? this.validatorOptions.groups : undefined;
|
|
var strictGroups = (this.validatorOptions && this.validatorOptions.strictGroups) || false;
|
|
var always = (this.validatorOptions && this.validatorOptions.always) || false;
|
|
/** Forbid unknown values are turned on by default and any other value than false will enable it. */
|
|
var forbidUnknownValues = ((_b = this.validatorOptions) === null || _b === void 0 ? void 0 : _b.forbidUnknownValues) === undefined || this.validatorOptions.forbidUnknownValues !== false;
|
|
var targetMetadatas = this.metadataStorage.getTargetValidationMetadatas(object.constructor, targetSchema, always, strictGroups, groups);
|
|
var groupedMetadatas = this.metadataStorage.groupByPropertyName(targetMetadatas);
|
|
if (forbidUnknownValues && !targetMetadatas.length) {
|
|
var validationError = new ValidationError();
|
|
if (!this.validatorOptions ||
|
|
!this.validatorOptions.validationError ||
|
|
this.validatorOptions.validationError.target === undefined ||
|
|
this.validatorOptions.validationError.target === true)
|
|
validationError.target = object;
|
|
validationError.value = undefined;
|
|
validationError.property = undefined;
|
|
validationError.children = [];
|
|
validationError.constraints = { unknownValue: 'an unknown value was passed to the validate function' };
|
|
validationErrors.push(validationError);
|
|
return;
|
|
}
|
|
if (this.validatorOptions && this.validatorOptions.whitelist)
|
|
this.whitelist(object, groupedMetadatas, validationErrors);
|
|
// General validation
|
|
Object.keys(groupedMetadatas).forEach(function (propertyName) {
|
|
var value = object[propertyName];
|
|
var definedMetadatas = groupedMetadatas[propertyName].filter(function (metadata) { return metadata.type === ValidationTypes.IS_DEFINED; });
|
|
var metadatas = groupedMetadatas[propertyName].filter(function (metadata) { return metadata.type !== ValidationTypes.IS_DEFINED && metadata.type !== ValidationTypes.WHITELIST; });
|
|
if (value instanceof Promise &&
|
|
metadatas.find(function (metadata) { return metadata.type === ValidationTypes.PROMISE_VALIDATION; })) {
|
|
_this.awaitingPromises.push(value.then(function (resolvedValue) {
|
|
_this.performValidations(object, resolvedValue, propertyName, definedMetadatas, metadatas, validationErrors);
|
|
}));
|
|
}
|
|
else {
|
|
_this.performValidations(object, value, propertyName, definedMetadatas, metadatas, validationErrors);
|
|
}
|
|
});
|
|
};
|
|
ValidationExecutor.prototype.whitelist = function (object, groupedMetadatas, validationErrors) {
|
|
var _this = this;
|
|
var notAllowedProperties = [];
|
|
Object.keys(object).forEach(function (propertyName) {
|
|
// does this property have no metadata?
|
|
if (!groupedMetadatas[propertyName] || groupedMetadatas[propertyName].length === 0)
|
|
notAllowedProperties.push(propertyName);
|
|
});
|
|
if (notAllowedProperties.length > 0) {
|
|
if (this.validatorOptions && this.validatorOptions.forbidNonWhitelisted) {
|
|
// throw errors
|
|
notAllowedProperties.forEach(function (property) {
|
|
var _a;
|
|
var validationError = _this.generateValidationError(object, object[property], property);
|
|
validationError.constraints = (_a = {}, _a[ValidationTypes.WHITELIST] = "property ".concat(property, " should not exist"), _a);
|
|
validationError.children = undefined;
|
|
validationErrors.push(validationError);
|
|
});
|
|
}
|
|
else {
|
|
// strip non allowed properties
|
|
notAllowedProperties.forEach(function (property) { return delete object[property]; });
|
|
}
|
|
}
|
|
};
|
|
ValidationExecutor.prototype.stripEmptyErrors = function (errors) {
|
|
var _this = this;
|
|
return errors.filter(function (error) {
|
|
if (error.children) {
|
|
error.children = _this.stripEmptyErrors(error.children);
|
|
}
|
|
if (Object.keys(error.constraints).length === 0) {
|
|
if (error.children.length === 0) {
|
|
return false;
|
|
}
|
|
else {
|
|
delete error.constraints;
|
|
}
|
|
}
|
|
return true;
|
|
});
|
|
};
|
|
// -------------------------------------------------------------------------
|
|
// Private Methods
|
|
// -------------------------------------------------------------------------
|
|
ValidationExecutor.prototype.performValidations = function (object, value, propertyName, definedMetadatas, metadatas, validationErrors) {
|
|
var customValidationMetadatas = metadatas.filter(function (metadata) { return metadata.type === ValidationTypes.CUSTOM_VALIDATION; });
|
|
var nestedValidationMetadatas = metadatas.filter(function (metadata) { return metadata.type === ValidationTypes.NESTED_VALIDATION; });
|
|
var conditionalValidationMetadatas = metadatas.filter(function (metadata) { return metadata.type === ValidationTypes.CONDITIONAL_VALIDATION; });
|
|
var validationError = this.generateValidationError(object, value, propertyName);
|
|
validationErrors.push(validationError);
|
|
var canValidate = this.conditionalValidations(object, value, conditionalValidationMetadatas);
|
|
if (!canValidate) {
|
|
return;
|
|
}
|
|
// handle IS_DEFINED validation type the special way - it should work no matter skipUndefinedProperties/skipMissingProperties is set or not
|
|
this.customValidations(object, value, definedMetadatas, validationError);
|
|
this.mapContexts(object, value, definedMetadatas, validationError);
|
|
if (value === undefined && this.validatorOptions && this.validatorOptions.skipUndefinedProperties === true) {
|
|
return;
|
|
}
|
|
if (value === null && this.validatorOptions && this.validatorOptions.skipNullProperties === true) {
|
|
return;
|
|
}
|
|
if ((value === null || value === undefined) &&
|
|
this.validatorOptions &&
|
|
this.validatorOptions.skipMissingProperties === true) {
|
|
return;
|
|
}
|
|
this.customValidations(object, value, customValidationMetadatas, validationError);
|
|
this.nestedValidations(value, nestedValidationMetadatas, validationError);
|
|
this.mapContexts(object, value, metadatas, validationError);
|
|
this.mapContexts(object, value, customValidationMetadatas, validationError);
|
|
};
|
|
ValidationExecutor.prototype.generateValidationError = function (object, value, propertyName) {
|
|
var validationError = new ValidationError();
|
|
if (!this.validatorOptions ||
|
|
!this.validatorOptions.validationError ||
|
|
this.validatorOptions.validationError.target === undefined ||
|
|
this.validatorOptions.validationError.target === true)
|
|
validationError.target = object;
|
|
if (!this.validatorOptions ||
|
|
!this.validatorOptions.validationError ||
|
|
this.validatorOptions.validationError.value === undefined ||
|
|
this.validatorOptions.validationError.value === true)
|
|
validationError.value = value;
|
|
validationError.property = propertyName;
|
|
validationError.children = [];
|
|
validationError.constraints = {};
|
|
return validationError;
|
|
};
|
|
ValidationExecutor.prototype.conditionalValidations = function (object, value, metadatas) {
|
|
return metadatas
|
|
.map(function (metadata) { return metadata.constraints[0](object, value); })
|
|
.reduce(function (resultA, resultB) { return resultA && resultB; }, true);
|
|
};
|
|
ValidationExecutor.prototype.customValidations = function (object, value, metadatas, error) {
|
|
var _this = this;
|
|
metadatas.forEach(function (metadata) {
|
|
_this.metadataStorage.getTargetValidatorConstraints(metadata.constraintCls).forEach(function (customConstraintMetadata) {
|
|
if (customConstraintMetadata.async && _this.ignoreAsyncValidations)
|
|
return;
|
|
if (_this.validatorOptions &&
|
|
_this.validatorOptions.stopAtFirstError &&
|
|
Object.keys(error.constraints || {}).length > 0)
|
|
return;
|
|
var validationArguments = {
|
|
targetName: object.constructor ? object.constructor.name : undefined,
|
|
property: metadata.propertyName,
|
|
object: object,
|
|
value: value,
|
|
constraints: metadata.constraints,
|
|
};
|
|
if (!metadata.each || !(Array.isArray(value) || value instanceof Set || value instanceof Map)) {
|
|
var validatedValue = customConstraintMetadata.instance.validate(value, validationArguments);
|
|
if (isPromise(validatedValue)) {
|
|
var promise = validatedValue.then(function (isValid) {
|
|
if (!isValid) {
|
|
var _a = __read(_this.createValidationError(object, value, metadata, customConstraintMetadata), 2), type = _a[0], message = _a[1];
|
|
error.constraints[type] = message;
|
|
if (metadata.context) {
|
|
if (!error.contexts) {
|
|
error.contexts = {};
|
|
}
|
|
error.contexts[type] = Object.assign(error.contexts[type] || {}, metadata.context);
|
|
}
|
|
}
|
|
});
|
|
_this.awaitingPromises.push(promise);
|
|
}
|
|
else {
|
|
if (!validatedValue) {
|
|
var _a = __read(_this.createValidationError(object, value, metadata, customConstraintMetadata), 2), type = _a[0], message = _a[1];
|
|
error.constraints[type] = message;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
// convert set and map into array
|
|
var arrayValue = convertToArray(value);
|
|
// Validation needs to be applied to each array item
|
|
var validatedSubValues = arrayValue.map(function (subValue) {
|
|
return customConstraintMetadata.instance.validate(subValue, validationArguments);
|
|
});
|
|
var validationIsAsync = validatedSubValues.some(function (validatedSubValue) {
|
|
return isPromise(validatedSubValue);
|
|
});
|
|
if (validationIsAsync) {
|
|
// Wrap plain values (if any) in promises, so that all are async
|
|
var asyncValidatedSubValues = validatedSubValues.map(function (validatedSubValue) {
|
|
return isPromise(validatedSubValue) ? validatedSubValue : Promise.resolve(validatedSubValue);
|
|
});
|
|
var asyncValidationIsFinishedPromise = Promise.all(asyncValidatedSubValues).then(function (flatValidatedValues) {
|
|
var validationResult = flatValidatedValues.every(function (isValid) { return isValid; });
|
|
if (!validationResult) {
|
|
var _a = __read(_this.createValidationError(object, value, metadata, customConstraintMetadata), 2), type = _a[0], message = _a[1];
|
|
error.constraints[type] = message;
|
|
if (metadata.context) {
|
|
if (!error.contexts) {
|
|
error.contexts = {};
|
|
}
|
|
error.contexts[type] = Object.assign(error.contexts[type] || {}, metadata.context);
|
|
}
|
|
}
|
|
});
|
|
_this.awaitingPromises.push(asyncValidationIsFinishedPromise);
|
|
return;
|
|
}
|
|
var validationResult = validatedSubValues.every(function (isValid) { return isValid; });
|
|
if (!validationResult) {
|
|
var _b = __read(_this.createValidationError(object, value, metadata, customConstraintMetadata), 2), type = _b[0], message = _b[1];
|
|
error.constraints[type] = message;
|
|
}
|
|
});
|
|
});
|
|
};
|
|
ValidationExecutor.prototype.nestedValidations = function (value, metadatas, error) {
|
|
var _this = this;
|
|
if (value === void 0) {
|
|
return;
|
|
}
|
|
metadatas.forEach(function (metadata) {
|
|
if (metadata.type !== ValidationTypes.NESTED_VALIDATION && metadata.type !== ValidationTypes.PROMISE_VALIDATION) {
|
|
return;
|
|
}
|
|
else if (_this.validatorOptions &&
|
|
_this.validatorOptions.stopAtFirstError &&
|
|
Object.keys(error.constraints || {}).length > 0) {
|
|
return;
|
|
}
|
|
if (Array.isArray(value) || value instanceof Set || value instanceof Map) {
|
|
// Treats Set as an array - as index of Set value is value itself and it is common case to have Object as value
|
|
var arrayLikeValue = value instanceof Set ? Array.from(value) : value;
|
|
arrayLikeValue.forEach(function (subValue, index) {
|
|
_this.performValidations(value, subValue, index.toString(), [], metadatas, error.children);
|
|
});
|
|
}
|
|
else if (value instanceof Object) {
|
|
var targetSchema = typeof metadata.target === 'string' ? metadata.target : metadata.target.name;
|
|
_this.execute(value, targetSchema, error.children);
|
|
}
|
|
else {
|
|
var _a = __read(_this.createValidationError(metadata.target, value, metadata), 2), type = _a[0], message = _a[1];
|
|
error.constraints[type] = message;
|
|
}
|
|
});
|
|
};
|
|
ValidationExecutor.prototype.mapContexts = function (object, value, metadatas, error) {
|
|
var _this = this;
|
|
return metadatas.forEach(function (metadata) {
|
|
if (metadata.context) {
|
|
var customConstraint = void 0;
|
|
if (metadata.type === ValidationTypes.CUSTOM_VALIDATION) {
|
|
var customConstraints = _this.metadataStorage.getTargetValidatorConstraints(metadata.constraintCls);
|
|
customConstraint = customConstraints[0];
|
|
}
|
|
var type = _this.getConstraintType(metadata, customConstraint);
|
|
if (error.constraints[type]) {
|
|
if (!error.contexts) {
|
|
error.contexts = {};
|
|
}
|
|
error.contexts[type] = Object.assign(error.contexts[type] || {}, metadata.context);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
ValidationExecutor.prototype.createValidationError = function (object, value, metadata, customValidatorMetadata) {
|
|
var targetName = object.constructor ? object.constructor.name : undefined;
|
|
var type = this.getConstraintType(metadata, customValidatorMetadata);
|
|
var validationArguments = {
|
|
targetName: targetName,
|
|
property: metadata.propertyName,
|
|
object: object,
|
|
value: value,
|
|
constraints: metadata.constraints,
|
|
};
|
|
var message = metadata.message || '';
|
|
if (!metadata.message &&
|
|
(!this.validatorOptions || (this.validatorOptions && !this.validatorOptions.dismissDefaultMessages))) {
|
|
if (customValidatorMetadata && customValidatorMetadata.instance.defaultMessage instanceof Function) {
|
|
message = customValidatorMetadata.instance.defaultMessage(validationArguments);
|
|
}
|
|
}
|
|
var messageString = ValidationUtils.replaceMessageSpecialTokens(message, validationArguments);
|
|
return [type, messageString];
|
|
};
|
|
ValidationExecutor.prototype.getConstraintType = function (metadata, customValidatorMetadata) {
|
|
var type = customValidatorMetadata && customValidatorMetadata.name ? customValidatorMetadata.name : metadata.type;
|
|
return type;
|
|
};
|
|
return ValidationExecutor;
|
|
}());
|
|
export { ValidationExecutor };
|
|
//# sourceMappingURL=ValidationExecutor.js.map
|