express-storage 1.0.0 → 1.1.2

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 (48) hide show
  1. package/README.md +519 -348
  2. package/dist/drivers/azure.driver.d.ts +88 -0
  3. package/dist/drivers/azure.driver.d.ts.map +1 -0
  4. package/dist/drivers/azure.driver.js +367 -0
  5. package/dist/drivers/azure.driver.js.map +1 -0
  6. package/dist/drivers/base.driver.d.ts +125 -24
  7. package/dist/drivers/base.driver.d.ts.map +1 -1
  8. package/dist/drivers/base.driver.js +248 -62
  9. package/dist/drivers/base.driver.js.map +1 -1
  10. package/dist/drivers/gcs.driver.d.ts +60 -13
  11. package/dist/drivers/gcs.driver.d.ts.map +1 -1
  12. package/dist/drivers/gcs.driver.js +242 -41
  13. package/dist/drivers/gcs.driver.js.map +1 -1
  14. package/dist/drivers/local.driver.d.ts +89 -12
  15. package/dist/drivers/local.driver.d.ts.map +1 -1
  16. package/dist/drivers/local.driver.js +533 -45
  17. package/dist/drivers/local.driver.js.map +1 -1
  18. package/dist/drivers/s3.driver.d.ts +64 -13
  19. package/dist/drivers/s3.driver.d.ts.map +1 -1
  20. package/dist/drivers/s3.driver.js +269 -41
  21. package/dist/drivers/s3.driver.js.map +1 -1
  22. package/dist/factory/driver.factory.d.ts +35 -29
  23. package/dist/factory/driver.factory.d.ts.map +1 -1
  24. package/dist/factory/driver.factory.js +119 -59
  25. package/dist/factory/driver.factory.js.map +1 -1
  26. package/dist/index.d.ts +23 -22
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +26 -46
  29. package/dist/index.js.map +1 -1
  30. package/dist/storage-manager.d.ts +205 -52
  31. package/dist/storage-manager.d.ts.map +1 -1
  32. package/dist/storage-manager.js +644 -73
  33. package/dist/storage-manager.js.map +1 -1
  34. package/dist/types/storage.types.d.ts +243 -18
  35. package/dist/types/storage.types.d.ts.map +1 -1
  36. package/dist/utils/config.utils.d.ts +28 -4
  37. package/dist/utils/config.utils.d.ts.map +1 -1
  38. package/dist/utils/config.utils.js +121 -47
  39. package/dist/utils/config.utils.js.map +1 -1
  40. package/dist/utils/file.utils.d.ts +111 -14
  41. package/dist/utils/file.utils.d.ts.map +1 -1
  42. package/dist/utils/file.utils.js +215 -32
  43. package/dist/utils/file.utils.js.map +1 -1
  44. package/package.json +51 -27
  45. package/dist/drivers/oci.driver.d.ts +0 -37
  46. package/dist/drivers/oci.driver.d.ts.map +0 -1
  47. package/dist/drivers/oci.driver.js +0 -84
  48. package/dist/drivers/oci.driver.js.map +0 -1
@@ -1,35 +1,97 @@
1
1
  import path from 'path';
2
2
  import fs from 'fs';
3
+ import crypto from 'crypto';
3
4
  /**
4
- * Generate unique filename with unix timestamp
5
+ * Creates a unique filename that won't collide with existing files.
6
+ *
7
+ * Format: {timestamp}_{random}_{sanitized_name}.{extension}
8
+ * Example: 1769104576000_a1b2c3d4e5_my_image.jpeg
9
+ *
10
+ * The random part uses crypto.randomBytes() for extra collision resistance
11
+ * in high-throughput scenarios.
5
12
  */
6
13
  export function generateUniqueFileName(originalName) {
7
- const timestamp = Math.floor(Date.now() / 1000); // Unix timestamp
8
- const extension = path.extname(originalName);
9
- const sanitizedName = sanitizeFileName(originalName);
10
- const baseName = path.basename(sanitizedName, extension);
11
- return `${timestamp}_${baseName}${extension}`;
14
+ const timestamp = Date.now();
15
+ const randomSuffix = crypto.randomBytes(6).toString('hex').substring(0, 10);
16
+ // Handle dotfiles like .gitignore or .env (they have no extension)
17
+ let extension;
18
+ let baseName;
19
+ if (originalName.startsWith('.') && !originalName.slice(1).includes('.')) {
20
+ extension = '';
21
+ baseName = sanitizeFileName(originalName);
22
+ }
23
+ else {
24
+ extension = path.extname(originalName).toLowerCase();
25
+ const sanitizedName = sanitizeFileName(originalName);
26
+ baseName = path.basename(sanitizedName, path.extname(sanitizedName));
27
+ }
28
+ if (!baseName || baseName.trim() === '') {
29
+ baseName = 'file';
30
+ }
31
+ return `${timestamp}_${randomSuffix}_${baseName}${extension}`;
12
32
  }
13
33
  /**
14
- * Sanitize filename to prevent security issues
34
+ * Makes a filename safe for storage by removing problematic characters.
35
+ *
36
+ * Replaces anything that isn't alphanumeric, a dot, or a hyphen with underscores.
37
+ * This ensures compatibility with all filesystems and cloud storage providers.
38
+ *
39
+ * Note: Unicode characters like Chinese or emojis become underscores.
40
+ * If you need to preserve these, consider using your own sanitization function.
15
41
  */
16
42
  export function sanitizeFileName(fileName) {
17
- return fileName
18
- .replace(/[^a-zA-Z0-9.-]/g, '_') // Replace special characters with underscore
19
- .replace(/_{2,}/g, '_') // Replace multiple underscores with single
20
- .replace(/^_+|_+$/g, ''); // Remove leading/trailing underscores
43
+ const sanitized = fileName
44
+ .normalize('NFC')
45
+ .replace(/[^a-zA-Z0-9.-]/g, '_')
46
+ .replace(/_{2,}/g, '_')
47
+ .replace(/^_+|_+$/g, '');
48
+ return sanitized || 'file';
21
49
  }
22
50
  /**
23
- * Create month-based directory path
51
+ * Checks if a filename is safe to use.
52
+ *
53
+ * Rejects:
54
+ * - Empty filenames
55
+ * - Filenames over 255 characters
56
+ * - Path traversal attempts (../, /, \)
57
+ * - Null bytes
58
+ *
59
+ * Returns an error message if invalid, null if OK.
60
+ */
61
+ export function validateFileName(fileName) {
62
+ if (!fileName || typeof fileName !== 'string') {
63
+ return 'Filename is required';
64
+ }
65
+ const trimmed = fileName.trim();
66
+ if (trimmed.length === 0) {
67
+ return 'Filename cannot be empty';
68
+ }
69
+ if (trimmed.length > 255) {
70
+ return 'Filename is too long (max 255 characters)';
71
+ }
72
+ if (trimmed.includes('..') || trimmed.includes('/') || trimmed.includes('\\')) {
73
+ return 'Filename cannot contain path separators or traversal sequences';
74
+ }
75
+ if (trimmed.includes('\0')) {
76
+ return 'Filename cannot contain null bytes';
77
+ }
78
+ return null;
79
+ }
80
+ /**
81
+ * Creates a date-based folder path: YYYY/MM
82
+ *
83
+ * Uses UTC to keep things consistent across timezones.
84
+ * Example: For January 2026 -> 'uploads/2026/01'
24
85
  */
25
86
  export function createMonthBasedPath(basePath) {
26
87
  const now = new Date();
27
- const month = now.toLocaleString('en', { month: 'long' }).toLowerCase();
28
- const year = now.getFullYear();
29
- return path.join(basePath, month, year.toString());
88
+ const year = now.getUTCFullYear();
89
+ const month = String(now.getUTCMonth() + 1).padStart(2, '0');
90
+ return path.join(basePath, year.toString(), month);
30
91
  }
31
92
  /**
32
- * Ensure directory exists, create if it doesn't
93
+ * Creates a directory if it doesn't exist.
94
+ * Also creates any parent directories needed (recursive).
33
95
  */
34
96
  export function ensureDirectoryExists(dirPath) {
35
97
  if (!fs.existsSync(dirPath)) {
@@ -37,48 +99,72 @@ export function ensureDirectoryExists(dirPath) {
37
99
  }
38
100
  }
39
101
  /**
40
- * Get file size in human readable format
102
+ * Converts bytes to a human-readable string.
103
+ *
104
+ * Examples:
105
+ * - 1024 -> "1 KB"
106
+ * - 1048576 -> "1 MB"
107
+ * - 0 -> "0 Bytes"
41
108
  */
42
109
  export function formatFileSize(bytes) {
43
110
  const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
44
- if (bytes === 0)
111
+ if (typeof bytes !== 'number' || Number.isNaN(bytes)) {
112
+ return 'Invalid size';
113
+ }
114
+ if (!Number.isFinite(bytes)) {
115
+ return bytes > 0 ? 'Infinite' : 'Invalid size';
116
+ }
117
+ if (bytes < 0) {
118
+ return 'Invalid size (negative)';
119
+ }
120
+ if (bytes === 0) {
45
121
  return '0 Bytes';
46
- const i = Math.floor(Math.log(bytes) / Math.log(1024));
47
- return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
122
+ }
123
+ let i = 0;
124
+ let size = bytes;
125
+ while (size >= 1024 && i < sizes.length - 1) {
126
+ size /= 1024;
127
+ i++;
128
+ }
129
+ return Math.round(size * 100) / 100 + ' ' + sizes[i];
48
130
  }
49
131
  /**
50
- * Validate file size
132
+ * Checks if a file size is within the allowed limit.
51
133
  */
52
134
  export function validateFileSize(fileSize, maxSize) {
53
135
  return fileSize <= maxSize;
54
136
  }
55
137
  /**
56
- * Validate file type
138
+ * Checks if a MIME type is in the allowed list.
57
139
  */
58
140
  export function validateFileType(mimeType, allowedTypes) {
59
141
  return allowedTypes.includes(mimeType);
60
142
  }
61
143
  /**
62
- * Create relative URL for local files
63
- */
64
- export function createLocalFileUrl(filePath, baseUrl = '') {
65
- const relativePath = filePath.replace(/^public\//, '');
66
- return `${baseUrl}/${relativePath}`.replace(/\/+/g, '/');
67
- }
68
- /**
69
- * Get file extension from filename
144
+ * Extracts the file extension (lowercase, includes the dot).
145
+ *
146
+ * Examples:
147
+ * - 'photo.jpg' -> '.jpg'
148
+ * - '.gitignore' -> '' (dotfiles have no extension)
149
+ * - 'archive.tar.gz' -> '.gz' (only the last extension)
70
150
  */
71
151
  export function getFileExtension(fileName) {
152
+ if (!fileName)
153
+ return '';
154
+ // Dotfiles like .gitignore don't have extensions
155
+ if (fileName.startsWith('.') && !fileName.slice(1).includes('.')) {
156
+ return '';
157
+ }
72
158
  return path.extname(fileName).toLowerCase();
73
159
  }
74
160
  /**
75
- * Check if file is an image
161
+ * Checks if a MIME type indicates an image.
76
162
  */
77
163
  export function isImageFile(mimeType) {
78
164
  return mimeType.startsWith('image/');
79
165
  }
80
166
  /**
81
- * Check if file is a document
167
+ * Checks if a MIME type indicates a document (PDF, Word, Excel, etc.).
82
168
  */
83
169
  export function isDocumentFile(mimeType) {
84
170
  const documentTypes = [
@@ -92,4 +178,101 @@ export function isDocumentFile(mimeType) {
92
178
  ];
93
179
  return documentTypes.includes(mimeType);
94
180
  }
181
+ /**
182
+ * Retries an async operation with exponential backoff.
183
+ *
184
+ * Great for cloud operations that might fail due to network blips
185
+ * or rate limiting.
186
+ *
187
+ * @example
188
+ * // Retry up to 3 times with increasing delays
189
+ * const result = await withRetry(() => storage.uploadFile(file));
190
+ *
191
+ * // More aggressive retry strategy
192
+ * const result = await withRetry(() => fetchData(), {
193
+ * maxAttempts: 5,
194
+ * baseDelay: 500
195
+ * });
196
+ */
197
+ export async function withRetry(operation, options = {}) {
198
+ const { maxAttempts = 3, baseDelay = 1000, maxDelay = 10000, exponentialBackoff = true, } = options;
199
+ let lastError;
200
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
201
+ try {
202
+ return await operation();
203
+ }
204
+ catch (error) {
205
+ lastError = error instanceof Error ? error : new Error(String(error));
206
+ if (attempt < maxAttempts) {
207
+ const delay = exponentialBackoff
208
+ ? Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay)
209
+ : baseDelay;
210
+ await sleep(delay);
211
+ }
212
+ }
213
+ }
214
+ throw lastError || new Error(`Operation failed after ${maxAttempts} attempts`);
215
+ }
216
+ /**
217
+ * Pauses execution for the specified number of milliseconds.
218
+ */
219
+ export function sleep(ms) {
220
+ return new Promise(resolve => globalThis.setTimeout(resolve, ms));
221
+ }
222
+ /**
223
+ * Processes an array with a concurrency limit.
224
+ *
225
+ * Prevents overwhelming APIs or running out of resources by limiting
226
+ * how many operations run at once.
227
+ *
228
+ * Implementation uses pre-assigned chunk-based processing to avoid any
229
+ * potential race conditions with shared index counters. Each worker gets
230
+ * its own set of indices to process.
231
+ *
232
+ * Note: The input array is snapshotted at the start to prevent issues
233
+ * if the caller modifies it during processing.
234
+ *
235
+ * @example
236
+ * // Upload 100 files, but only 10 at a time
237
+ * const results = await withConcurrencyLimit(
238
+ * files,
239
+ * (file) => uploadFile(file),
240
+ * { maxConcurrent: 10 }
241
+ * );
242
+ */
243
+ export async function withConcurrencyLimit(items, operation, options = {}) {
244
+ const { maxConcurrent = 10 } = options;
245
+ if (items.length === 0) {
246
+ return [];
247
+ }
248
+ // Snapshot the array to prevent issues if caller modifies it during processing
249
+ const itemsCopy = [...items];
250
+ const itemCount = itemsCopy.length;
251
+ // For small batches, just process everything at once
252
+ if (itemCount <= maxConcurrent) {
253
+ return Promise.all(itemsCopy.map((item, index) => operation(item, index)));
254
+ }
255
+ const results = new Array(itemCount);
256
+ const workerCount = Math.min(maxConcurrent, itemCount);
257
+ // Pre-assign indices to each worker to avoid any race conditions
258
+ // Each worker gets a dedicated set of indices: worker 0 gets [0, workerCount, 2*workerCount, ...],
259
+ // worker 1 gets [1, workerCount+1, 2*workerCount+1, ...], etc.
260
+ const createWorker = (workerId) => {
261
+ return (async () => {
262
+ for (let index = workerId; index < itemCount; index += workerCount) {
263
+ const item = itemsCopy[index];
264
+ if (item !== undefined) {
265
+ results[index] = await operation(item, index);
266
+ }
267
+ }
268
+ })();
269
+ };
270
+ // Start all workers with their pre-assigned index ranges
271
+ const workers = [];
272
+ for (let i = 0; i < workerCount; i++) {
273
+ workers.push(createWorker(i));
274
+ }
275
+ await Promise.all(workers);
276
+ return results;
277
+ }
95
278
  //# sourceMappingURL=file.utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"file.utils.js","sourceRoot":"","sources":["../../src/utils/file.utils.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AAEpB;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,YAAoB;IACzD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,iBAAiB;IAClE,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7C,MAAM,aAAa,GAAG,gBAAgB,CAAC,YAAY,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;IAEzD,OAAO,GAAG,SAAS,IAAI,QAAQ,GAAG,SAAS,EAAE,CAAC;AAChD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,OAAO,QAAQ;SACZ,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC,6CAA6C;SAC7E,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,2CAA2C;SAClE,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC,sCAAsC;AACpE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAgB;IACnD,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,KAAK,GAAG,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACxE,MAAM,IAAI,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAE/B,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;AACrD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAe;IACnD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,MAAM,KAAK,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAChD,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAElC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IACvD,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;AAC5E,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB,EAAE,OAAe;IAChE,OAAO,QAAQ,IAAI,OAAO,CAAC;AAC7B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB,EAAE,YAAsB;IACvE,OAAO,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACzC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAgB,EAAE,UAAkB,EAAE;IACvE,MAAM,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACvD,OAAO,GAAG,OAAO,IAAI,YAAY,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAC3D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;AAC9C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,OAAO,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,MAAM,aAAa,GAAG;QACpB,iBAAiB;QACjB,oBAAoB;QACpB,yEAAyE;QACzE,0BAA0B;QAC1B,mEAAmE;QACnE,YAAY;QACZ,UAAU;KACX,CAAC;IACF,OAAO,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAC1C,CAAC"}
1
+ {"version":3,"file":"file.utils.js","sourceRoot":"","sources":["../../src/utils/file.utils.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "express-storage",
3
- "version": "1.0.0",
4
- "description": "A simple and powerful file upload and storage management package for Express.js applications. Supports multiple storage drivers including S3, GCS, OCI, and local storage with presigned URL support.",
3
+ "version": "1.1.2",
4
+ "description": "Secure, unified file upload and cloud storage management for Express.js. One API for AWS S3, Google Cloud Storage, Azure Blob Storage, and local disk. Built-in security with path traversal prevention, file validation, presigned URLs, and automatic filename sanitization. Switch storage providers with zero code changes — just update your environment variables.",
5
5
  "homepage": "https://github.com/th3hero/express-storage#readme",
6
6
  "bugs": {
7
7
  "url": "https://github.com/th3hero/express-storage/issues"
@@ -30,26 +30,48 @@
30
30
  "express",
31
31
  "file-upload",
32
32
  "storage",
33
+ "upload",
34
+ "file-storage",
35
+ "cloud-storage",
33
36
  "s3",
37
+ "aws-s3",
38
+ "amazon-s3",
34
39
  "gcs",
35
- "oci",
40
+ "google-cloud-storage",
41
+ "azure",
42
+ "azure-blob",
43
+ "azure-blob-storage",
44
+ "blob-storage",
36
45
  "presigned-url",
37
- "typescript",
46
+ "presigned-upload",
47
+ "direct-upload",
48
+ "secure-upload",
49
+ "file-validation",
38
50
  "multer",
39
- "cloud-storage"
51
+ "multer-storage",
52
+ "typescript",
53
+ "nodejs",
54
+ "expressjs",
55
+ "file-manager",
56
+ "upload-manager",
57
+ "storage-driver",
58
+ "multi-cloud",
59
+ "hybrid-cloud",
60
+ "file-security",
61
+ "upload-security"
40
62
  ],
41
63
  "scripts": {
42
64
  "build": "tsc",
43
65
  "dev": "tsc --watch",
44
66
  "clean": "rm -rf dist",
45
67
  "prepublishOnly": "npm run clean && npm run build",
46
- "test": "jest",
47
- "test:watch": "jest --watch",
48
- "test:coverage": "jest --coverage",
49
68
  "lint": "eslint src --ext .ts",
50
69
  "lint:fix": "eslint src --ext .ts --fix",
51
70
  "format": "prettier --write src/**/*.ts",
52
- "type-check": "tsc --noEmit"
71
+ "type-check": "tsc --noEmit",
72
+ "test": "vitest run",
73
+ "test:watch": "vitest",
74
+ "test:coverage": "vitest run --coverage"
53
75
  },
54
76
  "peerDependencies": {
55
77
  "express": "^4.21.2"
@@ -57,26 +79,28 @@
57
79
  "engines": {
58
80
  "node": ">=16.0.0"
59
81
  },
60
- "devDependencies": {
61
- "@types/express": "^5.0.3",
62
- "@types/jest": "^30.0.0",
63
- "@types/multer": "^2.0.0",
64
- "@types/node": "^24.1.0",
65
- "@typescript-eslint/eslint-plugin": "^8.38.0",
66
- "@typescript-eslint/parser": "^8.38.0",
67
- "eslint": "^9.32.0",
68
- "jest": "^30.0.5",
69
- "prettier": "^3.6.2",
70
- "ts-jest": "^29.4.0",
71
- "typescript": "^5.8.3"
72
- },
73
82
  "dependencies": {
74
- "@aws-sdk/client-s3": "^3.856.0",
75
- "@aws-sdk/s3-request-presigner": "^3.856.0",
76
- "@google-cloud/storage": "^7.16.0",
77
- "dotenv": "^17.2.1",
83
+ "@aws-sdk/client-s3": "^3.975.0",
84
+ "@aws-sdk/lib-storage": "^3.975.0",
85
+ "@aws-sdk/s3-request-presigner": "^3.975.0",
86
+ "@azure/identity": "^4.13.0",
87
+ "@azure/storage-blob": "^12.30.0",
88
+ "@google-cloud/storage": "^7.18.0",
89
+ "dotenv": "^17.2.3",
78
90
  "multer": "^2.0.2",
79
- "oci-sdk": "^2.114.0",
80
91
  "tslib": "^2.8.1"
92
+ },
93
+ "devDependencies": {
94
+ "@types/express": "^5.0.6",
95
+ "@types/multer": "^2.0.0",
96
+ "@types/node": "^25.0.10",
97
+ "@typescript-eslint/eslint-plugin": "^8.53.1",
98
+ "@typescript-eslint/parser": "^8.53.1",
99
+ "@vitest/coverage-v8": "^4.0.18",
100
+ "eslint": "^9.39.2",
101
+ "memfs": "^4.56.10",
102
+ "prettier": "^3.8.1",
103
+ "typescript": "^5.9.3",
104
+ "vitest": "^4.0.18"
81
105
  }
82
106
  }
@@ -1,37 +0,0 @@
1
- import { BaseStorageDriver } from './base.driver.js';
2
- import { FileUploadResult, PresignedUrlResult } from '../types/storage.types.js';
3
- /**
4
- * Oracle Cloud Infrastructure storage driver (placeholder implementation)
5
- */
6
- export declare class OCIStorageDriver extends BaseStorageDriver {
7
- private bucketName;
8
- private region;
9
- constructor(config: any);
10
- /**
11
- * Upload file to OCI (placeholder)
12
- */
13
- upload(file: Express.Multer.File): Promise<FileUploadResult>;
14
- /**
15
- * Generate presigned upload URL (placeholder)
16
- */
17
- generateUploadUrl(_fileName: string): Promise<PresignedUrlResult>;
18
- /**
19
- * Generate presigned view URL (placeholder)
20
- */
21
- generateViewUrl(_fileName: string): Promise<PresignedUrlResult>;
22
- /**
23
- * Delete file from OCI (placeholder)
24
- */
25
- delete(_fileName: string): Promise<boolean>;
26
- }
27
- /**
28
- * Oracle Cloud Infrastructure presigned driver
29
- */
30
- export declare class OCIPresignedStorageDriver extends OCIStorageDriver {
31
- constructor(config: any);
32
- /**
33
- * Override upload to return presigned URL instead of direct upload
34
- */
35
- upload(file: Express.Multer.File): Promise<FileUploadResult>;
36
- }
37
- //# sourceMappingURL=oci.driver.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"oci.driver.d.ts","sourceRoot":"","sources":["../../src/drivers/oci.driver.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEjF;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,iBAAiB;IACrD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,GAAG;IAOvB;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAsBlE;;OAEG;IACG,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAIvE;;OAEG;IACG,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAIrE;;OAEG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAIlD;AAED;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,gBAAgB;gBACjD,MAAM,EAAE,GAAG;IAIvB;;OAEG;IACY,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAyB5E"}
@@ -1,84 +0,0 @@
1
- // OCI SDK imports - simplified for now
2
- // Note: OCI SDK structure may vary, this is a placeholder implementation
3
- import { BaseStorageDriver } from './base.driver.js';
4
- /**
5
- * Oracle Cloud Infrastructure storage driver (placeholder implementation)
6
- */
7
- export class OCIStorageDriver extends BaseStorageDriver {
8
- constructor(config) {
9
- super(config);
10
- this.bucketName = config.bucketName;
11
- this.region = config.ociRegion;
12
- }
13
- /**
14
- * Upload file to OCI (placeholder)
15
- */
16
- async upload(file) {
17
- try {
18
- // Validate file
19
- const validationErrors = this.validateFile(file);
20
- if (validationErrors.length > 0) {
21
- return this.createErrorResult(validationErrors.join(', '));
22
- }
23
- // Generate unique filename
24
- const fileName = this.generateFileName(file.originalname);
25
- // Placeholder implementation
26
- const fileUrl = `https://objectstorage.${this.region}.oraclecloud.com/b/${this.bucketName}/o/${fileName}`;
27
- return this.createSuccessResult(fileName, fileUrl);
28
- }
29
- catch (error) {
30
- return this.createErrorResult(error instanceof Error ? error.message : 'Failed to upload file to OCI');
31
- }
32
- }
33
- /**
34
- * Generate presigned upload URL (placeholder)
35
- */
36
- async generateUploadUrl(_fileName) {
37
- return this.createPresignedErrorResult('OCI presigned URLs not implemented yet');
38
- }
39
- /**
40
- * Generate presigned view URL (placeholder)
41
- */
42
- async generateViewUrl(_fileName) {
43
- return this.createPresignedErrorResult('OCI presigned URLs not implemented yet');
44
- }
45
- /**
46
- * Delete file from OCI (placeholder)
47
- */
48
- async delete(_fileName) {
49
- // Placeholder implementation
50
- return true;
51
- }
52
- }
53
- /**
54
- * Oracle Cloud Infrastructure presigned driver
55
- */
56
- export class OCIPresignedStorageDriver extends OCIStorageDriver {
57
- constructor(config) {
58
- super(config);
59
- }
60
- /**
61
- * Override upload to return presigned URL instead of direct upload
62
- */
63
- async upload(file) {
64
- try {
65
- // Validate file
66
- const validationErrors = this.validateFile(file);
67
- if (validationErrors.length > 0) {
68
- return this.createErrorResult(validationErrors.join(', '));
69
- }
70
- // Generate unique filename
71
- const fileName = this.generateFileName(file.originalname);
72
- // Generate presigned upload URL
73
- const presignedResult = await this.generateUploadUrl(fileName);
74
- if (!presignedResult.success) {
75
- return this.createErrorResult(presignedResult.error || 'Failed to generate presigned URL');
76
- }
77
- return this.createSuccessResult(fileName, presignedResult.uploadUrl);
78
- }
79
- catch (error) {
80
- return this.createErrorResult(error instanceof Error ? error.message : 'Failed to generate presigned URL');
81
- }
82
- }
83
- }
84
- //# sourceMappingURL=oci.driver.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"oci.driver.js","sourceRoot":"","sources":["../../src/drivers/oci.driver.ts"],"names":[],"mappings":"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"}