@cooperation/vc-storage 1.0.10 → 1.0.12
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/models/CredentialEngine.js +53 -18
- package/dist/models/GoogleDriveStorage.js +160 -128
- package/dist/types/models/CredentialEngine.d.ts +16 -3
- package/dist/types/models/GoogleDriveStorage.d.ts +18 -19
- package/dist/types/utils/credential.d.ts +29 -10
- package/dist/types/utils/google.d.ts +23 -5
- package/dist/utils/credential.js +131 -109
- package/dist/utils/google.js +31 -24
- package/dist/utils/presentation.js +26 -29
- package/package.json +1 -2
- package/dist/types/utils/saveToGoogle.d.ts +0 -10
- package/dist/utils/saveToGoogle.js +0 -65
@@ -1,6 +1,6 @@
|
|
1
1
|
import { Ed25519VerificationKey2020 } from '@digitalbazaar/ed25519-verification-key-2020';
|
2
2
|
import { Ed25519Signature2020 } from '@digitalbazaar/ed25519-signature-2020';
|
3
|
-
import * as
|
3
|
+
import * as dbVc from '@digitalbazaar/vc';
|
4
4
|
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';
|
@@ -19,11 +19,9 @@ import { GoogleDriveStorage } from './GoogleDriveStorage.js';
|
|
19
19
|
* @method signPresentation - Sign a Verifiable Presentation (VP).
|
20
20
|
*/
|
21
21
|
export class CredentialEngine {
|
22
|
-
uuid;
|
23
22
|
storage;
|
24
23
|
keyPair;
|
25
24
|
constructor(accessToken) {
|
26
|
-
this.uuid = uuidv4();
|
27
25
|
this.storage = new GoogleDriveStorage(accessToken);
|
28
26
|
}
|
29
27
|
async getKeyPair(vc) {
|
@@ -75,8 +73,11 @@ export class CredentialEngine {
|
|
75
73
|
async createDID() {
|
76
74
|
try {
|
77
75
|
const keyPair = await this.generateKeyPair();
|
78
|
-
const keyFile = await saveToGoogleDrive(
|
79
|
-
|
76
|
+
const keyFile = await saveToGoogleDrive({
|
77
|
+
storage: this.storage,
|
78
|
+
data: keyPair,
|
79
|
+
type: 'KEYPAIR',
|
80
|
+
});
|
80
81
|
const didDocument = await generateDIDSchema(keyPair);
|
81
82
|
return { didDocument, keyPair };
|
82
83
|
}
|
@@ -85,6 +86,23 @@ export class CredentialEngine {
|
|
85
86
|
throw error;
|
86
87
|
}
|
87
88
|
}
|
89
|
+
async findKeysAndDIDs() {
|
90
|
+
// find the DID document for the current user
|
91
|
+
const keyPairs = (await this.storage.getAllFilesByType('KEYPAIRs'));
|
92
|
+
console.log('🚀 ~ CredentialEngine ~ findDid ~ keyPairs:', keyPairs[0].data);
|
93
|
+
const DIDs = (await this.storage.getAllFilesByType('DIDs'));
|
94
|
+
console.log('🚀 ~ CredentialEngine ~ findKeysAndDIDs ~ DIDs:', DIDs);
|
95
|
+
console.log('🚀 ~ CredentialEngine ~ findKeysAndDIDs ~ DIDs:', DIDs[0]);
|
96
|
+
if (DIDs.length === 0 || keyPairs.length === 0)
|
97
|
+
return null;
|
98
|
+
const keyPair = keyPairs[0].data;
|
99
|
+
const didDocument = DIDs[0].data.didDocument;
|
100
|
+
console.log('🚀 ~ CredentialEngine ~ findKeysAndDIDs ~ didDocument:', didDocument);
|
101
|
+
return {
|
102
|
+
didDocument,
|
103
|
+
keyPair,
|
104
|
+
};
|
105
|
+
}
|
88
106
|
/**
|
89
107
|
* Create a new DID with user metamask address as controller
|
90
108
|
* @param walletrAddress
|
@@ -94,7 +112,11 @@ export class CredentialEngine {
|
|
94
112
|
async createWalletDID(walletrAddress) {
|
95
113
|
try {
|
96
114
|
const keyPair = await this.generateKeyPair(walletrAddress);
|
97
|
-
const keyFile = await saveToGoogleDrive(
|
115
|
+
const keyFile = await saveToGoogleDrive({
|
116
|
+
storage: this.storage,
|
117
|
+
data: keyPair,
|
118
|
+
type: 'KEYPAIR',
|
119
|
+
});
|
98
120
|
console.log('🚀 ~ CredentialEngine ~ createWalletDID ~ keyFile:', keyFile);
|
99
121
|
const didDocument = await generateDIDSchema(keyPair);
|
100
122
|
return { didDocument, keyPair };
|
@@ -109,20 +131,33 @@ export class CredentialEngine {
|
|
109
131
|
* @param {'VC' | 'RECOMMENDATION'} type - The signature type.
|
110
132
|
* @param {string} issuerId - The ID of the issuer [currently we put it as the did id]
|
111
133
|
* @param {KeyPair} keyPair - The key pair to use for signing.
|
134
|
+
* @param {FormDataI | RecommendationFormDataI} formData - The form data to include in the VC.
|
135
|
+
* @param {string} VCId - The ID of the credential when the type is RECOMMENDATION
|
112
136
|
* @returns {Promise<Credential>} The signed VC.
|
113
137
|
* @throws Will throw an error if VC signing fails.
|
114
138
|
*/
|
115
|
-
async signVC(
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
139
|
+
async signVC({ data, type, keyPair, issuerId, vcFileId }) {
|
140
|
+
console.log('🚀 ~ CredentialEngine ~ signVC ~ { data, type, keyPair, issuerId, vcFileId }:', {
|
141
|
+
data,
|
142
|
+
type,
|
143
|
+
keyPair,
|
144
|
+
issuerId,
|
145
|
+
vcFileId,
|
146
|
+
});
|
147
|
+
let vc;
|
148
|
+
let credential = generateUnsignedVC({ formData: data, issuerDid: issuerId });
|
149
|
+
if (type == 'RECOMMENDATION' && vcFileId) {
|
150
|
+
console.log('WOW');
|
151
|
+
vc = (await this.storage.retrieve(vcFileId));
|
152
|
+
credential = generateUnsignedRecommendation({ vc, recommendation: data, issuerDid: issuerId });
|
122
153
|
}
|
123
|
-
const suite = new Ed25519Signature2020({ key: keyPair, verificationMethod: keyPair.id });
|
124
154
|
try {
|
125
|
-
|
155
|
+
console.log('🚀 ~ CredentialEngine ~ signVC ~ credential:', credential);
|
156
|
+
if (!credential)
|
157
|
+
throw new Error('Invalid credential type');
|
158
|
+
const suite = new Ed25519Signature2020({ key: keyPair, verificationMethod: keyPair.id });
|
159
|
+
console.log('before');
|
160
|
+
const signedVC = await dbVc.issue({ credential, suite, documentLoader: customDocumentLoader });
|
126
161
|
return signedVC;
|
127
162
|
}
|
128
163
|
catch (error) {
|
@@ -143,7 +178,7 @@ export class CredentialEngine {
|
|
143
178
|
key: keyPair,
|
144
179
|
verificationMethod: keyPair.id,
|
145
180
|
});
|
146
|
-
const result = await
|
181
|
+
const result = await dbVc.verifyCredential({
|
147
182
|
credential,
|
148
183
|
suite,
|
149
184
|
documentLoader: customDocumentLoader,
|
@@ -169,7 +204,7 @@ export class CredentialEngine {
|
|
169
204
|
const id = `urn:uuid:${uuidv4()}`;
|
170
205
|
const keyPair = await this.getKeyPair(verifiableCredential[0]);
|
171
206
|
console.log('🚀 ~ CredentialEngine ~ createPresentation ~ keyPair:', keyPair);
|
172
|
-
const VP = await
|
207
|
+
const VP = await dbVc.createPresentation({ verifiableCredential, id, holder: keyPair.controller });
|
173
208
|
return VP;
|
174
209
|
}
|
175
210
|
catch (error) {
|
@@ -198,7 +233,7 @@ export class CredentialEngine {
|
|
198
233
|
verificationMethod: this.keyPair.id,
|
199
234
|
});
|
200
235
|
// Sign the presentation
|
201
|
-
const signedVP = await
|
236
|
+
const signedVP = await dbVc.signPresentation({
|
202
237
|
presentation,
|
203
238
|
suite,
|
204
239
|
documentLoader: customDocumentLoader,
|
@@ -1,4 +1,3 @@
|
|
1
|
-
import { generateViewLink } from '../utils/google.js';
|
2
1
|
/**
|
3
2
|
* @class GoogleDriveStorage
|
4
3
|
* @description Class to interact with Google Drive API
|
@@ -19,7 +18,6 @@ export class GoogleDriveStorage {
|
|
19
18
|
constructor(accessToken) {
|
20
19
|
this.accessToken = accessToken;
|
21
20
|
}
|
22
|
-
// Method to fetch data from Google Drive API
|
23
21
|
async fetcher({ method, headers, body, url }) {
|
24
22
|
try {
|
25
23
|
const res = await fetch(url, {
|
@@ -30,26 +28,39 @@ export class GoogleDriveStorage {
|
|
30
28
|
}),
|
31
29
|
body,
|
32
30
|
});
|
33
|
-
// Check
|
34
|
-
const
|
31
|
+
// Check the Content-Type to ensure it's JSON before parsing
|
32
|
+
const contentType = res.headers.get('Content-Type') || '';
|
33
|
+
let data;
|
34
|
+
if (contentType.includes('application/json')) {
|
35
|
+
data = await res.json();
|
36
|
+
}
|
37
|
+
else {
|
38
|
+
const text = await res.text();
|
39
|
+
console.error('Unexpected Response Type:', text);
|
40
|
+
throw new Error(`Expected JSON response but got: ${contentType}`);
|
41
|
+
}
|
42
|
+
// Handle non-200 HTTP responses
|
35
43
|
if (!res.ok) {
|
36
44
|
console.error('Error Response:', JSON.stringify(data));
|
37
|
-
throw new Error(data
|
45
|
+
throw new Error(data?.error?.message || 'Unknown error occurred');
|
38
46
|
}
|
39
47
|
return data;
|
40
48
|
}
|
41
49
|
catch (error) {
|
42
|
-
console.error('Error fetching data:', error.message);
|
50
|
+
console.error('Error fetching data:', error.message || error);
|
43
51
|
throw error;
|
44
52
|
}
|
45
53
|
}
|
46
|
-
// Method to search for files in Google Drive by query
|
47
54
|
async searchFiles(query) {
|
48
55
|
const result = await this.fetcher({
|
49
56
|
method: 'GET',
|
50
57
|
headers: {},
|
51
58
|
url: `https://www.googleapis.com/drive/v3/files?q=${encodeURIComponent(query)}&trashed=false&fields=files(id,name,mimeType,parents)`,
|
52
59
|
});
|
60
|
+
if (!result.files) {
|
61
|
+
console.error('No files found:', result);
|
62
|
+
return [];
|
63
|
+
}
|
53
64
|
return result.files;
|
54
65
|
}
|
55
66
|
async createFolder(folderName, parentFolderId) {
|
@@ -67,7 +78,7 @@ export class GoogleDriveStorage {
|
|
67
78
|
console.log('Folder ID:', folder.id);
|
68
79
|
return folder.id;
|
69
80
|
}
|
70
|
-
async
|
81
|
+
async saveFile({ data, folderId }) {
|
71
82
|
try {
|
72
83
|
// Define file metadata, ensure correct folder is assigned
|
73
84
|
const fileMetadata = {
|
@@ -79,86 +90,32 @@ export class GoogleDriveStorage {
|
|
79
90
|
const formData = new FormData();
|
80
91
|
formData.append('metadata', new Blob([JSON.stringify(fileMetadata)], { type: 'application/json' }));
|
81
92
|
formData.append('file', new Blob([data.body], { type: fileMetadata.mimeType })); // Set file data and MIME type
|
93
|
+
// Upload file to Google Drive
|
82
94
|
const file = await this.fetcher({
|
83
95
|
method: 'POST',
|
84
96
|
headers: {},
|
85
97
|
body: formData,
|
86
|
-
url: uploadUrl,
|
87
|
-
});
|
88
|
-
console.log('File uploaded successfully:', file.id);
|
89
|
-
return file;
|
90
|
-
}
|
91
|
-
catch (error) {
|
92
|
-
console.error('Error uploading file:', error.message);
|
93
|
-
return null;
|
94
|
-
}
|
95
|
-
}
|
96
|
-
/**
|
97
|
-
* Add comment to VC
|
98
|
-
* @param fileId - th id of VC file
|
99
|
-
* @returns
|
100
|
-
*/
|
101
|
-
async addCommentToFile(vcFileId, recommendationFileId) {
|
102
|
-
if (!recommendationFileId || !vcFileId || !this.accessToken) {
|
103
|
-
throw new Error('Missing required parameters: fileId, commentText, or accessToken');
|
104
|
-
}
|
105
|
-
const url = `https://www.googleapis.com/drive/v3/files/${vcFileId}/comments?fields=id,content,createdTime`;
|
106
|
-
const body = {
|
107
|
-
content: generateViewLink(recommendationFileId),
|
108
|
-
};
|
109
|
-
try {
|
110
|
-
const response = await fetch(url, {
|
111
|
-
method: 'POST',
|
112
|
-
headers: {
|
113
|
-
Authorization: `Bearer ${this.accessToken}`,
|
114
|
-
'Content-Type': 'application/json',
|
115
|
-
},
|
116
|
-
body: JSON.stringify(body),
|
98
|
+
url: `${uploadUrl}&fields=id,parents`, // Request the file ID and parent folder IDs
|
117
99
|
});
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
}
|
126
|
-
catch (error) {
|
127
|
-
console.error('Error adding comment to file:', error);
|
128
|
-
throw error;
|
129
|
-
}
|
130
|
-
}
|
131
|
-
/**
|
132
|
-
* Add commenter role to a file
|
133
|
-
* @param fileId
|
134
|
-
* @returns
|
135
|
-
*/
|
136
|
-
async addCommenterRoleToFile(fileId) {
|
137
|
-
const url = `https://www.googleapis.com/drive/v3/files/${fileId}/permissions`;
|
138
|
-
const body = {
|
139
|
-
role: 'commenter',
|
140
|
-
type: 'anyone',
|
141
|
-
};
|
142
|
-
try {
|
143
|
-
const response = await fetch(url, {
|
100
|
+
// Set the file permission to "Anyone with the link" can view
|
101
|
+
const permissionUrl = `https://www.googleapis.com/drive/v3/files/${file.id}/permissions`;
|
102
|
+
const permissionData = {
|
103
|
+
role: 'reader',
|
104
|
+
type: 'anyone', // Public access
|
105
|
+
};
|
106
|
+
await this.fetcher({
|
144
107
|
method: 'POST',
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
},
|
149
|
-
body: JSON.stringify(body),
|
108
|
+
url: permissionUrl,
|
109
|
+
headers: {},
|
110
|
+
body: JSON.stringify(permissionData),
|
150
111
|
});
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
}
|
155
|
-
const result = await response.json();
|
156
|
-
console.log('Permission added successfully:', result);
|
157
|
-
return result;
|
112
|
+
console.log('Permission set to public for file:', file.id);
|
113
|
+
console.log('Parent folder IDs:', file.parents);
|
114
|
+
return file;
|
158
115
|
}
|
159
116
|
catch (error) {
|
160
|
-
console.error('Error
|
161
|
-
|
117
|
+
console.error('Error uploading file or setting permission:', error.message);
|
118
|
+
return null;
|
162
119
|
}
|
163
120
|
}
|
164
121
|
/**
|
@@ -167,25 +124,24 @@ export class GoogleDriveStorage {
|
|
167
124
|
* @returns file content
|
168
125
|
*/
|
169
126
|
async retrieve(id) {
|
170
|
-
const metadataUrl = `https://www.googleapis.com/drive/v3/files/${id}?fields=name`;
|
127
|
+
const metadataUrl = `https://www.googleapis.com/drive/v3/files/${id}?fields=id,name`;
|
171
128
|
const dataUrl = `https://www.googleapis.com/drive/v3/files/${id}?alt=media`;
|
172
129
|
try {
|
173
|
-
|
174
|
-
|
175
|
-
const metadataResponse = await fetch(metadataUrl, {
|
130
|
+
// Initial "touch" request to ensure file accessibility for the current user
|
131
|
+
const touchResponse = await fetch(metadataUrl, {
|
176
132
|
method: 'GET',
|
177
133
|
headers: {
|
178
134
|
Authorization: `Bearer ${this.accessToken}`,
|
179
135
|
},
|
180
136
|
});
|
181
|
-
if (!
|
182
|
-
const errorData = await
|
183
|
-
console.error(`Failed to
|
137
|
+
if (!touchResponse.ok) {
|
138
|
+
const errorData = await touchResponse.json();
|
139
|
+
console.error(`Failed to "touch" file for accessibility with ID ${id}:`, errorData);
|
184
140
|
return null;
|
185
141
|
}
|
186
|
-
|
142
|
+
// Fetch file metadata to get the name
|
143
|
+
const metadata = await touchResponse.json();
|
187
144
|
const fileName = metadata.name;
|
188
|
-
console.log(`File name retrieved: ${fileName}`);
|
189
145
|
// Fetch actual file data
|
190
146
|
const dataResponse = await fetch(dataUrl, {
|
191
147
|
method: 'GET',
|
@@ -221,9 +177,8 @@ export class GoogleDriveStorage {
|
|
221
177
|
else {
|
222
178
|
fileData = await dataResponse.arrayBuffer(); // Fallback for other binary types
|
223
179
|
}
|
224
|
-
console.log('🚀 ~ GoogleDriveStorage ~ retrieve ~ fileData:', fileData);
|
225
180
|
// Return file ID, name, and data
|
226
|
-
return { id, name: fileName, data: fileData };
|
181
|
+
return { id: metadata.id, name: fileName, data: fileData };
|
227
182
|
}
|
228
183
|
catch (error) {
|
229
184
|
console.error(`Error retrieving file with ID ${id}:`, error.message);
|
@@ -232,7 +187,7 @@ export class GoogleDriveStorage {
|
|
232
187
|
}
|
233
188
|
/**
|
234
189
|
* Get folder by folderId, if folderId == null you will have them all
|
235
|
-
* @param
|
190
|
+
* @param folderId [Optional]
|
236
191
|
* @returns
|
237
192
|
*/
|
238
193
|
findFolders = async (folderId) => {
|
@@ -294,22 +249,6 @@ export class GoogleDriveStorage {
|
|
294
249
|
return null;
|
295
250
|
}
|
296
251
|
};
|
297
|
-
async getFileComments(fileId) {
|
298
|
-
try {
|
299
|
-
// Fetch comments on the file using Google Drive API
|
300
|
-
const commentsResponse = await this.fetcher({
|
301
|
-
method: 'GET',
|
302
|
-
headers: {},
|
303
|
-
url: `https://www.googleapis.com/drive/v3/files/${fileId}/comments?fields=comments(content,author/displayName,createdTime)`,
|
304
|
-
});
|
305
|
-
// Return the comments data if available
|
306
|
-
return commentsResponse.comments || []; // Return an empty array if no comments
|
307
|
-
}
|
308
|
-
catch (error) {
|
309
|
-
console.error(`Failed to fetch comments for file ID: ${fileId}`, error);
|
310
|
-
return []; // Handle errors by returning an empty array or some error indication
|
311
|
-
}
|
312
|
-
}
|
313
252
|
/**
|
314
253
|
* Get all files content for the specified type ('KEYPAIRs' | 'VCs' | 'SESSIONs' | 'DIDs' | 'RECOMMENDATIONs')
|
315
254
|
* @param type
|
@@ -317,42 +256,60 @@ export class GoogleDriveStorage {
|
|
317
256
|
*/
|
318
257
|
async getAllFilesByType(type) {
|
319
258
|
try {
|
320
|
-
// Step 1: Find
|
259
|
+
// Step 1: Find the root 'Credentials' folder
|
321
260
|
const rootFolders = await this.findFolders();
|
322
261
|
const credentialsFolder = rootFolders.find((f) => f.name === 'Credentials');
|
323
|
-
if (!credentialsFolder)
|
262
|
+
if (!credentialsFolder) {
|
263
|
+
console.error('Credentials folder not found.');
|
324
264
|
return [];
|
265
|
+
}
|
325
266
|
const credentialsFolderId = credentialsFolder.id;
|
326
|
-
// Step 2:
|
267
|
+
// Step 2: Handle special case for 'VCs'
|
268
|
+
if (type === 'VCs') {
|
269
|
+
// Find the 'VCs' folder under 'Credentials'
|
270
|
+
const subfolders = await this.findFolders(credentialsFolderId);
|
271
|
+
const targetFolder = subfolders.find((f) => f.name === 'VCs');
|
272
|
+
if (!targetFolder) {
|
273
|
+
console.error(`Folder for type ${type} not found.`);
|
274
|
+
return [];
|
275
|
+
}
|
276
|
+
const targetFolderId = targetFolder.id;
|
277
|
+
// Fetch all 'VC-timestamp' subfolders under 'VCs'
|
278
|
+
const vcSubfolders = await this.findFolders(targetFolderId);
|
279
|
+
// Retrieve all 'VC.json' files from each 'VC-timestamp' subfolder
|
280
|
+
const fileContents = [];
|
281
|
+
for (const folder of vcSubfolders) {
|
282
|
+
const files = await this.findFilesUnderFolder(folder.id);
|
283
|
+
// Fetch the content of each file
|
284
|
+
for (const file of files) {
|
285
|
+
try {
|
286
|
+
const content = await this.retrieve(file.id);
|
287
|
+
fileContents.push(content);
|
288
|
+
}
|
289
|
+
catch (error) {
|
290
|
+
console.error(`Error retrieving content for file ${file.id}:`, error);
|
291
|
+
}
|
292
|
+
}
|
293
|
+
}
|
294
|
+
return fileContents;
|
295
|
+
}
|
296
|
+
// Step 3: Generic handling for other types
|
327
297
|
const subfolders = await this.findFolders(credentialsFolderId);
|
328
298
|
const targetFolder = subfolders.find((f) => f.name === type);
|
329
|
-
if (!targetFolder)
|
299
|
+
if (!targetFolder) {
|
300
|
+
console.error(`Folder for type ${type} not found.`);
|
330
301
|
return [];
|
331
|
-
|
302
|
+
}
|
332
303
|
const filesResponse = await this.fetcher({
|
333
304
|
method: 'GET',
|
334
305
|
headers: {},
|
335
306
|
url: `https://www.googleapis.com/drive/v3/files?q='${targetFolder.id}' in parents and trashed=false&fields=files(id,name,mimeType,parents)`,
|
336
307
|
});
|
337
308
|
const files = filesResponse.files;
|
338
|
-
// Step 4: Fetch the content and comments of each file
|
339
309
|
const fileContents = await Promise.all(files.map(async (file) => {
|
340
|
-
|
341
|
-
const content = await this.fetcher({
|
342
|
-
method: 'GET',
|
343
|
-
headers: {},
|
344
|
-
url: `https://www.googleapis.com/drive/v3/files/${file.id}?alt=media`,
|
345
|
-
});
|
346
|
-
// Fetch file comments (if applicable)
|
347
|
-
const comments = await this.getFileComments(file.id);
|
348
|
-
return {
|
349
|
-
id: file.id,
|
350
|
-
name: file.name,
|
351
|
-
content,
|
352
|
-
comments: comments.map((comment) => comment.content),
|
353
|
-
};
|
310
|
+
return await this.retrieve(file.id);
|
354
311
|
}));
|
355
|
-
return fileContents;
|
312
|
+
return fileContents;
|
356
313
|
}
|
357
314
|
catch (error) {
|
358
315
|
console.error(`Error getting files of type ${type}:`, error);
|
@@ -384,6 +341,81 @@ export class GoogleDriveStorage {
|
|
384
341
|
throw error;
|
385
342
|
}
|
386
343
|
}
|
344
|
+
async findFileByName(name) {
|
345
|
+
// find the file named under Credentials folder
|
346
|
+
const rootFolders = await this.findFolders();
|
347
|
+
const credentialsFolderId = rootFolders.find((f) => f.name === 'Credentials')?.id;
|
348
|
+
if (!credentialsFolderId)
|
349
|
+
throw new Error('Credentials folder not found');
|
350
|
+
const files = await this.searchFiles(`'${credentialsFolderId}' in parents and name='${name}'`);
|
351
|
+
return files[0];
|
352
|
+
}
|
353
|
+
async findFilesUnderFolder(folderId) {
|
354
|
+
if (!folderId)
|
355
|
+
throw new Error('Folder ID is required');
|
356
|
+
console.log('🚀 ~ GoogleDriveStorage ~ findFilesUnderFolder ~ folderId', folderId);
|
357
|
+
const files = await this.searchFiles(`'${folderId}' in parents`);
|
358
|
+
return files;
|
359
|
+
}
|
360
|
+
async updateFileData(fileId, data) {
|
361
|
+
const fileMetadata = {
|
362
|
+
name: data.fileName,
|
363
|
+
mimeType: data.mimeType,
|
364
|
+
};
|
365
|
+
let uploadUrl = `https://www.googleapis.com/upload/drive/v3/files/${fileId}?uploadType=multipart`;
|
366
|
+
const formData = new FormData();
|
367
|
+
formData.append('metadata', new Blob([JSON.stringify(fileMetadata)], { type: 'application/json' }));
|
368
|
+
formData.append('file', new Blob([data.body], { type: fileMetadata.mimeType }));
|
369
|
+
const updatedFile = await this.fetcher({
|
370
|
+
method: 'PATCH',
|
371
|
+
headers: {},
|
372
|
+
body: JSON.stringify(formData),
|
373
|
+
url: `${uploadUrl}&fields=id,parents`,
|
374
|
+
});
|
375
|
+
console.log('File updated:', updatedFile);
|
376
|
+
return updatedFile;
|
377
|
+
}
|
378
|
+
async getFileParents(fileId) {
|
379
|
+
console.log('🚀 ~ GoogleDriveStorage ~ getFileParents ~ fileId', fileId);
|
380
|
+
const file = await this.fetcher({
|
381
|
+
method: 'GET',
|
382
|
+
headers: {},
|
383
|
+
url: `https://www.googleapis.com/drive/v3/files/${fileId}?fields=parents`,
|
384
|
+
});
|
385
|
+
return file.parents;
|
386
|
+
}
|
387
|
+
async updateRelationsFile({ relationsFileId, recommendationFileId }) {
|
388
|
+
const relationsFileContent = await this.retrieve(relationsFileId);
|
389
|
+
const relationsData = relationsFileContent.data;
|
390
|
+
relationsData.recommendations.push(recommendationFileId);
|
391
|
+
const updatedContent = JSON.stringify(relationsData);
|
392
|
+
const updateResponse = await this.fetcher({
|
393
|
+
method: 'PATCH',
|
394
|
+
headers: {
|
395
|
+
'Content-Type': 'application/json',
|
396
|
+
},
|
397
|
+
body: updatedContent,
|
398
|
+
url: `https://www.googleapis.com/upload/drive/v3/files/${relationsFileId}?uploadType=media`,
|
399
|
+
});
|
400
|
+
console.log('🚀 ~ GoogleDriveStorage ~ updateRelationsFile ~ updateResponse:', updateResponse);
|
401
|
+
return updateResponse;
|
402
|
+
}
|
403
|
+
async createRelationsFile({ vcFolderId }) {
|
404
|
+
const files = await this.findFilesUnderFolder(vcFolderId);
|
405
|
+
const vcFile = files.find((file) => file.name === 'VC');
|
406
|
+
const relationsFile = await this.saveFile({
|
407
|
+
data: {
|
408
|
+
fileName: 'RELATIONS',
|
409
|
+
mimeType: 'application/json',
|
410
|
+
body: JSON.stringify({
|
411
|
+
vc_id: vcFile.id,
|
412
|
+
recommendations: [],
|
413
|
+
}),
|
414
|
+
},
|
415
|
+
folderId: vcFolderId,
|
416
|
+
});
|
417
|
+
return relationsFile;
|
418
|
+
}
|
387
419
|
/**
|
388
420
|
* Delete file by id
|
389
421
|
* @param id
|
@@ -1,4 +1,11 @@
|
|
1
|
-
import { DidDocument, KeyPair, VerifiableCredential } from '../../types/credential.js';
|
1
|
+
import { DidDocument, KeyPair, FormDataI, RecommendationFormDataI, VerifiableCredential } from '../../types/credential.js';
|
2
|
+
interface SignPropsI {
|
3
|
+
data: FormDataI | RecommendationFormDataI;
|
4
|
+
type: 'VC' | 'RECOMMENDATION';
|
5
|
+
keyPair: KeyPair;
|
6
|
+
issuerId: string;
|
7
|
+
vcFileId?: string;
|
8
|
+
}
|
2
9
|
/**
|
3
10
|
* Class representing the Credential Engine.
|
4
11
|
* @class CredentialEngine
|
@@ -12,7 +19,6 @@ import { DidDocument, KeyPair, VerifiableCredential } from '../../types/credenti
|
|
12
19
|
* @method signPresentation - Sign a Verifiable Presentation (VP).
|
13
20
|
*/
|
14
21
|
export declare class CredentialEngine {
|
15
|
-
private uuid;
|
16
22
|
private storage;
|
17
23
|
private keyPair;
|
18
24
|
constructor(accessToken: string);
|
@@ -28,6 +34,10 @@ export declare class CredentialEngine {
|
|
28
34
|
didDocument: DidDocument;
|
29
35
|
keyPair: KeyPair;
|
30
36
|
}>;
|
37
|
+
findKeysAndDIDs(): Promise<{
|
38
|
+
didDocument: any;
|
39
|
+
keyPair: any;
|
40
|
+
}>;
|
31
41
|
/**
|
32
42
|
* Create a new DID with user metamask address as controller
|
33
43
|
* @param walletrAddress
|
@@ -43,10 +53,12 @@ export declare class CredentialEngine {
|
|
43
53
|
* @param {'VC' | 'RECOMMENDATION'} type - The signature type.
|
44
54
|
* @param {string} issuerId - The ID of the issuer [currently we put it as the did id]
|
45
55
|
* @param {KeyPair} keyPair - The key pair to use for signing.
|
56
|
+
* @param {FormDataI | RecommendationFormDataI} formData - The form data to include in the VC.
|
57
|
+
* @param {string} VCId - The ID of the credential when the type is RECOMMENDATION
|
46
58
|
* @returns {Promise<Credential>} The signed VC.
|
47
59
|
* @throws Will throw an error if VC signing fails.
|
48
60
|
*/
|
49
|
-
signVC(
|
61
|
+
signVC({ data, type, keyPair, issuerId, vcFileId }: SignPropsI): Promise<any>;
|
50
62
|
/**
|
51
63
|
* Verify a Verifiable Credential (VC)
|
52
64
|
* @param {object} credential - The Verifiable Credential to verify.
|
@@ -67,3 +79,4 @@ export declare class CredentialEngine {
|
|
67
79
|
*/
|
68
80
|
signPresentation(presentation: any): Promise<any>;
|
69
81
|
}
|
82
|
+
export {};
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { DataToSaveI } from '../../types';
|
1
|
+
import { DataToSaveI, FilesType } from '../../types';
|
2
2
|
interface FileContent {
|
3
3
|
name: string;
|
4
4
|
content: any;
|
@@ -25,34 +25,23 @@ export declare class GoogleDriveStorage {
|
|
25
25
|
private fetcher;
|
26
26
|
private searchFiles;
|
27
27
|
createFolder(folderName: string, parentFolderId?: string): Promise<string>;
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
* Add comment to VC
|
33
|
-
* @param fileId - th id of VC file
|
34
|
-
* @returns
|
35
|
-
*/
|
36
|
-
addCommentToFile(vcFileId: string, recommendationFileId: string): Promise<any>;
|
37
|
-
/**
|
38
|
-
* Add commenter role to a file
|
39
|
-
* @param fileId
|
40
|
-
* @returns
|
41
|
-
*/
|
42
|
-
addCommenterRoleToFile(fileId: string): Promise<any>;
|
28
|
+
saveFile({ data, folderId }: {
|
29
|
+
data: DataToSaveI;
|
30
|
+
folderId: string;
|
31
|
+
}): Promise<any>;
|
43
32
|
/**
|
44
33
|
* Get file from google drive by id
|
45
34
|
* @param id
|
46
35
|
* @returns file content
|
47
36
|
*/
|
48
37
|
retrieve(id: string): Promise<{
|
49
|
-
id: string;
|
50
38
|
name: string;
|
51
39
|
data: any;
|
40
|
+
id: string;
|
52
41
|
} | null>;
|
53
42
|
/**
|
54
43
|
* Get folder by folderId, if folderId == null you will have them all
|
55
|
-
* @param
|
44
|
+
* @param folderId [Optional]
|
56
45
|
* @returns
|
57
46
|
*/
|
58
47
|
findFolders: (folderId?: string) => Promise<any[]>;
|
@@ -62,7 +51,6 @@ export declare class GoogleDriveStorage {
|
|
62
51
|
* @returns last file content from folder by folderId
|
63
52
|
*/
|
64
53
|
findLastFile: (folderId: string) => Promise<any>;
|
65
|
-
getFileComments(fileId: string): Promise<any>;
|
66
54
|
/**
|
67
55
|
* Get all files content for the specified type ('KEYPAIRs' | 'VCs' | 'SESSIONs' | 'DIDs' | 'RECOMMENDATIONs')
|
68
56
|
* @param type
|
@@ -76,6 +64,17 @@ export declare class GoogleDriveStorage {
|
|
76
64
|
* @returns The updated file metadata, including the new name
|
77
65
|
*/
|
78
66
|
updateFileName(fileId: string, newFileName: string): Promise<any>;
|
67
|
+
findFileByName(name: FilesType): Promise<any>;
|
68
|
+
findFilesUnderFolder(folderId: string): Promise<any[]>;
|
69
|
+
updateFileData(fileId: string, data: DataToSaveI): Promise<any>;
|
70
|
+
getFileParents(fileId: string): Promise<any>;
|
71
|
+
updateRelationsFile({ relationsFileId, recommendationFileId }: {
|
72
|
+
relationsFileId: string;
|
73
|
+
recommendationFileId: string;
|
74
|
+
}): Promise<any>;
|
75
|
+
createRelationsFile({ vcFolderId }: {
|
76
|
+
vcFolderId: string;
|
77
|
+
}): Promise<any>;
|
79
78
|
/**
|
80
79
|
* Delete file by id
|
81
80
|
* @param id
|