@cooperation/vc-storage 1.0.13 → 1.0.15

Sign up to get free protection for your applications and to get access to all the features.
package/dist/index.js CHANGED
@@ -2,3 +2,5 @@ export * from './models/GoogleDriveStorage.js';
2
2
  export * from './models/CredentialEngine.js';
3
3
  export * from './utils/google.js';
4
4
  export * from './utils/presentation.js';
5
+ export * from './models/Resume.js';
6
+ export * from './models/ResumeVC.js';
@@ -51,6 +51,22 @@ export class GoogleDriveStorage {
51
51
  throw error;
52
52
  }
53
53
  }
54
+ async getFileContent(fileId) {
55
+ const url = `https://www.googleapis.com/drive/v3/files/${fileId}?alt=media`;
56
+ try {
57
+ const response = await this.fetcher({
58
+ method: 'GET',
59
+ headers: {}, // Add additional headers if required
60
+ url,
61
+ });
62
+ console.log(`Content fetched for file ID: ${fileId}`);
63
+ return response; // This could be text, JSON, or binary, depending on the file type
64
+ }
65
+ catch (error) {
66
+ console.error(`Error fetching content for file ID: ${fileId}:`, error.message);
67
+ throw new Error(`Failed to fetch content for file ID: ${fileId}`);
68
+ }
69
+ }
54
70
  async searchFiles(query) {
55
71
  const result = await this.fetcher({
56
72
  method: 'GET',
@@ -63,11 +79,14 @@ export class GoogleDriveStorage {
63
79
  }
64
80
  return result.files;
65
81
  }
66
- async createFolder(folderName, parentFolderId) {
82
+ async createFolder({ folderName, parentFolderId }) {
83
+ if (!parentFolderId) {
84
+ throw new Error(`Parent folder ID must be provided when creating folder "${folderName}".`);
85
+ }
67
86
  const metadata = {
68
87
  name: folderName,
69
88
  mimeType: 'application/vnd.google-apps.folder',
70
- parents: parentFolderId ? [parentFolderId] : [],
89
+ parents: [parentFolderId], // Explicitly associate with the parent folder
71
90
  };
72
91
  const folder = await this.fetcher({
73
92
  method: 'POST',
@@ -75,27 +94,44 @@ export class GoogleDriveStorage {
75
94
  body: JSON.stringify(metadata),
76
95
  url: 'https://www.googleapis.com/drive/v3/files',
77
96
  });
78
- console.log('Folder ID:', folder.id);
79
- return folder.id;
97
+ console.log(`Folder created: "${folderName}" with ID: ${folder.id}, Parent: ${parentFolderId}`);
98
+ return folder;
80
99
  }
81
100
  async saveFile({ data, folderId }) {
82
101
  try {
102
+ if (!folderId) {
103
+ throw new Error('Folder ID is required to save a file.');
104
+ }
83
105
  // Define file metadata, ensure correct folder is assigned
84
106
  const fileMetadata = {
85
- name: data.fileName,
107
+ name: data.fileName || 'resume.json', // Use the provided fileName or default to 'resume.json'
86
108
  parents: [folderId], // Specify the folder ID
87
- mimeType: data.mimeType,
109
+ mimeType: 'application/json', // Ensure the MIME type is set to JSON
88
110
  };
89
- let uploadUrl = 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart';
111
+ // Check if the parent folder is in the trash
112
+ const folder = await this.fetcher({
113
+ method: 'GET',
114
+ headers: {},
115
+ url: `https://www.googleapis.com/drive/v3/files/${folderId}?fields=trashed`,
116
+ });
117
+ if (folder.trashed) {
118
+ throw new Error('Parent folder is in trash');
119
+ }
120
+ // Prepare the file content as a JSON string
121
+ const fileContent = JSON.stringify(data);
122
+ // Create a Blob from the JSON string
123
+ const fileBlob = new Blob([fileContent], { type: 'application/json' });
124
+ // Create FormData and append the metadata and file content
90
125
  const formData = new FormData();
91
126
  formData.append('metadata', new Blob([JSON.stringify(fileMetadata)], { type: 'application/json' }));
92
- formData.append('file', new Blob([data.body], { type: fileMetadata.mimeType })); // Set file data and MIME type
127
+ formData.append('file', fileBlob);
93
128
  // Upload file to Google Drive
129
+ console.log('Uploading file...');
94
130
  const file = await this.fetcher({
95
131
  method: 'POST',
96
132
  headers: {},
97
133
  body: formData,
98
- url: `${uploadUrl}&fields=id,parents`, // Request the file ID and parent folder IDs
134
+ url: `https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id,parents`,
99
135
  });
100
136
  // Set the file permission to "Anyone with the link" can view
101
137
  const permissionUrl = `https://www.googleapis.com/drive/v3/files/${file.id}/permissions`;
@@ -109,8 +145,6 @@ export class GoogleDriveStorage {
109
145
  headers: {},
110
146
  body: JSON.stringify(permissionData),
111
147
  });
112
- console.log('Permission set to public for file:', file.id);
113
- console.log('Parent folder IDs:', file.parents);
114
148
  return file;
115
149
  }
116
150
  catch (error) {
@@ -197,58 +231,6 @@ export class GoogleDriveStorage {
197
231
  const folders = await this.searchFiles(query);
198
232
  return folders.filter((file) => file.mimeType === 'application/vnd.google-apps.folder');
199
233
  };
200
- /**
201
- * Get the last file from folder by folderId
202
- * @param folderId
203
- * @returns last file content from folder by folderId
204
- */
205
- findLastFile = async (folderId) => {
206
- try {
207
- const files = await this.searchFiles(`'${folderId}' in parents`);
208
- const fileContents = await Promise.all(files
209
- .filter((file) => file.mimeType !== 'application/vnd.google-apps.folder')
210
- .map(async (file) => {
211
- const content = await this.fetcher({
212
- method: 'GET',
213
- headers: {},
214
- url: `https://www.googleapis.com/drive/v3/files/${file.id}?alt=media`,
215
- });
216
- return {
217
- ...file,
218
- content,
219
- };
220
- }));
221
- // Find the latest file based on the timestamp in the file name
222
- const latestFile = fileContents.reduce((latest, current) => {
223
- // Check if the file name has the expected structure
224
- const nameParts = current.name.split('_');
225
- let currentTimestampStr;
226
- if (nameParts.length === 3) {
227
- // Structure with UUID: `${uuid}_${type}_${timestamp}.json`
228
- currentTimestampStr = nameParts[2];
229
- }
230
- else if (nameParts.length === 2) {
231
- // Structure without UUID: `${type}_${timestamp}.json`
232
- currentTimestampStr = nameParts[1];
233
- }
234
- else {
235
- // Log warning and skip this file if the structure is not as expected
236
- console.warn(`Unexpected file name format: ${current.name}`);
237
- return latest;
238
- }
239
- // Parse the timestamp from the file name
240
- const latestTimestamp = latest ? parseInt(latest.name.split('_').pop().split('.')[0], 10) : 0;
241
- const currentTimestamp = parseInt(currentTimestampStr.split('.')[0], 10);
242
- return currentTimestamp > latestTimestamp ? current : latest;
243
- }, null);
244
- // Return the content of the latest file
245
- return latestFile ? latestFile.content : null;
246
- }
247
- catch (error) {
248
- console.error('Error finding last file:', error);
249
- return null;
250
- }
251
- };
252
234
  /**
253
235
  * Get all files content for the specified type ('KEYPAIRs' | 'VCs' | 'SESSIONs' | 'DIDs' | 'RECOMMENDATIONs')
254
236
  * @param type
@@ -283,18 +265,6 @@ export class GoogleDriveStorage {
283
265
  return await this.retrieve(file.id);
284
266
  }));
285
267
  }));
286
- // for (const folder of vcSubfolders) {
287
- // const files = await this.findFilesUnderFolder(folder.id);
288
- // // Fetch the content of each file
289
- // for (const file of files) {
290
- // try {
291
- // const content = await this.retrieve(file.id);
292
- // fileContents.push(content);
293
- // } catch (error) {
294
- // console.error(`Error retrieving content for file ${file.id}:`, error);
295
- // }
296
- // }
297
- // }
298
268
  return fileContents;
299
269
  }
300
270
  // Step 3: Generic handling for other types
@@ -358,8 +328,24 @@ export class GoogleDriveStorage {
358
328
  if (!folderId)
359
329
  throw new Error('Folder ID is required');
360
330
  console.log('🚀 ~ GoogleDriveStorage ~ findFilesUnderFolder ~ folderId', folderId);
331
+ // Fetch files under the folder
361
332
  const files = await this.searchFiles(`'${folderId}' in parents`);
362
- return files;
333
+ if (files.length === 0) {
334
+ console.log('No files found in the folder.');
335
+ return [];
336
+ }
337
+ // Fetch content for each file
338
+ const filesWithContent = await Promise.all(files.map(async (file) => {
339
+ try {
340
+ const content = await this.getFileContent(file.id);
341
+ return { ...file, content }; // Merge file metadata with its content
342
+ }
343
+ catch (error) {
344
+ console.error(`Error fetching content for file "${file.name}" (ID: ${file.id}):`, error);
345
+ return { ...file, content: null }; // Handle errors gracefully
346
+ }
347
+ }));
348
+ return filesWithContent;
363
349
  }
364
350
  async updateFileData(fileId, data) {
365
351
  const fileMetadata = {
@@ -0,0 +1,110 @@
1
+ export const resumeFolderTypes = {
2
+ root: 'RESUMES_AUTHOR',
3
+ nonSigned: 'NON_SIGNED_RESUMES',
4
+ signed: 'SIGNED_RESUMES',
5
+ };
6
+ export class StorageHandler {
7
+ storage;
8
+ constructor(storage) {
9
+ this.storage = storage;
10
+ }
11
+ async getOrCreateFolder(folderName, parentId) {
12
+ const folders = await this.storage.findFolders(parentId); // Fetch all child folders of the parent
13
+ let folder = folders.find((folder) => {
14
+ return folder.name === folderName;
15
+ });
16
+ if (!folder) {
17
+ folder = await this.storage.createFolder({
18
+ folderName,
19
+ parentFolderId: parentId,
20
+ });
21
+ }
22
+ return folder;
23
+ }
24
+ async findFilesInFolder(folderName) {
25
+ const folders = await this.storage.findFolders();
26
+ const folder = folders.find((folder) => folder.name === folderName);
27
+ if (!folder) {
28
+ throw new Error(`${folderName} folder not found`);
29
+ }
30
+ return this.storage.findFilesUnderFolder(folder.id);
31
+ }
32
+ }
33
+ export class Resume extends StorageHandler {
34
+ constructor(storage) {
35
+ super(storage);
36
+ }
37
+ async saveResume({ resume, type }) {
38
+ try {
39
+ // Get or create the root folder
40
+ const rootFolders = await this.storage.findFolders();
41
+ let rootFolder = rootFolders.find((folder) => folder.name === resumeFolderTypes.root);
42
+ if (!rootFolder) {
43
+ rootFolder = await this.storage.createFolder({ folderName: resumeFolderTypes.root, parentFolderId: 'root' });
44
+ }
45
+ // Get or create the subfolder
46
+ const subFolderName = type === 'sign' ? resumeFolderTypes.signed : resumeFolderTypes.nonSigned;
47
+ const subFolder = await this.getOrCreateFolder(subFolderName, rootFolder.id);
48
+ // Save the file in the subfolder
49
+ const savedResume = await this.storage.saveFile({
50
+ folderId: subFolder.id, // Ensure this points to the subfolder
51
+ data: resume,
52
+ });
53
+ return savedResume;
54
+ }
55
+ catch (error) {
56
+ throw new Error(`Error while saving ${type} resume: ${error.message}`);
57
+ }
58
+ }
59
+ async find() {
60
+ try {
61
+ const signedResumes = await this.getSignedResumes();
62
+ const nonSignedResumes = await this.getNonSignedResumes();
63
+ return {
64
+ signed: signedResumes,
65
+ nonSigned: nonSignedResumes,
66
+ };
67
+ }
68
+ catch (error) {
69
+ throw new Error('Error while fetching resume: ' + error.message);
70
+ }
71
+ }
72
+ async getSignedResumes() {
73
+ try {
74
+ // Find the root folder first
75
+ const rootFolder = await this.findRootFolder();
76
+ // Find or create the signed resumes folder
77
+ const signedFolder = await this.getOrCreateFolder(resumeFolderTypes.signed, rootFolder.id);
78
+ // Retrieve all files from the signed folder
79
+ const files = await this.storage.findFilesUnderFolder(signedFolder.id);
80
+ return files;
81
+ }
82
+ catch (error) {
83
+ throw new Error('Error while fetching signed resumes: ' + error.message);
84
+ }
85
+ }
86
+ async getNonSignedResumes() {
87
+ try {
88
+ // Find the root folder first
89
+ const rootFolder = await this.findRootFolder();
90
+ // Find or create the non-signed resumes folder
91
+ const nonSignedFolder = await this.getOrCreateFolder(resumeFolderTypes.nonSigned, rootFolder.id);
92
+ // Retrieve all files from the non-signed folder
93
+ const files = await this.storage.findFilesUnderFolder(nonSignedFolder.id);
94
+ return files;
95
+ }
96
+ catch (error) {
97
+ throw new Error('Error while fetching non-signed resumes: ' + error.message);
98
+ }
99
+ }
100
+ async findRootFolder() {
101
+ const rootFolders = await this.storage.findFolders(); // Fetch all root-level folders
102
+ const rootFolder = rootFolders.find((folder) => folder.name === resumeFolderTypes.root);
103
+ if (!rootFolder) {
104
+ throw new Error(`Root folder "${resumeFolderTypes.root}" not found in the root directory.`);
105
+ }
106
+ return rootFolder;
107
+ }
108
+ isResumeFolderExist() { }
109
+ }
110
+ export default Resume;
@@ -0,0 +1,84 @@
1
+ import { Ed25519Signature2020 } from '@digitalbazaar/ed25519-signature-2020';
2
+ import { customDocumentLoader } from '../utils/digitalbazaar.js';
3
+ import { v4 as uuidv4 } from 'uuid';
4
+ import * as dbVc from '@digitalbazaar/vc';
5
+ import { Ed25519VerificationKey2020 } from '@digitalbazaar/ed25519-verification-key-2020';
6
+ import { generateDIDSchema } from '../utils/credential.js';
7
+ import { inlineResumeContext } from '../utils/context.js';
8
+ export class ResumeVC {
9
+ async sign({ formData, issuerDid, keyPair }) {
10
+ const unsignedCredential = this.generateUnsignedCredential({ formData, issuerDid });
11
+ const suite = new Ed25519Signature2020({
12
+ key: new Ed25519VerificationKey2020(keyPair), // Ensure proper initialization
13
+ verificationMethod: keyPair.id,
14
+ });
15
+ try {
16
+ const signedVC = await dbVc.issue({
17
+ credential: unsignedCredential,
18
+ suite,
19
+ documentLoader: customDocumentLoader,
20
+ });
21
+ console.log('Signed VC:', signedVC);
22
+ }
23
+ catch (error) {
24
+ console.error('Error signing VC:', error.message);
25
+ if (error.details) {
26
+ console.error('Error details:', JSON.stringify(error.details, null, 2));
27
+ }
28
+ throw error;
29
+ }
30
+ return unsignedCredential;
31
+ }
32
+ generateUnsignedCredential({ formData, issuerDid }) {
33
+ const unsignedCredential = {
34
+ '@context': [
35
+ 'https://www.w3.org/2018/credentials/v1', // Standard VC context
36
+ inlineResumeContext['@context'], // Inline context
37
+ ],
38
+ id: `urn:uuid:${uuidv4()}`, // Generate a dynamic UUID
39
+ type: ['VerifiableCredential'],
40
+ issuer: issuerDid,
41
+ issuanceDate: new Date().toISOString(),
42
+ credentialSubject: {
43
+ type: 'Resume',
44
+ person: {
45
+ name: {
46
+ formattedName: formData.formattedName,
47
+ },
48
+ primaryLanguage: formData.primaryLanguage,
49
+ },
50
+ narrative: formData.narrative,
51
+ employmentHistory: formData.employmentHistory,
52
+ skills: formData.skills,
53
+ educationAndLearning: formData.educationAndLearning,
54
+ },
55
+ };
56
+ return unsignedCredential;
57
+ }
58
+ generateKeyPair = async (address) => {
59
+ // Generate the key pair using the library's method
60
+ const keyPair = await Ed25519VerificationKey2020.generate();
61
+ // Configure key pair attributes
62
+ const a = address || keyPair.publicKeyMultibase;
63
+ keyPair.controller = `did:key:${a}`;
64
+ keyPair.id = `${keyPair.controller}#${a}`;
65
+ keyPair.revoked = false;
66
+ // The `signer` is already provided by the `Ed25519VerificationKey2020` instance
67
+ return keyPair;
68
+ };
69
+ /**
70
+ * Create a new DID with Digital Bazaar's Ed25519VerificationKey2020 key pair.
71
+ * @returns {Promise<{didDocument: object, keyPair: object}>} The created DID document and key pair.
72
+ * @throws Will throw an error if DID creation fails.
73
+ */
74
+ async createDID({ keyPair }) {
75
+ try {
76
+ const didDocument = await generateDIDSchema(keyPair);
77
+ return didDocument;
78
+ }
79
+ catch (error) {
80
+ console.error('Error creating DID:', error);
81
+ throw error;
82
+ }
83
+ }
84
+ }
@@ -2,3 +2,5 @@ export * from './models/GoogleDriveStorage.js';
2
2
  export * from './models/CredentialEngine.js';
3
3
  export * from './utils/google.js';
4
4
  export * from './utils/presentation.js';
5
+ export * from './models/Resume.js';
6
+ export * from './models/ResumeVC.js';
@@ -1,4 +1,4 @@
1
- import { DataToSaveI, FilesType } from '../../types';
1
+ import { DataToSaveI } from '../../types';
2
2
  interface FileContent {
3
3
  name: string;
4
4
  content: any;
@@ -23,10 +23,19 @@ export declare class GoogleDriveStorage {
23
23
  private accessToken;
24
24
  constructor(accessToken: string);
25
25
  private fetcher;
26
+ private getFileContent;
26
27
  private searchFiles;
27
- createFolder(folderName: string, parentFolderId?: string): Promise<string>;
28
+ createFolder({ folderName, parentFolderId }: {
29
+ folderName: string;
30
+ parentFolderId: string;
31
+ }): Promise<{
32
+ id: string;
33
+ name: string;
34
+ mimeType: string;
35
+ parents: string[];
36
+ }>;
28
37
  saveFile({ data, folderId }: {
29
- data: DataToSaveI;
38
+ data: any;
30
39
  folderId: string;
31
40
  }): Promise<any>;
32
41
  /**
@@ -45,12 +54,6 @@ export declare class GoogleDriveStorage {
45
54
  * @returns
46
55
  */
47
56
  findFolders: (folderId?: string) => Promise<any[]>;
48
- /**
49
- * Get the last file from folder by folderId
50
- * @param folderId
51
- * @returns last file content from folder by folderId
52
- */
53
- findLastFile: (folderId: string) => Promise<any>;
54
57
  /**
55
58
  * Get all files content for the specified type ('KEYPAIRs' | 'VCs' | 'SESSIONs' | 'DIDs' | 'RECOMMENDATIONs')
56
59
  * @param type
@@ -64,7 +67,7 @@ export declare class GoogleDriveStorage {
64
67
  * @returns The updated file metadata, including the new name
65
68
  */
66
69
  updateFileName(fileId: string, newFileName: string): Promise<any>;
67
- findFileByName(name: FilesType): Promise<any>;
70
+ findFileByName(name: string): Promise<any>;
68
71
  findFilesUnderFolder(folderId: string): Promise<any[]>;
69
72
  updateFileData(fileId: string, data: DataToSaveI): Promise<any>;
70
73
  getFileParents(fileId: string): Promise<any>;
@@ -0,0 +1,28 @@
1
+ import { GoogleDriveStorage } from './GoogleDriveStorage.js';
2
+ export declare const resumeFolderTypes: {
3
+ root: string;
4
+ nonSigned: string;
5
+ signed: string;
6
+ };
7
+ export declare class StorageHandler {
8
+ protected storage: GoogleDriveStorage;
9
+ constructor(storage: GoogleDriveStorage);
10
+ getOrCreateFolder(folderName: string, parentId: string): Promise<any>;
11
+ protected findFilesInFolder(folderName: string): Promise<any[]>;
12
+ }
13
+ export declare class Resume extends StorageHandler {
14
+ constructor(storage: GoogleDriveStorage);
15
+ saveResume({ resume, type }: {
16
+ resume: any;
17
+ type: 'sign' | 'unsigned';
18
+ }): Promise<any>;
19
+ find(): Promise<{
20
+ signed: any[];
21
+ nonSigned: any[];
22
+ }>;
23
+ getSignedResumes(): Promise<any[]>;
24
+ getNonSignedResumes(): Promise<any[]>;
25
+ private findRootFolder;
26
+ private isResumeFolderExist;
27
+ }
28
+ export default Resume;
@@ -0,0 +1,17 @@
1
+ export declare class ResumeVC {
2
+ sign({ formData, issuerDid, keyPair }: {
3
+ formData: any;
4
+ issuerDid: string;
5
+ keyPair: any;
6
+ }): Promise<any>;
7
+ private generateUnsignedCredential;
8
+ generateKeyPair: (address?: string) => Promise<any>;
9
+ /**
10
+ * Create a new DID with Digital Bazaar's Ed25519VerificationKey2020 key pair.
11
+ * @returns {Promise<{didDocument: object, keyPair: object}>} The created DID document and key pair.
12
+ * @throws Will throw an error if DID creation fails.
13
+ */
14
+ createDID({ keyPair }: {
15
+ keyPair: any;
16
+ }): Promise<import("../../types/credential.js").DidDocument>;
17
+ }
@@ -1,3 +1,33 @@
1
+ export declare const inlineResumeContext: {
2
+ '@context': {
3
+ '@vocab': string;
4
+ name: string;
5
+ formattedName: string;
6
+ primaryLanguage: string;
7
+ narrative: string;
8
+ text: string;
9
+ employmentHistory: {
10
+ '@id': string;
11
+ '@container': string;
12
+ };
13
+ company: string;
14
+ position: string;
15
+ duration: string;
16
+ skills: {
17
+ '@id': string;
18
+ '@container': string;
19
+ };
20
+ educationAndLearning: string;
21
+ degree: string;
22
+ institution: string;
23
+ year: string;
24
+ issuanceDate: string;
25
+ issuer: string;
26
+ credentialSubject: string;
27
+ person: string;
28
+ Resume: string;
29
+ };
30
+ };
1
31
  declare let localOBContext: {
2
32
  '@context': {
3
33
  '@protected': boolean;
@@ -1,4 +1,33 @@
1
- // Load the local context files
1
+ export const inlineResumeContext = {
2
+ '@context': {
3
+ '@vocab': 'https://schema.hropenstandards.org/4.4/',
4
+ name: 'https://schema.org/name',
5
+ formattedName: 'https://schema.org/formattedName',
6
+ primaryLanguage: 'https://schema.org/primaryLanguage',
7
+ narrative: 'https://schema.org/narrative',
8
+ text: 'https://schema.org/text',
9
+ employmentHistory: {
10
+ '@id': 'https://schema.org/employmentHistory',
11
+ '@container': '@list', // Specify list container
12
+ },
13
+ company: 'https://schema.org/company',
14
+ position: 'https://schema.org/jobTitle',
15
+ duration: 'https://schema.org/temporalCoverage',
16
+ skills: {
17
+ '@id': 'https://schema.org/skills',
18
+ '@container': '@list', // Specify list container
19
+ },
20
+ educationAndLearning: 'https://schema.org/educationAndLearning',
21
+ degree: 'https://schema.org/degree',
22
+ institution: 'https://schema.org/institution',
23
+ year: 'https://schema.org/year',
24
+ issuanceDate: 'https://schema.org/issuanceDate',
25
+ issuer: 'https://schema.org/issuer',
26
+ credentialSubject: 'https://schema.org/credentialSubject',
27
+ person: 'https://schema.org/Person', // Added person
28
+ Resume: 'https://schema.hropenstandards.org/4.4#Resume', // Map Resume to an absolute IRI
29
+ },
30
+ };
2
31
  let localOBContext = {
3
32
  '@context': {
4
33
  '@protected': true,
@@ -1,6 +1,6 @@
1
1
  import { Ed25519VerificationKey2020 } from '@digitalbazaar/ed25519-verification-key-2020';
2
- import crypto from 'crypto';
3
2
  import { v4 as uuidv4 } from 'uuid';
3
+ import CryptoJS from 'crypto-js';
4
4
  /**
5
5
  * Utility function to generate a hashed ID for a credential.
6
6
  * Excludes the `id` field when hashing.
@@ -11,7 +11,7 @@ function generateHashedId(credential) {
11
11
  // Exclude the `id` field from the hash
12
12
  const credentialWithoutId = { ...credential, id: undefined };
13
13
  const serialized = JSON.stringify(credentialWithoutId);
14
- return crypto.createHash('sha256').update(serialized).digest('hex');
14
+ return CryptoJS.SHA256(serialized).toString(CryptoJS.enc.Hex);
15
15
  }
16
16
  /**
17
17
  * Create a DID document using the provided key pair.
@@ -29,37 +29,31 @@ export async function saveToGoogleDrive({ storage, data, type }) {
29
29
  };
30
30
  // Get all root folders
31
31
  const rootFolders = await storage.findFolders();
32
- console.log('Root folders:', rootFolders);
33
32
  // Find or create the "Credentials" folder
34
33
  let credentialsFolder = rootFolders.find((f) => f.name === 'Credentials');
35
34
  let credentialsFolderId;
36
35
  if (!credentialsFolder) {
37
- credentialsFolderId = await storage.createFolder('Credentials');
38
- }
39
- else {
40
- credentialsFolderId = credentialsFolder.id;
36
+ credentialsFolder = await storage.createFolder({ folderName: 'Credentials', parentFolderId: 'root' });
41
37
  }
38
+ credentialsFolderId = credentialsFolder.id;
42
39
  // Get subfolders within the "Credentials" folder
43
40
  const subfolders = await storage.findFolders(credentialsFolderId);
44
41
  // Find or create the specific subfolder (DIDs or VCs)
45
42
  let typeFolder = subfolders.find((f) => f.name === `${type}s`);
46
43
  let typeFolderId;
47
44
  if (!typeFolder) {
48
- typeFolderId = await storage.createFolder(`${type}s`, credentialsFolderId);
49
- }
50
- else {
51
- typeFolderId = typeFolder.id;
45
+ typeFolder = await storage.createFolder({ folderName: `${type}s`, parentFolderId: credentialsFolderId });
52
46
  }
47
+ typeFolderId = typeFolder.id;
53
48
  if (type === 'VC') {
54
49
  // save the data in Credentials/VCs/VC-timestamp/vc.json
55
- const vcFolderId = await storage.createFolder(`${fileData.fileName}-${Date.now()}`, typeFolderId);
56
- const file = await storage.saveFile({ data: fileData, folderId: vcFolderId });
57
- console.log(`File uploaded: ${file?.id} under ${fileData.fileName} folder in VCs folder`);
50
+ const vcFolder = await storage.createFolder({ folderName: `${fileData.fileName}-${Date.now()}`, parentFolderId: typeFolderId });
51
+ const file = await storage.saveFile({ data: fileData, folderId: vcFolder.id });
58
52
  return file;
59
53
  }
60
54
  // Save the file in the specific subfolder
61
55
  const file = await storage.saveFile({ data: fileData, folderId: typeFolderId });
62
- console.log(`File uploaded: ${file?.id} under ${type}s with ID ${typeFolderId} folder in Credentials folder`);
56
+ console.log('🚀 ~ file:', file);
63
57
  return file;
64
58
  }
65
59
  catch (error) {
@@ -78,22 +72,16 @@ export async function uploadImageToGoogleDrive(storage, imageFile) {
78
72
  try {
79
73
  const rootFolders = await storage.findFolders();
80
74
  let credentialsFolder = rootFolders.find((f) => f.name === 'Credentials');
81
- let credentialsFolderId;
82
75
  if (!credentialsFolder) {
83
- credentialsFolderId = await storage.createFolder('Credentials');
76
+ credentialsFolder = await storage.createFolder({ folderName: 'Credentials', parentFolderId: 'root' });
84
77
  }
85
- else {
86
- credentialsFolderId = credentialsFolder.id;
87
- }
88
- const subfolders = await storage.findFolders(credentialsFolderId);
78
+ const credentialsFolderId = credentialsFolder.id;
79
+ const subfolders = await storage.findFolders(credentialsFolder.id);
89
80
  let mediasFolder = subfolders.find((f) => f.name === 'MEDIAs');
90
- let mediasFolderId;
91
81
  if (!mediasFolder) {
92
- mediasFolderId = await storage.createFolder('MEDIAs', credentialsFolderId);
93
- }
94
- else {
95
- mediasFolderId = mediasFolder.id;
82
+ mediasFolder = await storage.createFolder({ folderName: 'MEDIAs', parentFolderId: credentialsFolderId });
96
83
  }
84
+ const mediasFolderId = mediasFolder.id;
97
85
  // Prepare the image file data
98
86
  const imageData = {
99
87
  fileName: imageFile.name,
@@ -105,7 +93,7 @@ export async function uploadImageToGoogleDrive(storage, imageFile) {
105
93
  data: imageData,
106
94
  folderId: mediasFolderId,
107
95
  });
108
- console.log(`Image uploaded: ${uploadedImage?.id} to MEDIAs folder in Credentials`);
96
+ console.log('🚀 ~ uploadedImage:', uploadedImage);
109
97
  return uploadedImage;
110
98
  }
111
99
  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.13",
4
+ "version": "1.0.15",
5
5
  "description": "Sign and store your verifiable credentials.",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/types/index.d.ts",
@@ -20,6 +20,7 @@
20
20
  "@digitalbazaar/ed25519-signature-2020": "^5.3.0",
21
21
  "@digitalbazaar/ed25519-verification-key-2020": "^4.1.0",
22
22
  "@digitalbazaar/vc": "^6.3.0",
23
+ "crypto-js": "^4.2.0",
23
24
  "ethers": "^6.13.2",
24
25
  "ts-node": "^10.9.2",
25
26
  "tsc": "^2.0.4",