@cooperation/vc-storage 1.0.13 → 1.0.15

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/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",