@cooperation/vc-storage 1.0.21 → 1.0.23
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/models/CredentialEngine.js +12 -19
- package/dist/models/GoogleDriveStorage.js +203 -159
- package/dist/models/ResumeVC.js +15 -12
- package/dist/types/models/CredentialEngine.d.ts +2 -2
- package/dist/types/models/GoogleDriveStorage.d.ts +12 -13
- package/dist/types/models/ResumeVC.d.ts +4 -1
- package/dist/types/utils/credential.d.ts +2 -2
- package/dist/types/utils/google.d.ts +7 -5
- package/dist/utils/credential.js +2 -7
- package/dist/utils/google.js +25 -23
- package/package.json +8 -16
@@ -5,11 +5,9 @@ import { v4 as uuidv4 } from 'uuid';
|
|
5
5
|
import { extractKeyPairFromCredential, generateDIDSchema, generateUnsignedRecommendation, generateUnsignedVC, } from '../utils/credential.js';
|
6
6
|
import { customDocumentLoader } from '../utils/digitalbazaar.js';
|
7
7
|
import { saveToGoogleDrive } from '../utils/google.js';
|
8
|
-
import { GoogleDriveStorage } from './GoogleDriveStorage.js';
|
9
8
|
/**
|
10
9
|
* Class representing the Credential Engine.
|
11
10
|
* @class CredentialEngine
|
12
|
-
* @param {string} accessToken - The access token for the user.
|
13
11
|
* @classdesc Credential Engine class to create DIDs and VCs.
|
14
12
|
* @method createDID - Create a new DID with Digital Bazaar's Ed25519VerificationKey2020 key pair.
|
15
13
|
* @method createWalletDID - Create a new DID with user metamask address as controller.
|
@@ -21,8 +19,8 @@ import { GoogleDriveStorage } from './GoogleDriveStorage.js';
|
|
21
19
|
export class CredentialEngine {
|
22
20
|
storage;
|
23
21
|
keyPair;
|
24
|
-
constructor(
|
25
|
-
this.storage =
|
22
|
+
constructor(storage) {
|
23
|
+
this.storage = storage;
|
26
24
|
}
|
27
25
|
async getKeyPair(vc) {
|
28
26
|
// Fetch all stored key pairs
|
@@ -73,11 +71,11 @@ export class CredentialEngine {
|
|
73
71
|
async createDID() {
|
74
72
|
try {
|
75
73
|
const keyPair = await this.generateKeyPair();
|
76
|
-
const keyFile = await saveToGoogleDrive({
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
});
|
74
|
+
// const keyFile = await saveToGoogleDrive({
|
75
|
+
// storage: this.storage,
|
76
|
+
// data: keyPair,
|
77
|
+
// type: 'KEYPAIR',
|
78
|
+
// });
|
81
79
|
const didDocument = await generateDIDSchema(keyPair);
|
82
80
|
return { didDocument, keyPair };
|
83
81
|
}
|
@@ -132,19 +130,14 @@ export class CredentialEngine {
|
|
132
130
|
* @throws Will throw an error if VC signing fails.
|
133
131
|
*/
|
134
132
|
async signVC({ data, type, keyPair, issuerId, vcFileId }) {
|
135
|
-
console.log('🚀 ~ CredentialEngine ~ signVC ~ { data, type, keyPair, issuerId, vcFileId }:', {
|
136
|
-
data,
|
137
|
-
type,
|
138
|
-
keyPair,
|
139
|
-
issuerId,
|
140
|
-
vcFileId,
|
141
|
-
});
|
142
|
-
let vc;
|
143
133
|
let credential = generateUnsignedVC({ formData: data, issuerDid: issuerId });
|
144
134
|
if (type == 'RECOMMENDATION' && vcFileId) {
|
145
135
|
console.log('WOW');
|
146
|
-
|
147
|
-
|
136
|
+
credential = generateUnsignedRecommendation({
|
137
|
+
vcId: vcFileId,
|
138
|
+
recommendation: data,
|
139
|
+
issuerDid: issuerId,
|
140
|
+
});
|
148
141
|
}
|
149
142
|
try {
|
150
143
|
console.log('🚀 ~ CredentialEngine ~ signVC ~ credential:', credential);
|
@@ -15,6 +15,58 @@
|
|
15
15
|
*/
|
16
16
|
export class GoogleDriveStorage {
|
17
17
|
accessToken;
|
18
|
+
folderCache = {};
|
19
|
+
fileIdsCache = null;
|
20
|
+
async updateFileIdsJson(newFileId) {
|
21
|
+
const constructUrl = () => {
|
22
|
+
const baseUrl = 'https://www.googleapis.com/drive/v3/files';
|
23
|
+
const queryParams = new URLSearchParams({
|
24
|
+
spaces: 'appDataFolder',
|
25
|
+
q: "name='file_ids.json'",
|
26
|
+
fields: 'files(id)',
|
27
|
+
});
|
28
|
+
return `${baseUrl}?${queryParams.toString()}`;
|
29
|
+
};
|
30
|
+
try {
|
31
|
+
// ✅ Fetch `file_ids.json` ID once per session (cached)
|
32
|
+
if (!this.fileIdsCache) {
|
33
|
+
const existingFile = await this.fetcher({
|
34
|
+
method: 'GET',
|
35
|
+
headers: {},
|
36
|
+
url: constructUrl(),
|
37
|
+
});
|
38
|
+
if (existingFile.files.length > 0) {
|
39
|
+
this.fileIdsCache = existingFile.files[0].id;
|
40
|
+
}
|
41
|
+
else {
|
42
|
+
console.log('No existing file_ids.json found, creating a new one.');
|
43
|
+
this.fileIdsCache = null;
|
44
|
+
}
|
45
|
+
}
|
46
|
+
let existingFileIds = [];
|
47
|
+
// ✅ Fetch existing file IDs **only if `file_ids.json` exists**
|
48
|
+
if (this.fileIdsCache) {
|
49
|
+
try {
|
50
|
+
const fileContent = await this.fetcher({
|
51
|
+
method: 'GET',
|
52
|
+
headers: {},
|
53
|
+
url: `https://www.googleapis.com/drive/v3/files/${this.fileIdsCache}?alt=media`,
|
54
|
+
});
|
55
|
+
existingFileIds = fileContent;
|
56
|
+
}
|
57
|
+
catch (error) {
|
58
|
+
console.log('Error fetching existing file_ids.json content, creating new list.');
|
59
|
+
}
|
60
|
+
}
|
61
|
+
// ✅ Append the new file ID to the list
|
62
|
+
existingFileIds.push(newFileId);
|
63
|
+
console.log('File ID saved to appDataFolder.', this.fileIdsCache);
|
64
|
+
}
|
65
|
+
catch (error) {
|
66
|
+
console.error('Error updating file_ids.json:', error.message);
|
67
|
+
throw error;
|
68
|
+
}
|
69
|
+
}
|
18
70
|
constructor(accessToken) {
|
19
71
|
this.accessToken = accessToken;
|
20
72
|
}
|
@@ -28,7 +80,6 @@ export class GoogleDriveStorage {
|
|
28
80
|
},
|
29
81
|
body,
|
30
82
|
});
|
31
|
-
// Check the Content-Type to ensure it's JSON before parsing
|
32
83
|
const contentType = res.headers.get('Content-Type') || '';
|
33
84
|
let data;
|
34
85
|
if (contentType.includes('application/json')) {
|
@@ -39,7 +90,6 @@ export class GoogleDriveStorage {
|
|
39
90
|
console.error('Unexpected Response Type:', text);
|
40
91
|
throw new Error(`Expected JSON response but got: ${contentType}`);
|
41
92
|
}
|
42
|
-
// Handle non-200 HTTP responses
|
43
93
|
if (!res.ok) {
|
44
94
|
console.error('Error Response:', JSON.stringify(data));
|
45
95
|
throw new Error(data?.error?.message || 'Unknown error occurred');
|
@@ -54,11 +104,7 @@ export class GoogleDriveStorage {
|
|
54
104
|
async getFileContent(fileId) {
|
55
105
|
const url = `https://www.googleapis.com/drive/v3/files/${fileId}?alt=media`;
|
56
106
|
try {
|
57
|
-
const response = await this.fetcher({
|
58
|
-
method: 'GET',
|
59
|
-
headers: {}, // Add additional headers if required
|
60
|
-
url,
|
61
|
-
});
|
107
|
+
const response = await this.fetcher({ method: 'GET', headers: {}, url });
|
62
108
|
return response;
|
63
109
|
}
|
64
110
|
catch (error) {
|
@@ -72,11 +118,7 @@ export class GoogleDriveStorage {
|
|
72
118
|
headers: {},
|
73
119
|
url: `https://www.googleapis.com/drive/v3/files?q=${encodeURIComponent(query)}&trashed=false&fields=files(id,name,mimeType,parents)`,
|
74
120
|
});
|
75
|
-
|
76
|
-
console.error('No files found:', result);
|
77
|
-
return [];
|
78
|
-
}
|
79
|
-
return result.files;
|
121
|
+
return result.files || [];
|
80
122
|
}
|
81
123
|
async createFolder({ folderName, parentFolderId }) {
|
82
124
|
if (!parentFolderId) {
|
@@ -85,7 +127,7 @@ export class GoogleDriveStorage {
|
|
85
127
|
const metadata = {
|
86
128
|
name: folderName,
|
87
129
|
mimeType: 'application/vnd.google-apps.folder',
|
88
|
-
parents: [parentFolderId],
|
130
|
+
parents: [parentFolderId],
|
89
131
|
};
|
90
132
|
const folder = await this.fetcher({
|
91
133
|
method: 'POST',
|
@@ -94,110 +136,107 @@ export class GoogleDriveStorage {
|
|
94
136
|
url: 'https://www.googleapis.com/drive/v3/files',
|
95
137
|
});
|
96
138
|
console.log(`Folder created: "${folderName}" with ID: ${folder.id}, Parent: ${parentFolderId}`);
|
139
|
+
await this.fetcher({
|
140
|
+
method: 'POST',
|
141
|
+
url: `https://www.googleapis.com/drive/v3/files/${folder.id}/permissions`,
|
142
|
+
headers: {},
|
143
|
+
body: JSON.stringify({ role: 'reader', type: 'anyone' }),
|
144
|
+
});
|
97
145
|
return folder;
|
98
146
|
}
|
147
|
+
async getMediaFolderId() {
|
148
|
+
if (this.folderCache['MEDIAs']) {
|
149
|
+
return this.folderCache['MEDIAs'];
|
150
|
+
}
|
151
|
+
const rootFolders = await this.findFolders();
|
152
|
+
let credentialsFolder = rootFolders.find((f) => f.name === 'Credentials');
|
153
|
+
if (!credentialsFolder) {
|
154
|
+
credentialsFolder = await this.createFolder({ folderName: 'Credentials', parentFolderId: 'root' });
|
155
|
+
}
|
156
|
+
const credentialsFolderId = credentialsFolder.id;
|
157
|
+
const subfolders = await this.findFolders(credentialsFolder.id);
|
158
|
+
let mediasFolder = subfolders.find((f) => f.name === 'MEDIAs');
|
159
|
+
if (!mediasFolder) {
|
160
|
+
mediasFolder = await this.createFolder({ folderName: 'MEDIAs', parentFolderId: credentialsFolderId });
|
161
|
+
}
|
162
|
+
const mediasFolderId = mediasFolder.id;
|
163
|
+
this.folderCache['MEDIAs'] = mediasFolderId;
|
164
|
+
return mediasFolderId;
|
165
|
+
}
|
166
|
+
async uploadBinaryFile({ file }) {
|
167
|
+
try {
|
168
|
+
const accessToken = this.accessToken; // Ensure access token is available
|
169
|
+
if (!accessToken) {
|
170
|
+
throw new Error('Missing Google OAuth access token.');
|
171
|
+
}
|
172
|
+
const folderId = await this.getMediaFolderId(); // Ensure folderId is correct
|
173
|
+
// ✅ Correct metadata for Google Drive API
|
174
|
+
const metadata = {
|
175
|
+
name: file.name,
|
176
|
+
mimeType: file.type,
|
177
|
+
parents: [folderId], // Store in the correct folder
|
178
|
+
};
|
179
|
+
// ✅ Create FormData for multipart upload
|
180
|
+
const formData = new FormData();
|
181
|
+
formData.append('metadata', new Blob([JSON.stringify(metadata)], { type: 'application/json' }));
|
182
|
+
formData.append('file', file);
|
183
|
+
// ✅ Correct Google Drive Upload URL
|
184
|
+
const url = `https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id,parents`;
|
185
|
+
const response = await fetch(url, {
|
186
|
+
method: 'POST',
|
187
|
+
headers: {
|
188
|
+
Authorization: `Bearer ${accessToken}`, // ✅ Include valid OAuth token
|
189
|
+
},
|
190
|
+
body: formData,
|
191
|
+
});
|
192
|
+
const data = await response.json();
|
193
|
+
if (!response.ok) {
|
194
|
+
throw new Error(`Google Drive Upload Error: ${data.error?.message || 'Unknown error'}`);
|
195
|
+
}
|
196
|
+
console.log('✅ File uploaded successfully:', data);
|
197
|
+
return data; // Returns the uploaded file ID and parents
|
198
|
+
}
|
199
|
+
catch (error) {
|
200
|
+
console.error('❌ Error uploading file to Google Drive:', error);
|
201
|
+
throw error;
|
202
|
+
}
|
203
|
+
}
|
99
204
|
async saveFile({ data, folderId }) {
|
205
|
+
console.log('🚀 ~ GoogleDriveStorage ~ saveFile ~ data:', data);
|
100
206
|
try {
|
101
207
|
if (!folderId) {
|
102
208
|
throw new Error('Folder ID is required to save a file.');
|
103
209
|
}
|
104
|
-
// Define file metadata, ensure correct folder is assigned
|
105
210
|
const fileMetadata = {
|
106
|
-
name: data.fileName || 'resume.json',
|
107
|
-
parents: [folderId],
|
108
|
-
mimeType: 'application/json',
|
211
|
+
name: data.fileName || 'resume.json',
|
212
|
+
parents: [folderId],
|
213
|
+
mimeType: data.mimeType || 'application/json',
|
109
214
|
};
|
110
|
-
|
111
|
-
const folder = await this.fetcher({
|
112
|
-
method: 'GET',
|
113
|
-
headers: {},
|
114
|
-
url: `https://www.googleapis.com/drive/v3/files/${folderId}?fields=trashed`,
|
115
|
-
});
|
116
|
-
if (folder.trashed) {
|
117
|
-
throw new Error('Parent folder is in trash');
|
118
|
-
}
|
119
|
-
// Prepare the file content as a JSON string
|
120
|
-
const fileContent = JSON.stringify(data);
|
121
|
-
// Create a Blob from the JSON string
|
122
|
-
const fileBlob = new Blob([fileContent], { type: 'application/json' });
|
123
|
-
// Create FormData and append the metadata and file content
|
215
|
+
const fileBlob = new Blob([JSON.stringify(data)], { type: 'application/json' });
|
124
216
|
const formData = new FormData();
|
125
217
|
formData.append('metadata', new Blob([JSON.stringify(fileMetadata)], { type: 'application/json' }));
|
126
218
|
formData.append('file', fileBlob);
|
127
|
-
// Upload file to Google Drive
|
128
|
-
console.log('Uploading file...');
|
129
219
|
const file = await this.fetcher({
|
130
220
|
method: 'POST',
|
131
221
|
headers: {},
|
132
222
|
body: formData,
|
133
223
|
url: `https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id,parents`,
|
134
224
|
});
|
135
|
-
// Set
|
225
|
+
// Set public read permissions
|
136
226
|
await this.fetcher({
|
137
227
|
method: 'POST',
|
138
228
|
url: `https://www.googleapis.com/drive/v3/files/${file.id}/permissions`,
|
139
|
-
headers: {},
|
229
|
+
headers: { 'Content-Type': 'application/json' },
|
140
230
|
body: JSON.stringify({
|
141
231
|
role: 'reader',
|
142
232
|
type: 'anyone',
|
143
233
|
}),
|
144
234
|
});
|
145
|
-
|
146
|
-
let existingFileId = null;
|
147
|
-
let existingFileIds = [];
|
148
|
-
try {
|
149
|
-
const existingFileQuery = await this.fetcher({
|
150
|
-
method: 'GET',
|
151
|
-
headers: {},
|
152
|
-
url: `https://www.googleapis.com/drive/v3/files?spaces=appDataFolder&q=name='file_ids.json'&fields=files(id)`,
|
153
|
-
});
|
154
|
-
if (existingFileQuery.files?.length > 0) {
|
155
|
-
existingFileId = existingFileQuery.files[0].id;
|
156
|
-
const fileContent = await this.fetcher({
|
157
|
-
method: 'GET',
|
158
|
-
headers: {},
|
159
|
-
url: `https://www.googleapis.com/drive/v3/files/${existingFileId}?alt=media`,
|
160
|
-
});
|
161
|
-
existingFileIds = JSON.parse(fileContent);
|
162
|
-
}
|
163
|
-
console.log('existingFileId', existingFileId);
|
164
|
-
}
|
165
|
-
catch (error) {
|
166
|
-
console.log('Creating new file_ids.json');
|
167
|
-
}
|
168
|
-
// Add new file ID
|
169
|
-
existingFileIds.push(file.id);
|
170
|
-
// Metadata for app data file
|
171
|
-
const appDataFileMetadata = {
|
172
|
-
name: 'file_ids.json',
|
173
|
-
mimeType: 'application/json',
|
174
|
-
spaces: ['appDataFolder'],
|
175
|
-
};
|
176
|
-
// Update or create file_ids.json
|
177
|
-
const formDataForAppData = new FormData();
|
178
|
-
formDataForAppData.append('metadata', new Blob([JSON.stringify(appDataFileMetadata)], { type: 'application/json' }));
|
179
|
-
formDataForAppData.append('file', new Blob([JSON.stringify(existingFileIds)], { type: 'application/json' }));
|
180
|
-
if (existingFileId) {
|
181
|
-
await this.fetcher({
|
182
|
-
method: 'PATCH',
|
183
|
-
headers: {},
|
184
|
-
body: formDataForAppData,
|
185
|
-
url: `https://www.googleapis.com/upload/drive/v3/files/${existingFileId}?uploadType=multipart`,
|
186
|
-
});
|
187
|
-
}
|
188
|
-
else {
|
189
|
-
await this.fetcher({
|
190
|
-
method: 'POST',
|
191
|
-
headers: {},
|
192
|
-
body: formDataForAppData,
|
193
|
-
url: `https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&spaces=appDataFolder`,
|
194
|
-
});
|
195
|
-
}
|
196
|
-
console.log(file);
|
235
|
+
console.log(`File uploaded successfully: ${file.id}`);
|
197
236
|
return file;
|
198
237
|
}
|
199
238
|
catch (error) {
|
200
|
-
console.error('Error:', error
|
239
|
+
console.error('Error in saveFile:', error);
|
201
240
|
throw error;
|
202
241
|
}
|
203
242
|
}
|
@@ -207,10 +246,8 @@ export class GoogleDriveStorage {
|
|
207
246
|
* @returns file content
|
208
247
|
*/
|
209
248
|
async retrieve(id) {
|
210
|
-
const metadataUrl = `https://www.googleapis.com/drive/v3/files/${id}?fields=id,name`;
|
211
249
|
const dataUrl = `https://www.googleapis.com/drive/v3/files/${id}?alt=media`;
|
212
250
|
try {
|
213
|
-
// Fetch actual file data
|
214
251
|
const dataResponse = await fetch(dataUrl, {
|
215
252
|
method: 'GET',
|
216
253
|
headers: {
|
@@ -223,7 +260,6 @@ export class GoogleDriveStorage {
|
|
223
260
|
return null;
|
224
261
|
}
|
225
262
|
const contentType = dataResponse.headers.get('Content-Type');
|
226
|
-
console.log(`File content type: ${contentType}`);
|
227
263
|
let fileData;
|
228
264
|
if (contentType?.includes('application/json')) {
|
229
265
|
fileData = await dataResponse.json();
|
@@ -242,7 +278,7 @@ export class GoogleDriveStorage {
|
|
242
278
|
else {
|
243
279
|
fileData = await dataResponse.arrayBuffer();
|
244
280
|
}
|
245
|
-
return { data: fileData };
|
281
|
+
return { data: fileData, id: id };
|
246
282
|
}
|
247
283
|
catch (error) {
|
248
284
|
console.error(`Error retrieving file with ID ${id}:`, error.message);
|
@@ -254,13 +290,18 @@ export class GoogleDriveStorage {
|
|
254
290
|
* @param folderId [Optional]
|
255
291
|
* @returns
|
256
292
|
*/
|
257
|
-
|
293
|
+
async findFolders(folderId) {
|
294
|
+
const cacheKey = folderId || 'root';
|
295
|
+
if (this.folderCache[cacheKey]) {
|
296
|
+
return this.folderCache[cacheKey];
|
297
|
+
}
|
258
298
|
const query = folderId
|
259
299
|
? `'${folderId}' in parents and mimeType='application/vnd.google-apps.folder'`
|
260
300
|
: `'root' in parents and mimeType='application/vnd.google-apps.folder'`;
|
261
301
|
const folders = await this.searchFiles(query);
|
262
|
-
|
263
|
-
|
302
|
+
this.folderCache[cacheKey] = folders;
|
303
|
+
return folders;
|
304
|
+
}
|
264
305
|
/**
|
265
306
|
* Get all files content for the specified type ('KEYPAIRs' | 'VCs' | 'SESSIONs' | 'DIDs' | 'RECOMMENDATIONs')
|
266
307
|
* @param type
|
@@ -268,58 +309,56 @@ export class GoogleDriveStorage {
|
|
268
309
|
*/
|
269
310
|
async getAllFilesByType(type) {
|
270
311
|
try {
|
271
|
-
|
272
|
-
|
273
|
-
|
312
|
+
if (!this.folderCache['Credentials']) {
|
313
|
+
const rootFolders = await this.findFolders();
|
314
|
+
this.folderCache['Credentials'] = rootFolders;
|
315
|
+
}
|
316
|
+
const credentialsFolder = this.folderCache['Credentials'].find((f) => f.name === 'Credentials');
|
274
317
|
if (!credentialsFolder) {
|
275
318
|
console.error('Credentials folder not found.');
|
276
319
|
return [];
|
277
320
|
}
|
278
|
-
const credentialsFolderId = credentialsFolder.id;
|
279
|
-
// Step 2: Handle special case for 'VCs'
|
280
321
|
if (type === 'VCs') {
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
322
|
+
if (!this.folderCache['VCs']) {
|
323
|
+
const vcSubfolder = await this.findFolders(credentialsFolder.id);
|
324
|
+
const vcsFolder = vcSubfolder.find((f) => f.name === 'VCs');
|
325
|
+
const vcSubFolders = await this.findFolders(vcsFolder.id);
|
326
|
+
this.folderCache['VCs'] = vcSubFolders.filter((folder) => folder.name.startsWith('VC-'));
|
327
|
+
}
|
328
|
+
const vcSubfolders = this.folderCache['VCs'];
|
329
|
+
if (!vcSubfolders.length) {
|
330
|
+
console.error(`No subfolders found for type: ${type}`);
|
286
331
|
return [];
|
287
332
|
}
|
288
|
-
const
|
289
|
-
|
290
|
-
const
|
291
|
-
|
292
|
-
|
293
|
-
const files = await this.findFilesUnderFolder(folder.id);
|
294
|
-
console.log('🚀 ~ GoogleDriveStorage ~ vcSubfolders.map ~ files:', files);
|
295
|
-
return Promise.all(files.map(async (file) => {
|
296
|
-
return await this.retrieve(file.id);
|
297
|
-
}));
|
298
|
-
}));
|
299
|
-
console.log('🚀 ~ GoogleDriveStorage ~ getAllFilesByType ~ fileContents:', fileContents);
|
300
|
-
return fileContents;
|
333
|
+
const allFilesNested = await Promise.all(vcSubfolders.map(async (folder) => await this.findFilesUnderFolder(folder.id)));
|
334
|
+
const allVcJsonFiles = allFilesNested.flat().filter((file) => file.mimeType === 'application/json');
|
335
|
+
const fileContentsResults = await Promise.allSettled(allVcJsonFiles.map((file) => this.retrieve(file.id)));
|
336
|
+
const validFileContents = fileContentsResults.filter((result) => result.status === 'fulfilled').map((result) => result.value);
|
337
|
+
return validFileContents.filter((file) => file.data.fileName !== 'RELATIONS');
|
301
338
|
}
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
339
|
+
if (!this.folderCache[type]) {
|
340
|
+
const subfolders = await this.findFolders(credentialsFolder.id);
|
341
|
+
const targetFolder = subfolders.find((f) => f.name === type);
|
342
|
+
this.folderCache[type] = targetFolder ? targetFolder.id : null;
|
343
|
+
}
|
344
|
+
const targetFolderId = this.folderCache[type];
|
345
|
+
if (!targetFolderId) {
|
306
346
|
console.error(`Folder for type ${type} not found.`);
|
307
347
|
return [];
|
308
348
|
}
|
309
349
|
const filesResponse = await this.fetcher({
|
310
350
|
method: 'GET',
|
311
351
|
headers: {},
|
312
|
-
url: `https://www.googleapis.com/drive/v3/files?q='${
|
352
|
+
url: `https://www.googleapis.com/drive/v3/files?q='${targetFolderId}' in parents and trashed=false&fields=files(id,name,mimeType)`,
|
313
353
|
});
|
314
|
-
const files = filesResponse.files;
|
315
|
-
const fileContents = await Promise.
|
316
|
-
|
317
|
-
|
318
|
-
return fileContents;
|
354
|
+
const files = filesResponse.files || [];
|
355
|
+
const fileContents = await Promise.allSettled(files.map((file) => this.retrieve(file.id)));
|
356
|
+
console.log('🚀 ~ GoogleDriveStorage ~ getAllFilesByType ~ fileContents:', fileContents);
|
357
|
+
return fileContents.filter((res) => res.status === 'fulfilled').map((res) => res.value);
|
319
358
|
}
|
320
359
|
catch (error) {
|
321
360
|
console.error(`Error getting files of type ${type}:`, error);
|
322
|
-
return [];
|
361
|
+
return [];
|
323
362
|
}
|
324
363
|
}
|
325
364
|
/**
|
@@ -330,9 +369,7 @@ export class GoogleDriveStorage {
|
|
330
369
|
*/
|
331
370
|
async updateFileName(fileId, newFileName) {
|
332
371
|
try {
|
333
|
-
const metadata = {
|
334
|
-
name: newFileName, // New name for the file
|
335
|
-
};
|
372
|
+
const metadata = { name: newFileName };
|
336
373
|
const updatedFile = await this.fetcher({
|
337
374
|
method: 'PATCH',
|
338
375
|
headers: { 'Content-Type': 'application/json' },
|
@@ -348,7 +385,6 @@ export class GoogleDriveStorage {
|
|
348
385
|
}
|
349
386
|
}
|
350
387
|
async findFileByName(name) {
|
351
|
-
// find the file named under Credentials folder
|
352
388
|
const rootFolders = await this.findFolders();
|
353
389
|
const credentialsFolderId = rootFolders.find((f) => f.name === 'Credentials')?.id;
|
354
390
|
if (!credentialsFolderId)
|
@@ -364,7 +400,6 @@ export class GoogleDriveStorage {
|
|
364
400
|
console.log('No files found in the folder.');
|
365
401
|
return [];
|
366
402
|
}
|
367
|
-
// Fetch content for each file
|
368
403
|
const filesWithContent = await Promise.all(files.map(async (file) => {
|
369
404
|
try {
|
370
405
|
const content = await this.getFileContent(file.id);
|
@@ -372,7 +407,7 @@ export class GoogleDriveStorage {
|
|
372
407
|
}
|
373
408
|
catch (error) {
|
374
409
|
console.error(`Error fetching content for file "${file.name}" (ID: ${file.id}):`, error);
|
375
|
-
return { ...file, content: null };
|
410
|
+
return { ...file, content: null };
|
376
411
|
}
|
377
412
|
}));
|
378
413
|
return filesWithContent;
|
@@ -382,12 +417,8 @@ export class GoogleDriveStorage {
|
|
382
417
|
const updateUrl = `https://www.googleapis.com/drive/v3/files/${fileId}`;
|
383
418
|
const updatedFile = await this.fetcher({
|
384
419
|
method: 'PATCH',
|
385
|
-
headers: {
|
386
|
-
|
387
|
-
},
|
388
|
-
body: JSON.stringify({
|
389
|
-
name: data.fileName,
|
390
|
-
}),
|
420
|
+
headers: { 'Content-Type': 'application/json' },
|
421
|
+
body: JSON.stringify({ name: data.fileName }),
|
391
422
|
url: updateUrl,
|
392
423
|
});
|
393
424
|
console.log('✅ File renamed successfully:', updatedFile);
|
@@ -398,44 +429,50 @@ export class GoogleDriveStorage {
|
|
398
429
|
}
|
399
430
|
}
|
400
431
|
async getFileParents(fileId) {
|
401
|
-
console.log('🚀 ~ GoogleDriveStorage ~ getFileParents ~ fileId', fileId);
|
402
432
|
const file = await this.fetcher({
|
403
433
|
method: 'GET',
|
404
434
|
headers: {},
|
405
435
|
url: `https://www.googleapis.com/drive/v3/files/${fileId}?fields=parents`,
|
406
436
|
});
|
437
|
+
console.log('FILE: ', file);
|
407
438
|
return file.parents;
|
408
439
|
}
|
409
440
|
async updateRelationsFile({ relationsFileId, recommendationFileId }) {
|
410
441
|
const relationsFileContent = await this.retrieve(relationsFileId);
|
411
|
-
const relationsData = relationsFileContent.data;
|
442
|
+
const relationsData = relationsFileContent.data.body ? JSON.parse(relationsFileContent.data.body) : relationsFileContent.data;
|
412
443
|
relationsData.recommendations.push(recommendationFileId);
|
413
444
|
const updatedContent = JSON.stringify(relationsData);
|
414
445
|
const updateResponse = await this.fetcher({
|
415
446
|
method: 'PATCH',
|
416
|
-
headers: {
|
417
|
-
'Content-Type': 'application/json',
|
418
|
-
},
|
447
|
+
headers: { 'Content-Type': 'application/json' },
|
419
448
|
body: updatedContent,
|
420
449
|
url: `https://www.googleapis.com/upload/drive/v3/files/${relationsFileId}?uploadType=media`,
|
421
450
|
});
|
422
|
-
|
451
|
+
this.updateFileIdsJson(relationsFileId);
|
423
452
|
return updateResponse;
|
424
453
|
}
|
425
454
|
async createRelationsFile({ vcFolderId }) {
|
426
455
|
const files = await this.findFilesUnderFolder(vcFolderId);
|
427
456
|
const vcFile = files.find((file) => file.name === 'VC');
|
457
|
+
const vcContent = await this.getFileContent(vcFile.id);
|
458
|
+
console.log('🚀 ~ GoogleDriveStorage ~ createRelationsFile ~ vcContent:', vcContent);
|
459
|
+
const subject = JSON.parse(vcContent.body).credentialSubject;
|
460
|
+
console.log('🚀 ~ GoogleDriveStorage ~ createRelationsFile ~ subject:', subject);
|
428
461
|
const relationsFile = await this.saveFile({
|
429
462
|
data: {
|
430
463
|
fileName: 'RELATIONS',
|
431
464
|
mimeType: 'application/json',
|
432
465
|
body: JSON.stringify({
|
433
|
-
|
466
|
+
vc: {
|
467
|
+
fileId: vcContent.id,
|
468
|
+
subject,
|
469
|
+
},
|
434
470
|
recommendations: [],
|
435
471
|
}),
|
436
472
|
},
|
437
473
|
folderId: vcFolderId,
|
438
474
|
});
|
475
|
+
await this.updateFileIdsJson(relationsFile.id);
|
439
476
|
return relationsFile;
|
440
477
|
}
|
441
478
|
/**
|
@@ -459,25 +496,19 @@ export class GoogleDriveStorage {
|
|
459
496
|
}
|
460
497
|
}
|
461
498
|
async update(fileId, data) {
|
462
|
-
console.log('🚀 ~ GoogleDriveStorage ~ update ~ data:', data);
|
463
|
-
console.log('🚀 ~ GoogleDriveStorage ~ update ~ fileId:', fileId);
|
464
|
-
// ✅ Ensure JSON file type
|
465
499
|
const metadata = {
|
466
500
|
name: data.fileName || 'resume.json',
|
467
501
|
mimeType: 'application/json',
|
468
502
|
};
|
469
503
|
const uploadUrl = `https://www.googleapis.com/upload/drive/v3/files/${fileId}?uploadType=multipart`;
|
470
|
-
// ✅ Create multipart request to update Google Drive JSON file
|
471
504
|
const formData = new FormData();
|
472
505
|
formData.append('metadata', new Blob([JSON.stringify(metadata)], { type: 'application/json' }));
|
473
|
-
formData.append('file', new Blob([JSON.stringify(data.body)], { type: 'application/json' })
|
474
|
-
);
|
475
|
-
console.log('🚀 ~ GoogleDriveStorage ~ update ~ FormData:', formData);
|
506
|
+
formData.append('file', new Blob([JSON.stringify(data.body)], { type: 'application/json' }));
|
476
507
|
try {
|
477
508
|
const response = await this.fetcher({
|
478
509
|
method: 'PATCH',
|
479
|
-
headers: {},
|
480
|
-
body: formData,
|
510
|
+
headers: {},
|
511
|
+
body: formData,
|
481
512
|
url: `${uploadUrl}&fields=id,name,mimeType`,
|
482
513
|
});
|
483
514
|
console.log('✅ File updated successfully:', response);
|
@@ -490,12 +521,22 @@ export class GoogleDriveStorage {
|
|
490
521
|
}
|
491
522
|
async getFileIdsFromAppDataFolder() {
|
492
523
|
try {
|
524
|
+
const constructUrl = () => {
|
525
|
+
const baseUrl = 'https://www.googleapis.com/drive/v3/files';
|
526
|
+
const queryParams = new URLSearchParams({
|
527
|
+
spaces: 'appDataFolder',
|
528
|
+
q: "name='file_ids.json'",
|
529
|
+
fields: 'files(id)',
|
530
|
+
});
|
531
|
+
return `${baseUrl}?${queryParams.toString()}`;
|
532
|
+
};
|
493
533
|
// Step 1: Search for the file_ids.json file in the appDataFolder
|
494
534
|
const response = await this.fetcher({
|
495
535
|
method: 'GET',
|
496
536
|
headers: {},
|
497
|
-
url:
|
537
|
+
url: constructUrl(),
|
498
538
|
});
|
539
|
+
console.log(': GoogleDriveStorage getFileIdsFromAppDataFolder response', response);
|
499
540
|
// Step 2: Check if the file exists
|
500
541
|
if (!response.files || response.files.length === 0) {
|
501
542
|
console.log('No file_ids.json found in appDataFolder.');
|
@@ -503,14 +544,17 @@ export class GoogleDriveStorage {
|
|
503
544
|
}
|
504
545
|
// Step 3: Get the file ID of file_ids.json
|
505
546
|
const fileId = response.files[0].id;
|
547
|
+
console.log(': GoogleDriveStorage getFileIdsFromAppDataFolder fileId', fileId);
|
506
548
|
// Step 4: Fetch the content of file_ids.json
|
507
549
|
const fileContent = await this.fetcher({
|
508
550
|
method: 'GET',
|
509
551
|
headers: {},
|
510
552
|
url: `https://www.googleapis.com/drive/v3/files/${fileId}?alt=media`,
|
511
553
|
});
|
554
|
+
console.log(': GoogleDriveStorage getFileIdsFromAppDataFolder fileContent', fileContent);
|
512
555
|
// Step 5: Parse the file content (array of file IDs)
|
513
|
-
const fileIds =
|
556
|
+
const fileIds = fileContent;
|
557
|
+
console.log(': GoogleDriveStorage getFileIdsFromAppDataFolder fileIds', fileIds);
|
514
558
|
return fileIds;
|
515
559
|
}
|
516
560
|
catch (error) {
|
package/dist/models/ResumeVC.js
CHANGED
@@ -30,30 +30,33 @@ export class ResumeVC {
|
|
30
30
|
return unsignedCredential;
|
31
31
|
}
|
32
32
|
generateUnsignedCredential({ formData, issuerDid }) {
|
33
|
-
const
|
33
|
+
const unsignedResumeVC = {
|
34
34
|
'@context': [
|
35
|
-
'https://www.w3.org/2018/credentials/v1',
|
35
|
+
'https://www.w3.org/2018/credentials/v1',
|
36
|
+
'https://schema.hropenstandards.org/4.4/context.jsonld',
|
36
37
|
inlineResumeContext['@context'], // Inline context
|
37
38
|
],
|
38
|
-
id: `urn:uuid:${uuidv4()}`, // Generate a
|
39
|
-
type: ['VerifiableCredential'],
|
39
|
+
id: `urn:uuid:${uuidv4()}`, // Generate a unique UUID
|
40
|
+
type: ['VerifiableCredential', 'LERRSCredential'], // LER-RS compliant credential type
|
40
41
|
issuer: issuerDid,
|
41
|
-
issuanceDate: new Date().toISOString(),
|
42
|
+
issuanceDate: new Date().toISOString(), // Current date/time in ISO format
|
42
43
|
credentialSubject: {
|
43
44
|
type: 'Resume',
|
44
45
|
person: {
|
45
46
|
name: {
|
46
|
-
formattedName: formData.formattedName,
|
47
|
+
formattedName: formData.formattedName || '',
|
47
48
|
},
|
48
|
-
primaryLanguage: formData.primaryLanguage,
|
49
|
+
primaryLanguage: formData.primaryLanguage || 'en',
|
49
50
|
},
|
50
|
-
narrative:
|
51
|
-
|
52
|
-
|
53
|
-
|
51
|
+
narrative: {
|
52
|
+
text: formData.narrative || 'Narrative text goes here',
|
53
|
+
},
|
54
|
+
employmentHistory: formData.employmentHistory || [],
|
55
|
+
skills: formData.skills || [],
|
56
|
+
educationAndLearning: formData.educationAndLearning || {},
|
54
57
|
},
|
55
58
|
};
|
56
|
-
return
|
59
|
+
return unsignedResumeVC;
|
57
60
|
}
|
58
61
|
generateKeyPair = async (address) => {
|
59
62
|
// Generate the key pair using the library's method
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import { DidDocument, KeyPair, FormDataI, RecommendationFormDataI, VerifiableCredential } from '../../types/credential.js';
|
2
|
+
import { GoogleDriveStorage } from './GoogleDriveStorage.js';
|
2
3
|
interface SignPropsI {
|
3
4
|
data: FormDataI | RecommendationFormDataI;
|
4
5
|
type: 'VC' | 'RECOMMENDATION';
|
@@ -9,7 +10,6 @@ interface SignPropsI {
|
|
9
10
|
/**
|
10
11
|
* Class representing the Credential Engine.
|
11
12
|
* @class CredentialEngine
|
12
|
-
* @param {string} accessToken - The access token for the user.
|
13
13
|
* @classdesc Credential Engine class to create DIDs and VCs.
|
14
14
|
* @method createDID - Create a new DID with Digital Bazaar's Ed25519VerificationKey2020 key pair.
|
15
15
|
* @method createWalletDID - Create a new DID with user metamask address as controller.
|
@@ -21,7 +21,7 @@ interface SignPropsI {
|
|
21
21
|
export declare class CredentialEngine {
|
22
22
|
private storage;
|
23
23
|
private keyPair;
|
24
|
-
constructor(
|
24
|
+
constructor(storage: GoogleDriveStorage);
|
25
25
|
private getKeyPair;
|
26
26
|
private generateKeyPair;
|
27
27
|
private verifyCreds;
|
@@ -1,8 +1,4 @@
|
|
1
|
-
|
2
|
-
name: string;
|
3
|
-
content: any;
|
4
|
-
comments: string[];
|
5
|
-
}
|
1
|
+
type FileType = 'KEYPAIRs' | 'VCs' | 'SESSIONs' | 'DIDs' | 'RECOMMENDATIONs' | 'MEDIAs';
|
6
2
|
/**
|
7
3
|
* @class GoogleDriveStorage
|
8
4
|
* @description Class to interact with Google Drive API
|
@@ -20,6 +16,9 @@ interface FileContent {
|
|
20
16
|
*/
|
21
17
|
export declare class GoogleDriveStorage {
|
22
18
|
private accessToken;
|
19
|
+
folderCache: any;
|
20
|
+
private fileIdsCache;
|
21
|
+
private updateFileIdsJson;
|
23
22
|
constructor(accessToken: string);
|
24
23
|
private fetcher;
|
25
24
|
private getFileContent;
|
@@ -27,12 +26,11 @@ export declare class GoogleDriveStorage {
|
|
27
26
|
createFolder({ folderName, parentFolderId }: {
|
28
27
|
folderName: string;
|
29
28
|
parentFolderId: string;
|
30
|
-
}): Promise<
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
}>;
|
29
|
+
}): Promise<any>;
|
30
|
+
getMediaFolderId(): Promise<any>;
|
31
|
+
uploadBinaryFile({ file }: {
|
32
|
+
file: File;
|
33
|
+
}): Promise<any>;
|
36
34
|
saveFile({ data, folderId }: {
|
37
35
|
data: any;
|
38
36
|
folderId: string;
|
@@ -44,19 +42,20 @@ export declare class GoogleDriveStorage {
|
|
44
42
|
*/
|
45
43
|
retrieve(id: string): Promise<{
|
46
44
|
data: any;
|
45
|
+
id: string;
|
47
46
|
} | null>;
|
48
47
|
/**
|
49
48
|
* Get folder by folderId, if folderId == null you will have them all
|
50
49
|
* @param folderId [Optional]
|
51
50
|
* @returns
|
52
51
|
*/
|
53
|
-
findFolders
|
52
|
+
findFolders(folderId?: string): Promise<any[]>;
|
54
53
|
/**
|
55
54
|
* Get all files content for the specified type ('KEYPAIRs' | 'VCs' | 'SESSIONs' | 'DIDs' | 'RECOMMENDATIONs')
|
56
55
|
* @param type
|
57
56
|
* @returns
|
58
57
|
*/
|
59
|
-
getAllFilesByType(type:
|
58
|
+
getAllFilesByType(type: FileType): Promise<any[]>;
|
60
59
|
/**
|
61
60
|
* Update the name of a file in Google Drive
|
62
61
|
* @param fileId - The ID of the file to update
|
@@ -4,7 +4,10 @@ export declare class ResumeVC {
|
|
4
4
|
issuerDid: string;
|
5
5
|
keyPair: any;
|
6
6
|
}): Promise<any>;
|
7
|
-
|
7
|
+
generateUnsignedCredential({ formData, issuerDid }: {
|
8
|
+
formData: any;
|
9
|
+
issuerDid: string;
|
10
|
+
}): any;
|
8
11
|
generateKeyPair: (address?: string) => Promise<any>;
|
9
12
|
/**
|
10
13
|
* Create a new DID with Digital Bazaar's Ed25519VerificationKey2020 key pair.
|
@@ -29,8 +29,8 @@ export declare function generateUnsignedVC({ formData, issuerDid }: {
|
|
29
29
|
* @returns {RecommendationCredential} The created unsigned Recommendation Credential.
|
30
30
|
* @throws Will throw an error if the recommendation creation fails or if issuance date exceeds expiration date.
|
31
31
|
*/
|
32
|
-
export declare function generateUnsignedRecommendation({
|
33
|
-
|
32
|
+
export declare function generateUnsignedRecommendation({ vcId, recommendation, issuerDid, }: {
|
33
|
+
vcId: string;
|
34
34
|
recommendation: RecommendationFormDataI;
|
35
35
|
issuerDid: string;
|
36
36
|
}): RecommendationCredential;
|
@@ -12,8 +12,9 @@ export declare const getVCWithRecommendations: ({ vcId, storage }: {
|
|
12
12
|
}) => Promise<{
|
13
13
|
vc: {
|
14
14
|
data: any;
|
15
|
+
id: string;
|
15
16
|
};
|
16
|
-
|
17
|
+
recommendationIds: any;
|
17
18
|
relationsFileId: any;
|
18
19
|
}>;
|
19
20
|
/**
|
@@ -26,13 +27,14 @@ export declare const getVCWithRecommendations: ({ vcId, storage }: {
|
|
26
27
|
*/
|
27
28
|
export declare function saveToGoogleDrive({ storage, data, type }: SaveToGooglePropsI): Promise<any>;
|
28
29
|
/**
|
29
|
-
* Upload
|
30
|
+
* Upload any type of file to Google Drive in the Credentials/MEDIAs folder.
|
30
31
|
* @param {GoogleDriveStorage} storage - The GoogleDriveStorage instance.
|
31
|
-
* @param {File}
|
32
|
-
* @
|
32
|
+
* @param {File} file - The file to upload.
|
33
|
+
* @param {string} folderName - The name of the folder where the file will be saved (default is 'MEDIAs').
|
34
|
+
* @returns {Promise<{ id: string }>} - The uploaded file object.
|
33
35
|
* @throws Will throw an error if the upload operation fails.
|
34
36
|
*/
|
35
|
-
export declare function
|
37
|
+
export declare function uploadToGoogleDrive(storage: GoogleDriveStorage, file: File, folderName?: string): Promise<{
|
36
38
|
id: string;
|
37
39
|
}>;
|
38
40
|
export declare function generateViewLink(fileId: string): string;
|
package/dist/utils/credential.js
CHANGED
@@ -131,10 +131,7 @@ export function generateUnsignedVC({ formData, issuerDid }) {
|
|
131
131
|
* @returns {RecommendationCredential} The created unsigned Recommendation Credential.
|
132
132
|
* @throws Will throw an error if the recommendation creation fails or if issuance date exceeds expiration date.
|
133
133
|
*/
|
134
|
-
export function generateUnsignedRecommendation({
|
135
|
-
console.log('🚀 ~ vc.id:', vc.id);
|
136
|
-
console.log('🚀 ~ vc:', vc);
|
137
|
-
console.log('🚀 ~ recommendation:', recommendation);
|
134
|
+
export function generateUnsignedRecommendation({ vcId, recommendation, issuerDid, }) {
|
138
135
|
const issuanceDate = new Date().toISOString();
|
139
136
|
if (issuanceDate > recommendation.expirationDate)
|
140
137
|
throw new Error('issuanceDate cannot be after expirationDate');
|
@@ -150,7 +147,7 @@ export function generateUnsignedRecommendation({ vc, recommendation, issuerDid,
|
|
150
147
|
portfolio: 'https://schema.org/portfolio',
|
151
148
|
},
|
152
149
|
],
|
153
|
-
id:
|
150
|
+
id: `urn:${generateHashedId({ id: vcId })}`,
|
154
151
|
type: ['VerifiableCredential', 'https://schema.org/RecommendationCredential'],
|
155
152
|
issuer: {
|
156
153
|
id: issuerDid,
|
@@ -170,8 +167,6 @@ export function generateUnsignedRecommendation({ vc, recommendation, issuerDid,
|
|
170
167
|
})),
|
171
168
|
},
|
172
169
|
};
|
173
|
-
// Use the VC's hashed ID for the Recommendation's ID
|
174
|
-
unsignedRecommendation.id = vc.data.id;
|
175
170
|
return unsignedRecommendation;
|
176
171
|
}
|
177
172
|
/**
|
package/dist/utils/google.js
CHANGED
@@ -1,16 +1,18 @@
|
|
1
1
|
export const getVCWithRecommendations = async ({ vcId, storage }) => {
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
2
|
+
try {
|
3
|
+
const vcFolderId = await storage.getFileParents(vcId);
|
4
|
+
const files = await storage.findFilesUnderFolder(vcFolderId);
|
5
|
+
const relationsFile = files.find((f) => f.name === 'RELATIONS');
|
6
|
+
const relationsContent = await storage.retrieve(relationsFile.id);
|
7
|
+
const relationsData = relationsContent.data.body ? JSON.parse(relationsContent.data.body) : relationsContent.data;
|
8
|
+
const recommendationIds = relationsData.recommendations || [];
|
9
|
+
const vc = await storage.retrieve(vcId);
|
10
|
+
return { vc: vc, recommendationIds, relationsFileId: relationsFile.id };
|
11
|
+
}
|
12
|
+
catch (error) {
|
13
|
+
console.error('Error getting VC with recommendations:', error);
|
14
|
+
throw error;
|
15
|
+
}
|
14
16
|
};
|
15
17
|
/**
|
16
18
|
* Save data to Google Drive in the specified folder type.
|
@@ -53,7 +55,6 @@ export async function saveToGoogleDrive({ storage, data, type }) {
|
|
53
55
|
}
|
54
56
|
// Save the file in the specific subfolder
|
55
57
|
const file = await storage.saveFile({ data: fileData, folderId: typeFolderId });
|
56
|
-
console.log('🚀 ~ file:', file);
|
57
58
|
return file;
|
58
59
|
}
|
59
60
|
catch (error) {
|
@@ -62,17 +63,19 @@ export async function saveToGoogleDrive({ storage, data, type }) {
|
|
62
63
|
}
|
63
64
|
}
|
64
65
|
/**
|
65
|
-
* Upload
|
66
|
+
* Upload any type of file to Google Drive in the Credentials/MEDIAs folder.
|
66
67
|
* @param {GoogleDriveStorage} storage - The GoogleDriveStorage instance.
|
67
|
-
* @param {File}
|
68
|
-
* @
|
68
|
+
* @param {File} file - The file to upload.
|
69
|
+
* @param {string} folderName - The name of the folder where the file will be saved (default is 'MEDIAs').
|
70
|
+
* @returns {Promise<{ id: string }>} - The uploaded file object.
|
69
71
|
* @throws Will throw an error if the upload operation fails.
|
70
72
|
*/
|
71
|
-
export async function
|
73
|
+
export async function uploadToGoogleDrive(storage, file, folderName = 'MEDIAs') {
|
72
74
|
try {
|
73
75
|
const rootFolders = await storage.findFolders();
|
74
76
|
let credentialsFolder = rootFolders.find((f) => f.name === 'Credentials');
|
75
77
|
if (!credentialsFolder) {
|
78
|
+
console.log('Creating Credentials folder...');
|
76
79
|
credentialsFolder = await storage.createFolder({ folderName: 'Credentials', parentFolderId: 'root' });
|
77
80
|
}
|
78
81
|
const credentialsFolderId = credentialsFolder.id;
|
@@ -83,17 +86,16 @@ export async function uploadImageToGoogleDrive(storage, imageFile) {
|
|
83
86
|
}
|
84
87
|
const mediasFolderId = mediasFolder.id;
|
85
88
|
// Prepare the image file data
|
86
|
-
const
|
87
|
-
fileName:
|
88
|
-
mimeType:
|
89
|
-
body:
|
89
|
+
const fileMetaData = {
|
90
|
+
fileName: file.name,
|
91
|
+
mimeType: file.type,
|
92
|
+
body: file,
|
90
93
|
};
|
91
94
|
// SaveFile the image in the "MEDIAs" folder
|
92
95
|
const uploadedImage = await storage.saveFile({
|
93
|
-
data:
|
96
|
+
data: fileMetaData,
|
94
97
|
folderId: mediasFolderId,
|
95
98
|
});
|
96
|
-
console.log('🚀 ~ uploadedImage:', uploadedImage);
|
97
99
|
return uploadedImage;
|
98
100
|
}
|
99
101
|
catch (error) {
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@cooperation/vc-storage",
|
3
3
|
"type": "module",
|
4
|
-
"version": "1.0.
|
4
|
+
"version": "1.0.23",
|
5
5
|
"description": "Sign and store your verifiable credentials.",
|
6
6
|
"main": "dist/index.js",
|
7
7
|
"types": "dist/types/index.d.ts",
|
@@ -10,7 +10,7 @@
|
|
10
10
|
],
|
11
11
|
"scripts": {
|
12
12
|
"build": "tsc",
|
13
|
-
"test": "
|
13
|
+
"test": "vitest",
|
14
14
|
"version": "npm version patch"
|
15
15
|
},
|
16
16
|
"author": "cooperation",
|
@@ -22,26 +22,18 @@
|
|
22
22
|
"@digitalbazaar/vc": "^6.3.0",
|
23
23
|
"crypto-js": "^4.2.0",
|
24
24
|
"ethers": "^6.13.2",
|
25
|
+
"jest": "^29.7.0",
|
26
|
+
"ts-jest": "^29.2.5",
|
25
27
|
"ts-node": "^10.9.2",
|
26
28
|
"tsc": "^2.0.4",
|
27
29
|
"uuid": "^10.0.0"
|
28
30
|
},
|
29
31
|
"devDependencies": {
|
30
32
|
"@types/fs-extra": "^11.0.4",
|
31
|
-
"@types/jest": "^29.5.
|
33
|
+
"@types/jest": "^29.5.14",
|
32
34
|
"@types/uuid": "^10.0.0",
|
33
|
-
"
|
34
|
-
|
35
|
-
|
36
|
-
"preset": "ts-jest",
|
37
|
-
"testEnvironment": "node",
|
38
|
-
"moduleFileExtensions": [
|
39
|
-
"ts",
|
40
|
-
"tsx",
|
41
|
-
"js"
|
42
|
-
],
|
43
|
-
"roots": [
|
44
|
-
"<rootDir>/tests"
|
45
|
-
]
|
35
|
+
"babel-jest": "^29.7.0",
|
36
|
+
"typescript": "^5.6.2",
|
37
|
+
"vitest": "^3.0.5"
|
46
38
|
}
|
47
39
|
}
|