express-storage 2.0.3 → 3.0.0
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/README.md +366 -34
- package/dist/cjs/config/index.d.ts +10 -0
- package/dist/cjs/config/index.d.ts.map +1 -0
- package/dist/cjs/config/index.js +19 -0
- package/dist/cjs/config/index.js.map +1 -0
- package/dist/cjs/drivers/azure.driver.d.ts +27 -42
- package/dist/cjs/drivers/azure.driver.d.ts.map +1 -1
- package/dist/cjs/drivers/azure.driver.js +206 -212
- package/dist/cjs/drivers/azure.driver.js.map +1 -1
- package/dist/cjs/drivers/base.driver.d.ts +69 -103
- package/dist/cjs/drivers/base.driver.d.ts.map +1 -1
- package/dist/cjs/drivers/base.driver.js +170 -167
- package/dist/cjs/drivers/base.driver.js.map +1 -1
- package/dist/cjs/drivers/gcs.driver.d.ts +20 -38
- package/dist/cjs/drivers/gcs.driver.d.ts.map +1 -1
- package/dist/cjs/drivers/gcs.driver.js +160 -176
- package/dist/cjs/drivers/gcs.driver.js.map +1 -1
- package/dist/cjs/drivers/index.d.ts +15 -0
- package/dist/cjs/drivers/index.d.ts.map +1 -0
- package/dist/cjs/drivers/index.js +26 -0
- package/dist/cjs/drivers/index.js.map +1 -0
- package/dist/cjs/drivers/local.driver.d.ts +24 -45
- package/dist/cjs/drivers/local.driver.d.ts.map +1 -1
- package/dist/cjs/drivers/local.driver.js +266 -338
- package/dist/cjs/drivers/local.driver.js.map +1 -1
- package/dist/cjs/drivers/s3.driver.d.ts +19 -39
- package/dist/cjs/drivers/s3.driver.d.ts.map +1 -1
- package/dist/cjs/drivers/s3.driver.js +205 -197
- package/dist/cjs/drivers/s3.driver.js.map +1 -1
- package/dist/cjs/factory/driver.factory.d.ts +32 -51
- package/dist/cjs/factory/driver.factory.d.ts.map +1 -1
- package/dist/cjs/factory/driver.factory.js +75 -155
- package/dist/cjs/factory/driver.factory.js.map +1 -1
- package/dist/cjs/index.d.ts +11 -15
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +14 -47
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/storage-manager.d.ts +107 -125
- package/dist/cjs/storage-manager.d.ts.map +1 -1
- package/dist/cjs/storage-manager.js +346 -416
- package/dist/cjs/storage-manager.js.map +1 -1
- package/dist/cjs/types/storage.types.d.ts +250 -107
- package/dist/cjs/types/storage.types.d.ts.map +1 -1
- package/dist/cjs/utils/file.utils.d.ts +62 -8
- package/dist/cjs/utils/file.utils.d.ts.map +1 -1
- package/dist/cjs/utils/file.utils.js +196 -29
- package/dist/cjs/utils/file.utils.js.map +1 -1
- package/dist/cjs/utils/index.d.ts +12 -0
- package/dist/cjs/utils/index.d.ts.map +1 -0
- package/dist/cjs/utils/index.js +36 -0
- package/dist/cjs/utils/index.js.map +1 -0
- package/dist/cjs/utils/rate-limiter.d.ts +40 -0
- package/dist/cjs/utils/rate-limiter.d.ts.map +1 -0
- package/dist/cjs/utils/rate-limiter.js +87 -0
- package/dist/cjs/utils/rate-limiter.js.map +1 -0
- package/dist/esm/config/index.d.ts +10 -0
- package/dist/esm/config/index.d.ts.map +1 -0
- package/dist/esm/config/index.js +10 -0
- package/dist/esm/config/index.js.map +1 -0
- package/dist/esm/drivers/azure.driver.d.ts +27 -42
- package/dist/esm/drivers/azure.driver.d.ts.map +1 -1
- package/dist/esm/drivers/azure.driver.js +172 -210
- package/dist/esm/drivers/azure.driver.js.map +1 -1
- package/dist/esm/drivers/base.driver.d.ts +69 -103
- package/dist/esm/drivers/base.driver.d.ts.map +1 -1
- package/dist/esm/drivers/base.driver.js +171 -168
- package/dist/esm/drivers/base.driver.js.map +1 -1
- package/dist/esm/drivers/gcs.driver.d.ts +20 -38
- package/dist/esm/drivers/gcs.driver.d.ts.map +1 -1
- package/dist/esm/drivers/gcs.driver.js +126 -174
- package/dist/esm/drivers/gcs.driver.js.map +1 -1
- package/dist/esm/drivers/index.d.ts +15 -0
- package/dist/esm/drivers/index.d.ts.map +1 -0
- package/dist/esm/drivers/index.js +15 -0
- package/dist/esm/drivers/index.js.map +1 -0
- package/dist/esm/drivers/local.driver.d.ts +24 -45
- package/dist/esm/drivers/local.driver.d.ts.map +1 -1
- package/dist/esm/drivers/local.driver.js +266 -338
- package/dist/esm/drivers/local.driver.js.map +1 -1
- package/dist/esm/drivers/s3.driver.d.ts +19 -39
- package/dist/esm/drivers/s3.driver.d.ts.map +1 -1
- package/dist/esm/drivers/s3.driver.js +171 -195
- package/dist/esm/drivers/s3.driver.js.map +1 -1
- package/dist/esm/factory/driver.factory.d.ts +32 -51
- package/dist/esm/factory/driver.factory.d.ts.map +1 -1
- package/dist/esm/factory/driver.factory.js +73 -158
- package/dist/esm/factory/driver.factory.js.map +1 -1
- package/dist/esm/index.d.ts +11 -15
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +12 -19
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/storage-manager.d.ts +107 -125
- package/dist/esm/storage-manager.d.ts.map +1 -1
- package/dist/esm/storage-manager.js +348 -418
- package/dist/esm/storage-manager.js.map +1 -1
- package/dist/esm/types/storage.types.d.ts +250 -107
- package/dist/esm/types/storage.types.d.ts.map +1 -1
- package/dist/esm/utils/file.utils.d.ts +62 -8
- package/dist/esm/utils/file.utils.d.ts.map +1 -1
- package/dist/esm/utils/file.utils.js +190 -29
- package/dist/esm/utils/file.utils.js.map +1 -1
- package/dist/esm/utils/index.d.ts +12 -0
- package/dist/esm/utils/index.d.ts.map +1 -0
- package/dist/esm/utils/index.js +11 -0
- package/dist/esm/utils/index.js.map +1 -0
- package/dist/esm/utils/rate-limiter.d.ts +40 -0
- package/dist/esm/utils/rate-limiter.d.ts.map +1 -0
- package/dist/esm/utils/rate-limiter.js +82 -0
- package/dist/esm/utils/rate-limiter.js.map +1 -0
- package/package.json +83 -48
- package/src/config/index.ts +17 -0
- package/src/drivers/azure.driver.ts +434 -0
- package/src/drivers/base.driver.ts +436 -0
- package/src/drivers/gcs.driver.ts +366 -0
- package/src/drivers/index.ts +15 -0
- package/src/drivers/local.driver.ts +626 -0
- package/src/drivers/s3.driver.ts +459 -0
- package/src/factory/driver.factory.ts +101 -0
- package/src/index.ts +72 -0
- package/src/storage-manager.ts +801 -0
- package/src/types/storage.types.ts +561 -0
- package/src/utils/config.utils.ts +229 -0
- package/src/utils/file.utils.ts +536 -0
- package/src/utils/index.ts +35 -0
- package/src/utils/rate-limiter.ts +94 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
|
+
import fsPromises from 'fs/promises';
|
|
2
3
|
import { Readable } from 'stream';
|
|
3
|
-
import { generateUniqueFileName
|
|
4
|
+
import { generateUniqueFileName } from '../utils/file.utils.js';
|
|
4
5
|
/** Threshold for using streaming uploads (100MB) */
|
|
5
6
|
const STREAM_THRESHOLD = 100 * 1024 * 1024;
|
|
6
7
|
/**
|
|
@@ -11,10 +12,23 @@ const STREAM_THRESHOLD = 100 * 1024 * 1024;
|
|
|
11
12
|
*
|
|
12
13
|
* If you're building a custom driver, extend this class and implement the
|
|
13
14
|
* abstract methods. You'll get all the helper methods for free.
|
|
15
|
+
*
|
|
16
|
+
* **Security validation contract:**
|
|
17
|
+
* - **StorageManager** (layer 1): validates raw user input at the public API
|
|
18
|
+
* boundary — path traversal, MIME type format, file size limits.
|
|
19
|
+
* - **Driver** (layer 2): decodes URL-encoded filenames via `decodeFileName()`
|
|
20
|
+
* and rejects traversal/encoding attacks. This ensures safety when drivers
|
|
21
|
+
* are used directly without StorageManager.
|
|
22
|
+
* - **Local driver internals** (layer 3): containment checks (`path.resolve`
|
|
23
|
+
* stays within `basePath`), symlink rejection, file-type verification.
|
|
24
|
+
* These are filesystem-specific concerns, not input validation.
|
|
14
25
|
*/
|
|
15
26
|
export class BaseStorageDriver {
|
|
27
|
+
config;
|
|
28
|
+
presignedMode;
|
|
16
29
|
constructor(config) {
|
|
17
30
|
this.config = config;
|
|
31
|
+
this.presignedMode = config.driver.endsWith('-presigned');
|
|
18
32
|
}
|
|
19
33
|
/**
|
|
20
34
|
* Builds the full storage path by combining the bucket path with the filename.
|
|
@@ -29,126 +43,55 @@ export class BaseStorageDriver {
|
|
|
29
43
|
return `${normalizedPath}/${fileName}`;
|
|
30
44
|
}
|
|
31
45
|
/**
|
|
32
|
-
*
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return this.config.bucketPath?.trim()?.replace(/^\/+|\/+$/g, '') || '';
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Uploads multiple files with smart concurrency control.
|
|
39
|
-
* Processes up to 10 files at a time to balance speed and system resources.
|
|
40
|
-
*/
|
|
41
|
-
async uploadMultiple(files, options) {
|
|
42
|
-
if (!files || files.length === 0) {
|
|
43
|
-
return [];
|
|
44
|
-
}
|
|
45
|
-
return withConcurrencyLimit(files, async (file) => {
|
|
46
|
-
try {
|
|
47
|
-
return await this.upload(file, options);
|
|
48
|
-
}
|
|
49
|
-
catch (error) {
|
|
50
|
-
return {
|
|
51
|
-
success: false,
|
|
52
|
-
error: error instanceof Error ? error.message : 'Failed to upload file',
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
}, { maxConcurrent: 10 });
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Deletes multiple files with smart concurrency control.
|
|
59
|
-
* Returns detailed results so you know exactly what happened with each file.
|
|
46
|
+
* Releases SDK clients and internal resources. Override in drivers that
|
|
47
|
+
* hold connection pools (S3Client, GCS Storage, Azure BlobServiceClient).
|
|
48
|
+
* Default implementation is a no-op.
|
|
60
49
|
*/
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return [];
|
|
64
|
-
}
|
|
65
|
-
return withConcurrencyLimit(fileNames, async (fileName) => {
|
|
66
|
-
try {
|
|
67
|
-
const success = await this.delete(fileName);
|
|
68
|
-
const result = { success, fileName };
|
|
69
|
-
if (!success) {
|
|
70
|
-
result.error = 'File not found or already deleted';
|
|
71
|
-
}
|
|
72
|
-
return result;
|
|
73
|
-
}
|
|
74
|
-
catch (error) {
|
|
75
|
-
return {
|
|
76
|
-
success: false,
|
|
77
|
-
fileName,
|
|
78
|
-
error: error instanceof Error ? error.message : 'Failed to delete file',
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
}, { maxConcurrent: 10 });
|
|
50
|
+
destroy() {
|
|
51
|
+
// No-op — subclasses override to close SDK clients
|
|
82
52
|
}
|
|
83
53
|
/**
|
|
84
54
|
* Creates a unique filename that won't collide with existing files.
|
|
85
|
-
* Format: {timestamp}_{random}_{original_name}.{ext}
|
|
86
55
|
*/
|
|
87
56
|
generateFileName(originalName) {
|
|
88
57
|
return generateUniqueFileName(originalName);
|
|
89
58
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
createSuccessResult(
|
|
94
|
-
|
|
95
|
-
success: true,
|
|
96
|
-
fileName,
|
|
97
|
-
};
|
|
98
|
-
if (fileUrl) {
|
|
99
|
-
result.fileUrl = fileUrl;
|
|
100
|
-
}
|
|
101
|
-
return result;
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
// Result builders — return proper discriminated union variants
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
createSuccessResult(reference, fileUrl) {
|
|
63
|
+
return { success: true, reference, fileUrl };
|
|
102
64
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
*/
|
|
106
|
-
createErrorResult(error) {
|
|
107
|
-
return {
|
|
108
|
-
success: false,
|
|
109
|
-
error,
|
|
110
|
-
};
|
|
65
|
+
createErrorResult(error, code = 'PROVIDER_ERROR') {
|
|
66
|
+
return { success: false, error, code };
|
|
111
67
|
}
|
|
112
|
-
/**
|
|
113
|
-
* Builds a success response for presigned URL operations.
|
|
114
|
-
*/
|
|
115
68
|
createPresignedSuccessResult(uploadUrl, viewUrl) {
|
|
116
|
-
const result = {
|
|
117
|
-
|
|
118
|
-
};
|
|
119
|
-
if (uploadUrl) {
|
|
69
|
+
const result = { success: true };
|
|
70
|
+
if (uploadUrl)
|
|
120
71
|
result.uploadUrl = uploadUrl;
|
|
121
|
-
|
|
122
|
-
if (viewUrl) {
|
|
72
|
+
if (viewUrl)
|
|
123
73
|
result.viewUrl = viewUrl;
|
|
124
|
-
}
|
|
125
74
|
return result;
|
|
126
75
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
*/
|
|
130
|
-
createPresignedErrorResult(error) {
|
|
131
|
-
return {
|
|
132
|
-
success: false,
|
|
133
|
-
error,
|
|
134
|
-
};
|
|
76
|
+
createPresignedErrorResult(error, code = 'PROVIDER_ERROR') {
|
|
77
|
+
return { success: false, error, code };
|
|
135
78
|
}
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// File validation
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
136
82
|
/**
|
|
137
83
|
* Validates a file before upload.
|
|
138
84
|
*
|
|
139
|
-
* Checks
|
|
140
|
-
*
|
|
141
|
-
* - No original name
|
|
142
|
-
* - No MIME type
|
|
143
|
-
* - Empty content
|
|
85
|
+
* Checks: missing file, no name, no MIME type, empty content, and
|
|
86
|
+
* maxFileSize from config (enforced here so direct driver usage is safe).
|
|
144
87
|
*
|
|
145
88
|
* Works with both Multer memory storage (file.buffer) and disk storage (file.path).
|
|
146
89
|
*/
|
|
147
|
-
validateFile(file) {
|
|
90
|
+
async validateFile(file) {
|
|
148
91
|
const errors = [];
|
|
149
92
|
if (!file) {
|
|
150
93
|
errors.push('No file provided');
|
|
151
|
-
return errors;
|
|
94
|
+
return { errors, resolvedSize: 0 };
|
|
152
95
|
}
|
|
153
96
|
if (!file.originalname) {
|
|
154
97
|
errors.push('File must have an original name');
|
|
@@ -156,77 +99,58 @@ export class BaseStorageDriver {
|
|
|
156
99
|
if (!file.mimetype) {
|
|
157
100
|
errors.push('File must have a MIME type');
|
|
158
101
|
}
|
|
159
|
-
// Check for file content (could be in memory or on disk)
|
|
160
102
|
const hasBuffer = (file.buffer?.length ?? 0) > 0;
|
|
161
103
|
const hasPath = typeof file.path === 'string' && file.path.length > 0;
|
|
162
104
|
const hasEmptyBuffer = file.buffer !== null && file.buffer !== undefined && file.buffer.length === 0;
|
|
105
|
+
let resolvedSize = file.size || 0;
|
|
163
106
|
if (hasEmptyBuffer && !hasPath) {
|
|
164
107
|
errors.push('File is empty (0 bytes)');
|
|
165
108
|
}
|
|
166
109
|
else if (!hasBuffer && !hasPath) {
|
|
167
110
|
errors.push('File must have either buffer (memory storage) or path (disk storage)');
|
|
168
111
|
}
|
|
169
|
-
// For disk storage, verify the file exists and isn't empty
|
|
170
112
|
if (hasPath && !hasBuffer) {
|
|
171
113
|
try {
|
|
172
|
-
const stats =
|
|
114
|
+
const stats = await fsPromises.stat(file.path);
|
|
173
115
|
if (stats.size === 0) {
|
|
174
116
|
errors.push('File is empty (0 bytes)');
|
|
175
117
|
}
|
|
176
|
-
|
|
177
|
-
file.size = stats.size;
|
|
178
|
-
}
|
|
118
|
+
resolvedSize = stats.size;
|
|
179
119
|
}
|
|
180
120
|
catch {
|
|
181
121
|
errors.push('Cannot read file from disk storage path');
|
|
182
122
|
}
|
|
183
123
|
}
|
|
184
|
-
|
|
124
|
+
if (hasBuffer && !resolvedSize) {
|
|
125
|
+
resolvedSize = file.buffer.length;
|
|
126
|
+
}
|
|
127
|
+
if (this.config.maxFileSize && resolvedSize > this.config.maxFileSize) {
|
|
128
|
+
errors.push(`File size ${resolvedSize} exceeds maximum allowed size of ${this.config.maxFileSize} bytes`);
|
|
129
|
+
}
|
|
130
|
+
return { errors, resolvedSize };
|
|
185
131
|
}
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
// File content helpers
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
186
135
|
/**
|
|
187
136
|
* Reads the file content, whether it's in memory or on disk.
|
|
188
137
|
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
* @warning **MEMORY IMPLICATIONS**: This method loads the ENTIRE file into memory.
|
|
193
|
-
* For large files, this can cause memory exhaustion and application crashes.
|
|
194
|
-
*
|
|
195
|
-
* **ALWAYS call `shouldUseStreaming(file)` first** and use `getFileStream()`
|
|
196
|
-
* for files larger than 100MB. Example:
|
|
197
|
-
*
|
|
198
|
-
* ```typescript
|
|
199
|
-
* if (this.shouldUseStreaming(file)) {
|
|
200
|
-
* return this.uploadWithStream(file);
|
|
201
|
-
* }
|
|
202
|
-
* const content = this.getFileContent(file); // Safe for smaller files
|
|
203
|
-
* ```
|
|
204
|
-
*
|
|
205
|
-
* Memory usage: A 1GB file will allocate ~1GB of heap memory.
|
|
206
|
-
* Node.js default heap limit is ~1.5GB, so large files WILL crash your app.
|
|
207
|
-
*
|
|
208
|
-
* @param file - The Multer file object
|
|
209
|
-
* @returns Buffer containing the entire file contents
|
|
210
|
-
* @throws Error if file has neither buffer nor path
|
|
138
|
+
* @warning **MEMORY IMPLICATIONS**: Loads the ENTIRE file into memory.
|
|
139
|
+
* ALWAYS call `shouldUseStreaming(file)` first and use `getFileStream()`
|
|
140
|
+
* for files larger than 100MB.
|
|
211
141
|
*/
|
|
212
|
-
getFileContent(file) {
|
|
142
|
+
async getFileContent(file) {
|
|
213
143
|
if ((file.buffer?.length ?? 0) > 0) {
|
|
214
144
|
return file.buffer;
|
|
215
145
|
}
|
|
216
146
|
if (file.path) {
|
|
217
|
-
return
|
|
147
|
+
return fsPromises.readFile(file.path);
|
|
218
148
|
}
|
|
219
149
|
throw new Error('File has neither buffer nor path - cannot read content');
|
|
220
150
|
}
|
|
221
151
|
/**
|
|
222
152
|
* Returns a readable stream for the file content.
|
|
223
|
-
*
|
|
224
|
-
* Use this instead of getFileContent() for large files to avoid
|
|
225
|
-
* loading the entire file into memory. Particularly useful for
|
|
226
|
-
* files larger than 100MB.
|
|
227
|
-
*
|
|
228
|
-
* @param file - The Multer file object
|
|
229
|
-
* @returns A readable stream of the file content
|
|
153
|
+
* Use this instead of getFileContent() for large files (>100MB).
|
|
230
154
|
*/
|
|
231
155
|
getFileStream(file) {
|
|
232
156
|
if ((file.buffer?.length ?? 0) > 0) {
|
|
@@ -239,23 +163,15 @@ export class BaseStorageDriver {
|
|
|
239
163
|
}
|
|
240
164
|
/**
|
|
241
165
|
* Determines if a file should use streaming based on its size.
|
|
242
|
-
*
|
|
243
166
|
* Files larger than 100MB benefit from streaming to reduce memory usage.
|
|
244
|
-
*
|
|
245
|
-
* @param file - The Multer file object
|
|
246
|
-
* @returns true if the file should use streaming
|
|
247
167
|
*/
|
|
248
|
-
shouldUseStreaming(
|
|
249
|
-
|
|
250
|
-
return size > STREAM_THRESHOLD;
|
|
168
|
+
shouldUseStreaming(fileSize) {
|
|
169
|
+
return fileSize > STREAM_THRESHOLD;
|
|
251
170
|
}
|
|
252
171
|
/**
|
|
253
172
|
* Gets the file size, reading from disk if necessary.
|
|
254
|
-
*
|
|
255
|
-
* @param file - The Multer file object
|
|
256
|
-
* @returns The file size in bytes
|
|
257
173
|
*/
|
|
258
|
-
getFileSize(file) {
|
|
174
|
+
async getFileSize(file) {
|
|
259
175
|
if (file.size && file.size > 0) {
|
|
260
176
|
return file.size;
|
|
261
177
|
}
|
|
@@ -264,7 +180,7 @@ export class BaseStorageDriver {
|
|
|
264
180
|
}
|
|
265
181
|
if (file.path) {
|
|
266
182
|
try {
|
|
267
|
-
const stats =
|
|
183
|
+
const stats = await fsPromises.stat(file.path);
|
|
268
184
|
return stats.size;
|
|
269
185
|
}
|
|
270
186
|
catch {
|
|
@@ -274,40 +190,30 @@ export class BaseStorageDriver {
|
|
|
274
190
|
return 0;
|
|
275
191
|
}
|
|
276
192
|
/**
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
* Call this after a successful upload if you're using disk storage
|
|
280
|
-
* and want to clean up. Memory storage doesn't need this — the
|
|
281
|
-
* garbage collector handles cleanup automatically.
|
|
282
|
-
*
|
|
283
|
-
* @returns true if the file was deleted, false otherwise
|
|
193
|
+
* Cleans up a Multer disk storage temp file if it exists.
|
|
194
|
+
* Call this in upload error paths to prevent temp file leaks.
|
|
284
195
|
*/
|
|
285
|
-
cleanupTempFile(file) {
|
|
286
|
-
if (file.path
|
|
196
|
+
async cleanupTempFile(file) {
|
|
197
|
+
if (file.path) {
|
|
287
198
|
try {
|
|
288
|
-
|
|
289
|
-
return true;
|
|
290
|
-
}
|
|
291
|
-
catch {
|
|
292
|
-
return false;
|
|
199
|
+
await fsPromises.unlink(file.path);
|
|
293
200
|
}
|
|
201
|
+
catch { /* best-effort */ }
|
|
294
202
|
}
|
|
295
|
-
return false;
|
|
296
203
|
}
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
// Presigned URL helpers
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
297
207
|
/**
|
|
298
208
|
* Returns how long presigned URLs should be valid (in seconds).
|
|
299
|
-
*
|
|
300
|
-
* Clamps the value to stay within cloud provider limits:
|
|
301
|
-
* - Minimum: 1 second
|
|
302
|
-
* - Maximum: 7 days (604800 seconds)
|
|
303
|
-
* - Default: 10 minutes (600 seconds)
|
|
209
|
+
* Clamped to [1, 604800] (1 second to 7 days). Default: 600 (10 minutes).
|
|
304
210
|
*/
|
|
305
211
|
getPresignedUrlExpiry() {
|
|
306
212
|
const MAX_EXPIRY = 604800;
|
|
307
213
|
const MIN_EXPIRY = 1;
|
|
308
214
|
const DEFAULT_EXPIRY = 600;
|
|
309
215
|
const expiry = this.config.presignedUrlExpiry;
|
|
310
|
-
if (expiry === undefined ||
|
|
216
|
+
if (expiry === undefined || Number.isNaN(expiry)) {
|
|
311
217
|
return DEFAULT_EXPIRY;
|
|
312
218
|
}
|
|
313
219
|
if (expiry < MIN_EXPIRY) {
|
|
@@ -318,6 +224,54 @@ export class BaseStorageDriver {
|
|
|
318
224
|
}
|
|
319
225
|
return expiry;
|
|
320
226
|
}
|
|
227
|
+
/**
|
|
228
|
+
* Decodes a URL-encoded filename and checks for path traversal attacks.
|
|
229
|
+
* Throws on malformed encoding or traversal sequences.
|
|
230
|
+
*/
|
|
231
|
+
decodeFileName(fileName) {
|
|
232
|
+
let decoded;
|
|
233
|
+
try {
|
|
234
|
+
decoded = decodeURIComponent(fileName);
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
throw new Error('Invalid fileName: malformed URL encoding');
|
|
238
|
+
}
|
|
239
|
+
if (decoded.includes('..') || decoded.includes('\0')) {
|
|
240
|
+
throw new Error('Invalid fileName: path traversal sequences are not allowed');
|
|
241
|
+
}
|
|
242
|
+
return decoded;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Validates and clamps maxResults for list operations.
|
|
246
|
+
*/
|
|
247
|
+
validateMaxResults(maxResults) {
|
|
248
|
+
return Math.floor(Math.max(1, Math.min(Number.isNaN(maxResults) ? 1000 : maxResults, 1000)));
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Shared upload logic for presigned mode.
|
|
252
|
+
* Validates the file, generates a unique name, and returns a presigned upload URL.
|
|
253
|
+
*/
|
|
254
|
+
async presignedUpload(file) {
|
|
255
|
+
try {
|
|
256
|
+
const { errors, resolvedSize } = await this.validateFile(file);
|
|
257
|
+
if (errors.length > 0) {
|
|
258
|
+
return this.createErrorResult(errors.join(', '), 'VALIDATION_FAILED');
|
|
259
|
+
}
|
|
260
|
+
const fileName = this.generateFileName(file.originalname);
|
|
261
|
+
const filePath = this.buildFilePath(fileName);
|
|
262
|
+
const presignedResult = await this.generateUploadUrl(filePath, file.mimetype, resolvedSize);
|
|
263
|
+
if (!presignedResult.success) {
|
|
264
|
+
return this.createErrorResult(presignedResult.error, presignedResult.code);
|
|
265
|
+
}
|
|
266
|
+
return this.createSuccessResult(filePath, presignedResult.uploadUrl || '');
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
return this.createErrorResult(error instanceof Error ? error.message : 'Failed to generate presigned URL');
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// ---------------------------------------------------------------------------
|
|
273
|
+
// Upload validation (post-upload confirmation)
|
|
274
|
+
// ---------------------------------------------------------------------------
|
|
321
275
|
/**
|
|
322
276
|
* Confirms that an upload completed successfully.
|
|
323
277
|
*
|
|
@@ -340,8 +294,57 @@ export class BaseStorageDriver {
|
|
|
340
294
|
}
|
|
341
295
|
return {
|
|
342
296
|
success: false,
|
|
343
|
-
error: viewResult.error
|
|
297
|
+
error: viewResult.error,
|
|
298
|
+
code: 'FILE_NOT_FOUND',
|
|
344
299
|
};
|
|
345
300
|
}
|
|
301
|
+
/**
|
|
302
|
+
* Validates uploaded file metadata against expected values.
|
|
303
|
+
* Shared by cloud drivers to avoid duplicating content-type and file-size checks.
|
|
304
|
+
* Returns a validation error result if checks fail, null if everything passes.
|
|
305
|
+
*/
|
|
306
|
+
async checkUploadedFileMetadata(reference, actual, options) {
|
|
307
|
+
const deleteOnFailure = options?.deleteOnFailure !== false;
|
|
308
|
+
if (options?.expectedContentType && actual.contentType !== options.expectedContentType) {
|
|
309
|
+
if (deleteOnFailure)
|
|
310
|
+
await this.delete(reference);
|
|
311
|
+
return this.buildValidationError(`Content type mismatch: expected '${options.expectedContentType}', got '${actual.contentType}'`, deleteOnFailure, actual.contentType, actual.fileSize);
|
|
312
|
+
}
|
|
313
|
+
if (options?.expectedFileSize !== undefined && actual.fileSize !== options.expectedFileSize) {
|
|
314
|
+
if (deleteOnFailure)
|
|
315
|
+
await this.delete(reference);
|
|
316
|
+
return this.buildValidationError(`File size mismatch: expected ${options.expectedFileSize} bytes, got ${actual.fileSize} bytes`, deleteOnFailure, actual.contentType, actual.fileSize);
|
|
317
|
+
}
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Builds a successful validation result with optional view URL and metadata.
|
|
322
|
+
*/
|
|
323
|
+
buildValidationSuccess(reference, viewUrl, actualContentType, actualFileSize) {
|
|
324
|
+
const result = {
|
|
325
|
+
success: true,
|
|
326
|
+
reference,
|
|
327
|
+
expiresIn: this.getPresignedUrlExpiry(),
|
|
328
|
+
};
|
|
329
|
+
if (viewUrl)
|
|
330
|
+
result.viewUrl = viewUrl;
|
|
331
|
+
if (actualContentType)
|
|
332
|
+
result.actualContentType = actualContentType;
|
|
333
|
+
if (actualFileSize !== undefined)
|
|
334
|
+
result.actualFileSize = actualFileSize;
|
|
335
|
+
return result;
|
|
336
|
+
}
|
|
337
|
+
buildValidationError(message, deleted, contentType, fileSize) {
|
|
338
|
+
const result = {
|
|
339
|
+
success: false,
|
|
340
|
+
error: `${message}${deleted ? ' (file deleted)' : ' (file kept for inspection)'}`,
|
|
341
|
+
code: 'VALIDATION_FAILED',
|
|
342
|
+
};
|
|
343
|
+
if (contentType)
|
|
344
|
+
result.actualContentType = contentType;
|
|
345
|
+
if (fileSize !== undefined)
|
|
346
|
+
result.actualFileSize = fileSize;
|
|
347
|
+
return result;
|
|
348
|
+
}
|
|
346
349
|
}
|
|
347
350
|
//# sourceMappingURL=base.driver.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.driver.js","sourceRoot":"","sources":["../../../src/drivers/base.driver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC,OAAO,EAAE,sBAAsB,EAAE,
|
|
1
|
+
{"version":3,"file":"base.driver.js","sourceRoot":"","sources":["../../../src/drivers/base.driver.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseStorageDriver } from './base.driver.js';
|
|
2
|
-
import { FileUploadResult, PresignedUrlResult, StorageConfig, BlobValidationOptions, BlobValidationResult, ListFilesResult, UploadOptions } from '../types/storage.types.js';
|
|
2
|
+
import { FileUploadResult, PresignedUrlResult, StorageConfig, BlobValidationOptions, BlobValidationResult, ListFilesResult, UploadOptions, FileInfo, DeleteResult } from '../types/storage.types.js';
|
|
3
3
|
/**
|
|
4
4
|
* GCSStorageDriver - Handles file operations with Google Cloud Storage.
|
|
5
5
|
*
|
|
@@ -9,16 +9,22 @@ import { FileUploadResult, PresignedUrlResult, StorageConfig, BlobValidationOpti
|
|
|
9
9
|
*
|
|
10
10
|
* If you're running on GCP (Cloud Run, GKE, etc.), you usually don't need
|
|
11
11
|
* to provide credentials — the SDK picks them up automatically.
|
|
12
|
+
*
|
|
13
|
+
* When driver is 'gcs-presigned', upload() returns presigned URLs
|
|
14
|
+
* instead of uploading directly.
|
|
15
|
+
*
|
|
16
|
+
* Required package: @google-cloud/storage
|
|
12
17
|
*/
|
|
13
18
|
export declare class GCSStorageDriver extends BaseStorageDriver {
|
|
14
|
-
private
|
|
15
|
-
private
|
|
16
|
-
private bucketName;
|
|
17
|
-
private projectId;
|
|
19
|
+
private _storage?;
|
|
20
|
+
private _bucket?;
|
|
21
|
+
private readonly bucketName;
|
|
22
|
+
private readonly projectId;
|
|
18
23
|
constructor(config: StorageConfig);
|
|
24
|
+
private ensureBucket;
|
|
25
|
+
destroy(): void;
|
|
19
26
|
/**
|
|
20
|
-
* Uploads a file
|
|
21
|
-
* Handles both memory and disk storage from Multer.
|
|
27
|
+
* Uploads a file to GCS, or returns a presigned URL when in presigned mode.
|
|
22
28
|
*
|
|
23
29
|
* For large files (>100MB), uses streaming upload to reduce
|
|
24
30
|
* memory usage and improve reliability.
|
|
@@ -26,9 +32,7 @@ export declare class GCSStorageDriver extends BaseStorageDriver {
|
|
|
26
32
|
upload(file: Express.Multer.File, options?: UploadOptions): Promise<FileUploadResult>;
|
|
27
33
|
/**
|
|
28
34
|
* Uploads a large file using streaming.
|
|
29
|
-
*
|
|
30
|
-
* This method pipes the file stream directly to GCS, which is more
|
|
31
|
-
* memory-efficient for large files.
|
|
35
|
+
* Pipes the file stream directly to GCS for memory efficiency.
|
|
32
36
|
*/
|
|
33
37
|
private uploadWithStream;
|
|
34
38
|
/**
|
|
@@ -37,8 +41,6 @@ export declare class GCSStorageDriver extends BaseStorageDriver {
|
|
|
37
41
|
* The URL enforces:
|
|
38
42
|
* - Exact content type
|
|
39
43
|
* - Exact file size (via x-goog-content-length-range header)
|
|
40
|
-
*
|
|
41
|
-
* Clients that try to upload different content will get a 403.
|
|
42
44
|
*/
|
|
43
45
|
generateUploadUrl(fileName: string, contentType?: string, fileSize?: number): Promise<PresignedUrlResult>;
|
|
44
46
|
/**
|
|
@@ -47,40 +49,20 @@ export declare class GCSStorageDriver extends BaseStorageDriver {
|
|
|
47
49
|
generateViewUrl(fileName: string): Promise<PresignedUrlResult>;
|
|
48
50
|
/**
|
|
49
51
|
* Deletes a file from GCS.
|
|
50
|
-
* Returns false if the file doesn't exist, throws on real errors.
|
|
51
52
|
*/
|
|
52
|
-
delete(fileName: string): Promise<
|
|
53
|
+
delete(fileName: string): Promise<DeleteResult>;
|
|
53
54
|
/**
|
|
54
55
|
* Confirms an upload and optionally validates the file.
|
|
55
|
-
*
|
|
56
|
-
* For GCS, validation is optional since constraints are enforced at URL level.
|
|
57
|
-
* But you can still use this to verify the file matches expectations.
|
|
56
|
+
* Uses shared validation logic from BaseStorageDriver.
|
|
58
57
|
*/
|
|
59
58
|
validateAndConfirmUpload(reference: string, options?: BlobValidationOptions): Promise<BlobValidationResult>;
|
|
60
59
|
/**
|
|
61
|
-
*
|
|
60
|
+
* Returns metadata about a file from GCS without downloading it.
|
|
62
61
|
*/
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* GCSPresignedStorageDriver - GCS driver that returns presigned URLs from upload().
|
|
67
|
-
*
|
|
68
|
-
* Use this when you want clients to upload directly to GCS without
|
|
69
|
-
* the file passing through your server.
|
|
70
|
-
*/
|
|
71
|
-
export declare class GCSPresignedStorageDriver extends GCSStorageDriver {
|
|
72
|
-
constructor(config: StorageConfig);
|
|
62
|
+
getMetadata(reference: string): Promise<FileInfo | null>;
|
|
73
63
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* The returned fileUrl is the presigned upload URL.
|
|
77
|
-
* After the client uploads, use validateAndConfirmUpload() to get a view URL.
|
|
78
|
-
*
|
|
79
|
-
* Note: The `options` parameter (metadata, cacheControl, etc.) is NOT applied
|
|
80
|
-
* when using presigned uploads. These options must be set by the client when
|
|
81
|
-
* making the actual upload request to GCS, or configured via bucket settings.
|
|
82
|
-
* For server-side uploads with full options support, use the regular 'gcs' driver.
|
|
64
|
+
* Lists files in the bucket with optional prefix filtering and pagination.
|
|
83
65
|
*/
|
|
84
|
-
|
|
66
|
+
listFiles(prefix?: string, maxResults?: number, continuationToken?: string): Promise<ListFilesResult>;
|
|
85
67
|
}
|
|
86
68
|
//# sourceMappingURL=gcs.driver.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gcs.driver.d.ts","sourceRoot":"","sources":["../../../src/drivers/gcs.driver.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,aAAa,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,eAAe,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"gcs.driver.d.ts","sourceRoot":"","sources":["../../../src/drivers/gcs.driver.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,aAAa,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,eAAe,EAAE,aAAa,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAwBrM;;;;;;;;;;;;;;GAcG;AACH,qBAAa,gBAAiB,SAAQ,iBAAiB;IACrD,OAAO,CAAC,QAAQ,CAAC,CAA0B;IAC3C,OAAO,CAAC,OAAO,CAAC,CAAyB;IACzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,MAAM,EAAE,aAAa;YAcnB,YAAY;IAiBjB,OAAO,IAAI,IAAI;IAKxB;;;;;OAKG;IACG,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAuD3F;;;OAGG;YACW,gBAAgB;IAqB9B;;;;;;OAMG;IACG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAqC/G;;OAEG;IACG,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAqBpE;;OAEG;IACG,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAkBrD;;;OAGG;IACY,wBAAwB,CACrC,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,oBAAoB,CAAC;IAyBhC;;OAEG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAoB9D;;OAEG;IACG,SAAS,CACb,MAAM,CAAC,EAAE,MAAM,EACf,UAAU,GAAE,MAAa,EACzB,iBAAiB,CAAC,EAAE,MAAM,GACzB,OAAO,CAAC,eAAe,CAAC;CA6C5B"}
|