@uppy/core
Version:
Core module for the extensible JavaScript file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Instagram, Dropbox, Google Drive, S3 and more :dog:
157 lines (151 loc) • 5.21 kB
JavaScript
/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable max-classes-per-file, class-methods-use-this */
import prettierBytes from '@transloadit/prettier-bytes';
// @ts-ignore untyped
import match from 'mime-match';
/**
* The minimal required properties to be present from UppyFile in order to validate it.
*/
const defaultOptions = {
maxFileSize: null,
minFileSize: null,
maxTotalFileSize: null,
maxNumberOfFiles: null,
minNumberOfFiles: null,
allowedFileTypes: null,
requiredMetaFields: []
};
class RestrictionError extends Error {
constructor(message, opts) {
var _opts$isUserFacing;
super(message);
this.isRestriction = true;
this.isUserFacing = (_opts$isUserFacing = opts == null ? void 0 : opts.isUserFacing) != null ? _opts$isUserFacing : true;
if (opts != null && opts.file) {
this.file = opts.file; // only some restriction errors are related to a particular file
}
}
}
class Restricter {
constructor(getOpts, getI18n) {
this.getI18n = getI18n;
this.getOpts = () => {
var _opts$restrictions;
const opts = getOpts();
if (((_opts$restrictions = opts.restrictions) == null ? void 0 : _opts$restrictions.allowedFileTypes) != null && !Array.isArray(opts.restrictions.allowedFileTypes)) {
throw new TypeError('`restrictions.allowedFileTypes` must be an array');
}
return opts;
};
}
// Because these operations are slow, we cannot run them for every file (if we are adding multiple files)
validateAggregateRestrictions(existingFiles, addingFiles) {
const {
maxTotalFileSize,
maxNumberOfFiles
} = this.getOpts().restrictions;
if (maxNumberOfFiles) {
const nonGhostFiles = existingFiles.filter(f => !f.isGhost);
if (nonGhostFiles.length + addingFiles.length > maxNumberOfFiles) {
throw new RestrictionError(`${this.getI18n()('youCanOnlyUploadX', {
smart_count: maxNumberOfFiles
})}`);
}
}
if (maxTotalFileSize) {
const totalFilesSize = [...existingFiles, ...addingFiles].reduce((total, f) => {
var _f$size;
return total + ((_f$size = f.size) != null ? _f$size : 0);
}, 0);
if (totalFilesSize > maxTotalFileSize) {
throw new RestrictionError(this.getI18n()('aggregateExceedsSize', {
sizeAllowed: prettierBytes(maxTotalFileSize),
size: prettierBytes(totalFilesSize)
}));
}
}
}
validateSingleFile(file) {
const {
maxFileSize,
minFileSize,
allowedFileTypes
} = this.getOpts().restrictions;
if (allowedFileTypes) {
const isCorrectFileType = allowedFileTypes.some(type => {
// check if this is a mime-type
if (type.includes('/')) {
if (!file.type) return false;
return match(file.type.replace(/;.*?$/, ''), type);
}
// otherwise this is likely an extension
if (type[0] === '.' && file.extension) {
return file.extension.toLowerCase() === type.slice(1).toLowerCase();
}
return false;
});
if (!isCorrectFileType) {
const allowedFileTypesString = allowedFileTypes.join(', ');
throw new RestrictionError(this.getI18n()('youCanOnlyUploadFileTypes', {
types: allowedFileTypesString
}), {
file
});
}
}
// We can't check maxFileSize if the size is unknown.
if (maxFileSize && file.size != null && file.size > maxFileSize) {
var _file$name;
throw new RestrictionError(this.getI18n()('exceedsSize', {
size: prettierBytes(maxFileSize),
file: (_file$name = file.name) != null ? _file$name : this.getI18n()('unnamed')
}), {
file
});
}
// We can't check minFileSize if the size is unknown.
if (minFileSize && file.size != null && file.size < minFileSize) {
throw new RestrictionError(this.getI18n()('inferiorSize', {
size: prettierBytes(minFileSize)
}), {
file
});
}
}
validate(existingFiles, addingFiles) {
addingFiles.forEach(addingFile => {
this.validateSingleFile(addingFile);
});
this.validateAggregateRestrictions(existingFiles, addingFiles);
}
validateMinNumberOfFiles(files) {
const {
minNumberOfFiles
} = this.getOpts().restrictions;
if (minNumberOfFiles && Object.keys(files).length < minNumberOfFiles) {
throw new RestrictionError(this.getI18n()('youHaveToAtLeastSelectX', {
smart_count: minNumberOfFiles
}));
}
}
getMissingRequiredMetaFields(file) {
var _file$name2;
const error = new RestrictionError(this.getI18n()('missingRequiredMetaFieldOnFile', {
fileName: (_file$name2 = file.name) != null ? _file$name2 : this.getI18n()('unnamed')
}));
const {
requiredMetaFields
} = this.getOpts().restrictions;
const missingFields = [];
for (const field of requiredMetaFields) {
if (!Object.hasOwn(file.meta, field) || file.meta[field] === '') {
missingFields.push(field);
}
}
return {
missingFields,
error
};
}
}
export { Restricter, defaultOptions, RestrictionError };