@memberjunction/storage 3.1.1 → 3.2.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/dist/__tests__/FileStorageBase.test.d.ts +6 -0
- package/dist/__tests__/FileStorageBase.test.d.ts.map +1 -0
- package/dist/__tests__/FileStorageBase.test.js +213 -0
- package/dist/__tests__/FileStorageBase.test.js.map +1 -0
- package/dist/__tests__/util.test.d.ts +7 -0
- package/dist/__tests__/util.test.d.ts.map +1 -0
- package/dist/__tests__/util.test.js +326 -0
- package/dist/__tests__/util.test.js.map +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +28 -14
- package/dist/config.js.map +1 -1
- package/dist/drivers/AWSFileStorage.d.ts +20 -0
- package/dist/drivers/AWSFileStorage.d.ts.map +1 -1
- package/dist/drivers/AWSFileStorage.js +43 -18
- package/dist/drivers/AWSFileStorage.js.map +1 -1
- package/dist/drivers/AzureFileStorage.d.ts +1 -1
- package/dist/drivers/AzureFileStorage.d.ts.map +1 -1
- package/dist/drivers/AzureFileStorage.js +17 -17
- package/dist/drivers/AzureFileStorage.js.map +1 -1
- package/dist/drivers/BoxFileStorage.d.ts +47 -1
- package/dist/drivers/BoxFileStorage.d.ts.map +1 -1
- package/dist/drivers/BoxFileStorage.js +219 -95
- package/dist/drivers/BoxFileStorage.js.map +1 -1
- package/dist/drivers/DropboxFileStorage.d.ts +59 -0
- package/dist/drivers/DropboxFileStorage.d.ts.map +1 -1
- package/dist/drivers/DropboxFileStorage.js +314 -62
- package/dist/drivers/DropboxFileStorage.js.map +1 -1
- package/dist/drivers/GoogleDriveFileStorage.d.ts +29 -0
- package/dist/drivers/GoogleDriveFileStorage.d.ts.map +1 -1
- package/dist/drivers/GoogleDriveFileStorage.js +220 -72
- package/dist/drivers/GoogleDriveFileStorage.js.map +1 -1
- package/dist/drivers/GoogleFileStorage.d.ts.map +1 -1
- package/dist/drivers/GoogleFileStorage.js +12 -12
- package/dist/drivers/GoogleFileStorage.js.map +1 -1
- package/dist/drivers/SharePointFileStorage.d.ts +64 -5
- package/dist/drivers/SharePointFileStorage.d.ts.map +1 -1
- package/dist/drivers/SharePointFileStorage.js +265 -94
- package/dist/drivers/SharePointFileStorage.js.map +1 -1
- package/dist/generic/FileStorageBase.d.ts +79 -13
- package/dist/generic/FileStorageBase.d.ts.map +1 -1
- package/dist/generic/FileStorageBase.js +57 -12
- package/dist/generic/FileStorageBase.js.map +1 -1
- package/dist/util.d.ts +429 -11
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +677 -16
- package/dist/util.js.map +1 -1
- package/package.json +11 -5
|
@@ -115,19 +115,28 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
115
115
|
if (accessToken) {
|
|
116
116
|
// Use access token directly
|
|
117
117
|
this._accessToken = accessToken;
|
|
118
|
-
|
|
118
|
+
const dropboxConfig = { accessToken };
|
|
119
|
+
// For Dropbox Business/Team accounts, specify the team member
|
|
120
|
+
if (config && 'selectUser' in config && config.selectUser) {
|
|
121
|
+
dropboxConfig.selectUser = config.selectUser;
|
|
122
|
+
}
|
|
123
|
+
this._client = new dropbox_1.Dropbox(dropboxConfig);
|
|
119
124
|
}
|
|
120
125
|
else if (refreshToken && appKey && appSecret) {
|
|
121
126
|
// Use refresh token with app credentials
|
|
122
|
-
|
|
127
|
+
const dropboxConfig = {
|
|
123
128
|
refreshToken,
|
|
124
129
|
clientId: appKey,
|
|
125
|
-
clientSecret: appSecret
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
clientSecret: appSecret,
|
|
131
|
+
};
|
|
132
|
+
// For Dropbox Business/Team accounts, specify the team member
|
|
133
|
+
if (config && 'selectUser' in config && config.selectUser) {
|
|
134
|
+
dropboxConfig.selectUser = config.selectUser;
|
|
135
|
+
}
|
|
136
|
+
this._client = new dropbox_1.Dropbox(dropboxConfig);
|
|
130
137
|
}
|
|
138
|
+
// Note: If no credentials are available, client will be initialized in initialize() method
|
|
139
|
+
// This allows for database-driven configuration to be passed after construction
|
|
131
140
|
// Root path, optional (defaults to empty which is root)
|
|
132
141
|
this._rootPath = config?.rootPath || env.get('STORAGE_DROPBOX_ROOT_PATH').default('').asString();
|
|
133
142
|
// Ensure root path starts with / if not empty
|
|
@@ -135,12 +144,67 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
135
144
|
this._rootPath = '/' + this._rootPath;
|
|
136
145
|
}
|
|
137
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Initialize the Dropbox client with configuration from database or other runtime source.
|
|
149
|
+
* This allows configuration to be passed after construction, overriding environment variables.
|
|
150
|
+
*
|
|
151
|
+
* @param config - Optional configuration object with accessToken, refreshToken, etc.
|
|
152
|
+
*/
|
|
153
|
+
async initialize(config) {
|
|
154
|
+
// Always call super to store accountId and accountName
|
|
155
|
+
await super.initialize(config);
|
|
156
|
+
if (!config) {
|
|
157
|
+
return; // Nothing to do, constructor already handled config from env/file
|
|
158
|
+
}
|
|
159
|
+
// If config is provided, reinitialize the client with it
|
|
160
|
+
const accessToken = config.accessToken;
|
|
161
|
+
const refreshToken = config.refreshToken;
|
|
162
|
+
// Support both naming conventions: clientID/clientSecret (standard) and appKey/appSecret (Dropbox terminology)
|
|
163
|
+
const appKey = config.clientID || config.appKey;
|
|
164
|
+
const appSecret = config.clientSecret || config.appSecret;
|
|
165
|
+
// Prefer refresh token over access token when both are available
|
|
166
|
+
// Access tokens expire (4 hours), refresh tokens are long-lived and auto-refresh
|
|
167
|
+
if (refreshToken && appKey && appSecret) {
|
|
168
|
+
// Use refresh token with app credentials - this is the preferred method
|
|
169
|
+
const dropboxConfig = {
|
|
170
|
+
refreshToken,
|
|
171
|
+
clientId: appKey,
|
|
172
|
+
clientSecret: appSecret,
|
|
173
|
+
};
|
|
174
|
+
// For Dropbox Business/Team accounts, specify the team member
|
|
175
|
+
if (config.selectUser) {
|
|
176
|
+
dropboxConfig.selectUser = config.selectUser;
|
|
177
|
+
}
|
|
178
|
+
this._client = new dropbox_1.Dropbox(dropboxConfig);
|
|
179
|
+
// Set a placeholder for IsConfigured check - the SDK will get a real token on first API call
|
|
180
|
+
this._accessToken = 'refresh-token-mode';
|
|
181
|
+
}
|
|
182
|
+
else if (accessToken) {
|
|
183
|
+
// Fall back to access token if no refresh token available
|
|
184
|
+
// Note: This will fail when the access token expires (typically 4 hours)
|
|
185
|
+
this._accessToken = accessToken;
|
|
186
|
+
const dropboxConfig = { accessToken };
|
|
187
|
+
// For Dropbox Business/Team accounts, specify the team member
|
|
188
|
+
if (config.selectUser) {
|
|
189
|
+
dropboxConfig.selectUser = config.selectUser;
|
|
190
|
+
}
|
|
191
|
+
this._client = new dropbox_1.Dropbox(dropboxConfig);
|
|
192
|
+
}
|
|
193
|
+
// Update root path if provided
|
|
194
|
+
if (config.rootPath) {
|
|
195
|
+
this._rootPath = config.rootPath;
|
|
196
|
+
// Ensure root path starts with / if not empty
|
|
197
|
+
if (this._rootPath && !this._rootPath.startsWith('/')) {
|
|
198
|
+
this._rootPath = '/' + this._rootPath;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
138
202
|
/**
|
|
139
203
|
* Checks if Dropbox provider is properly configured.
|
|
140
204
|
* Returns true if access token is present.
|
|
141
205
|
*/
|
|
142
206
|
get IsConfigured() {
|
|
143
|
-
return !!
|
|
207
|
+
return !!this._accessToken;
|
|
144
208
|
}
|
|
145
209
|
/**
|
|
146
210
|
* Normalizes a path to be compatible with Dropbox API
|
|
@@ -153,20 +217,25 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
153
217
|
* @returns A normalized path string suitable for Dropbox API calls
|
|
154
218
|
*/
|
|
155
219
|
_normalizePath(path) {
|
|
220
|
+
console.log('[DropboxFileStorage._normalizePath] Input:', {
|
|
221
|
+
path,
|
|
222
|
+
rootPath: this._rootPath,
|
|
223
|
+
});
|
|
156
224
|
// Combine root path with the given path
|
|
157
225
|
let fullPath = path;
|
|
158
226
|
if (this._rootPath) {
|
|
159
|
-
fullPath = path ?
|
|
160
|
-
(path.startsWith('/') ? this._rootPath + path : this._rootPath + '/' + path) :
|
|
161
|
-
this._rootPath;
|
|
227
|
+
fullPath = path ? (path.startsWith('/') ? this._rootPath + path : this._rootPath + '/' + path) : this._rootPath;
|
|
162
228
|
}
|
|
163
229
|
else if (!fullPath.startsWith('/') && fullPath !== '') {
|
|
164
230
|
fullPath = '/' + fullPath;
|
|
165
231
|
}
|
|
232
|
+
console.log('[DropboxFileStorage._normalizePath] After combining root path:', { fullPath });
|
|
166
233
|
// For root, Dropbox uses empty string instead of "/"
|
|
167
234
|
if (fullPath === '/') {
|
|
235
|
+
console.log('[DropboxFileStorage._normalizePath] Converted root "/" to empty string');
|
|
168
236
|
return '';
|
|
169
237
|
}
|
|
238
|
+
console.log('[DropboxFileStorage._normalizePath] Final result:', { fullPath });
|
|
170
239
|
return fullPath;
|
|
171
240
|
}
|
|
172
241
|
/**
|
|
@@ -184,7 +253,7 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
184
253
|
try {
|
|
185
254
|
const response = await this._client.filesGetMetadata({
|
|
186
255
|
path: normalizedPath,
|
|
187
|
-
include_media_info: false
|
|
256
|
+
include_media_info: false,
|
|
188
257
|
});
|
|
189
258
|
return response.result;
|
|
190
259
|
}
|
|
@@ -206,6 +275,12 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
206
275
|
_convertToMetadata(item, parentPath = '') {
|
|
207
276
|
const isDirectory = item['.tag'] === 'folder';
|
|
208
277
|
const name = item.name;
|
|
278
|
+
console.log('[DropboxFileStorage._convertToMetadata] Processing item:', {
|
|
279
|
+
name,
|
|
280
|
+
path_display: item.path_display,
|
|
281
|
+
rootPath: this._rootPath,
|
|
282
|
+
parentPath,
|
|
283
|
+
});
|
|
209
284
|
// Extract path from item.path_display
|
|
210
285
|
let path = '';
|
|
211
286
|
if (item.path_display) {
|
|
@@ -213,35 +288,53 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
213
288
|
const pathParts = item.path_display.split('/');
|
|
214
289
|
pathParts.pop();
|
|
215
290
|
path = pathParts.join('/');
|
|
291
|
+
console.log('[DropboxFileStorage._convertToMetadata] After extracting directory:', { path });
|
|
216
292
|
// Remove root path if present
|
|
217
293
|
if (this._rootPath && path.startsWith(this._rootPath)) {
|
|
294
|
+
const oldPath = path;
|
|
218
295
|
path = path.substring(this._rootPath.length);
|
|
296
|
+
console.log('[DropboxFileStorage._convertToMetadata] Removed root path:', {
|
|
297
|
+
oldPath,
|
|
298
|
+
newPath: path,
|
|
299
|
+
rootPath: this._rootPath,
|
|
300
|
+
});
|
|
219
301
|
}
|
|
220
302
|
// Remove leading slash
|
|
221
303
|
if (path.startsWith('/')) {
|
|
222
304
|
path = path.substring(1);
|
|
305
|
+
console.log('[DropboxFileStorage._convertToMetadata] Removed leading slash:', { path });
|
|
223
306
|
}
|
|
224
307
|
}
|
|
225
308
|
// Use parentPath if provided
|
|
226
309
|
if (parentPath) {
|
|
310
|
+
console.log('[DropboxFileStorage._convertToMetadata] Using parentPath:', { parentPath });
|
|
227
311
|
path = parentPath;
|
|
228
312
|
}
|
|
229
|
-
// Construct full path
|
|
230
|
-
|
|
313
|
+
// Construct full path - ensure no double slashes
|
|
314
|
+
let fullPath = name;
|
|
315
|
+
if (path) {
|
|
316
|
+
// Remove trailing slash from path and leading slash from name
|
|
317
|
+
const cleanPath = path.endsWith('/') ? path.slice(0, -1) : path;
|
|
318
|
+
const cleanName = name.startsWith('/') ? name.slice(1) : name;
|
|
319
|
+
fullPath = `${cleanPath}/${cleanName}`;
|
|
320
|
+
}
|
|
321
|
+
console.log('[DropboxFileStorage._convertToMetadata] Final result:', {
|
|
322
|
+
name,
|
|
323
|
+
path,
|
|
324
|
+
fullPath,
|
|
325
|
+
});
|
|
231
326
|
return {
|
|
232
327
|
name,
|
|
233
328
|
path,
|
|
234
329
|
fullPath,
|
|
235
|
-
size: isDirectory ? 0 :
|
|
236
|
-
contentType: isDirectory ?
|
|
237
|
-
'application/x-directory' :
|
|
238
|
-
(mime.lookup(name) || 'application/octet-stream'),
|
|
330
|
+
size: isDirectory ? 0 : item.size || 0,
|
|
331
|
+
contentType: isDirectory ? 'application/x-directory' : mime.lookup(name) || 'application/octet-stream',
|
|
239
332
|
lastModified: isDirectory ? new Date() : new Date(item.server_modified || Date.now()),
|
|
240
333
|
isDirectory,
|
|
241
334
|
customMetadata: {
|
|
242
335
|
id: item.id,
|
|
243
|
-
rev: item.rev
|
|
244
|
-
}
|
|
336
|
+
rev: isDirectory ? undefined : item.rev,
|
|
337
|
+
},
|
|
245
338
|
};
|
|
246
339
|
}
|
|
247
340
|
/**
|
|
@@ -267,10 +360,77 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
267
360
|
* }
|
|
268
361
|
* ```
|
|
269
362
|
*/
|
|
363
|
+
/**
|
|
364
|
+
* Creates a pre-authenticated upload URL for a file
|
|
365
|
+
*
|
|
366
|
+
* This method generates a time-limited URL that can be used to upload
|
|
367
|
+
* a file directly to Dropbox without additional authentication.
|
|
368
|
+
*
|
|
369
|
+
* @param objectName - Path where the file should be uploaded (e.g., 'documents/report.pdf')
|
|
370
|
+
* @returns A Promise that resolves to an object containing the upload URL and provider key
|
|
371
|
+
* @throws Error if URL creation fails
|
|
372
|
+
*
|
|
373
|
+
* @remarks
|
|
374
|
+
* - Dropbox temporary upload links typically expire after 4 hours
|
|
375
|
+
* - Maximum file size for upload via temporary link is 150MB
|
|
376
|
+
* - The upload must use Content-Type: application/octet-stream
|
|
377
|
+
* - The URL is for one-time use only
|
|
378
|
+
*
|
|
379
|
+
* @example
|
|
380
|
+
* ```typescript
|
|
381
|
+
* try {
|
|
382
|
+
* // Generate a pre-authenticated upload URL
|
|
383
|
+
* const uploadPayload = await storage.CreatePreAuthUploadUrl('documents/financial-report.pdf');
|
|
384
|
+
*
|
|
385
|
+
* console.log(`Upload the file to this URL: ${uploadPayload.UploadUrl}`);
|
|
386
|
+
*
|
|
387
|
+
* // Use the URL to upload file directly from client
|
|
388
|
+
* // POST request with Content-Type: application/octet-stream
|
|
389
|
+
* } catch (error) {
|
|
390
|
+
* console.error('Error creating upload URL:', error.message);
|
|
391
|
+
* }
|
|
392
|
+
* ```
|
|
393
|
+
*/
|
|
270
394
|
async CreatePreAuthUploadUrl(objectName) {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
395
|
+
try {
|
|
396
|
+
console.log('[DropboxFileStorage.CreatePreAuthUploadUrl] Input:', {
|
|
397
|
+
objectName,
|
|
398
|
+
rootPath: this._rootPath,
|
|
399
|
+
});
|
|
400
|
+
const normalizedPath = this._normalizePath(objectName);
|
|
401
|
+
console.log('[DropboxFileStorage.CreatePreAuthUploadUrl] After normalization:', {
|
|
402
|
+
normalizedPath,
|
|
403
|
+
});
|
|
404
|
+
// Create a temporary upload link
|
|
405
|
+
// Note: commit_info is optional, defaults to overwrite mode
|
|
406
|
+
const response = await this._client.filesGetTemporaryUploadLink({
|
|
407
|
+
commit_info: {
|
|
408
|
+
path: normalizedPath,
|
|
409
|
+
mode: { '.tag': 'overwrite' },
|
|
410
|
+
autorename: false,
|
|
411
|
+
mute: true,
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
console.log('[DropboxFileStorage.CreatePreAuthUploadUrl] Success:', {
|
|
415
|
+
link: response.result.link,
|
|
416
|
+
});
|
|
417
|
+
return {
|
|
418
|
+
UploadUrl: response.result.link,
|
|
419
|
+
ProviderKey: normalizedPath,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
catch (error) {
|
|
423
|
+
console.error('[DropboxFileStorage.CreatePreAuthUploadUrl] Error:', {
|
|
424
|
+
objectName,
|
|
425
|
+
rootPath: this._rootPath,
|
|
426
|
+
error: error.message || error,
|
|
427
|
+
errorDetails: error.error || error,
|
|
428
|
+
errorStatus: error.status,
|
|
429
|
+
fullError: JSON.stringify(error, null, 2),
|
|
430
|
+
});
|
|
431
|
+
const errorMsg = error.error?.error_summary || error.message || JSON.stringify(error);
|
|
432
|
+
throw new Error(`Failed to create upload URL for: ${objectName} - ${errorMsg}`);
|
|
433
|
+
}
|
|
274
434
|
}
|
|
275
435
|
/**
|
|
276
436
|
* Creates a pre-authenticated download URL for a file
|
|
@@ -303,15 +463,29 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
303
463
|
*/
|
|
304
464
|
async CreatePreAuthDownloadUrl(objectName) {
|
|
305
465
|
try {
|
|
466
|
+
console.log('[DropboxFileStorage.CreatePreAuthDownloadUrl] Input:', {
|
|
467
|
+
objectName,
|
|
468
|
+
rootPath: this._rootPath,
|
|
469
|
+
});
|
|
306
470
|
const normalizedPath = this._normalizePath(objectName);
|
|
471
|
+
console.log('[DropboxFileStorage.CreatePreAuthDownloadUrl] After normalization:', {
|
|
472
|
+
normalizedPath,
|
|
473
|
+
});
|
|
307
474
|
// Create a temporary download link
|
|
308
475
|
const response = await this._client.filesGetTemporaryLink({
|
|
309
|
-
path: normalizedPath
|
|
476
|
+
path: normalizedPath,
|
|
477
|
+
});
|
|
478
|
+
console.log('[DropboxFileStorage.CreatePreAuthDownloadUrl] Success:', {
|
|
479
|
+
link: response.result.link,
|
|
310
480
|
});
|
|
311
481
|
return response.result.link;
|
|
312
482
|
}
|
|
313
483
|
catch (error) {
|
|
314
|
-
console.error('Error
|
|
484
|
+
console.error('[DropboxFileStorage.CreatePreAuthDownloadUrl] Error:', {
|
|
485
|
+
objectName,
|
|
486
|
+
rootPath: this._rootPath,
|
|
487
|
+
error: error.message || error,
|
|
488
|
+
});
|
|
315
489
|
throw new Error(`Failed to create download URL for: ${objectName}`);
|
|
316
490
|
}
|
|
317
491
|
}
|
|
@@ -352,7 +526,7 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
352
526
|
await this._client.filesMoveV2({
|
|
353
527
|
from_path: fromPath,
|
|
354
528
|
to_path: toPath,
|
|
355
|
-
autorename: false
|
|
529
|
+
autorename: false,
|
|
356
530
|
});
|
|
357
531
|
return true;
|
|
358
532
|
}
|
|
@@ -388,18 +562,31 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
388
562
|
*/
|
|
389
563
|
async DeleteObject(objectName) {
|
|
390
564
|
try {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
565
|
+
// Remove trailing slash if present (Dropbox doesn't accept trailing slashes for folder deletion)
|
|
566
|
+
const normalizedPath = objectName.endsWith('/') ? this._normalizePath(objectName.substring(0, objectName.length - 1)) : this._normalizePath(objectName);
|
|
567
|
+
console.log('[DropboxFileStorage] DeleteObject called:', {
|
|
568
|
+
objectName,
|
|
569
|
+
normalizedPath,
|
|
570
|
+
rootPath: this._rootPath,
|
|
571
|
+
hadTrailingSlash: objectName.endsWith('/'),
|
|
572
|
+
});
|
|
573
|
+
const result = await this._client.filesDeleteV2({
|
|
574
|
+
path: normalizedPath,
|
|
394
575
|
});
|
|
576
|
+
console.log('[DropboxFileStorage] filesDeleteV2 result:', result);
|
|
395
577
|
return true;
|
|
396
578
|
}
|
|
397
579
|
catch (error) {
|
|
398
580
|
// If the path doesn't exist, consider it success for idempotency
|
|
399
581
|
if (error.status === 409 && error.error?.error?.['.tag'] === 'path_lookup') {
|
|
582
|
+
console.log('[DropboxFileStorage] Path not found (already deleted):', objectName);
|
|
400
583
|
return true;
|
|
401
584
|
}
|
|
402
|
-
console.error('Error deleting object', {
|
|
585
|
+
console.error('[DropboxFileStorage] Error deleting object', {
|
|
586
|
+
objectName,
|
|
587
|
+
normalizedPath: objectName.endsWith('/') ? this._normalizePath(objectName.substring(0, objectName.length - 1)) : this._normalizePath(objectName),
|
|
588
|
+
error,
|
|
589
|
+
});
|
|
403
590
|
return false;
|
|
404
591
|
}
|
|
405
592
|
}
|
|
@@ -440,30 +627,83 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
440
627
|
async ListObjects(prefix, delimiter = '/') {
|
|
441
628
|
try {
|
|
442
629
|
const normalizedPath = this._normalizePath(prefix);
|
|
630
|
+
console.log('[DropboxFileStorage] ListObjects called:', {
|
|
631
|
+
prefix,
|
|
632
|
+
normalizedPath,
|
|
633
|
+
delimiter,
|
|
634
|
+
hasClient: !!this._client,
|
|
635
|
+
isConfigured: this.IsConfigured,
|
|
636
|
+
rootPath: this._rootPath,
|
|
637
|
+
});
|
|
638
|
+
// Debug: Try to get current account info to understand access type
|
|
639
|
+
try {
|
|
640
|
+
const accountInfo = await this._client.usersGetCurrentAccount();
|
|
641
|
+
console.log('[DropboxFileStorage] Account info:', {
|
|
642
|
+
accountId: accountInfo.result.account_id,
|
|
643
|
+
email: accountInfo.result.email,
|
|
644
|
+
name: accountInfo.result.name.display_name,
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
catch (error) {
|
|
648
|
+
console.log('[DropboxFileStorage] Could not get account info:', error?.message);
|
|
649
|
+
}
|
|
443
650
|
const response = await this._client.filesListFolder({
|
|
444
651
|
path: normalizedPath,
|
|
445
652
|
recursive: false,
|
|
446
653
|
include_media_info: false,
|
|
447
654
|
include_deleted: false,
|
|
448
|
-
include_has_explicit_shared_members: false
|
|
655
|
+
include_has_explicit_shared_members: false,
|
|
449
656
|
});
|
|
657
|
+
console.log('[DropboxFileStorage] filesListFolder response:', {
|
|
658
|
+
entriesCount: response.result.entries.length,
|
|
659
|
+
entries: response.result.entries.map((e) => ({ name: e.name, tag: e['.tag'] })),
|
|
660
|
+
has_more: response.result.has_more,
|
|
661
|
+
cursor: response.result.cursor,
|
|
662
|
+
});
|
|
663
|
+
// Check if we're in an app folder scenario
|
|
664
|
+
if (response.result.entries.length === 0 && normalizedPath === '') {
|
|
665
|
+
console.log('[DropboxFileStorage] Empty root - this might be an app-folder-only token');
|
|
666
|
+
console.log('[DropboxFileStorage] Note: If using app folder access, files are in /Apps/[YourAppName]/');
|
|
667
|
+
console.log('[DropboxFileStorage] You can set rootPath in configuration to point to your app folder');
|
|
668
|
+
// Try to list a few common app folder paths to help diagnose
|
|
669
|
+
const testPaths = ['/Apps', '/Apps/MJ-Files-Test', '/MJ-FileTest'];
|
|
670
|
+
for (const testPath of testPaths) {
|
|
671
|
+
try {
|
|
672
|
+
console.log(`[DropboxFileStorage] Testing path: ${testPath}`);
|
|
673
|
+
const testResponse = await this._client.filesListFolder({ path: testPath, recursive: false });
|
|
674
|
+
console.log(`[DropboxFileStorage] Found ${testResponse.result.entries.length} items at ${testPath}:`, testResponse.result.entries.map((e) => ({ name: e.name, tag: e['.tag'] })));
|
|
675
|
+
}
|
|
676
|
+
catch (testError) {
|
|
677
|
+
const errorMsg = testError.error?.error_summary || testError.message;
|
|
678
|
+
console.log(`[DropboxFileStorage] Cannot access ${testPath}: ${errorMsg}`);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
450
682
|
const objects = [];
|
|
451
683
|
const prefixes = [];
|
|
452
684
|
// Process entries
|
|
453
685
|
for (const entry of response.result.entries) {
|
|
686
|
+
// Skip deleted entries
|
|
687
|
+
if (entry['.tag'] === 'deleted') {
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
454
690
|
objects.push(this._convertToMetadata(entry, prefix));
|
|
455
691
|
// If it's a folder, add to prefixes
|
|
456
692
|
if (entry['.tag'] === 'folder') {
|
|
457
|
-
const folderPath = prefix
|
|
458
|
-
|
|
459
|
-
: entry.name;
|
|
693
|
+
const folderPath = prefix ? (prefix.endsWith('/') ? `${prefix}${entry.name}` : `${prefix}/${entry.name}`) : entry.name;
|
|
694
|
+
console.log('[DropboxFileStorage] Adding folder prefix:', folderPath);
|
|
460
695
|
prefixes.push(`${folderPath}/`);
|
|
461
696
|
}
|
|
462
697
|
}
|
|
698
|
+
console.log('[DropboxFileStorage] Final result:', {
|
|
699
|
+
objectsCount: objects.length,
|
|
700
|
+
prefixesCount: prefixes.length,
|
|
701
|
+
prefixes,
|
|
702
|
+
});
|
|
463
703
|
return { objects, prefixes };
|
|
464
704
|
}
|
|
465
705
|
catch (error) {
|
|
466
|
-
console.error('Error listing objects', { prefix, error });
|
|
706
|
+
console.error('[DropboxFileStorage] Error listing objects:', { prefix, error, errorMessage: error?.message, errorStatus: error?.status });
|
|
467
707
|
return { objects: [], prefixes: [] };
|
|
468
708
|
}
|
|
469
709
|
}
|
|
@@ -507,7 +747,7 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
507
747
|
: this._normalizePath(directoryPath);
|
|
508
748
|
await this._client.filesCreateFolderV2({
|
|
509
749
|
path: normalizedPath,
|
|
510
|
-
autorename: false
|
|
750
|
+
autorename: false,
|
|
511
751
|
});
|
|
512
752
|
return true;
|
|
513
753
|
}
|
|
@@ -564,14 +804,14 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
564
804
|
// Check if directory is empty first
|
|
565
805
|
const listing = await this._client.filesListFolder({
|
|
566
806
|
path: normalizedPath,
|
|
567
|
-
recursive: false
|
|
807
|
+
recursive: false,
|
|
568
808
|
});
|
|
569
809
|
if (listing.result.entries.length > 0) {
|
|
570
810
|
throw new Error('Directory is not empty');
|
|
571
811
|
}
|
|
572
812
|
}
|
|
573
813
|
await this._client.filesDeleteV2({
|
|
574
|
-
path: normalizedPath
|
|
814
|
+
path: normalizedPath,
|
|
575
815
|
});
|
|
576
816
|
return true;
|
|
577
817
|
}
|
|
@@ -643,8 +883,12 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
643
883
|
}
|
|
644
884
|
const response = await this._client.filesGetMetadata({
|
|
645
885
|
path: path,
|
|
646
|
-
include_media_info: false
|
|
886
|
+
include_media_info: false,
|
|
647
887
|
});
|
|
888
|
+
// Check if the result is a deleted entry
|
|
889
|
+
if (response.result['.tag'] === 'deleted') {
|
|
890
|
+
throw new Error(`Object not found (deleted): ${params.objectId || params.fullPath}`);
|
|
891
|
+
}
|
|
648
892
|
return this._convertToMetadata(response.result, parentPath);
|
|
649
893
|
}
|
|
650
894
|
catch (error) {
|
|
@@ -704,10 +948,11 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
704
948
|
console.log(`🐌 Slow path: Using path: ${path}`);
|
|
705
949
|
}
|
|
706
950
|
const response = await this._client.filesDownload({
|
|
707
|
-
path: path
|
|
951
|
+
path: path,
|
|
708
952
|
});
|
|
709
953
|
// Extract file content as Buffer
|
|
710
954
|
// Note: In Dropbox SDK, the file content is in response.result.fileBinary
|
|
955
|
+
// The TypeScript definitions don't include fileBinary, but it's present in the actual response
|
|
711
956
|
return Buffer.from(response.result.fileBinary);
|
|
712
957
|
}
|
|
713
958
|
catch (error) {
|
|
@@ -763,7 +1008,7 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
763
1008
|
contents: data,
|
|
764
1009
|
mode: { '.tag': 'overwrite' },
|
|
765
1010
|
autorename: false,
|
|
766
|
-
mute: true
|
|
1011
|
+
mute: true,
|
|
767
1012
|
});
|
|
768
1013
|
}
|
|
769
1014
|
else {
|
|
@@ -772,7 +1017,7 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
772
1017
|
// Start upload session
|
|
773
1018
|
const sessionStart = await this._client.filesUploadSessionStart({
|
|
774
1019
|
close: false,
|
|
775
|
-
contents: data.slice(0, CHUNK_SIZE)
|
|
1020
|
+
contents: data.slice(0, CHUNK_SIZE),
|
|
776
1021
|
});
|
|
777
1022
|
const sessionId = sessionStart.result.session_id;
|
|
778
1023
|
let offset = CHUNK_SIZE;
|
|
@@ -785,15 +1030,15 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
785
1030
|
await this._client.filesUploadSessionFinish({
|
|
786
1031
|
cursor: {
|
|
787
1032
|
session_id: sessionId,
|
|
788
|
-
offset: offset
|
|
1033
|
+
offset: offset,
|
|
789
1034
|
},
|
|
790
1035
|
commit: {
|
|
791
1036
|
path: normalizedPath,
|
|
792
1037
|
mode: { '.tag': 'overwrite' },
|
|
793
1038
|
autorename: false,
|
|
794
|
-
mute: true
|
|
1039
|
+
mute: true,
|
|
795
1040
|
},
|
|
796
|
-
contents: chunk
|
|
1041
|
+
contents: chunk,
|
|
797
1042
|
});
|
|
798
1043
|
}
|
|
799
1044
|
else {
|
|
@@ -801,10 +1046,10 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
801
1046
|
await this._client.filesUploadSessionAppendV2({
|
|
802
1047
|
cursor: {
|
|
803
1048
|
session_id: sessionId,
|
|
804
|
-
offset: offset
|
|
1049
|
+
offset: offset,
|
|
805
1050
|
},
|
|
806
1051
|
close: false,
|
|
807
|
-
contents: chunk
|
|
1052
|
+
contents: chunk,
|
|
808
1053
|
});
|
|
809
1054
|
}
|
|
810
1055
|
offset += chunk.length;
|
|
@@ -854,7 +1099,7 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
854
1099
|
await this._client.filesCopyV2({
|
|
855
1100
|
from_path: fromPath,
|
|
856
1101
|
to_path: toPath,
|
|
857
|
-
autorename: false
|
|
1102
|
+
autorename: false,
|
|
858
1103
|
});
|
|
859
1104
|
return true;
|
|
860
1105
|
}
|
|
@@ -924,9 +1169,7 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
924
1169
|
async DirectoryExists(directoryPath) {
|
|
925
1170
|
try {
|
|
926
1171
|
// Remove trailing slash if present
|
|
927
|
-
const normalizedPath = directoryPath.endsWith('/')
|
|
928
|
-
? directoryPath.substring(0, directoryPath.length - 1)
|
|
929
|
-
: directoryPath;
|
|
1172
|
+
const normalizedPath = directoryPath.endsWith('/') ? directoryPath.substring(0, directoryPath.length - 1) : directoryPath;
|
|
930
1173
|
const item = await this._getMetadata(normalizedPath);
|
|
931
1174
|
return item['.tag'] === 'folder';
|
|
932
1175
|
}
|
|
@@ -976,13 +1219,13 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
976
1219
|
const maxResults = options?.maxResults || 100;
|
|
977
1220
|
// Build Dropbox search options
|
|
978
1221
|
const searchOptions = {
|
|
979
|
-
query
|
|
1222
|
+
query,
|
|
980
1223
|
options: {
|
|
981
1224
|
max_results: Math.min(maxResults, 1000), // Dropbox max is 1000
|
|
982
1225
|
path: options?.pathPrefix ? this._normalizePath(options.pathPrefix) : undefined,
|
|
983
|
-
file_status: 'active', // Exclude deleted files
|
|
984
|
-
filename_only: !options?.searchContent // Search filename only or filename + content
|
|
985
|
-
}
|
|
1226
|
+
file_status: { '.tag': 'active' }, // Exclude deleted files
|
|
1227
|
+
filename_only: !options?.searchContent, // Search filename only or filename + content
|
|
1228
|
+
},
|
|
986
1229
|
};
|
|
987
1230
|
// Add file extension filter if fileTypes provided
|
|
988
1231
|
if (options?.fileTypes && options.fileTypes.length > 0) {
|
|
@@ -996,10 +1239,19 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
996
1239
|
const results = [];
|
|
997
1240
|
// Process search results
|
|
998
1241
|
for (const match of response.result.matches || []) {
|
|
999
|
-
// The metadata field
|
|
1000
|
-
|
|
1242
|
+
// The metadata field is MetadataV2, which can be MetadataV2Metadata or MetadataV2Other
|
|
1243
|
+
// We only want MetadataV2Metadata which has the actual file/folder metadata
|
|
1244
|
+
if (match.metadata['.tag'] !== 'metadata') {
|
|
1245
|
+
continue;
|
|
1246
|
+
}
|
|
1247
|
+
const metadataV2 = match.metadata;
|
|
1248
|
+
// Skip deleted entries
|
|
1249
|
+
if (metadataV2.metadata['.tag'] === 'deleted') {
|
|
1250
|
+
continue;
|
|
1251
|
+
}
|
|
1252
|
+
const metadata = metadataV2.metadata;
|
|
1001
1253
|
// Skip if not a file (could be folder or other type)
|
|
1002
|
-
if (
|
|
1254
|
+
if (metadata['.tag'] !== 'file') {
|
|
1003
1255
|
continue;
|
|
1004
1256
|
}
|
|
1005
1257
|
// Apply date filters client-side (Dropbox search doesn't support date filters directly)
|
|
@@ -1026,12 +1278,12 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
1026
1278
|
matchInFilename: this._checkFilenameMatch(fileName, query),
|
|
1027
1279
|
customMetadata: {
|
|
1028
1280
|
id: metadata.id,
|
|
1029
|
-
rev: metadata.rev
|
|
1281
|
+
rev: metadata.rev,
|
|
1030
1282
|
},
|
|
1031
1283
|
providerData: {
|
|
1032
1284
|
dropboxId: metadata.id,
|
|
1033
|
-
pathLower: metadata.path_lower
|
|
1034
|
-
}
|
|
1285
|
+
pathLower: metadata.path_lower,
|
|
1286
|
+
},
|
|
1035
1287
|
});
|
|
1036
1288
|
}
|
|
1037
1289
|
// Check if there are more results available
|
|
@@ -1040,7 +1292,7 @@ let DropboxFileStorage = class DropboxFileStorage extends FileStorageBase_1.File
|
|
|
1040
1292
|
results,
|
|
1041
1293
|
totalMatches: undefined, // Dropbox doesn't provide total count
|
|
1042
1294
|
hasMore,
|
|
1043
|
-
nextPageToken: hasMore ? 'continue' : undefined // Dropbox uses continue endpoint for pagination
|
|
1295
|
+
nextPageToken: hasMore ? 'continue' : undefined, // Dropbox uses continue endpoint for pagination
|
|
1044
1296
|
};
|
|
1045
1297
|
}
|
|
1046
1298
|
catch (error) {
|