@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
|
@@ -31,6 +31,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
31
31
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
32
32
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
33
33
|
};
|
|
34
|
+
var GoogleDriveFileStorage_1;
|
|
34
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
36
|
exports.GoogleDriveFileStorage = void 0;
|
|
36
37
|
const googleapis_1 = require("googleapis");
|
|
@@ -73,6 +74,7 @@ const config_1 = require("../config");
|
|
|
73
74
|
* ```
|
|
74
75
|
*/
|
|
75
76
|
let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBase_1.FileStorageBase {
|
|
77
|
+
static { GoogleDriveFileStorage_1 = this; }
|
|
76
78
|
/** The name of this storage provider, used in error messages */
|
|
77
79
|
providerName = 'Google Drive';
|
|
78
80
|
/** The Google Drive API client */
|
|
@@ -102,11 +104,12 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
102
104
|
this._refreshToken = config?.refreshToken || env.get('STORAGE_GDRIVE_REFRESH_TOKEN').asString();
|
|
103
105
|
const redirectURI = config?.redirectURI || env.get('STORAGE_GDRIVE_REDIRECT_URI').asString();
|
|
104
106
|
// Initialize the Google Drive client - support THREE auth methods
|
|
107
|
+
// Note: If no credentials are found here, initialize() will be called later with database config
|
|
105
108
|
if (keyFile) {
|
|
106
109
|
// Method 1: Using key file (service account)
|
|
107
110
|
const auth = new googleapis_1.google.auth.GoogleAuth({
|
|
108
111
|
keyFile,
|
|
109
|
-
scopes: ['https://www.googleapis.com/auth/drive']
|
|
112
|
+
scopes: ['https://www.googleapis.com/auth/drive'],
|
|
110
113
|
});
|
|
111
114
|
this._drive = googleapis_1.google.drive({ version: 'v3', auth });
|
|
112
115
|
}
|
|
@@ -122,9 +125,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
122
125
|
auth.setCredentials({ refresh_token: this._refreshToken });
|
|
123
126
|
this._drive = googleapis_1.google.drive({ version: 'v3', auth });
|
|
124
127
|
}
|
|
125
|
-
|
|
126
|
-
throw new Error('Google Drive storage requires either STORAGE_GDRIVE_KEY_FILE, STORAGE_GDRIVE_CREDENTIALS_JSON, or OAuth2 credentials (CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN) to be set');
|
|
127
|
-
}
|
|
128
|
+
// If no credentials found, _drive will be undefined and initialize() must be called
|
|
128
129
|
// Optionally set a root folder ID to restrict operations
|
|
129
130
|
this._rootFolderId = config?.rootFolderID || env.get('STORAGE_GDRIVE_ROOT_FOLDER_ID').asString();
|
|
130
131
|
}
|
|
@@ -135,6 +136,37 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
135
136
|
get IsConfigured() {
|
|
136
137
|
return !!(this._clientID && this._clientSecret && this._refreshToken);
|
|
137
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Initializes the Google Drive storage provider with configuration from the database.
|
|
141
|
+
* This method is called by the FileStorageProviderEngine when loading provider configurations.
|
|
142
|
+
*
|
|
143
|
+
* @param config - Configuration object containing OAuth2 credentials
|
|
144
|
+
*/
|
|
145
|
+
async initialize(config) {
|
|
146
|
+
// Always call super to store accountId and accountName
|
|
147
|
+
await super.initialize(config);
|
|
148
|
+
if (!config) {
|
|
149
|
+
return; // Nothing to do, constructor already handled config from env/file
|
|
150
|
+
}
|
|
151
|
+
// Update OAuth2 credentials
|
|
152
|
+
this._clientID = config.clientID || this._clientID;
|
|
153
|
+
this._clientSecret = config.clientSecret || this._clientSecret;
|
|
154
|
+
this._refreshToken = config.refreshToken || this._refreshToken;
|
|
155
|
+
// Update root folder ID if provided
|
|
156
|
+
if (config.rootFolderID) {
|
|
157
|
+
this._rootFolderId = config.rootFolderID;
|
|
158
|
+
}
|
|
159
|
+
// Reinitialize the Google Drive client with new OAuth2 credentials
|
|
160
|
+
if (this._clientID && this._clientSecret && this._refreshToken) {
|
|
161
|
+
const redirectURI = 'urn:ietf:wg:oauth:2.0:oob';
|
|
162
|
+
const auth = new googleapis_1.google.auth.OAuth2(this._clientID, this._clientSecret, redirectURI);
|
|
163
|
+
auth.setCredentials({ refresh_token: this._refreshToken });
|
|
164
|
+
this._drive = googleapis_1.google.drive({ version: 'v3', auth });
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
throw new Error('Google Drive storage requires clientID, clientSecret, and refreshToken to be set');
|
|
168
|
+
}
|
|
169
|
+
}
|
|
138
170
|
/**
|
|
139
171
|
* Finds a file or folder by path.
|
|
140
172
|
*
|
|
@@ -148,16 +180,21 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
148
180
|
* @private
|
|
149
181
|
*/
|
|
150
182
|
async _getItemByPath(path) {
|
|
151
|
-
|
|
183
|
+
console.log('[GoogleDriveFileStorage] _getItemByPath called with:', JSON.stringify(path));
|
|
184
|
+
// Normalize path: remove leading/trailing slashes and collapse multiple slashes
|
|
185
|
+
const normalizedPath = path ? path.replace(/^\/+|\/+$/g, '').replace(/\/+/g, '/') : '';
|
|
186
|
+
console.log('[GoogleDriveFileStorage] Normalized path:', JSON.stringify(normalizedPath));
|
|
187
|
+
if (!normalizedPath) {
|
|
152
188
|
// Return the root folder or the specified root folder
|
|
153
189
|
return {
|
|
154
190
|
id: this._rootFolderId || 'root',
|
|
155
191
|
name: 'Root',
|
|
156
|
-
mimeType: 'application/vnd.google-apps.folder'
|
|
192
|
+
mimeType: 'application/vnd.google-apps.folder',
|
|
157
193
|
};
|
|
158
194
|
}
|
|
159
195
|
// Split path into parts
|
|
160
|
-
const pathParts =
|
|
196
|
+
const pathParts = normalizedPath.split('/').filter((p) => p);
|
|
197
|
+
console.log('[GoogleDriveFileStorage] Path parts:', pathParts);
|
|
161
198
|
// Start with root folder or the specified root folder
|
|
162
199
|
let currentParentId = this._rootFolderId || 'root';
|
|
163
200
|
let currentItem = null;
|
|
@@ -165,14 +202,22 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
165
202
|
for (let i = 0; i < pathParts.length; i++) {
|
|
166
203
|
const part = pathParts[i];
|
|
167
204
|
const isLastPart = i === pathParts.length - 1;
|
|
205
|
+
// Escape single quotes in the part name for Google Drive query syntax
|
|
206
|
+
const escapedPart = part.replace(/'/g, "\\'");
|
|
168
207
|
// Query for the item
|
|
169
208
|
const query = isLastPart
|
|
170
|
-
? `name = '${
|
|
171
|
-
: `name = '${
|
|
209
|
+
? `name = '${escapedPart}' and '${currentParentId}' in parents and trashed = false`
|
|
210
|
+
: `name = '${escapedPart}' and '${currentParentId}' in parents and mimeType = 'application/vnd.google-apps.folder' and trashed = false`;
|
|
211
|
+
console.log('[GoogleDriveFileStorage] Querying for part:', part, 'Query:', query);
|
|
172
212
|
const response = await this._drive.files.list({
|
|
173
213
|
q: query,
|
|
174
214
|
fields: 'files(id, name, mimeType, size, modifiedTime, parents)',
|
|
175
|
-
spaces: 'drive'
|
|
215
|
+
spaces: 'drive',
|
|
216
|
+
});
|
|
217
|
+
console.log('[GoogleDriveFileStorage] Query result:', {
|
|
218
|
+
part,
|
|
219
|
+
filesFound: response.data.files?.length || 0,
|
|
220
|
+
files: response.data.files?.map((f) => ({ id: f.id, name: f.name })),
|
|
176
221
|
});
|
|
177
222
|
if (!response.data.files || response.data.files.length === 0) {
|
|
178
223
|
throw new Error(`Path not found: ${path} (at part: ${part})`);
|
|
@@ -183,6 +228,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
183
228
|
if (!currentItem) {
|
|
184
229
|
throw new Error(`Path not found: ${path}`);
|
|
185
230
|
}
|
|
231
|
+
console.log('[GoogleDriveFileStorage] Found item:', { id: currentItem.id, name: currentItem.name });
|
|
186
232
|
return currentItem;
|
|
187
233
|
}
|
|
188
234
|
/**
|
|
@@ -201,7 +247,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
201
247
|
return this._rootFolderId || 'root';
|
|
202
248
|
}
|
|
203
249
|
// Split path into parts
|
|
204
|
-
const pathParts = path.split('/').filter(p => p);
|
|
250
|
+
const pathParts = path.split('/').filter((p) => p);
|
|
205
251
|
// Start with root folder or the specified root folder
|
|
206
252
|
let currentParentId = this._rootFolderId || 'root';
|
|
207
253
|
// Navigate through path parts, creating folders as needed
|
|
@@ -211,7 +257,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
211
257
|
const response = await this._drive.files.list({
|
|
212
258
|
q: query,
|
|
213
259
|
fields: 'files(id)',
|
|
214
|
-
spaces: 'drive'
|
|
260
|
+
spaces: 'drive',
|
|
215
261
|
});
|
|
216
262
|
if (response.data.files && response.data.files.length > 0) {
|
|
217
263
|
// Folder exists, use it
|
|
@@ -222,11 +268,11 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
222
268
|
const folderMetadata = {
|
|
223
269
|
name: part,
|
|
224
270
|
mimeType: 'application/vnd.google-apps.folder',
|
|
225
|
-
parents: [currentParentId]
|
|
271
|
+
parents: [currentParentId],
|
|
226
272
|
};
|
|
227
273
|
const folder = await this._drive.files.create({
|
|
228
274
|
requestBody: folderMetadata,
|
|
229
|
-
fields: 'id'
|
|
275
|
+
fields: 'id',
|
|
230
276
|
});
|
|
231
277
|
currentParentId = folder.data.id;
|
|
232
278
|
}
|
|
@@ -247,7 +293,9 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
247
293
|
*/
|
|
248
294
|
_fileToMetadata(file, parentPath = '') {
|
|
249
295
|
const isDirectory = file.mimeType === 'application/vnd.google-apps.folder';
|
|
250
|
-
|
|
296
|
+
// Normalize parent path: remove trailing slash to avoid double slashes, handle root properly
|
|
297
|
+
const normalizedParent = parentPath && parentPath !== '/' ? parentPath.replace(/\/+$/, '') : '';
|
|
298
|
+
const fullPath = normalizedParent ? `${normalizedParent}/${file.name}` : file.name;
|
|
251
299
|
return {
|
|
252
300
|
name: file.name,
|
|
253
301
|
path: parentPath,
|
|
@@ -256,10 +304,10 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
256
304
|
contentType: file.mimeType || mime.lookup(file.name) || 'application/octet-stream',
|
|
257
305
|
lastModified: new Date(file.modifiedTime || Date.now()),
|
|
258
306
|
isDirectory,
|
|
259
|
-
etag: file.
|
|
307
|
+
etag: file.md5Checksum || undefined,
|
|
260
308
|
customMetadata: {
|
|
261
|
-
fileId: file.id
|
|
262
|
-
}
|
|
309
|
+
fileId: file.id,
|
|
310
|
+
},
|
|
263
311
|
};
|
|
264
312
|
}
|
|
265
313
|
/**
|
|
@@ -298,6 +346,32 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
298
346
|
* console.log(downloadUrl);
|
|
299
347
|
* ```
|
|
300
348
|
*/
|
|
349
|
+
/**
|
|
350
|
+
* Map of Google Workspace MIME types to their export formats.
|
|
351
|
+
* Google Workspace files must be exported to these formats for download.
|
|
352
|
+
*/
|
|
353
|
+
static GOOGLE_WORKSPACE_EXPORT_MAP = {
|
|
354
|
+
'application/vnd.google-apps.document': {
|
|
355
|
+
mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
356
|
+
format: 'docx',
|
|
357
|
+
urlBase: 'https://docs.google.com/document/d',
|
|
358
|
+
},
|
|
359
|
+
'application/vnd.google-apps.spreadsheet': {
|
|
360
|
+
mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
361
|
+
format: 'xlsx',
|
|
362
|
+
urlBase: 'https://docs.google.com/spreadsheets/d',
|
|
363
|
+
},
|
|
364
|
+
'application/vnd.google-apps.presentation': {
|
|
365
|
+
mimeType: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
366
|
+
format: 'pptx',
|
|
367
|
+
urlBase: 'https://docs.google.com/presentation/d',
|
|
368
|
+
},
|
|
369
|
+
'application/vnd.google-apps.drawing': {
|
|
370
|
+
mimeType: 'image/png',
|
|
371
|
+
format: 'png',
|
|
372
|
+
urlBase: 'https://docs.google.com/drawings/d',
|
|
373
|
+
},
|
|
374
|
+
};
|
|
301
375
|
async CreatePreAuthDownloadUrl(objectName) {
|
|
302
376
|
try {
|
|
303
377
|
// Get the file by path
|
|
@@ -305,26 +379,71 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
305
379
|
if (!file.id) {
|
|
306
380
|
throw new Error(`File not found: ${objectName}`);
|
|
307
381
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
382
|
+
console.log(`[GoogleDrive] CreatePreAuthDownloadUrl for: ${objectName}, mimeType: ${file.mimeType}`);
|
|
383
|
+
// Check if this is a Google Workspace file that needs export
|
|
384
|
+
const exportInfo = GoogleDriveFileStorage_1.GOOGLE_WORKSPACE_EXPORT_MAP[file.mimeType];
|
|
385
|
+
if (exportInfo) {
|
|
386
|
+
// Google Workspace file - return export URL
|
|
387
|
+
// These URLs work without authentication if the file is shared
|
|
388
|
+
const exportUrl = `${exportInfo.urlBase}/${file.id}/export?format=${exportInfo.format}`;
|
|
389
|
+
console.log(`[GoogleDrive] Google Workspace file, using export URL: ${exportUrl}`);
|
|
390
|
+
return exportUrl;
|
|
391
|
+
}
|
|
392
|
+
// Check for other Google Workspace types that can't be exported
|
|
393
|
+
if (file.mimeType?.startsWith('application/vnd.google-apps.')) {
|
|
394
|
+
// For unsupported types (Forms, Sites, Maps, etc.), return the web view link
|
|
395
|
+
console.log(`[GoogleDrive] Unsupported Google Workspace type: ${file.mimeType}, returning webViewLink`);
|
|
396
|
+
const fileInfo = await this._drive.files.get({
|
|
397
|
+
fileId: file.id,
|
|
398
|
+
fields: 'webViewLink',
|
|
399
|
+
});
|
|
400
|
+
if (fileInfo.data.webViewLink) {
|
|
401
|
+
return fileInfo.data.webViewLink;
|
|
315
402
|
}
|
|
316
|
-
|
|
317
|
-
|
|
403
|
+
throw new Error(`Cannot download Google Workspace file of type: ${file.mimeType}`);
|
|
404
|
+
}
|
|
405
|
+
// Regular file - try to get direct download link
|
|
318
406
|
const fileInfo = await this._drive.files.get({
|
|
319
407
|
fileId: file.id,
|
|
320
|
-
fields: 'webViewLink, webContentLink'
|
|
408
|
+
fields: 'webViewLink, webContentLink',
|
|
321
409
|
});
|
|
322
|
-
//
|
|
323
|
-
|
|
410
|
+
// webContentLink is the direct download link (only available for non-Google files)
|
|
411
|
+
if (fileInfo.data.webContentLink) {
|
|
412
|
+
console.log(`[GoogleDrive] Using webContentLink for direct download`);
|
|
413
|
+
return fileInfo.data.webContentLink;
|
|
414
|
+
}
|
|
415
|
+
// Try to create a public sharing permission to enable download
|
|
416
|
+
try {
|
|
417
|
+
await this._drive.permissions.create({
|
|
418
|
+
fileId: file.id,
|
|
419
|
+
requestBody: {
|
|
420
|
+
role: 'reader',
|
|
421
|
+
type: 'anyone',
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
// Fetch updated file info
|
|
425
|
+
const updatedFileInfo = await this._drive.files.get({
|
|
426
|
+
fileId: file.id,
|
|
427
|
+
fields: 'webContentLink',
|
|
428
|
+
});
|
|
429
|
+
if (updatedFileInfo.data.webContentLink) {
|
|
430
|
+
return updatedFileInfo.data.webContentLink;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
catch (permError) {
|
|
434
|
+
console.warn(`[GoogleDrive] Could not create public permission:`, permError);
|
|
435
|
+
}
|
|
436
|
+
// Fallback to web view link
|
|
437
|
+
if (fileInfo.data.webViewLink) {
|
|
438
|
+
console.log(`[GoogleDrive] Falling back to webViewLink`);
|
|
439
|
+
return fileInfo.data.webViewLink;
|
|
440
|
+
}
|
|
441
|
+
throw new Error(`No download link available for: ${objectName}`);
|
|
324
442
|
}
|
|
325
443
|
catch (error) {
|
|
326
|
-
|
|
327
|
-
|
|
444
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
445
|
+
console.error('Error creating pre-auth download URL', { objectName, error: errorMessage });
|
|
446
|
+
throw new Error(`Failed to create download URL for: ${objectName}. ${errorMessage}`);
|
|
328
447
|
}
|
|
329
448
|
}
|
|
330
449
|
/**
|
|
@@ -370,15 +489,15 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
370
489
|
await this._drive.files.update({
|
|
371
490
|
fileId: file.id,
|
|
372
491
|
requestBody: {
|
|
373
|
-
name: newName
|
|
374
|
-
}
|
|
492
|
+
name: newName,
|
|
493
|
+
},
|
|
375
494
|
});
|
|
376
495
|
// Update parents (remove old parents and add new parent)
|
|
377
496
|
await this._drive.files.update({
|
|
378
497
|
fileId: file.id,
|
|
379
498
|
removeParents: file.parents?.join(','),
|
|
380
499
|
addParents: newParentId,
|
|
381
|
-
fields: 'id, parents'
|
|
500
|
+
fields: 'id, parents',
|
|
382
501
|
});
|
|
383
502
|
return true;
|
|
384
503
|
}
|
|
@@ -419,7 +538,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
419
538
|
}
|
|
420
539
|
// Delete the file (move to trash)
|
|
421
540
|
await this._drive.files.delete({
|
|
422
|
-
fileId: file.id
|
|
541
|
+
fileId: file.id,
|
|
423
542
|
});
|
|
424
543
|
return true;
|
|
425
544
|
}
|
|
@@ -461,8 +580,15 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
461
580
|
*/
|
|
462
581
|
async ListObjects(prefix, delimiter = '/') {
|
|
463
582
|
try {
|
|
583
|
+
console.log('[GoogleDriveFileStorage] ListObjects called with prefix:', prefix);
|
|
584
|
+
// Check if drive client is initialized
|
|
585
|
+
if (!this._drive) {
|
|
586
|
+
console.error('[GoogleDriveFileStorage] Drive client not initialized!');
|
|
587
|
+
throw new Error('Google Drive client not initialized. Call initialize() first.');
|
|
588
|
+
}
|
|
464
589
|
// Get the folder
|
|
465
590
|
const folder = await this._getItemByPath(prefix);
|
|
591
|
+
console.log('[GoogleDriveFileStorage] Got folder:', { id: folder.id, name: folder.name });
|
|
466
592
|
if (!folder.id) {
|
|
467
593
|
throw new Error(`Folder not found: ${prefix}`);
|
|
468
594
|
}
|
|
@@ -470,7 +596,11 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
470
596
|
const response = await this._drive.files.list({
|
|
471
597
|
q: `'${folder.id}' in parents and trashed = false`,
|
|
472
598
|
fields: 'files(id, name, mimeType, size, modifiedTime, parents)',
|
|
473
|
-
spaces: 'drive'
|
|
599
|
+
spaces: 'drive',
|
|
600
|
+
});
|
|
601
|
+
console.log('[GoogleDriveFileStorage] Got files response:', {
|
|
602
|
+
fileCount: response.data.files?.length || 0,
|
|
603
|
+
files: response.data.files?.map((f) => ({ name: f.name, mimeType: f.mimeType })),
|
|
474
604
|
});
|
|
475
605
|
const objects = [];
|
|
476
606
|
const prefixes = [];
|
|
@@ -480,17 +610,16 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
480
610
|
objects.push(this._fileToMetadata(file, prefix));
|
|
481
611
|
// If it's a folder, add to prefixes
|
|
482
612
|
if (file.mimeType === 'application/vnd.google-apps.folder') {
|
|
483
|
-
const folderPath = prefix
|
|
484
|
-
? (prefix.endsWith('/') ? `${prefix}${file.name}` : `${prefix}/${file.name}`)
|
|
485
|
-
: file.name;
|
|
613
|
+
const folderPath = prefix ? (prefix.endsWith('/') ? `${prefix}${file.name}` : `${prefix}/${file.name}`) : file.name;
|
|
486
614
|
prefixes.push(`${folderPath}/`);
|
|
487
615
|
}
|
|
488
616
|
}
|
|
489
617
|
}
|
|
618
|
+
console.log('[GoogleDriveFileStorage] Returning:', { objectCount: objects.length, prefixCount: prefixes.length });
|
|
490
619
|
return { objects, prefixes };
|
|
491
620
|
}
|
|
492
621
|
catch (error) {
|
|
493
|
-
console.error('Error listing objects', { prefix, error });
|
|
622
|
+
console.error('[GoogleDriveFileStorage] Error listing objects', { prefix, error });
|
|
494
623
|
return { objects: [], prefixes: [] };
|
|
495
624
|
}
|
|
496
625
|
}
|
|
@@ -520,9 +649,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
520
649
|
async CreateDirectory(directoryPath) {
|
|
521
650
|
try {
|
|
522
651
|
// Remove trailing slash if present
|
|
523
|
-
const normalizedPath = directoryPath.endsWith('/')
|
|
524
|
-
? directoryPath.substring(0, directoryPath.length - 1)
|
|
525
|
-
: directoryPath;
|
|
652
|
+
const normalizedPath = directoryPath.endsWith('/') ? directoryPath.substring(0, directoryPath.length - 1) : directoryPath;
|
|
526
653
|
// Parse path
|
|
527
654
|
const pathParts = normalizedPath.split('/');
|
|
528
655
|
const folderName = pathParts.pop() || '';
|
|
@@ -533,11 +660,11 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
533
660
|
const folderMetadata = {
|
|
534
661
|
name: folderName,
|
|
535
662
|
mimeType: 'application/vnd.google-apps.folder',
|
|
536
|
-
parents: [parentId]
|
|
663
|
+
parents: [parentId],
|
|
537
664
|
};
|
|
538
665
|
await this._drive.files.create({
|
|
539
666
|
requestBody: folderMetadata,
|
|
540
|
-
fields: 'id'
|
|
667
|
+
fields: 'id',
|
|
541
668
|
});
|
|
542
669
|
return true;
|
|
543
670
|
}
|
|
@@ -569,9 +696,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
569
696
|
async DeleteDirectory(directoryPath, recursive = false) {
|
|
570
697
|
try {
|
|
571
698
|
// Remove trailing slash if present
|
|
572
|
-
const normalizedPath = directoryPath.endsWith('/')
|
|
573
|
-
? directoryPath.substring(0, directoryPath.length - 1)
|
|
574
|
-
: directoryPath;
|
|
699
|
+
const normalizedPath = directoryPath.endsWith('/') ? directoryPath.substring(0, directoryPath.length - 1) : directoryPath;
|
|
575
700
|
// Get the folder
|
|
576
701
|
const folder = await this._getItemByPath(normalizedPath);
|
|
577
702
|
if (!folder.id) {
|
|
@@ -583,7 +708,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
583
708
|
const response = await this._drive.files.list({
|
|
584
709
|
q: `'${folder.id}' in parents and trashed = false`,
|
|
585
710
|
fields: 'files(id)',
|
|
586
|
-
spaces: 'drive'
|
|
711
|
+
spaces: 'drive',
|
|
587
712
|
});
|
|
588
713
|
if (response.data.files && response.data.files.length > 0) {
|
|
589
714
|
throw new Error('Directory is not empty');
|
|
@@ -591,7 +716,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
591
716
|
}
|
|
592
717
|
// Delete the folder
|
|
593
718
|
await this._drive.files.delete({
|
|
594
|
-
fileId: folder.id
|
|
719
|
+
fileId: folder.id,
|
|
595
720
|
});
|
|
596
721
|
return true;
|
|
597
722
|
}
|
|
@@ -641,7 +766,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
641
766
|
console.log(`⚡ Fast path: Using Object ID directly: ${params.objectId}`);
|
|
642
767
|
const response = await this._drive.files.get({
|
|
643
768
|
fileId: params.objectId,
|
|
644
|
-
fields: 'id, name, mimeType, size, modifiedTime, createdTime, parents'
|
|
769
|
+
fields: 'id, name, mimeType, size, modifiedTime, createdTime, parents',
|
|
645
770
|
});
|
|
646
771
|
file = response.data;
|
|
647
772
|
}
|
|
@@ -698,10 +823,17 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
698
823
|
throw new Error('Either objectId or fullPath must be provided');
|
|
699
824
|
}
|
|
700
825
|
let fileId;
|
|
826
|
+
let mimeType;
|
|
701
827
|
// Fast path: Use objectId if provided
|
|
702
828
|
if (params.objectId) {
|
|
703
829
|
fileId = params.objectId;
|
|
704
830
|
console.log(`⚡ Fast path: Using Object ID directly: ${fileId}`);
|
|
831
|
+
// Need to get the mimeType to check if it's a Google Workspace file
|
|
832
|
+
const fileInfo = await this._drive.files.get({
|
|
833
|
+
fileId: fileId,
|
|
834
|
+
fields: 'mimeType',
|
|
835
|
+
});
|
|
836
|
+
mimeType = fileInfo.data.mimeType || undefined;
|
|
705
837
|
}
|
|
706
838
|
else {
|
|
707
839
|
// Slow path: Resolve path to ID
|
|
@@ -711,13 +843,31 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
711
843
|
throw new Error(`File not found: ${params.fullPath}`);
|
|
712
844
|
}
|
|
713
845
|
fileId = file.id;
|
|
846
|
+
mimeType = file.mimeType || undefined;
|
|
714
847
|
}
|
|
715
|
-
//
|
|
848
|
+
// Check if this is a Google Workspace file that needs export
|
|
849
|
+
const exportInfo = mimeType ? GoogleDriveFileStorage_1.GOOGLE_WORKSPACE_EXPORT_MAP[mimeType] : undefined;
|
|
850
|
+
if (exportInfo) {
|
|
851
|
+
// Google Workspace file - use export instead of direct download
|
|
852
|
+
console.log(`[GoogleDrive] Exporting Google Workspace file (${mimeType}) as ${exportInfo.format}`);
|
|
853
|
+
const response = await this._drive.files.export({
|
|
854
|
+
fileId: fileId,
|
|
855
|
+
mimeType: exportInfo.mimeType,
|
|
856
|
+
}, {
|
|
857
|
+
responseType: 'arraybuffer',
|
|
858
|
+
});
|
|
859
|
+
return Buffer.from(response.data);
|
|
860
|
+
}
|
|
861
|
+
// Check for other Google Workspace types that can't be exported
|
|
862
|
+
if (mimeType?.startsWith('application/vnd.google-apps.')) {
|
|
863
|
+
throw new Error(`Cannot download Google Workspace file of type: ${mimeType}. This file type does not support export.`);
|
|
864
|
+
}
|
|
865
|
+
// Regular file - download directly
|
|
716
866
|
const response = await this._drive.files.get({
|
|
717
867
|
fileId: fileId,
|
|
718
|
-
alt: 'media'
|
|
868
|
+
alt: 'media',
|
|
719
869
|
}, {
|
|
720
|
-
responseType: 'arraybuffer'
|
|
870
|
+
responseType: 'arraybuffer',
|
|
721
871
|
});
|
|
722
872
|
return Buffer.from(response.data);
|
|
723
873
|
}
|
|
@@ -781,8 +931,8 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
781
931
|
fileId: existingFileId,
|
|
782
932
|
media: {
|
|
783
933
|
body: data,
|
|
784
|
-
mimeType: effectiveContentType
|
|
785
|
-
}
|
|
934
|
+
mimeType: effectiveContentType,
|
|
935
|
+
},
|
|
786
936
|
});
|
|
787
937
|
}
|
|
788
938
|
else {
|
|
@@ -790,13 +940,13 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
790
940
|
await this._drive.files.create({
|
|
791
941
|
requestBody: {
|
|
792
942
|
name: fileName,
|
|
793
|
-
parents: [parentId]
|
|
943
|
+
parents: [parentId],
|
|
794
944
|
},
|
|
795
945
|
media: {
|
|
796
946
|
body: data,
|
|
797
|
-
mimeType: effectiveContentType
|
|
947
|
+
mimeType: effectiveContentType,
|
|
798
948
|
},
|
|
799
|
-
fields: 'id'
|
|
949
|
+
fields: 'id',
|
|
800
950
|
});
|
|
801
951
|
}
|
|
802
952
|
return true;
|
|
@@ -849,8 +999,8 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
849
999
|
fileId: sourceFile.id,
|
|
850
1000
|
requestBody: {
|
|
851
1001
|
name: destFileName,
|
|
852
|
-
parents: [destParentId]
|
|
853
|
-
}
|
|
1002
|
+
parents: [destParentId],
|
|
1003
|
+
},
|
|
854
1004
|
});
|
|
855
1005
|
return true;
|
|
856
1006
|
}
|
|
@@ -918,9 +1068,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
918
1068
|
async DirectoryExists(directoryPath) {
|
|
919
1069
|
try {
|
|
920
1070
|
// Remove trailing slash if present
|
|
921
|
-
const normalizedPath = directoryPath.endsWith('/')
|
|
922
|
-
? directoryPath.substring(0, directoryPath.length - 1)
|
|
923
|
-
: directoryPath;
|
|
1071
|
+
const normalizedPath = directoryPath.endsWith('/') ? directoryPath.substring(0, directoryPath.length - 1) : directoryPath;
|
|
924
1072
|
const item = await this._getItemByPath(normalizedPath);
|
|
925
1073
|
return item.mimeType === 'application/vnd.google-apps.folder';
|
|
926
1074
|
}
|
|
@@ -976,11 +1124,11 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
976
1124
|
}
|
|
977
1125
|
// Add file type filter
|
|
978
1126
|
if (options?.fileTypes && options.fileTypes.length > 0) {
|
|
979
|
-
const mimeTypes = options.fileTypes.map(ft => {
|
|
1127
|
+
const mimeTypes = options.fileTypes.map((ft) => {
|
|
980
1128
|
// Convert extensions to MIME types if needed
|
|
981
1129
|
return ft.includes('/') ? ft : mime.lookup(ft) || ft;
|
|
982
1130
|
});
|
|
983
|
-
const mimeQuery = mimeTypes.map(mt => `mimeType='${mt}'`).join(' or ');
|
|
1131
|
+
const mimeQuery = mimeTypes.map((mt) => `mimeType='${mt}'`).join(' or ');
|
|
984
1132
|
queryParts.push(`(${mimeQuery})`);
|
|
985
1133
|
}
|
|
986
1134
|
// Add date filters
|
|
@@ -1024,7 +1172,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
1024
1172
|
q: finalQuery,
|
|
1025
1173
|
pageSize: maxResults,
|
|
1026
1174
|
fields: 'nextPageToken, files(id, name, mimeType, size, modifiedTime, parents, properties)',
|
|
1027
|
-
orderBy: 'modifiedTime desc'
|
|
1175
|
+
orderBy: 'modifiedTime desc',
|
|
1028
1176
|
});
|
|
1029
1177
|
const files = response.data.files || [];
|
|
1030
1178
|
const results = [];
|
|
@@ -1042,14 +1190,14 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
1042
1190
|
objectId: file.id || '', // Google Drive file ID for direct access
|
|
1043
1191
|
matchInFilename: file.name.toLowerCase().includes(query.toLowerCase()),
|
|
1044
1192
|
customMetadata: file.properties,
|
|
1045
|
-
providerData: { driveFileId: file.id }
|
|
1193
|
+
providerData: { driveFileId: file.id },
|
|
1046
1194
|
});
|
|
1047
1195
|
}
|
|
1048
1196
|
return {
|
|
1049
1197
|
results,
|
|
1050
1198
|
totalMatches: undefined, // Drive API doesn't provide total count
|
|
1051
1199
|
hasMore: !!response.data.nextPageToken,
|
|
1052
|
-
nextPageToken: response.data.nextPageToken || undefined
|
|
1200
|
+
nextPageToken: response.data.nextPageToken || undefined,
|
|
1053
1201
|
};
|
|
1054
1202
|
}
|
|
1055
1203
|
catch (error) {
|
|
@@ -1074,7 +1222,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
1074
1222
|
while (currentId && currentId !== 'root' && currentId !== this._rootFolderId) {
|
|
1075
1223
|
const file = await this._drive.files.get({
|
|
1076
1224
|
fileId: currentId,
|
|
1077
|
-
fields: 'id, name, parents'
|
|
1225
|
+
fields: 'id, name, parents',
|
|
1078
1226
|
});
|
|
1079
1227
|
if (file.data.name) {
|
|
1080
1228
|
pathParts.unshift(file.data.name);
|
|
@@ -1090,7 +1238,7 @@ let GoogleDriveFileStorage = class GoogleDriveFileStorage extends FileStorageBas
|
|
|
1090
1238
|
}
|
|
1091
1239
|
};
|
|
1092
1240
|
exports.GoogleDriveFileStorage = GoogleDriveFileStorage;
|
|
1093
|
-
exports.GoogleDriveFileStorage = GoogleDriveFileStorage = __decorate([
|
|
1241
|
+
exports.GoogleDriveFileStorage = GoogleDriveFileStorage = GoogleDriveFileStorage_1 = __decorate([
|
|
1094
1242
|
(0, global_1.RegisterClass)(FileStorageBase_1.FileStorageBase, 'Google Drive Storage'),
|
|
1095
1243
|
__metadata("design:paramtypes", [])
|
|
1096
1244
|
], GoogleDriveFileStorage);
|