@startupjs-ui/file-input 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/package.json +6 -2
- package/providers/awsS3.js +241 -0
- package/providers/azureblob.js +5 -4
- package/providers/index.js +10 -8
- package/providers/mongo.js +5 -4
- package/providers/sqlite.js +5 -4
- package/server/uploadBuffer.js +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [0.1.9](https://github.com/startupjs/startupjs-ui/compare/v0.1.8...v0.1.9) (2026-01-16)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @startupjs-ui/file-input
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
6
14
|
## [0.1.8](https://github.com/startupjs/startupjs-ui/compare/v0.1.7...v0.1.8) (2026-01-08)
|
|
7
15
|
|
|
8
16
|
**Note:** Version bump only for package @startupjs-ui/file-input
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@startupjs-ui/file-input",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"sharp": "^0.34.5"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
+
"@aws-sdk/client-s3": "*",
|
|
31
32
|
"@azure/storage-blob": "*",
|
|
32
33
|
"mongodb": "*",
|
|
33
34
|
"react": "*",
|
|
@@ -35,6 +36,9 @@
|
|
|
35
36
|
"startupjs": "*"
|
|
36
37
|
},
|
|
37
38
|
"peerDependenciesMeta": {
|
|
39
|
+
"@aws-sdk/client-s3": {
|
|
40
|
+
"optional": true
|
|
41
|
+
},
|
|
38
42
|
"@azure/storage-blob": {
|
|
39
43
|
"optional": true
|
|
40
44
|
},
|
|
@@ -42,5 +46,5 @@
|
|
|
42
46
|
"optional": true
|
|
43
47
|
}
|
|
44
48
|
},
|
|
45
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "215449157f986f8cd7139ebe27015db5f39da4ce"
|
|
46
50
|
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { $, sub } from 'startupjs'
|
|
2
|
+
import { S3Client, GetObjectCommand, PutObjectCommand, DeleteObjectCommand, HeadObjectCommand } from '@aws-sdk/client-s3'
|
|
3
|
+
|
|
4
|
+
// AWS S3 Configuration from environment variables
|
|
5
|
+
const AWS_REGION = process.env.AWS_REGION
|
|
6
|
+
const AWS_ACCESS_KEY_ID = process.env.AWS_ACCESS_KEY_ID
|
|
7
|
+
const AWS_SECRET_ACCESS_KEY = process.env.AWS_SECRET_ACCESS_KEY
|
|
8
|
+
const AWS_S3_BUCKET_NAME = process.env.AWS_S3_BUCKET_NAME
|
|
9
|
+
const AWS_S3_ENDPOINT = process.env.AWS_S3_ENDPOINT // Optional: for MinIO, LocalStack, etc.
|
|
10
|
+
|
|
11
|
+
let s3Client
|
|
12
|
+
let bucketName
|
|
13
|
+
|
|
14
|
+
export function validateSupport () {
|
|
15
|
+
if (!AWS_REGION) {
|
|
16
|
+
throw new Error(ERRORS.awsRegionNotAvailable)
|
|
17
|
+
}
|
|
18
|
+
if (!AWS_ACCESS_KEY_ID) {
|
|
19
|
+
throw new Error(ERRORS.awsAccessKeyNotAvailable)
|
|
20
|
+
}
|
|
21
|
+
if (!AWS_SECRET_ACCESS_KEY) {
|
|
22
|
+
throw new Error(ERRORS.awsSecretKeyNotAvailable)
|
|
23
|
+
}
|
|
24
|
+
if (!AWS_S3_BUCKET_NAME) {
|
|
25
|
+
throw new Error(ERRORS.awsBucketNotAvailable)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Initialize the S3Client once
|
|
29
|
+
if (!s3Client) {
|
|
30
|
+
console.log('[@startupjs-ui] FileInput: Connecting to AWS S3', {
|
|
31
|
+
region: AWS_REGION,
|
|
32
|
+
bucket: AWS_S3_BUCKET_NAME,
|
|
33
|
+
endpoint: AWS_S3_ENDPOINT
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const clientConfig = {
|
|
37
|
+
region: AWS_REGION,
|
|
38
|
+
credentials: {
|
|
39
|
+
accessKeyId: AWS_ACCESS_KEY_ID,
|
|
40
|
+
secretAccessKey: AWS_SECRET_ACCESS_KEY
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Support for custom endpoints (like MinIO, LocalStack, etc.)
|
|
45
|
+
if (AWS_S3_ENDPOINT) {
|
|
46
|
+
clientConfig.endpoint = AWS_S3_ENDPOINT
|
|
47
|
+
clientConfig.forcePathStyle = true
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
s3Client = new S3Client(clientConfig)
|
|
51
|
+
bucketName = AWS_S3_BUCKET_NAME
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function getFileBlob (fileId, options = {}) {
|
|
56
|
+
validateSupport()
|
|
57
|
+
|
|
58
|
+
const { range } = options
|
|
59
|
+
const filePath = await getFilePath(fileId)
|
|
60
|
+
const params = {
|
|
61
|
+
Bucket: bucketName,
|
|
62
|
+
Key: filePath
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
// Get file info first to validate range
|
|
67
|
+
const headCommand = new HeadObjectCommand(params)
|
|
68
|
+
const headResponse = await s3Client.send(headCommand)
|
|
69
|
+
const actualFileSize = headResponse.ContentLength
|
|
70
|
+
|
|
71
|
+
if (range) {
|
|
72
|
+
console.log('[AWS S3] Using Range request for optimal streaming:', { fileId, range, filePath })
|
|
73
|
+
|
|
74
|
+
// Validate range boundaries
|
|
75
|
+
if (range.start >= actualFileSize || range.start < 0) {
|
|
76
|
+
console.log('[AWS S3] Range start out of bounds:', { start: range.start, actualFileSize })
|
|
77
|
+
throw new Error('Range start out of bounds')
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Ensure end is within file bounds
|
|
81
|
+
const adjustedEnd = Math.min(range.end, actualFileSize - 1)
|
|
82
|
+
|
|
83
|
+
// Ensure end is not before start
|
|
84
|
+
if (adjustedEnd < range.start) {
|
|
85
|
+
console.log('[AWS S3] Invalid range:', { start: range.start, end: adjustedEnd, actualFileSize })
|
|
86
|
+
throw new Error('Invalid range')
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
console.log('[AWS S3] Downloading object with range:', {
|
|
90
|
+
start: range.start,
|
|
91
|
+
end: adjustedEnd,
|
|
92
|
+
actualFileSize,
|
|
93
|
+
originalEnd: range.end
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
// Download with range
|
|
97
|
+
params.Range = `bytes=${range.start}-${adjustedEnd}`
|
|
98
|
+
const command = new GetObjectCommand(params)
|
|
99
|
+
const response = await s3Client.send(command)
|
|
100
|
+
|
|
101
|
+
// Convert stream to buffer
|
|
102
|
+
const chunks = []
|
|
103
|
+
for await (const chunk of response.Body) {
|
|
104
|
+
chunks.push(chunk)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const result = Buffer.concat(chunks)
|
|
108
|
+
const expectedSize = adjustedEnd - range.start + 1
|
|
109
|
+
|
|
110
|
+
console.log('[AWS S3] Range response:', {
|
|
111
|
+
expected: expectedSize,
|
|
112
|
+
actual: result.length,
|
|
113
|
+
start: range.start,
|
|
114
|
+
end: adjustedEnd,
|
|
115
|
+
fileId,
|
|
116
|
+
filePath
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
if (result.length === 0) {
|
|
120
|
+
console.warn('[AWS S3] Empty range response - this may indicate a problem')
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return result
|
|
124
|
+
} else {
|
|
125
|
+
// Regular download for non-Range requests
|
|
126
|
+
const command = new GetObjectCommand(params)
|
|
127
|
+
const response = await s3Client.send(command)
|
|
128
|
+
|
|
129
|
+
const chunks = []
|
|
130
|
+
for await (const chunk of response.Body) {
|
|
131
|
+
chunks.push(chunk)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return Buffer.concat(chunks)
|
|
135
|
+
}
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (error.name === 'NoSuchKey' || error.$metadata?.httpStatusCode === 404) {
|
|
138
|
+
throw new Error(ERRORS.fileNotFound)
|
|
139
|
+
}
|
|
140
|
+
console.error('[AWS S3] Error downloading object:', error)
|
|
141
|
+
throw error
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export async function getFileSize (fileId, options) {
|
|
146
|
+
validateSupport()
|
|
147
|
+
|
|
148
|
+
const filePath = await getFilePath(fileId)
|
|
149
|
+
const params = {
|
|
150
|
+
Bucket: bucketName,
|
|
151
|
+
Key: filePath
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
const command = new HeadObjectCommand(params)
|
|
156
|
+
const response = await s3Client.send(command)
|
|
157
|
+
return response.ContentLength
|
|
158
|
+
} catch (error) {
|
|
159
|
+
if (error.name === 'NoSuchKey' || error.$metadata?.httpStatusCode === 404) {
|
|
160
|
+
throw new Error(ERRORS.fileNotFound)
|
|
161
|
+
}
|
|
162
|
+
console.error('[AWS S3] Error getting object size:', error)
|
|
163
|
+
throw error
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export async function saveFileBlob (fileId, blob, options = {}) {
|
|
168
|
+
validateSupport()
|
|
169
|
+
const filePath = generateFilePath(fileId, options)
|
|
170
|
+
const params = {
|
|
171
|
+
Bucket: bucketName,
|
|
172
|
+
Key: filePath,
|
|
173
|
+
Body: blob
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
// Upload object (overwrites if exists)
|
|
178
|
+
const command = new PutObjectCommand(params)
|
|
179
|
+
await s3Client.send(command)
|
|
180
|
+
console.log('[AWS S3] Object uploaded successfully:', filePath)
|
|
181
|
+
} catch (error) {
|
|
182
|
+
console.error('[AWS S3] Error uploading object:', error)
|
|
183
|
+
throw error
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export async function deleteFile (fileId, options) {
|
|
188
|
+
validateSupport()
|
|
189
|
+
|
|
190
|
+
const filePath = await getFilePath(fileId)
|
|
191
|
+
const params = {
|
|
192
|
+
Bucket: bucketName,
|
|
193
|
+
Key: filePath
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
const command = new DeleteObjectCommand(params)
|
|
198
|
+
await s3Client.send(command)
|
|
199
|
+
console.log('[AWS S3] Object deleted successfully:', filePath)
|
|
200
|
+
} catch (error) {
|
|
201
|
+
if (error.name === 'NoSuchKey' || error.$metadata?.httpStatusCode === 404) {
|
|
202
|
+
throw new Error(ERRORS.fileNotFound)
|
|
203
|
+
}
|
|
204
|
+
console.error('[AWS S3] Error deleting object:', error)
|
|
205
|
+
throw error
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async function getFilePath (fileId) {
|
|
210
|
+
const $file = await sub($.files[fileId])
|
|
211
|
+
const file = $file.get()
|
|
212
|
+
return generateFilePath(fileId, file)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function generateFilePath (fileId, options) {
|
|
216
|
+
const { filename, path, setUniqName } = options
|
|
217
|
+
const newFilename = setUniqName ? fileId : filename || fileId
|
|
218
|
+
return path ? path + newFilename : newFilename
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const ERRORS = {
|
|
222
|
+
awsRegionNotAvailable: `
|
|
223
|
+
AWS S3 region is not available.
|
|
224
|
+
Make sure you have configured the AWS_REGION environment variable.
|
|
225
|
+
`,
|
|
226
|
+
awsAccessKeyNotAvailable: `
|
|
227
|
+
AWS access key ID is not available.
|
|
228
|
+
Make sure you have configured the AWS_ACCESS_KEY_ID environment variable.
|
|
229
|
+
`,
|
|
230
|
+
awsSecretKeyNotAvailable: `
|
|
231
|
+
AWS secret access key is not available.
|
|
232
|
+
Make sure you have configured the AWS_SECRET_ACCESS_KEY environment variable.
|
|
233
|
+
`,
|
|
234
|
+
awsBucketNotAvailable: `
|
|
235
|
+
AWS S3 bucket name is not available.
|
|
236
|
+
Make sure you have configured the AWS_S3_BUCKET_NAME environment variable.
|
|
237
|
+
`,
|
|
238
|
+
fileNotFound: `
|
|
239
|
+
File not found in AWS S3.
|
|
240
|
+
`
|
|
241
|
+
}
|
package/providers/azureblob.js
CHANGED
|
@@ -23,8 +23,9 @@ export function validateSupport () {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export async function getFileBlob (fileId,
|
|
26
|
+
export async function getFileBlob (fileId, options = {}) {
|
|
27
27
|
validateSupport()
|
|
28
|
+
const { range } = options
|
|
28
29
|
const blobClient = containerClient.getBlobClient(fileId)
|
|
29
30
|
|
|
30
31
|
try {
|
|
@@ -101,7 +102,7 @@ export async function getFileBlob (fileId, range) {
|
|
|
101
102
|
}
|
|
102
103
|
}
|
|
103
104
|
|
|
104
|
-
export async function getFileSize (fileId) {
|
|
105
|
+
export async function getFileSize (fileId, options) {
|
|
105
106
|
validateSupport()
|
|
106
107
|
const blobClient = containerClient.getBlobClient(fileId)
|
|
107
108
|
|
|
@@ -117,7 +118,7 @@ export async function getFileSize (fileId) {
|
|
|
117
118
|
}
|
|
118
119
|
}
|
|
119
120
|
|
|
120
|
-
export async function saveFileBlob (fileId, blob) {
|
|
121
|
+
export async function saveFileBlob (fileId, blob, options) {
|
|
121
122
|
validateSupport()
|
|
122
123
|
const blobClient = containerClient.getBlockBlobClient(fileId)
|
|
123
124
|
|
|
@@ -131,7 +132,7 @@ export async function saveFileBlob (fileId, blob) {
|
|
|
131
132
|
}
|
|
132
133
|
}
|
|
133
134
|
|
|
134
|
-
export async function deleteFile (fileId) {
|
|
135
|
+
export async function deleteFile (fileId, options) {
|
|
135
136
|
validateSupport()
|
|
136
137
|
const blobClient = containerClient.getBlobClient(fileId)
|
|
137
138
|
|
package/providers/index.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { mongo, sqlite } from 'startupjs/server'
|
|
2
2
|
|
|
3
|
-
export async function getFileBlob (storageType, fileId,
|
|
4
|
-
return (await getStorageProvider(storageType)).getFileBlob(fileId,
|
|
3
|
+
export async function getFileBlob (storageType, fileId, options) {
|
|
4
|
+
return (await getStorageProvider(storageType)).getFileBlob(fileId, options)
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
export async function getFileSize (storageType, fileId) {
|
|
8
|
-
return (await getStorageProvider(storageType)).getFileSize(fileId)
|
|
7
|
+
export async function getFileSize (storageType, fileId, options) {
|
|
8
|
+
return (await getStorageProvider(storageType)).getFileSize(fileId, options)
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export async function saveFileBlob (storageType, fileId, blob) {
|
|
12
|
-
return (await getStorageProvider(storageType)).saveFileBlob(fileId, blob)
|
|
11
|
+
export async function saveFileBlob (storageType, fileId, blob, options) {
|
|
12
|
+
return (await getStorageProvider(storageType)).saveFileBlob(fileId, blob, options)
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export async function deleteFile (storageType, fileId) {
|
|
16
|
-
return (await getStorageProvider(storageType)).deleteFile(fileId)
|
|
15
|
+
export async function deleteFile (storageType, fileId, options) {
|
|
16
|
+
return (await getStorageProvider(storageType)).deleteFile(fileId, options)
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export async function getDefaultStorageType () {
|
|
@@ -37,6 +37,8 @@ async function getStorageProvider (storageType) {
|
|
|
37
37
|
theModule = await import('./mongo.js')
|
|
38
38
|
} else if (storageType === 'azureblob') {
|
|
39
39
|
theModule = await import('./azureblob.js')
|
|
40
|
+
} else if (storageType === 's3') {
|
|
41
|
+
theModule = await import('./awsS3.js')
|
|
40
42
|
} else {
|
|
41
43
|
throw Error(ERRORS.unsupportedStorageType(storageType))
|
|
42
44
|
}
|
package/providers/mongo.js
CHANGED
|
@@ -11,8 +11,9 @@ export function validateSupport () {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export async function getFileBlob (fileId,
|
|
14
|
+
export async function getFileBlob (fileId, options = {}) {
|
|
15
15
|
validateSupport()
|
|
16
|
+
const { range } = options
|
|
16
17
|
const files = await bucket.find({ filename: fileId }).toArray()
|
|
17
18
|
if (!files || files.length === 0) {
|
|
18
19
|
throw new Error(ERRORS.fileNotFound)
|
|
@@ -94,7 +95,7 @@ export async function getFileBlob (fileId, range) {
|
|
|
94
95
|
})
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
export async function getFileSize (fileId) {
|
|
98
|
+
export async function getFileSize (fileId, options) {
|
|
98
99
|
validateSupport()
|
|
99
100
|
const files = await bucket.find({ filename: fileId }).toArray()
|
|
100
101
|
if (!files || files.length === 0) {
|
|
@@ -103,7 +104,7 @@ export async function getFileSize (fileId) {
|
|
|
103
104
|
return files[0].length
|
|
104
105
|
}
|
|
105
106
|
|
|
106
|
-
export async function saveFileBlob (fileId, blob) {
|
|
107
|
+
export async function saveFileBlob (fileId, blob, options) {
|
|
107
108
|
console.log('[MongoDB GridFS] Saving file:', {
|
|
108
109
|
fileId,
|
|
109
110
|
blobType: blob ? blob.constructor.name : 'undefined',
|
|
@@ -164,7 +165,7 @@ export async function saveFileBlob (fileId, blob) {
|
|
|
164
165
|
})
|
|
165
166
|
}
|
|
166
167
|
|
|
167
|
-
export async function deleteFile (fileId) {
|
|
168
|
+
export async function deleteFile (fileId, options) {
|
|
168
169
|
validateSupport()
|
|
169
170
|
const files = await bucket.find({ filename: fileId }).toArray()
|
|
170
171
|
if (!files || files.length === 0) {
|
package/providers/sqlite.js
CHANGED
|
@@ -4,7 +4,8 @@ export async function validateSupport () {
|
|
|
4
4
|
if (!sqlite) throw Error(ERRORS.disabled)
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
export async function getFileBlob (fileId,
|
|
7
|
+
export async function getFileBlob (fileId, options = {}) {
|
|
8
|
+
const { range } = options
|
|
8
9
|
return await new Promise((resolve, reject) => {
|
|
9
10
|
sqlite.get('SELECT * FROM files WHERE id = ?', [fileId], (err, row) => {
|
|
10
11
|
if (err) return reject(err)
|
|
@@ -64,7 +65,7 @@ export async function getFileBlob (fileId, range) {
|
|
|
64
65
|
})
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
export async function saveFileBlob (fileId, blob) {
|
|
68
|
+
export async function saveFileBlob (fileId, blob, options) {
|
|
68
69
|
return await new Promise((resolve, reject) => {
|
|
69
70
|
sqlite.run('INSERT OR REPLACE INTO files (id, data) VALUES (?, ?)', [fileId, blob], err => {
|
|
70
71
|
if (err) return reject(err)
|
|
@@ -73,7 +74,7 @@ export async function saveFileBlob (fileId, blob) {
|
|
|
73
74
|
})
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
export async function deleteFile (fileId) {
|
|
77
|
+
export async function deleteFile (fileId, options) {
|
|
77
78
|
return await new Promise((resolve, reject) => {
|
|
78
79
|
sqlite.run('DELETE FROM files WHERE id = ?', [fileId], err => {
|
|
79
80
|
if (err) return reject(err)
|
|
@@ -82,7 +83,7 @@ export async function deleteFile (fileId) {
|
|
|
82
83
|
})
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
export async function getFileSize (fileId) {
|
|
86
|
+
export async function getFileSize (fileId, options) {
|
|
86
87
|
await validateSupport()
|
|
87
88
|
return await new Promise((resolve, reject) => {
|
|
88
89
|
sqlite.get('SELECT data FROM files WHERE id = ?', [fileId], (err, row) => {
|
package/server/uploadBuffer.js
CHANGED
|
@@ -17,11 +17,12 @@ export default async function uploadBuffer (buff, options = {}) {
|
|
|
17
17
|
|
|
18
18
|
// try to save file to sqlite first to do an early exit if it fails
|
|
19
19
|
try {
|
|
20
|
-
await saveFileBlob(storageType, fileId, buff)
|
|
20
|
+
await saveFileBlob(storageType, fileId, buff, meta)
|
|
21
21
|
} catch (err) {
|
|
22
22
|
console.error(err)
|
|
23
23
|
throw new Error('Error saving file')
|
|
24
24
|
}
|
|
25
|
+
|
|
25
26
|
if (create) {
|
|
26
27
|
const doc = { id: fileId, ...meta, storageType }
|
|
27
28
|
// if some of the meta fields were undefined, remove them from the doc
|