@cooperation/vc-storage 1.0.33 → 1.0.40
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 +1 -1
- package/app.config.js +1 -0
- package/dist/index.js +3 -1
- package/dist/models/CredentialEngine.js +35 -24
- package/dist/models/GoogleDriveStorage.js +121 -273
- package/dist/models/Resume.js +16 -4
- package/dist/models/WASStorage.js +71 -0
- package/dist/scripts/test-was.js +61 -0
- package/dist/types/index.d.ts +3 -1
- package/dist/types/models/CredentialEngine.d.ts +7 -4
- package/dist/types/models/GoogleDriveStorage.d.ts +31 -55
- package/dist/types/models/Resume.d.ts +3 -2
- package/dist/types/models/WASStorage.d.ts +30 -0
- package/dist/types/scripts/test-was.d.ts +1 -0
- package/dist/types/utils/context.d.ts +73 -0
- package/dist/types/utils/createWASSpace.d.ts +9 -0
- package/dist/types/utils/credential.d.ts +171 -1
- package/dist/types/utils/getOrCreateAppDID.d.ts +11 -0
- package/dist/types/utils/google.d.ts +1 -1
- package/dist/utils/context.js +76 -0
- package/dist/utils/createWASSpace.js +27 -0
- package/dist/utils/credential.js +96 -0
- package/dist/utils/getOrCreateAppDID.js +22 -0
- package/dist/utils/google.js +1 -1
- package/package.json +6 -3
- package/dist/types/utils/presentation.d.ts +0 -10
- package/dist/utils/presentation.js +0 -45
@@ -2,21 +2,25 @@
|
|
2
2
|
* @class GoogleDriveStorage
|
3
3
|
* @description Class to interact with Google Drive API
|
4
4
|
* @param accessToken - Access token to authenticate with Google Drive API
|
5
|
-
* @method
|
6
|
-
* @method save - Save data to Google Drive
|
7
|
-
* @method addCommentToFile - Add a comment to a file in Google Drive
|
8
|
-
* @method addCommenterRoleToFile - Add commenter role to a file in Google Drive
|
5
|
+
* @method saveFile - Save a file to Google Drive
|
9
6
|
* @method retrieve - Retrieve a file from Google Drive
|
10
|
-
* @method
|
11
|
-
* @method
|
12
|
-
* @method
|
13
|
-
* @method
|
7
|
+
* @method createFolder - Create a new folder in Google Drive
|
8
|
+
* @method getOrCreateMediaFolder - Get the ID of the MEDIAs folder
|
9
|
+
* @method uploadBinaryFile - Upload a binary file to Google Drive
|
10
|
+
* @method updateFileData - Update the data of a file
|
11
|
+
* @method updateRelationsFile - Update the relations file
|
14
12
|
* @method delete - Delete a file from Google Drive
|
13
|
+
* @method checkEmailExists - Check if an email VC exists and return its content
|
14
|
+
* @method findFolders - Find folders in Google Drive
|
15
|
+
* @method findFolderFiles - Find files in a folder
|
15
16
|
*/
|
16
17
|
export class GoogleDriveStorage {
|
17
18
|
accessToken;
|
18
|
-
folderCache = {};
|
19
|
-
fileIdsCache = null;
|
19
|
+
static folderCache = {};
|
20
|
+
static fileIdsCache = null;
|
21
|
+
constructor(accessToken) {
|
22
|
+
this.accessToken = accessToken;
|
23
|
+
}
|
20
24
|
async updateFileIdsJson(newFileId) {
|
21
25
|
const constructUrl = () => {
|
22
26
|
const baseUrl = 'https://www.googleapis.com/drive/v3/files';
|
@@ -29,28 +33,28 @@ export class GoogleDriveStorage {
|
|
29
33
|
};
|
30
34
|
try {
|
31
35
|
// ✅ Fetch `file_ids.json` ID once per session (cached)
|
32
|
-
if (!
|
36
|
+
if (!GoogleDriveStorage.fileIdsCache) {
|
33
37
|
const existingFile = await this.fetcher({
|
34
38
|
method: 'GET',
|
35
39
|
headers: {},
|
36
40
|
url: constructUrl(),
|
37
41
|
});
|
38
42
|
if (existingFile.files.length > 0) {
|
39
|
-
|
43
|
+
GoogleDriveStorage.fileIdsCache = existingFile.files[0].id;
|
40
44
|
}
|
41
45
|
else {
|
42
46
|
console.log('No existing file_ids.json found, creating a new one.');
|
43
|
-
|
47
|
+
GoogleDriveStorage.fileIdsCache = null;
|
44
48
|
}
|
45
49
|
}
|
46
50
|
let existingFileIds = [];
|
47
51
|
// ✅ Fetch existing file IDs **only if `file_ids.json` exists**
|
48
|
-
if (
|
52
|
+
if (GoogleDriveStorage.fileIdsCache) {
|
49
53
|
try {
|
50
54
|
const fileContent = await this.fetcher({
|
51
55
|
method: 'GET',
|
52
56
|
headers: {},
|
53
|
-
url: `https://www.googleapis.com/drive/v3/files/${
|
57
|
+
url: `https://www.googleapis.com/drive/v3/files/${GoogleDriveStorage.fileIdsCache}?alt=media`,
|
54
58
|
});
|
55
59
|
existingFileIds = fileContent;
|
56
60
|
}
|
@@ -60,16 +64,13 @@ export class GoogleDriveStorage {
|
|
60
64
|
}
|
61
65
|
// ✅ Append the new file ID to the list
|
62
66
|
existingFileIds.push(newFileId);
|
63
|
-
console.log('File ID saved to appDataFolder.',
|
67
|
+
console.log('File ID saved to appDataFolder.', GoogleDriveStorage.fileIdsCache);
|
64
68
|
}
|
65
69
|
catch (error) {
|
66
70
|
console.error('Error updating file_ids.json:', error.message);
|
67
71
|
throw error;
|
68
72
|
}
|
69
73
|
}
|
70
|
-
constructor(accessToken) {
|
71
|
-
this.accessToken = accessToken;
|
72
|
-
}
|
73
74
|
async fetcher({ method, headers, body, url }) {
|
74
75
|
try {
|
75
76
|
const res = await fetch(url, {
|
@@ -120,6 +121,43 @@ export class GoogleDriveStorage {
|
|
120
121
|
});
|
121
122
|
return result.files || [];
|
122
123
|
}
|
124
|
+
async getOrCreateMediaFolder() {
|
125
|
+
if (GoogleDriveStorage.folderCache['MEDIAs']) {
|
126
|
+
return GoogleDriveStorage.folderCache['MEDIAs'];
|
127
|
+
}
|
128
|
+
const rootFolders = await this.findFolders();
|
129
|
+
let credentialsFolder = rootFolders.find((f) => f.name === 'Credentials');
|
130
|
+
if (!credentialsFolder) {
|
131
|
+
credentialsFolder = await this.createFolder({ folderName: 'Credentials', parentFolderId: 'root' });
|
132
|
+
}
|
133
|
+
const subfolders = await this.findFolders(credentialsFolder.id);
|
134
|
+
let mediasFolder = subfolders.find((f) => f.name === 'MEDIAs');
|
135
|
+
if (!mediasFolder) {
|
136
|
+
mediasFolder = await this.createFolder({ folderName: 'MEDIAs', parentFolderId: credentialsFolder.id });
|
137
|
+
}
|
138
|
+
GoogleDriveStorage.folderCache['MEDIAs'] = mediasFolder.id;
|
139
|
+
return mediasFolder.id;
|
140
|
+
}
|
141
|
+
async findFolderFiles(folderId) {
|
142
|
+
if (!folderId)
|
143
|
+
throw new Error('Folder ID is required');
|
144
|
+
const files = await this.searchFiles(`'${folderId}' in parents`);
|
145
|
+
if (files.length === 0) {
|
146
|
+
console.log('No files found in the folder.');
|
147
|
+
return [];
|
148
|
+
}
|
149
|
+
const filesWithContent = await Promise.all(files.map(async (file) => {
|
150
|
+
try {
|
151
|
+
const content = await this.getFileContent(file.id);
|
152
|
+
return { ...file, content };
|
153
|
+
}
|
154
|
+
catch (error) {
|
155
|
+
console.error(`Error fetching content for file "${file.name}" (ID: ${file.id}):`, error);
|
156
|
+
return { ...file, content: null };
|
157
|
+
}
|
158
|
+
}));
|
159
|
+
return filesWithContent;
|
160
|
+
}
|
123
161
|
async createFolder({ folderName, parentFolderId }) {
|
124
162
|
if (!parentFolderId) {
|
125
163
|
throw new Error(`Parent folder ID must be provided when creating folder "${folderName}".`);
|
@@ -143,42 +181,29 @@ export class GoogleDriveStorage {
|
|
143
181
|
body: JSON.stringify({ role: 'reader', type: 'anyone' }),
|
144
182
|
});
|
145
183
|
// Invalidate cache for this parent folder
|
146
|
-
if (
|
147
|
-
delete
|
184
|
+
if (GoogleDriveStorage.folderCache[parentFolderId]) {
|
185
|
+
delete GoogleDriveStorage.folderCache[parentFolderId];
|
148
186
|
}
|
149
187
|
// Also clear 'root' cache if parent is root
|
150
|
-
if (parentFolderId === 'root' &&
|
151
|
-
delete
|
188
|
+
if (parentFolderId === 'root' && GoogleDriveStorage.folderCache['root']) {
|
189
|
+
delete GoogleDriveStorage.folderCache['root'];
|
152
190
|
}
|
153
191
|
return folder;
|
154
192
|
}
|
193
|
+
/**
|
194
|
+
* Get the ID of the MEDIAs folder (public wrapper for getOrCreateMediaFolder)
|
195
|
+
* @returns The folder ID for the MEDIAs folder
|
196
|
+
*/
|
155
197
|
async getMediaFolderId() {
|
156
|
-
|
157
|
-
return this.folderCache['MEDIAs'];
|
158
|
-
}
|
159
|
-
const rootFolders = await this.findFolders();
|
160
|
-
let credentialsFolder = rootFolders.find((f) => f.name === 'Credentials');
|
161
|
-
if (!credentialsFolder) {
|
162
|
-
credentialsFolder = await this.createFolder({ folderName: 'Credentials', parentFolderId: 'root' });
|
163
|
-
}
|
164
|
-
const credentialsFolderId = credentialsFolder.id;
|
165
|
-
const subfolders = await this.findFolders(credentialsFolder.id);
|
166
|
-
let mediasFolder = subfolders.find((f) => f.name === 'MEDIAs');
|
167
|
-
if (!mediasFolder) {
|
168
|
-
mediasFolder = await this.createFolder({ folderName: 'MEDIAs', parentFolderId: credentialsFolderId });
|
169
|
-
}
|
170
|
-
const mediasFolderId = mediasFolder.id;
|
171
|
-
this.folderCache['MEDIAs'] = mediasFolderId;
|
172
|
-
return mediasFolderId;
|
198
|
+
return await this.getOrCreateMediaFolder();
|
173
199
|
}
|
174
200
|
async uploadBinaryFile({ file }) {
|
175
201
|
try {
|
176
|
-
const accessToken = this.accessToken;
|
202
|
+
const accessToken = this.accessToken;
|
177
203
|
if (!accessToken) {
|
178
204
|
throw new Error('Missing Google OAuth access token.');
|
179
205
|
}
|
180
|
-
const folderId = await this.
|
181
|
-
// ✅ Correct metadata for Google Drive API
|
206
|
+
const folderId = await this.getOrCreateMediaFolder();
|
182
207
|
const metadata = {
|
183
208
|
name: file.name,
|
184
209
|
mimeType: file.type,
|
@@ -209,14 +234,20 @@ export class GoogleDriveStorage {
|
|
209
234
|
throw error;
|
210
235
|
}
|
211
236
|
}
|
212
|
-
async saveFile({ data, folderId }) {
|
237
|
+
async saveFile({ data, folderId, fileId }) {
|
213
238
|
console.log('🚀 ~ GoogleDriveStorage ~ saveFile ~ data:', data);
|
214
239
|
try {
|
240
|
+
// If fileId is provided, update the existing file instead of creating a new one
|
241
|
+
if (fileId) {
|
242
|
+
console.log(`Updating existing file with ID: ${fileId}`);
|
243
|
+
return await this.updateFileContent({ fileId, data });
|
244
|
+
}
|
245
|
+
// For new files, folderId is required
|
215
246
|
if (!folderId) {
|
216
|
-
throw new Error('Folder ID is required to save a file.');
|
247
|
+
throw new Error('Folder ID is required to save a new file.');
|
217
248
|
}
|
218
249
|
const fileMetadata = {
|
219
|
-
name: data.fileName || data.name + '.json' || data.credentialSubject
|
250
|
+
name: data.fileName || data.name + '.json' || data.credentialSubject?.person?.name?.formattedName + '.json' || 'Untitled file.json',
|
220
251
|
parents: [folderId],
|
221
252
|
mimeType: data.mimeType || 'application/json',
|
222
253
|
};
|
@@ -248,11 +279,6 @@ export class GoogleDriveStorage {
|
|
248
279
|
throw error;
|
249
280
|
}
|
250
281
|
}
|
251
|
-
/**
|
252
|
-
* Get file from google drive by id
|
253
|
-
* @param id
|
254
|
-
* @returns file content
|
255
|
-
*/
|
256
282
|
async retrieve(id) {
|
257
283
|
const dataUrl = `https://www.googleapis.com/drive/v3/files/${id}?alt=media`;
|
258
284
|
try {
|
@@ -293,63 +319,53 @@ export class GoogleDriveStorage {
|
|
293
319
|
return null;
|
294
320
|
}
|
295
321
|
}
|
296
|
-
/**
|
297
|
-
* Get folder by folderId, if folderId == null you will have them all
|
298
|
-
* @param folderId [Optional]
|
299
|
-
* @returns
|
300
|
-
*/
|
301
322
|
async findFolders(folderId) {
|
302
323
|
const cacheKey = folderId || 'root';
|
303
|
-
if (
|
304
|
-
return
|
324
|
+
if (GoogleDriveStorage.folderCache[cacheKey]) {
|
325
|
+
return GoogleDriveStorage.folderCache[cacheKey];
|
305
326
|
}
|
306
327
|
const query = folderId
|
307
328
|
? `'${folderId}' in parents and mimeType='application/vnd.google-apps.folder'`
|
308
329
|
: `'root' in parents and mimeType='application/vnd.google-apps.folder'`;
|
309
330
|
const folders = await this.searchFiles(query);
|
310
|
-
|
331
|
+
GoogleDriveStorage.folderCache[cacheKey] = folders;
|
311
332
|
return folders;
|
312
333
|
}
|
313
|
-
/**
|
314
|
-
* Get all files content for the specified type ('KEYPAIRs' | 'VCs' | 'SESSIONs' | 'DIDs' | 'RECOMMENDATIONs')
|
315
|
-
* @param type
|
316
|
-
* @returns
|
317
|
-
*/
|
318
334
|
async getAllFilesByType(type) {
|
319
335
|
try {
|
320
|
-
if (!
|
336
|
+
if (!GoogleDriveStorage.folderCache['Credentials']) {
|
321
337
|
const rootFolders = await this.findFolders();
|
322
|
-
|
338
|
+
GoogleDriveStorage.folderCache['Credentials'] = rootFolders;
|
323
339
|
}
|
324
|
-
const credentialsFolder =
|
340
|
+
const credentialsFolder = GoogleDriveStorage.folderCache['Credentials'].find((f) => f.name === 'Credentials');
|
325
341
|
if (!credentialsFolder) {
|
326
342
|
console.error('Credentials folder not found.');
|
327
343
|
return [];
|
328
344
|
}
|
329
345
|
if (type === 'VCs') {
|
330
|
-
if (!
|
346
|
+
if (!GoogleDriveStorage.folderCache['VCs']) {
|
331
347
|
const vcSubfolder = await this.findFolders(credentialsFolder.id);
|
332
348
|
const vcsFolder = vcSubfolder.find((f) => f.name === 'VCs');
|
333
349
|
const vcSubFolders = await this.findFolders(vcsFolder.id);
|
334
|
-
|
350
|
+
GoogleDriveStorage.folderCache['VCs'] = vcSubFolders.filter((folder) => folder.name.startsWith('VC-'));
|
335
351
|
}
|
336
|
-
const vcSubfolders =
|
352
|
+
const vcSubfolders = GoogleDriveStorage.folderCache['VCs'];
|
337
353
|
if (!vcSubfolders.length) {
|
338
354
|
console.error(`No subfolders found for type: ${type}`);
|
339
355
|
return [];
|
340
356
|
}
|
341
|
-
const allFilesNested = await Promise.all(vcSubfolders.map(async (folder) => await this.
|
357
|
+
const allFilesNested = await Promise.all(vcSubfolders.map(async (folder) => await this.findFolderFiles(folder.id)));
|
342
358
|
const allVcJsonFiles = allFilesNested.flat().filter((file) => file.mimeType === 'application/json');
|
343
359
|
const fileContentsResults = await Promise.allSettled(allVcJsonFiles.map((file) => this.retrieve(file.id)));
|
344
360
|
const validFileContents = fileContentsResults.filter((result) => result.status === 'fulfilled').map((result) => result.value);
|
345
361
|
return validFileContents.filter((file) => file.data.fileName !== 'RELATIONS');
|
346
362
|
}
|
347
|
-
if (!
|
363
|
+
if (!GoogleDriveStorage.folderCache[type]) {
|
348
364
|
const subfolders = await this.findFolders(credentialsFolder.id);
|
349
365
|
const targetFolder = subfolders.find((f) => f.name === type);
|
350
|
-
|
366
|
+
GoogleDriveStorage.folderCache[type] = targetFolder ? targetFolder.id : null;
|
351
367
|
}
|
352
|
-
const targetFolderId =
|
368
|
+
const targetFolderId = GoogleDriveStorage.folderCache[type];
|
353
369
|
if (!targetFolderId) {
|
354
370
|
console.error(`Folder for type ${type} not found.`);
|
355
371
|
return [];
|
@@ -369,70 +385,52 @@ export class GoogleDriveStorage {
|
|
369
385
|
return [];
|
370
386
|
}
|
371
387
|
}
|
372
|
-
|
373
|
-
* Update the name of a file in Google Drive
|
374
|
-
* @param fileId - The ID of the file to update
|
375
|
-
* @param newFileName - The new name for the file
|
376
|
-
* @returns The updated file metadata, including the new name
|
377
|
-
*/
|
378
|
-
async updateFileName(fileId, newFileName) {
|
388
|
+
async updateFileData(fileId, data) {
|
379
389
|
try {
|
380
|
-
const
|
390
|
+
const updateUrl = `https://www.googleapis.com/drive/v3/files/${fileId}`;
|
381
391
|
const updatedFile = await this.fetcher({
|
382
392
|
method: 'PATCH',
|
383
393
|
headers: { 'Content-Type': 'application/json' },
|
384
|
-
body: JSON.stringify(
|
385
|
-
url:
|
394
|
+
body: JSON.stringify({ name: data.fileName }),
|
395
|
+
url: updateUrl,
|
386
396
|
});
|
387
|
-
console.log('File
|
397
|
+
console.log('✅ File renamed successfully:', updatedFile);
|
388
398
|
return updatedFile;
|
389
399
|
}
|
390
400
|
catch (error) {
|
391
|
-
console.error('Error updating file name:', error.message);
|
392
401
|
throw error;
|
393
402
|
}
|
394
403
|
}
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
}
|
403
|
-
async findFilesUnderFolder(folderId) {
|
404
|
-
if (!folderId)
|
405
|
-
throw new Error('Folder ID is required');
|
406
|
-
const files = await this.searchFiles(`'${folderId}' in parents`);
|
407
|
-
if (files.length === 0) {
|
408
|
-
console.log('No files found in the folder.');
|
409
|
-
return [];
|
410
|
-
}
|
411
|
-
const filesWithContent = await Promise.all(files.map(async (file) => {
|
412
|
-
try {
|
413
|
-
const content = await this.getFileContent(file.id);
|
414
|
-
return { ...file, content };
|
415
|
-
}
|
416
|
-
catch (error) {
|
417
|
-
console.error(`Error fetching content for file "${file.name}" (ID: ${file.id}):`, error);
|
418
|
-
return { ...file, content: null };
|
419
|
-
}
|
420
|
-
}));
|
421
|
-
return filesWithContent;
|
422
|
-
}
|
423
|
-
async updateFileData(fileId, data) {
|
404
|
+
/**
|
405
|
+
* Update the content of an existing file in Google Drive
|
406
|
+
* @param fileId - The ID of the file to update
|
407
|
+
* @param data - The new content for the file
|
408
|
+
* @returns The updated file metadata
|
409
|
+
*/
|
410
|
+
async updateFileContent({ fileId, data }) {
|
424
411
|
try {
|
425
|
-
|
426
|
-
|
412
|
+
if (!fileId) {
|
413
|
+
throw new Error('File ID is required to update a file.');
|
414
|
+
}
|
415
|
+
// Convert data to JSON blob
|
416
|
+
const fileBlob = new Blob([JSON.stringify(data)], { type: 'application/json' });
|
417
|
+
// Update the file content using media upload
|
418
|
+
// Note: When using uploadType=media, the response is the updated file resource
|
419
|
+
const response = await this.fetcher({
|
427
420
|
method: 'PATCH',
|
428
|
-
headers: {
|
429
|
-
|
430
|
-
|
421
|
+
headers: {
|
422
|
+
'Content-Type': 'application/json'
|
423
|
+
},
|
424
|
+
body: fileBlob,
|
425
|
+
url: `https://www.googleapis.com/upload/drive/v3/files/${fileId}?uploadType=media`,
|
431
426
|
});
|
432
|
-
console.log(
|
433
|
-
|
427
|
+
console.log(`✅ File content updated successfully: ${fileId}`);
|
428
|
+
// Return a consistent response format with id and data
|
429
|
+
// The response from media upload might be the file metadata or empty
|
430
|
+
return { id: fileId, data, ...response };
|
434
431
|
}
|
435
432
|
catch (error) {
|
433
|
+
console.error('Error updating file content:', error);
|
436
434
|
throw error;
|
437
435
|
}
|
438
436
|
}
|
@@ -460,12 +458,10 @@ export class GoogleDriveStorage {
|
|
460
458
|
return updateResponse;
|
461
459
|
}
|
462
460
|
async createRelationsFile({ vcFolderId }) {
|
463
|
-
const files = await this.
|
461
|
+
const files = await this.findFolderFiles(vcFolderId);
|
464
462
|
const vcFile = files.find((file) => file.name === 'VC');
|
465
463
|
const vcContent = await this.getFileContent(vcFile.id);
|
466
|
-
console.log('🚀 ~ GoogleDriveStorage ~ createRelationsFile ~ vcContent:', vcContent);
|
467
464
|
const subject = JSON.parse(vcContent.body).credentialSubject;
|
468
|
-
console.log('🚀 ~ GoogleDriveStorage ~ createRelationsFile ~ subject:', subject);
|
469
465
|
const relationsFile = await this.saveFile({
|
470
466
|
data: {
|
471
467
|
fileName: 'RELATIONS',
|
@@ -483,66 +479,6 @@ export class GoogleDriveStorage {
|
|
483
479
|
await this.updateFileIdsJson(relationsFile.id);
|
484
480
|
return relationsFile;
|
485
481
|
}
|
486
|
-
async updateResumeRelation({ authorFolderId, draftFileId, signedFileId, }) {
|
487
|
-
try {
|
488
|
-
const relationFileName = 'relations.json';
|
489
|
-
// Step 1: Check if `relations.json` exists in RESUMES_AUTHOR/
|
490
|
-
const existingRelationFiles = await this.searchFiles(`name='${relationFileName}' and '${authorFolderId}' in parents`);
|
491
|
-
let relationFileId = null;
|
492
|
-
let existingRelations = {};
|
493
|
-
if (existingRelationFiles.length > 0) {
|
494
|
-
relationFileId = existingRelationFiles[0].id;
|
495
|
-
existingRelations = await this.getFileContent(relationFileId);
|
496
|
-
}
|
497
|
-
else {
|
498
|
-
console.log('relations.json does not exist. Will create a new one.');
|
499
|
-
}
|
500
|
-
// Step 2: Update relations object
|
501
|
-
existingRelations[draftFileId] = signedFileId;
|
502
|
-
// Step 3: Create or update the file on Drive
|
503
|
-
const fileBlob = new Blob([JSON.stringify(existingRelations, null, 2)], {
|
504
|
-
type: 'application/json',
|
505
|
-
});
|
506
|
-
const formData = new FormData();
|
507
|
-
const metadata = {
|
508
|
-
name: relationFileName,
|
509
|
-
parents: [authorFolderId],
|
510
|
-
mimeType: 'application/json',
|
511
|
-
};
|
512
|
-
formData.append('metadata', new Blob([JSON.stringify(metadata)], { type: 'application/json' }));
|
513
|
-
formData.append('file', fileBlob);
|
514
|
-
let uploadedFile;
|
515
|
-
if (relationFileId) {
|
516
|
-
// Update existing file
|
517
|
-
uploadedFile = await this.fetcher({
|
518
|
-
method: 'PATCH',
|
519
|
-
headers: {},
|
520
|
-
body: formData,
|
521
|
-
url: `https://www.googleapis.com/upload/drive/v3/files/${relationFileId}?uploadType=multipart&fields=id,parents`,
|
522
|
-
});
|
523
|
-
}
|
524
|
-
else {
|
525
|
-
// Create new file
|
526
|
-
uploadedFile = await this.fetcher({
|
527
|
-
method: 'POST',
|
528
|
-
headers: {},
|
529
|
-
body: formData,
|
530
|
-
url: `https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id,parents`,
|
531
|
-
});
|
532
|
-
}
|
533
|
-
console.log(`✅ Resume relation updated. File ID: ${uploadedFile.id}`);
|
534
|
-
return uploadedFile;
|
535
|
-
}
|
536
|
-
catch (error) {
|
537
|
-
console.error('❌ Failed to update resume relation:', error);
|
538
|
-
throw error;
|
539
|
-
}
|
540
|
-
}
|
541
|
-
/**
|
542
|
-
* Delete file by id
|
543
|
-
* @param id
|
544
|
-
* @returns
|
545
|
-
*/
|
546
482
|
async delete(id) {
|
547
483
|
try {
|
548
484
|
const response = await this.fetcher({
|
@@ -558,94 +494,6 @@ export class GoogleDriveStorage {
|
|
558
494
|
return null;
|
559
495
|
}
|
560
496
|
}
|
561
|
-
async update(fileId, data) {
|
562
|
-
const metadata = {
|
563
|
-
name: data.fileName || 'resume.json',
|
564
|
-
mimeType: 'application/json',
|
565
|
-
};
|
566
|
-
const uploadUrl = `https://www.googleapis.com/upload/drive/v3/files/${fileId}?uploadType=multipart`;
|
567
|
-
const formData = new FormData();
|
568
|
-
formData.append('metadata', new Blob([JSON.stringify(metadata)], { type: 'application/json' }));
|
569
|
-
formData.append('file', new Blob([JSON.stringify(data.body)], { type: 'application/json' }));
|
570
|
-
try {
|
571
|
-
const response = await this.fetcher({
|
572
|
-
method: 'PATCH',
|
573
|
-
headers: {},
|
574
|
-
body: formData,
|
575
|
-
url: `${uploadUrl}&fields=id,name,mimeType`,
|
576
|
-
});
|
577
|
-
console.log('✅ File updated successfully:', response);
|
578
|
-
return response;
|
579
|
-
}
|
580
|
-
catch (error) {
|
581
|
-
console.error('❌ Error updating Google Drive file:', error);
|
582
|
-
throw error;
|
583
|
-
}
|
584
|
-
}
|
585
|
-
async getFileIdsFromAppDataFolder() {
|
586
|
-
try {
|
587
|
-
const constructUrl = () => {
|
588
|
-
const baseUrl = 'https://www.googleapis.com/drive/v3/files';
|
589
|
-
const queryParams = new URLSearchParams({
|
590
|
-
spaces: 'appDataFolder',
|
591
|
-
q: "name='file_ids.json'",
|
592
|
-
fields: 'files(id)',
|
593
|
-
});
|
594
|
-
return `${baseUrl}?${queryParams.toString()}`;
|
595
|
-
};
|
596
|
-
// Step 1: Search for the file_ids.json file in the appDataFolder
|
597
|
-
const response = await this.fetcher({
|
598
|
-
method: 'GET',
|
599
|
-
headers: {},
|
600
|
-
url: constructUrl(),
|
601
|
-
});
|
602
|
-
console.log(': GoogleDriveStorage getFileIdsFromAppDataFolder response', response);
|
603
|
-
// Step 2: Check if the file exists
|
604
|
-
if (!response.files || response.files.length === 0) {
|
605
|
-
console.log('No file_ids.json found in appDataFolder.');
|
606
|
-
return [];
|
607
|
-
}
|
608
|
-
// Step 3: Get the file ID of file_ids.json
|
609
|
-
const fileId = response.files[0].id;
|
610
|
-
console.log(': GoogleDriveStorage getFileIdsFromAppDataFolder fileId', fileId);
|
611
|
-
// Step 4: Fetch the content of file_ids.json
|
612
|
-
const fileContent = await this.fetcher({
|
613
|
-
method: 'GET',
|
614
|
-
headers: {},
|
615
|
-
url: `https://www.googleapis.com/drive/v3/files/${fileId}?alt=media`,
|
616
|
-
});
|
617
|
-
console.log(': GoogleDriveStorage getFileIdsFromAppDataFolder fileContent', fileContent);
|
618
|
-
// Step 5: Parse the file content (array of file IDs)
|
619
|
-
const fileIds = fileContent;
|
620
|
-
console.log(': GoogleDriveStorage getFileIdsFromAppDataFolder fileIds', fileIds);
|
621
|
-
return fileIds;
|
622
|
-
}
|
623
|
-
catch (error) {
|
624
|
-
console.error('Error fetching file IDs from appDataFolder:', error.message);
|
625
|
-
return [];
|
626
|
-
}
|
627
|
-
}
|
628
|
-
async getAllFilesData() {
|
629
|
-
try {
|
630
|
-
// Step 1: Get the file IDs from appDataFolder
|
631
|
-
const fileIds = await this.getFileIdsFromAppDataFolder();
|
632
|
-
if (fileIds.length === 0) {
|
633
|
-
console.log('No files found.');
|
634
|
-
return [];
|
635
|
-
}
|
636
|
-
// Step 2: Return the array of file IDs
|
637
|
-
return fileIds;
|
638
|
-
}
|
639
|
-
catch (error) {
|
640
|
-
console.error('Error fetching all files data:', error.message);
|
641
|
-
return [];
|
642
|
-
}
|
643
|
-
}
|
644
|
-
/**
|
645
|
-
* Check if an email VC exists and return its content
|
646
|
-
* @param email - The email address to check
|
647
|
-
* @returns {Promise<{data: any, id: string} | null>} - The email VC content and ID if exists, null otherwise
|
648
|
-
*/
|
649
497
|
async checkEmailExists(email) {
|
650
498
|
try {
|
651
499
|
// Get root folders
|
package/dist/models/Resume.js
CHANGED
@@ -61,15 +61,27 @@ export class StorageHandler {
|
|
61
61
|
if (!folder) {
|
62
62
|
throw new Error(`${folderName} folder not found`);
|
63
63
|
}
|
64
|
-
return this.storage.
|
64
|
+
return this.storage.findFolderFiles(folder.id);
|
65
65
|
}
|
66
66
|
}
|
67
67
|
export class Resume extends StorageHandler {
|
68
68
|
constructor(storage) {
|
69
69
|
super(storage);
|
70
70
|
}
|
71
|
-
async saveResume({ resume, type }) {
|
71
|
+
async saveResume({ resume, type, id }) {
|
72
72
|
try {
|
73
|
+
// If an ID is provided, update the existing resume
|
74
|
+
if (id) {
|
75
|
+
console.log(`🔄 Updating existing ${type} resume with ID: ${id}`);
|
76
|
+
// Update the file content directly
|
77
|
+
const updatedResume = await this.storage.updateFileContent({
|
78
|
+
fileId: id,
|
79
|
+
data: resume
|
80
|
+
});
|
81
|
+
// Return the updated resume with its ID
|
82
|
+
return { ...updatedResume, id };
|
83
|
+
}
|
84
|
+
// For new resumes, create folders and save as before
|
73
85
|
let rootFolder = await this.getOrCreateFolder(resumeFolderTypes.root, 'root');
|
74
86
|
console.log('🚀 ~ Resume ~ saveResume ~ rootFolder:', rootFolder);
|
75
87
|
// Get or create the subfolder
|
@@ -106,7 +118,7 @@ export class Resume extends StorageHandler {
|
|
106
118
|
// Find or create the signed resumes folder
|
107
119
|
const signedFolder = await this.getOrCreateFolder(resumeFolderTypes.signed, rootFolder.id);
|
108
120
|
// Retrieve all files from the signed folder
|
109
|
-
const files = await this.storage.
|
121
|
+
const files = await this.storage.findFolderFiles(signedFolder.id);
|
110
122
|
return files;
|
111
123
|
}
|
112
124
|
catch (error) {
|
@@ -120,7 +132,7 @@ export class Resume extends StorageHandler {
|
|
120
132
|
// Find or create the non-signed resumes folder
|
121
133
|
const nonSignedFolder = await this.getOrCreateFolder(resumeFolderTypes.nonSigned, rootFolder.id);
|
122
134
|
// Retrieve all files from the non-signed folder
|
123
|
-
const files = await this.storage.
|
135
|
+
const files = await this.storage.findFolderFiles(nonSignedFolder.id);
|
124
136
|
return files;
|
125
137
|
}
|
126
138
|
catch (error) {
|