/
Restricter.js
111 lines (93 loc) · 3.47 KB
/
Restricter.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/* eslint-disable max-classes-per-file, class-methods-use-this */
import prettierBytes from '@transloadit/prettier-bytes'
import match from 'mime-match'
const defaultOptions = {
maxFileSize: null,
minFileSize: null,
maxTotalFileSize: null,
maxNumberOfFiles: null,
minNumberOfFiles: null,
allowedFileTypes: null,
requiredMetaFields: [],
}
class RestrictionError extends Error {
isRestriction = true
}
class Restricter {
constructor (getOpts, i18n) {
this.i18n = i18n
this.getOpts = () => {
const opts = getOpts()
if (opts.restrictions.allowedFileTypes != null
&& !Array.isArray(opts.restrictions.allowedFileTypes)) {
throw new TypeError('`restrictions.allowedFileTypes` must be an array')
}
return opts
}
}
validate (file, files) {
const { maxFileSize, minFileSize, maxTotalFileSize, maxNumberOfFiles, allowedFileTypes } = this.getOpts().restrictions
if (maxNumberOfFiles && files.length + 1 > maxNumberOfFiles) {
throw new RestrictionError(`${this.i18n('youCanOnlyUploadX', { smart_count: maxNumberOfFiles })}`)
}
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.i18n('youCanOnlyUploadFileTypes', { types: allowedFileTypesString }))
}
}
// We can't check maxTotalFileSize if the size is unknown.
if (maxTotalFileSize && file.size != null) {
const totalFilesSize = files.reduce((total, f) => (total + f.size), file.size)
if (totalFilesSize > maxTotalFileSize) {
throw new RestrictionError(this.i18n('exceedsSize', {
size: prettierBytes(maxTotalFileSize),
file: file.name,
}))
}
}
// We can't check maxFileSize if the size is unknown.
if (maxFileSize && file.size != null && file.size > maxFileSize) {
throw new RestrictionError(this.i18n('exceedsSize', {
size: prettierBytes(maxFileSize),
file: file.name,
}))
}
// We can't check minFileSize if the size is unknown.
if (minFileSize && file.size != null && file.size < minFileSize) {
throw new RestrictionError(this.i18n('inferiorSize', {
size: prettierBytes(minFileSize),
}))
}
}
validateMinNumberOfFiles (files) {
const { minNumberOfFiles } = this.getOpts().restrictions
if (Object.keys(files).length < minNumberOfFiles) {
throw new RestrictionError(this.i18n('youHaveToAtLeastSelectX', { smart_count: minNumberOfFiles }))
}
}
getMissingRequiredMetaFields (file) {
const error = new RestrictionError(this.i18n('missingRequiredMetaFieldOnFile', { fileName: file.name }))
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 }