express-storage 2.0.2 → 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 +73 -0
- package/dist/cjs/drivers/azure.driver.d.ts.map +1 -0
- package/dist/cjs/drivers/azure.driver.js +390 -0
- package/dist/cjs/drivers/azure.driver.js.map +1 -0
- package/dist/cjs/drivers/base.driver.d.ts +136 -0
- package/dist/cjs/drivers/base.driver.d.ts.map +1 -0
- package/dist/cjs/drivers/base.driver.js +357 -0
- package/dist/cjs/drivers/base.driver.js.map +1 -0
- package/dist/{drivers → cjs/drivers}/gcs.driver.d.ts +20 -38
- package/dist/cjs/drivers/gcs.driver.d.ts.map +1 -0
- package/dist/cjs/drivers/gcs.driver.js +343 -0
- package/dist/cjs/drivers/gcs.driver.js.map +1 -0
- 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 +86 -0
- package/dist/cjs/drivers/local.driver.d.ts.map +1 -0
- package/dist/cjs/drivers/local.driver.js +556 -0
- package/dist/cjs/drivers/local.driver.js.map +1 -0
- package/dist/{drivers → cjs/drivers}/s3.driver.d.ts +19 -39
- package/dist/cjs/drivers/s3.driver.d.ts.map +1 -0
- package/dist/cjs/drivers/s3.driver.js +400 -0
- package/dist/cjs/drivers/s3.driver.js.map +1 -0
- package/dist/cjs/factory/driver.factory.d.ts +43 -0
- package/dist/cjs/factory/driver.factory.d.ts.map +1 -0
- package/dist/cjs/factory/driver.factory.js +101 -0
- package/dist/cjs/factory/driver.factory.js.map +1 -0
- package/dist/cjs/index.d.ts +26 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +31 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/storage-manager.d.ts +210 -0
- package/dist/cjs/storage-manager.d.ts.map +1 -0
- package/dist/cjs/storage-manager.js +649 -0
- package/dist/cjs/storage-manager.js.map +1 -0
- package/dist/cjs/types/storage.types.d.ts +438 -0
- package/dist/cjs/types/storage.types.d.ts.map +1 -0
- package/dist/cjs/types/storage.types.js +3 -0
- package/dist/cjs/types/storage.types.js.map +1 -0
- package/dist/cjs/utils/config.utils.d.ts.map +1 -0
- package/dist/cjs/utils/config.utils.js +213 -0
- package/dist/cjs/utils/config.utils.js.map +1 -0
- package/dist/{utils → cjs/utils}/file.utils.d.ts +62 -8
- package/dist/cjs/utils/file.utils.d.ts.map +1 -0
- package/dist/cjs/utils/file.utils.js +464 -0
- package/dist/cjs/utils/file.utils.js.map +1 -0
- 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 +73 -0
- package/dist/esm/drivers/azure.driver.d.ts.map +1 -0
- package/dist/esm/drivers/azure.driver.js +353 -0
- package/dist/esm/drivers/azure.driver.js.map +1 -0
- package/dist/esm/drivers/base.driver.d.ts +136 -0
- package/dist/esm/drivers/base.driver.d.ts.map +1 -0
- package/dist/esm/drivers/base.driver.js +350 -0
- package/dist/esm/drivers/base.driver.js.map +1 -0
- package/dist/esm/drivers/gcs.driver.d.ts +68 -0
- package/dist/esm/drivers/gcs.driver.d.ts.map +1 -0
- package/dist/esm/drivers/gcs.driver.js +306 -0
- package/dist/esm/drivers/gcs.driver.js.map +1 -0
- 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 +86 -0
- package/dist/esm/drivers/local.driver.d.ts.map +1 -0
- package/dist/esm/drivers/local.driver.js +549 -0
- package/dist/esm/drivers/local.driver.js.map +1 -0
- package/dist/esm/drivers/s3.driver.d.ts +69 -0
- package/dist/esm/drivers/s3.driver.d.ts.map +1 -0
- package/dist/esm/drivers/s3.driver.js +363 -0
- package/dist/esm/drivers/s3.driver.js.map +1 -0
- package/dist/esm/factory/driver.factory.d.ts +43 -0
- package/dist/esm/factory/driver.factory.d.ts.map +1 -0
- package/dist/esm/factory/driver.factory.js +92 -0
- package/dist/esm/factory/driver.factory.js.map +1 -0
- package/dist/esm/index.d.ts +26 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +26 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/storage-manager.d.ts +210 -0
- package/dist/esm/storage-manager.d.ts.map +1 -0
- package/dist/esm/storage-manager.js +645 -0
- package/dist/esm/storage-manager.js.map +1 -0
- package/dist/esm/types/storage.types.d.ts +438 -0
- package/dist/esm/types/storage.types.d.ts.map +1 -0
- package/dist/esm/types/storage.types.js.map +1 -0
- package/dist/esm/utils/config.utils.d.ts +45 -0
- package/dist/esm/utils/config.utils.d.ts.map +1 -0
- package/dist/esm/utils/config.utils.js.map +1 -0
- package/dist/esm/utils/file.utils.d.ts +196 -0
- package/dist/esm/utils/file.utils.d.ts.map +1 -0
- package/dist/esm/utils/file.utils.js +439 -0
- package/dist/esm/utils/file.utils.js.map +1 -0
- 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 +90 -52
- 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
- package/dist/drivers/azure.driver.d.ts +0 -88
- package/dist/drivers/azure.driver.d.ts.map +0 -1
- package/dist/drivers/azure.driver.js +0 -391
- package/dist/drivers/azure.driver.js.map +0 -1
- package/dist/drivers/base.driver.d.ts +0 -170
- package/dist/drivers/base.driver.d.ts.map +0 -1
- package/dist/drivers/base.driver.js +0 -347
- package/dist/drivers/base.driver.js.map +0 -1
- package/dist/drivers/gcs.driver.d.ts.map +0 -1
- package/dist/drivers/gcs.driver.js +0 -354
- package/dist/drivers/gcs.driver.js.map +0 -1
- package/dist/drivers/local.driver.d.ts +0 -107
- package/dist/drivers/local.driver.d.ts.map +0 -1
- package/dist/drivers/local.driver.js +0 -621
- package/dist/drivers/local.driver.js.map +0 -1
- package/dist/drivers/s3.driver.d.ts.map +0 -1
- package/dist/drivers/s3.driver.js +0 -387
- package/dist/drivers/s3.driver.js.map +0 -1
- package/dist/factory/driver.factory.d.ts +0 -62
- package/dist/factory/driver.factory.d.ts.map +0 -1
- package/dist/factory/driver.factory.js +0 -177
- package/dist/factory/driver.factory.js.map +0 -1
- package/dist/index.d.ts +0 -30
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -33
- package/dist/index.js.map +0 -1
- package/dist/storage-manager.d.ts +0 -228
- package/dist/storage-manager.d.ts.map +0 -1
- package/dist/storage-manager.js +0 -715
- package/dist/storage-manager.js.map +0 -1
- package/dist/types/storage.types.d.ts +0 -295
- package/dist/types/storage.types.d.ts.map +0 -1
- package/dist/types/storage.types.js.map +0 -1
- package/dist/utils/config.utils.d.ts.map +0 -1
- package/dist/utils/config.utils.js.map +0 -1
- package/dist/utils/file.utils.d.ts.map +0 -1
- package/dist/utils/file.utils.js +0 -278
- package/dist/utils/file.utils.js.map +0 -1
- /package/dist/{utils → cjs/utils}/config.utils.d.ts +0 -0
- /package/dist/{types → esm/types}/storage.types.js +0 -0
- /package/dist/{utils → esm/utils}/config.utils.js +0 -0
|
@@ -1,354 +0,0 @@
|
|
|
1
|
-
import { Storage } from '@google-cloud/storage';
|
|
2
|
-
import { BaseStorageDriver } from './base.driver.js';
|
|
3
|
-
/**
|
|
4
|
-
* GCSStorageDriver - Handles file operations with Google Cloud Storage.
|
|
5
|
-
*
|
|
6
|
-
* Supports two authentication methods:
|
|
7
|
-
* 1. Service account JSON file (GCS_CREDENTIALS path)
|
|
8
|
-
* 2. Application Default Credentials (when running on GCP)
|
|
9
|
-
*
|
|
10
|
-
* If you're running on GCP (Cloud Run, GKE, etc.), you usually don't need
|
|
11
|
-
* to provide credentials — the SDK picks them up automatically.
|
|
12
|
-
*/
|
|
13
|
-
export class GCSStorageDriver extends BaseStorageDriver {
|
|
14
|
-
constructor(config) {
|
|
15
|
-
super(config);
|
|
16
|
-
this.bucketName = config.bucketName;
|
|
17
|
-
this.projectId = config.gcsProjectId;
|
|
18
|
-
const storageOptions = {
|
|
19
|
-
projectId: this.projectId,
|
|
20
|
-
};
|
|
21
|
-
if (config.gcsCredentials) {
|
|
22
|
-
storageOptions.keyFilename = config.gcsCredentials;
|
|
23
|
-
}
|
|
24
|
-
this.storage = new Storage(storageOptions);
|
|
25
|
-
this.bucket = this.storage.bucket(this.bucketName);
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Uploads a file directly to GCS.
|
|
29
|
-
* Handles both memory and disk storage from Multer.
|
|
30
|
-
*
|
|
31
|
-
* For large files (>100MB), uses streaming upload to reduce
|
|
32
|
-
* memory usage and improve reliability.
|
|
33
|
-
*/
|
|
34
|
-
async upload(file, options) {
|
|
35
|
-
try {
|
|
36
|
-
const validationErrors = this.validateFile(file);
|
|
37
|
-
if (validationErrors.length > 0) {
|
|
38
|
-
return this.createErrorResult(validationErrors.join(', '));
|
|
39
|
-
}
|
|
40
|
-
const fileName = this.generateFileName(file.originalname);
|
|
41
|
-
const filePath = this.buildFilePath(fileName);
|
|
42
|
-
const gcsFile = this.bucket.file(filePath);
|
|
43
|
-
const metadata = {
|
|
44
|
-
contentType: options?.contentType || file.mimetype,
|
|
45
|
-
};
|
|
46
|
-
if (options?.cacheControl) {
|
|
47
|
-
metadata.cacheControl = options.cacheControl;
|
|
48
|
-
}
|
|
49
|
-
if (options?.contentDisposition) {
|
|
50
|
-
metadata.contentDisposition = options.contentDisposition;
|
|
51
|
-
}
|
|
52
|
-
if (options?.metadata) {
|
|
53
|
-
metadata.metadata = options.metadata;
|
|
54
|
-
}
|
|
55
|
-
// Use streaming upload for large files to reduce memory usage
|
|
56
|
-
if (this.shouldUseStreaming(file)) {
|
|
57
|
-
await this.uploadWithStream(gcsFile, file, metadata);
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
const fileContent = this.getFileContent(file);
|
|
61
|
-
await gcsFile.save(fileContent, { metadata });
|
|
62
|
-
}
|
|
63
|
-
// Build the public URL with proper encoding
|
|
64
|
-
const encodedPath = filePath.split('/').map(segment => encodeURIComponent(segment)).join('/');
|
|
65
|
-
const fileUrl = `https://storage.googleapis.com/${this.bucketName}/${encodedPath}`;
|
|
66
|
-
return this.createSuccessResult(filePath, fileUrl);
|
|
67
|
-
}
|
|
68
|
-
catch (error) {
|
|
69
|
-
return this.createErrorResult(error instanceof Error ? error.message : 'Failed to upload file to GCS');
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Uploads a large file using streaming.
|
|
74
|
-
*
|
|
75
|
-
* This method pipes the file stream directly to GCS, which is more
|
|
76
|
-
* memory-efficient for large files.
|
|
77
|
-
*/
|
|
78
|
-
async uploadWithStream(gcsFile, file, metadata) {
|
|
79
|
-
return new Promise((resolve, reject) => {
|
|
80
|
-
const fileStream = this.getFileStream(file);
|
|
81
|
-
const writeStream = gcsFile.createWriteStream({
|
|
82
|
-
metadata,
|
|
83
|
-
resumable: true, // Enable resumable uploads for reliability
|
|
84
|
-
});
|
|
85
|
-
// Handle errors from the source stream (fileStream) explicitly
|
|
86
|
-
// to ensure they propagate and don't get silently dropped
|
|
87
|
-
fileStream.on('error', reject);
|
|
88
|
-
fileStream
|
|
89
|
-
.pipe(writeStream)
|
|
90
|
-
.on('error', reject)
|
|
91
|
-
.on('finish', resolve);
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Creates a presigned URL for uploading directly to GCS.
|
|
96
|
-
*
|
|
97
|
-
* The URL enforces:
|
|
98
|
-
* - Exact content type
|
|
99
|
-
* - Exact file size (via x-goog-content-length-range header)
|
|
100
|
-
*
|
|
101
|
-
* Clients that try to upload different content will get a 403.
|
|
102
|
-
*/
|
|
103
|
-
async generateUploadUrl(fileName, contentType, fileSize) {
|
|
104
|
-
// Security: Defense-in-depth validation (StorageManager also validates)
|
|
105
|
-
// Decode URL-encoded characters first to catch encoded traversal attempts like %2e%2e%2f
|
|
106
|
-
let decodedFileName;
|
|
107
|
-
try {
|
|
108
|
-
decodedFileName = decodeURIComponent(fileName);
|
|
109
|
-
}
|
|
110
|
-
catch {
|
|
111
|
-
return this.createPresignedErrorResult('Invalid fileName: malformed URL encoding');
|
|
112
|
-
}
|
|
113
|
-
if (decodedFileName.includes('..') || decodedFileName.includes('\0')) {
|
|
114
|
-
return this.createPresignedErrorResult('Invalid fileName: path traversal sequences are not allowed');
|
|
115
|
-
}
|
|
116
|
-
try {
|
|
117
|
-
const gcsFile = this.bucket.file(decodedFileName);
|
|
118
|
-
const resolvedContentType = contentType || 'application/octet-stream';
|
|
119
|
-
const expiresOn = new Date(Date.now() + (this.getPresignedUrlExpiry() * 1000));
|
|
120
|
-
const options = {
|
|
121
|
-
version: 'v4',
|
|
122
|
-
action: 'write',
|
|
123
|
-
expires: expiresOn,
|
|
124
|
-
contentType: resolvedContentType,
|
|
125
|
-
};
|
|
126
|
-
// GCS enforces exact file size via this header
|
|
127
|
-
if (fileSize !== undefined) {
|
|
128
|
-
options.extensionHeaders = {
|
|
129
|
-
'x-goog-content-length-range': `${fileSize},${fileSize}`,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
const [uploadUrl] = await gcsFile.getSignedUrl(options);
|
|
133
|
-
return this.createPresignedSuccessResult(uploadUrl);
|
|
134
|
-
}
|
|
135
|
-
catch (error) {
|
|
136
|
-
return this.createPresignedErrorResult(error instanceof Error ? error.message : 'Failed to generate upload URL');
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Creates a presigned URL for downloading/viewing a file.
|
|
141
|
-
*/
|
|
142
|
-
async generateViewUrl(fileName) {
|
|
143
|
-
// Security: Defense-in-depth validation
|
|
144
|
-
// Decode URL-encoded characters first to catch encoded traversal attempts like %2e%2e%2f
|
|
145
|
-
let decodedFileName;
|
|
146
|
-
try {
|
|
147
|
-
decodedFileName = decodeURIComponent(fileName);
|
|
148
|
-
}
|
|
149
|
-
catch {
|
|
150
|
-
return this.createPresignedErrorResult('Invalid fileName: malformed URL encoding');
|
|
151
|
-
}
|
|
152
|
-
if (decodedFileName.includes('..') || decodedFileName.includes('\0')) {
|
|
153
|
-
return this.createPresignedErrorResult('Invalid fileName: path traversal sequences are not allowed');
|
|
154
|
-
}
|
|
155
|
-
try {
|
|
156
|
-
const gcsFile = this.bucket.file(decodedFileName);
|
|
157
|
-
const expiresOn = new Date(Date.now() + (this.getPresignedUrlExpiry() * 1000));
|
|
158
|
-
const [viewUrl] = await gcsFile.getSignedUrl({
|
|
159
|
-
version: 'v4',
|
|
160
|
-
action: 'read',
|
|
161
|
-
expires: expiresOn,
|
|
162
|
-
});
|
|
163
|
-
return this.createPresignedSuccessResult(undefined, viewUrl);
|
|
164
|
-
}
|
|
165
|
-
catch (error) {
|
|
166
|
-
return this.createPresignedErrorResult(error instanceof Error ? error.message : 'Failed to generate view URL');
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Deletes a file from GCS.
|
|
171
|
-
* Returns false if the file doesn't exist, throws on real errors.
|
|
172
|
-
*/
|
|
173
|
-
async delete(fileName) {
|
|
174
|
-
// Security: Defense-in-depth validation
|
|
175
|
-
// Decode URL-encoded characters first to catch encoded traversal attempts like %2e%2e%2f
|
|
176
|
-
let decodedFileName;
|
|
177
|
-
try {
|
|
178
|
-
decodedFileName = decodeURIComponent(fileName);
|
|
179
|
-
}
|
|
180
|
-
catch {
|
|
181
|
-
return false;
|
|
182
|
-
}
|
|
183
|
-
if (decodedFileName.includes('..') || decodedFileName.includes('\0')) {
|
|
184
|
-
return false;
|
|
185
|
-
}
|
|
186
|
-
const gcsFile = this.bucket.file(decodedFileName);
|
|
187
|
-
const [exists] = await gcsFile.exists();
|
|
188
|
-
if (!exists) {
|
|
189
|
-
return false;
|
|
190
|
-
}
|
|
191
|
-
await gcsFile.delete();
|
|
192
|
-
return true;
|
|
193
|
-
}
|
|
194
|
-
/**
|
|
195
|
-
* Confirms an upload and optionally validates the file.
|
|
196
|
-
*
|
|
197
|
-
* For GCS, validation is optional since constraints are enforced at URL level.
|
|
198
|
-
* But you can still use this to verify the file matches expectations.
|
|
199
|
-
*/
|
|
200
|
-
async validateAndConfirmUpload(reference, options) {
|
|
201
|
-
const deleteOnFailure = options?.deleteOnFailure !== false;
|
|
202
|
-
try {
|
|
203
|
-
const gcsFile = this.bucket.file(reference);
|
|
204
|
-
const [metadata] = await gcsFile.getMetadata();
|
|
205
|
-
const actualContentType = metadata.contentType;
|
|
206
|
-
let actualFileSize;
|
|
207
|
-
if (metadata.size !== undefined) {
|
|
208
|
-
const parsed = typeof metadata.size === 'string' ? parseInt(metadata.size, 10) : metadata.size;
|
|
209
|
-
actualFileSize = Number.isNaN(parsed) ? undefined : parsed;
|
|
210
|
-
}
|
|
211
|
-
// Validate content type if expected
|
|
212
|
-
if (options?.expectedContentType && actualContentType !== options.expectedContentType) {
|
|
213
|
-
if (deleteOnFailure) {
|
|
214
|
-
await this.delete(reference);
|
|
215
|
-
}
|
|
216
|
-
const errorResult = {
|
|
217
|
-
success: false,
|
|
218
|
-
error: `Content type mismatch: expected '${options.expectedContentType}', got '${actualContentType}'${deleteOnFailure ? ' (file deleted)' : ' (file kept for inspection)'}`,
|
|
219
|
-
};
|
|
220
|
-
if (actualContentType)
|
|
221
|
-
errorResult.actualContentType = actualContentType;
|
|
222
|
-
if (actualFileSize !== undefined)
|
|
223
|
-
errorResult.actualFileSize = actualFileSize;
|
|
224
|
-
return errorResult;
|
|
225
|
-
}
|
|
226
|
-
// Validate file size if expected
|
|
227
|
-
if (options?.expectedFileSize !== undefined && actualFileSize !== options.expectedFileSize) {
|
|
228
|
-
if (deleteOnFailure) {
|
|
229
|
-
await this.delete(reference);
|
|
230
|
-
}
|
|
231
|
-
const errorResult = {
|
|
232
|
-
success: false,
|
|
233
|
-
error: `File size mismatch: expected ${options.expectedFileSize} bytes, got ${actualFileSize} bytes${deleteOnFailure ? ' (file deleted)' : ' (file kept for inspection)'}`,
|
|
234
|
-
};
|
|
235
|
-
if (actualContentType)
|
|
236
|
-
errorResult.actualContentType = actualContentType;
|
|
237
|
-
if (actualFileSize !== undefined)
|
|
238
|
-
errorResult.actualFileSize = actualFileSize;
|
|
239
|
-
return errorResult;
|
|
240
|
-
}
|
|
241
|
-
const viewResult = await this.generateViewUrl(reference);
|
|
242
|
-
const result = {
|
|
243
|
-
success: true,
|
|
244
|
-
reference,
|
|
245
|
-
expiresIn: this.getPresignedUrlExpiry(),
|
|
246
|
-
};
|
|
247
|
-
if (viewResult.viewUrl) {
|
|
248
|
-
result.viewUrl = viewResult.viewUrl;
|
|
249
|
-
}
|
|
250
|
-
if (actualContentType) {
|
|
251
|
-
result.actualContentType = actualContentType;
|
|
252
|
-
}
|
|
253
|
-
if (actualFileSize !== undefined) {
|
|
254
|
-
result.actualFileSize = actualFileSize;
|
|
255
|
-
}
|
|
256
|
-
return result;
|
|
257
|
-
}
|
|
258
|
-
catch (error) {
|
|
259
|
-
const errorMessage = error instanceof Error ? error.message : 'File not found or access denied';
|
|
260
|
-
return {
|
|
261
|
-
success: false,
|
|
262
|
-
error: errorMessage,
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* Lists files in the bucket with optional prefix filtering and pagination.
|
|
268
|
-
*/
|
|
269
|
-
async listFiles(prefix, maxResults = 1000, continuationToken) {
|
|
270
|
-
try {
|
|
271
|
-
const validatedMaxResults = Math.floor(Math.max(1, Math.min(Number.isNaN(maxResults) ? 1000 : maxResults, 1000)));
|
|
272
|
-
const options = {
|
|
273
|
-
maxResults: validatedMaxResults,
|
|
274
|
-
};
|
|
275
|
-
if (prefix)
|
|
276
|
-
options.prefix = prefix;
|
|
277
|
-
if (continuationToken)
|
|
278
|
-
options.pageToken = continuationToken;
|
|
279
|
-
const [files, , apiResponse] = await this.bucket.getFiles(options);
|
|
280
|
-
const fileList = files.map((file) => {
|
|
281
|
-
const fileInfo = { name: file.name };
|
|
282
|
-
if (file.metadata.size) {
|
|
283
|
-
const parsed = parseInt(String(file.metadata.size), 10);
|
|
284
|
-
if (!Number.isNaN(parsed)) {
|
|
285
|
-
fileInfo.size = parsed;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
if (file.metadata.contentType) {
|
|
289
|
-
fileInfo.contentType = file.metadata.contentType;
|
|
290
|
-
}
|
|
291
|
-
if (file.metadata.updated) {
|
|
292
|
-
fileInfo.lastModified = new Date(file.metadata.updated);
|
|
293
|
-
}
|
|
294
|
-
return fileInfo;
|
|
295
|
-
});
|
|
296
|
-
const result = {
|
|
297
|
-
success: true,
|
|
298
|
-
files: fileList,
|
|
299
|
-
};
|
|
300
|
-
const responseWithToken = apiResponse;
|
|
301
|
-
if (responseWithToken?.nextPageToken) {
|
|
302
|
-
result.nextToken = responseWithToken.nextPageToken;
|
|
303
|
-
}
|
|
304
|
-
return result;
|
|
305
|
-
}
|
|
306
|
-
catch (error) {
|
|
307
|
-
return {
|
|
308
|
-
success: false,
|
|
309
|
-
error: error instanceof Error ? error.message : 'Failed to list files',
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
/**
|
|
315
|
-
* GCSPresignedStorageDriver - GCS driver that returns presigned URLs from upload().
|
|
316
|
-
*
|
|
317
|
-
* Use this when you want clients to upload directly to GCS without
|
|
318
|
-
* the file passing through your server.
|
|
319
|
-
*/
|
|
320
|
-
export class GCSPresignedStorageDriver extends GCSStorageDriver {
|
|
321
|
-
constructor(config) {
|
|
322
|
-
super(config);
|
|
323
|
-
}
|
|
324
|
-
/**
|
|
325
|
-
* Instead of uploading the file, returns a presigned URL for the client to use.
|
|
326
|
-
*
|
|
327
|
-
* The returned fileUrl is the presigned upload URL.
|
|
328
|
-
* After the client uploads, use validateAndConfirmUpload() to get a view URL.
|
|
329
|
-
*
|
|
330
|
-
* Note: The `options` parameter (metadata, cacheControl, etc.) is NOT applied
|
|
331
|
-
* when using presigned uploads. These options must be set by the client when
|
|
332
|
-
* making the actual upload request to GCS, or configured via bucket settings.
|
|
333
|
-
* For server-side uploads with full options support, use the regular 'gcs' driver.
|
|
334
|
-
*/
|
|
335
|
-
async upload(file, _options) {
|
|
336
|
-
try {
|
|
337
|
-
const validationErrors = this.validateFile(file);
|
|
338
|
-
if (validationErrors.length > 0) {
|
|
339
|
-
return this.createErrorResult(validationErrors.join(', '));
|
|
340
|
-
}
|
|
341
|
-
const fileName = this.generateFileName(file.originalname);
|
|
342
|
-
const filePath = this.buildFilePath(fileName);
|
|
343
|
-
const presignedResult = await this.generateUploadUrl(filePath, file.mimetype, file.size);
|
|
344
|
-
if (!presignedResult.success) {
|
|
345
|
-
return this.createErrorResult(presignedResult.error || 'Failed to generate presigned URL');
|
|
346
|
-
}
|
|
347
|
-
return this.createSuccessResult(filePath, presignedResult.uploadUrl);
|
|
348
|
-
}
|
|
349
|
-
catch (error) {
|
|
350
|
-
return this.createErrorResult(error instanceof Error ? error.message : 'Failed to generate presigned URL');
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
//# sourceMappingURL=gcs.driver.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gcs.driver.js","sourceRoot":"","sources":["../../src/drivers/gcs.driver.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { BaseStorageDriver } from './base.driver.js';
|
|
2
|
-
import { FileUploadResult, PresignedUrlResult, StorageConfig, ListFilesResult, UploadOptions } from '../types/storage.types.js';
|
|
3
|
-
/**
|
|
4
|
-
* LocalStorageDriver - Saves files to your local filesystem.
|
|
5
|
-
*
|
|
6
|
-
* Great for development and small-scale applications.
|
|
7
|
-
* Files are organized by year/month folders automatically.
|
|
8
|
-
*
|
|
9
|
-
* **Security features:**
|
|
10
|
-
* - Path traversal prevention (blocks ../ and null bytes)
|
|
11
|
-
* - Symlinks are NOT followed or deleted (prevents directory escape attacks)
|
|
12
|
-
* - Magic byte detection for content-type validation (prevents extension spoofing)
|
|
13
|
-
* - Files stay within the configured base directory
|
|
14
|
-
*
|
|
15
|
-
* **Symlink behavior:** This driver explicitly rejects symlinks for security.
|
|
16
|
-
* If a file is a symlink, it will not be read, deleted, or listed. This prevents
|
|
17
|
-
* attackers from using symlinks to access files outside the storage directory.
|
|
18
|
-
*
|
|
19
|
-
* Note: Local storage doesn't support presigned URLs since
|
|
20
|
-
* there's no external service to sign requests against.
|
|
21
|
-
*/
|
|
22
|
-
export declare class LocalStorageDriver extends BaseStorageDriver {
|
|
23
|
-
private basePath;
|
|
24
|
-
constructor(config: StorageConfig);
|
|
25
|
-
/**
|
|
26
|
-
* Saves a file to the local filesystem.
|
|
27
|
-
*
|
|
28
|
-
* Files are automatically organized into YYYY/MM folders.
|
|
29
|
-
* For example, a file uploaded in January 2026 goes into:
|
|
30
|
-
* {basePath}/2026/01/{unique_filename}.jpg
|
|
31
|
-
*
|
|
32
|
-
* For large files (>100MB), uses streaming to reduce memory usage
|
|
33
|
-
* and prevent application crashes.
|
|
34
|
-
*/
|
|
35
|
-
upload(file: Express.Multer.File, _options?: UploadOptions): Promise<FileUploadResult>;
|
|
36
|
-
/**
|
|
37
|
-
* Uploads a large file using streaming.
|
|
38
|
-
*
|
|
39
|
-
* This method pipes the file stream directly to disk, which is more
|
|
40
|
-
* memory-efficient for large files (>100MB).
|
|
41
|
-
*/
|
|
42
|
-
private uploadWithStream;
|
|
43
|
-
/**
|
|
44
|
-
* Builds a URL for accessing the file.
|
|
45
|
-
*
|
|
46
|
-
* If your basePath starts with 'public/', we strip that prefix
|
|
47
|
-
* since Express.static('public') serves files from /
|
|
48
|
-
*
|
|
49
|
-
* Example: public/uploads/2026/01/photo.jpg -> /uploads/2026/01/photo.jpg
|
|
50
|
-
*/
|
|
51
|
-
private generateFileUrl;
|
|
52
|
-
/**
|
|
53
|
-
* Converts Windows backslashes to forward slashes.
|
|
54
|
-
*/
|
|
55
|
-
private normalizePathSeparators;
|
|
56
|
-
/**
|
|
57
|
-
* Removes duplicate slashes from URLs.
|
|
58
|
-
*/
|
|
59
|
-
private normalizeUrl;
|
|
60
|
-
/**
|
|
61
|
-
* Local storage doesn't support presigned upload URLs.
|
|
62
|
-
*/
|
|
63
|
-
generateUploadUrl(_fileName: string, _contentType?: string, _maxSize?: number): Promise<PresignedUrlResult>;
|
|
64
|
-
/**
|
|
65
|
-
* Local storage doesn't support presigned view URLs.
|
|
66
|
-
*/
|
|
67
|
-
generateViewUrl(_fileName: string): Promise<PresignedUrlResult>;
|
|
68
|
-
/**
|
|
69
|
-
* Validates a local file exists and matches expected values.
|
|
70
|
-
*
|
|
71
|
-
* Content type detection uses a two-tier approach:
|
|
72
|
-
* 1. Magic byte detection (examines actual file content for security)
|
|
73
|
-
* 2. Extension-based fallback (when magic bytes don't match)
|
|
74
|
-
*
|
|
75
|
-
* This helps detect extension spoofing attacks where a malicious file
|
|
76
|
-
* is renamed with an innocent extension (e.g., malware.exe -> photo.jpg).
|
|
77
|
-
*/
|
|
78
|
-
validateAndConfirmUpload(reference: string, options?: import('../types/storage.types.js').BlobValidationOptions): Promise<import('../types/storage.types.js').BlobValidationResult>;
|
|
79
|
-
/**
|
|
80
|
-
* Deletes a file from local storage.
|
|
81
|
-
*
|
|
82
|
-
* Security checks:
|
|
83
|
-
* - Rejects path traversal attempts (../ sequences)
|
|
84
|
-
* - Rejects null bytes in paths
|
|
85
|
-
* - Verifies target stays within base directory
|
|
86
|
-
* - Won't follow or delete symlinks (security: prevents directory escape attacks)
|
|
87
|
-
* - Only deletes regular files (not directories)
|
|
88
|
-
*
|
|
89
|
-
* @param reference - The file path relative to the storage base directory
|
|
90
|
-
* @returns true if file was deleted, false if not found or security check failed
|
|
91
|
-
*/
|
|
92
|
-
delete(reference: string): Promise<boolean>;
|
|
93
|
-
/**
|
|
94
|
-
* Safely resolves a reference to a file path within our base directory.
|
|
95
|
-
* Returns null for any suspicious input.
|
|
96
|
-
*/
|
|
97
|
-
private resolveFilePath;
|
|
98
|
-
/**
|
|
99
|
-
* Lists files in local storage with optional prefix filtering and pagination.
|
|
100
|
-
*
|
|
101
|
-
* Uses early termination to avoid loading all files into memory when possible.
|
|
102
|
-
* Files are collected in sorted order and iteration stops once we have enough
|
|
103
|
-
* results for the requested page.
|
|
104
|
-
*/
|
|
105
|
-
listFiles(prefix?: string, maxResults?: number, continuationToken?: string): Promise<ListFilesResult>;
|
|
106
|
-
}
|
|
107
|
-
//# sourceMappingURL=local.driver.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"local.driver.d.ts","sourceRoot":"","sources":["../../src/drivers/local.driver.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,aAAa,EAAE,eAAe,EAAE,aAAa,EAAY,MAAM,2BAA2B,CAAC;AA0I1I;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,kBAAmB,SAAQ,iBAAiB;IACvD,OAAO,CAAC,QAAQ,CAAS;gBAEb,MAAM,EAAE,aAAa;IAKjC;;;;;;;;;OASG;IACG,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAwC5F;;;;;OAKG;YACW,gBAAgB;IAmC9B;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe;IAiBvB;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAI/B;;OAEG;IACH,OAAO,CAAC,YAAY;IAIpB;;OAEG;IACG,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAMjH;;OAEG;IACG,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAMrE;;;;;;;;;OASG;IACY,wBAAwB,CACrC,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,OAAO,2BAA2B,EAAE,qBAAqB,GAClE,OAAO,CAAC,OAAO,2BAA2B,EAAE,oBAAoB,CAAC;IAgFpE;;;;;;;;;;;;OAYG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiDjD;;;OAGG;IACH,OAAO,CAAC,eAAe;IAuCvB;;;;;;OAMG;IACG,SAAS,CACb,MAAM,CAAC,EAAE,MAAM,EACf,UAAU,GAAE,MAAa,EACzB,iBAAiB,CAAC,EAAE,MAAM,GACzB,OAAO,CAAC,eAAe,CAAC;CA2L5B"}
|