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.
Files changed (125) hide show
  1. package/README.md +366 -34
  2. package/dist/cjs/config/index.d.ts +10 -0
  3. package/dist/cjs/config/index.d.ts.map +1 -0
  4. package/dist/cjs/config/index.js +19 -0
  5. package/dist/cjs/config/index.js.map +1 -0
  6. package/dist/cjs/drivers/azure.driver.d.ts +27 -42
  7. package/dist/cjs/drivers/azure.driver.d.ts.map +1 -1
  8. package/dist/cjs/drivers/azure.driver.js +206 -212
  9. package/dist/cjs/drivers/azure.driver.js.map +1 -1
  10. package/dist/cjs/drivers/base.driver.d.ts +69 -103
  11. package/dist/cjs/drivers/base.driver.d.ts.map +1 -1
  12. package/dist/cjs/drivers/base.driver.js +170 -167
  13. package/dist/cjs/drivers/base.driver.js.map +1 -1
  14. package/dist/cjs/drivers/gcs.driver.d.ts +20 -38
  15. package/dist/cjs/drivers/gcs.driver.d.ts.map +1 -1
  16. package/dist/cjs/drivers/gcs.driver.js +160 -176
  17. package/dist/cjs/drivers/gcs.driver.js.map +1 -1
  18. package/dist/cjs/drivers/index.d.ts +15 -0
  19. package/dist/cjs/drivers/index.d.ts.map +1 -0
  20. package/dist/cjs/drivers/index.js +26 -0
  21. package/dist/cjs/drivers/index.js.map +1 -0
  22. package/dist/cjs/drivers/local.driver.d.ts +24 -45
  23. package/dist/cjs/drivers/local.driver.d.ts.map +1 -1
  24. package/dist/cjs/drivers/local.driver.js +266 -338
  25. package/dist/cjs/drivers/local.driver.js.map +1 -1
  26. package/dist/cjs/drivers/s3.driver.d.ts +19 -39
  27. package/dist/cjs/drivers/s3.driver.d.ts.map +1 -1
  28. package/dist/cjs/drivers/s3.driver.js +205 -197
  29. package/dist/cjs/drivers/s3.driver.js.map +1 -1
  30. package/dist/cjs/factory/driver.factory.d.ts +32 -51
  31. package/dist/cjs/factory/driver.factory.d.ts.map +1 -1
  32. package/dist/cjs/factory/driver.factory.js +75 -155
  33. package/dist/cjs/factory/driver.factory.js.map +1 -1
  34. package/dist/cjs/index.d.ts +11 -15
  35. package/dist/cjs/index.d.ts.map +1 -1
  36. package/dist/cjs/index.js +14 -47
  37. package/dist/cjs/index.js.map +1 -1
  38. package/dist/cjs/storage-manager.d.ts +107 -125
  39. package/dist/cjs/storage-manager.d.ts.map +1 -1
  40. package/dist/cjs/storage-manager.js +346 -416
  41. package/dist/cjs/storage-manager.js.map +1 -1
  42. package/dist/cjs/types/storage.types.d.ts +250 -107
  43. package/dist/cjs/types/storage.types.d.ts.map +1 -1
  44. package/dist/cjs/utils/file.utils.d.ts +62 -8
  45. package/dist/cjs/utils/file.utils.d.ts.map +1 -1
  46. package/dist/cjs/utils/file.utils.js +196 -29
  47. package/dist/cjs/utils/file.utils.js.map +1 -1
  48. package/dist/cjs/utils/index.d.ts +12 -0
  49. package/dist/cjs/utils/index.d.ts.map +1 -0
  50. package/dist/cjs/utils/index.js +36 -0
  51. package/dist/cjs/utils/index.js.map +1 -0
  52. package/dist/cjs/utils/rate-limiter.d.ts +40 -0
  53. package/dist/cjs/utils/rate-limiter.d.ts.map +1 -0
  54. package/dist/cjs/utils/rate-limiter.js +87 -0
  55. package/dist/cjs/utils/rate-limiter.js.map +1 -0
  56. package/dist/esm/config/index.d.ts +10 -0
  57. package/dist/esm/config/index.d.ts.map +1 -0
  58. package/dist/esm/config/index.js +10 -0
  59. package/dist/esm/config/index.js.map +1 -0
  60. package/dist/esm/drivers/azure.driver.d.ts +27 -42
  61. package/dist/esm/drivers/azure.driver.d.ts.map +1 -1
  62. package/dist/esm/drivers/azure.driver.js +172 -210
  63. package/dist/esm/drivers/azure.driver.js.map +1 -1
  64. package/dist/esm/drivers/base.driver.d.ts +69 -103
  65. package/dist/esm/drivers/base.driver.d.ts.map +1 -1
  66. package/dist/esm/drivers/base.driver.js +171 -168
  67. package/dist/esm/drivers/base.driver.js.map +1 -1
  68. package/dist/esm/drivers/gcs.driver.d.ts +20 -38
  69. package/dist/esm/drivers/gcs.driver.d.ts.map +1 -1
  70. package/dist/esm/drivers/gcs.driver.js +126 -174
  71. package/dist/esm/drivers/gcs.driver.js.map +1 -1
  72. package/dist/esm/drivers/index.d.ts +15 -0
  73. package/dist/esm/drivers/index.d.ts.map +1 -0
  74. package/dist/esm/drivers/index.js +15 -0
  75. package/dist/esm/drivers/index.js.map +1 -0
  76. package/dist/esm/drivers/local.driver.d.ts +24 -45
  77. package/dist/esm/drivers/local.driver.d.ts.map +1 -1
  78. package/dist/esm/drivers/local.driver.js +266 -338
  79. package/dist/esm/drivers/local.driver.js.map +1 -1
  80. package/dist/esm/drivers/s3.driver.d.ts +19 -39
  81. package/dist/esm/drivers/s3.driver.d.ts.map +1 -1
  82. package/dist/esm/drivers/s3.driver.js +171 -195
  83. package/dist/esm/drivers/s3.driver.js.map +1 -1
  84. package/dist/esm/factory/driver.factory.d.ts +32 -51
  85. package/dist/esm/factory/driver.factory.d.ts.map +1 -1
  86. package/dist/esm/factory/driver.factory.js +73 -158
  87. package/dist/esm/factory/driver.factory.js.map +1 -1
  88. package/dist/esm/index.d.ts +11 -15
  89. package/dist/esm/index.d.ts.map +1 -1
  90. package/dist/esm/index.js +12 -19
  91. package/dist/esm/index.js.map +1 -1
  92. package/dist/esm/storage-manager.d.ts +107 -125
  93. package/dist/esm/storage-manager.d.ts.map +1 -1
  94. package/dist/esm/storage-manager.js +348 -418
  95. package/dist/esm/storage-manager.js.map +1 -1
  96. package/dist/esm/types/storage.types.d.ts +250 -107
  97. package/dist/esm/types/storage.types.d.ts.map +1 -1
  98. package/dist/esm/utils/file.utils.d.ts +62 -8
  99. package/dist/esm/utils/file.utils.d.ts.map +1 -1
  100. package/dist/esm/utils/file.utils.js +190 -29
  101. package/dist/esm/utils/file.utils.js.map +1 -1
  102. package/dist/esm/utils/index.d.ts +12 -0
  103. package/dist/esm/utils/index.d.ts.map +1 -0
  104. package/dist/esm/utils/index.js +11 -0
  105. package/dist/esm/utils/index.js.map +1 -0
  106. package/dist/esm/utils/rate-limiter.d.ts +40 -0
  107. package/dist/esm/utils/rate-limiter.d.ts.map +1 -0
  108. package/dist/esm/utils/rate-limiter.js +82 -0
  109. package/dist/esm/utils/rate-limiter.js.map +1 -0
  110. package/package.json +83 -48
  111. package/src/config/index.ts +17 -0
  112. package/src/drivers/azure.driver.ts +434 -0
  113. package/src/drivers/base.driver.ts +436 -0
  114. package/src/drivers/gcs.driver.ts +366 -0
  115. package/src/drivers/index.ts +15 -0
  116. package/src/drivers/local.driver.ts +626 -0
  117. package/src/drivers/s3.driver.ts +459 -0
  118. package/src/factory/driver.factory.ts +101 -0
  119. package/src/index.ts +72 -0
  120. package/src/storage-manager.ts +801 -0
  121. package/src/types/storage.types.ts +561 -0
  122. package/src/utils/config.utils.ts +229 -0
  123. package/src/utils/file.utils.ts +536 -0
  124. package/src/utils/index.ts +35 -0
  125. 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, withConcurrencyLimit } from '../utils/file.utils.js';
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
- * Returns the configured bucket path, cleaned up and ready to use.
33
- */
34
- getBucketPath() {
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
- async deleteMultiple(fileNames) {
62
- if (!fileNames || fileNames.length === 0) {
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
- * Builds a success response for upload operations.
92
- */
93
- createSuccessResult(fileName, fileUrl) {
94
- const result = {
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
- * Builds an error response for upload operations.
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
- success: true,
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
- * Builds an error response for presigned URL operations.
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 for common issues:
140
- * - Missing file
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 = fs.statSync(file.path);
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
- if (!file.size || file.size === 0) {
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
- return errors;
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
- * Note: For disk storage, this reads the file but doesn't delete it.
190
- * Call cleanupTempFile() afterward if you need to remove the temp file.
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 fs.readFileSync(file.path);
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(file) {
249
- const size = file.size || 0;
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 = fs.statSync(file.path);
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
- * Removes a temporary file created by Multer disk storage.
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 && fs.existsSync(file.path)) {
196
+ async cleanupTempFile(file) {
197
+ if (file.path) {
287
198
  try {
288
- fs.unlinkSync(file.path);
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 || expiry === null || Number.isNaN(expiry)) {
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 || 'File not found',
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":"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"}
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 storage;
15
- private bucket;
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 directly to GCS.
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<boolean>;
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
- * Lists files in the bucket with optional prefix filtering and pagination.
60
+ * Returns metadata about a file from GCS without downloading it.
62
61
  */
63
- listFiles(prefix?: string, maxResults?: number, continuationToken?: string): Promise<ListFilesResult>;
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
- * Instead of uploading the file, returns a presigned URL for the client to use.
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
- upload(file: Express.Multer.File, _options?: UploadOptions): Promise<FileUploadResult>;
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,EAAY,MAAM,2BAA2B,CAAC;AAGvL;;;;;;;;;GASG;AACH,qBAAa,gBAAiB,SAAQ,iBAAiB;IACrD,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,SAAS,CAAS;gBAEd,MAAM,EAAE,aAAa;IAkBjC;;;;;;OAMG;IACG,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAkD3F;;;;;OAKG;YACW,gBAAgB;IAuB9B;;;;;;;;OAQG;IACG,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAiD/G;;OAEG;IACG,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAgCpE;;;OAGG;IACG,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAyBhD;;;;;OAKG;IACY,wBAAwB,CACrC,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,oBAAoB,CAAC;IAsEhC;;OAEG;IACG,SAAS,CACb,MAAM,CAAC,EAAE,MAAM,EACf,UAAU,GAAE,MAAa,EACzB,iBAAiB,CAAC,EAAE,MAAM,GACzB,OAAO,CAAC,eAAe,CAAC;CAkD5B;AAED;;;;;GAKG;AACH,qBAAa,yBAA0B,SAAQ,gBAAgB;gBACjD,MAAM,EAAE,aAAa;IAIjC;;;;;;;;;;OAUG;IACY,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC;CA2BtG"}
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"}