@welshare/react 0.0.1-alpha.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -0
- package/dist/esm/hooks/use-welshare.d.ts +5 -0
- package/dist/esm/hooks/use-welshare.d.ts.map +1 -1
- package/dist/esm/hooks/use-welshare.js +113 -4
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +4 -1
- package/dist/esm/lib/encryption.d.ts +19 -0
- package/dist/esm/lib/encryption.d.ts.map +1 -0
- package/dist/esm/lib/encryption.js +61 -0
- package/dist/esm/lib/uploads.d.ts +3 -0
- package/dist/esm/lib/uploads.d.ts.map +1 -0
- package/dist/esm/lib/uploads.js +17 -0
- package/dist/esm/types.d.ts +31 -1
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/node_modules/@welshare/react/.DS_Store +0 -0
- package/dist/node_modules/@welshare/react/README.md +44 -0
- package/dist/node_modules/@welshare/react/dist/esm/hooks/use-welshare.d.ts +5 -0
- package/dist/node_modules/@welshare/react/dist/esm/hooks/use-welshare.d.ts.map +1 -1
- package/dist/node_modules/@welshare/react/dist/esm/hooks/use-welshare.js +113 -4
- package/dist/node_modules/@welshare/react/dist/esm/index.d.ts +3 -0
- package/dist/node_modules/@welshare/react/dist/esm/index.d.ts.map +1 -1
- package/dist/node_modules/@welshare/react/dist/esm/index.js +4 -1
- package/dist/node_modules/@welshare/react/dist/esm/lib/encryption.d.ts +19 -0
- package/dist/node_modules/@welshare/react/dist/esm/lib/encryption.d.ts.map +1 -0
- package/dist/node_modules/@welshare/react/dist/esm/lib/encryption.js +61 -0
- package/dist/node_modules/@welshare/react/dist/esm/lib/uploads.d.ts +3 -0
- package/dist/node_modules/@welshare/react/dist/esm/lib/uploads.d.ts.map +1 -0
- package/dist/node_modules/@welshare/react/dist/esm/lib/uploads.js +17 -0
- package/dist/node_modules/@welshare/react/dist/esm/types.d.ts +31 -1
- package/dist/node_modules/@welshare/react/dist/esm/types.d.ts.map +1 -1
- package/dist/node_modules/@welshare/react/package.json +1 -1
- package/dist/node_modules/@welshare/react/src/hooks/use-welshare.ts +153 -4
- package/dist/node_modules/@welshare/react/src/index.ts +13 -5
- package/dist/node_modules/@welshare/react/src/lib/encryption.ts +110 -0
- package/dist/node_modules/@welshare/react/src/lib/uploads.ts +29 -0
- package/dist/node_modules/@welshare/react/src/types.ts +37 -1
- package/package.json +1 -1
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
export const ALGORITHM = "AES-GCM";
|
|
2
|
+
export type Algorithm = "AES-GCM";
|
|
3
|
+
|
|
4
|
+
export const generateRandomAESKey = async (): Promise<CryptoKey> => {
|
|
5
|
+
// Generate a 256-bit AES-GCM key for file encryption
|
|
6
|
+
return window.crypto.subtle.generateKey(
|
|
7
|
+
{
|
|
8
|
+
name: ALGORITHM,
|
|
9
|
+
length: 256, // 256-bit key
|
|
10
|
+
},
|
|
11
|
+
true, // Key is extractable (needed for storage/transmission)
|
|
12
|
+
["encrypt", "decrypt"] // Key usage
|
|
13
|
+
);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/// also Generates random IV (12 bytes for AES-GCM)
|
|
17
|
+
/// @return {arraybuffer ciphertext, uint8array iv}
|
|
18
|
+
export const encryptFile = async (
|
|
19
|
+
file: File,
|
|
20
|
+
key: CryptoKey
|
|
21
|
+
): Promise<{ encryptedData: ArrayBuffer; iv: Uint8Array }> => {
|
|
22
|
+
// Read file as ArrayBuffer
|
|
23
|
+
const fileData = await file.arrayBuffer();
|
|
24
|
+
const iv = window.crypto.getRandomValues(new Uint8Array(12));
|
|
25
|
+
|
|
26
|
+
// Encrypt the file data
|
|
27
|
+
const encryptedData = await window.crypto.subtle.encrypt(
|
|
28
|
+
{
|
|
29
|
+
name: ALGORITHM,
|
|
30
|
+
iv: iv,
|
|
31
|
+
},
|
|
32
|
+
key,
|
|
33
|
+
fileData
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
return { encryptedData, iv };
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type EncryptionKey = {
|
|
40
|
+
algorithm: Algorithm;
|
|
41
|
+
key: string;
|
|
42
|
+
iv: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const encodeEncryptionKey = async (
|
|
46
|
+
key: CryptoKey,
|
|
47
|
+
iv: Uint8Array
|
|
48
|
+
): Promise<EncryptionKey> => {
|
|
49
|
+
// Export the key as raw bytes
|
|
50
|
+
const exportedKey = await window.crypto.subtle.exportKey("raw", key);
|
|
51
|
+
const keyHex = Array.from(new Uint8Array(exportedKey))
|
|
52
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
53
|
+
.join("");
|
|
54
|
+
|
|
55
|
+
const ivHex = Array.from(iv)
|
|
56
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
57
|
+
.join("");
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
algorithm: ALGORITHM,
|
|
61
|
+
key: keyHex,
|
|
62
|
+
iv: ivHex,
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const decodeEncryptionKey = (
|
|
67
|
+
encryptionKey: EncryptionKey
|
|
68
|
+
): { key: Uint8Array<ArrayBuffer>; iv: Uint8Array<ArrayBuffer> } => {
|
|
69
|
+
const keyBytes = new Uint8Array(
|
|
70
|
+
encryptionKey.key
|
|
71
|
+
.match(/.{1,2}/g)!
|
|
72
|
+
.map((byte: string) => parseInt(byte, 16))
|
|
73
|
+
);
|
|
74
|
+
const ivBytes = new Uint8Array(
|
|
75
|
+
encryptionKey.iv.match(/.{1,2}/g)!.map((byte: string) => parseInt(byte, 16))
|
|
76
|
+
);
|
|
77
|
+
return { key: keyBytes, iv: ivBytes };
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Helper function to decrypt a file using encoded encryption key
|
|
81
|
+
export const decrypt = async (
|
|
82
|
+
encryptedData: ArrayBuffer,
|
|
83
|
+
encryptionKey: EncryptionKey
|
|
84
|
+
): Promise<ArrayBuffer | null> => {
|
|
85
|
+
try {
|
|
86
|
+
const { key: keyBytes, iv } = decodeEncryptionKey(encryptionKey);
|
|
87
|
+
|
|
88
|
+
const key = await window.crypto.subtle.importKey(
|
|
89
|
+
"raw",
|
|
90
|
+
keyBytes,
|
|
91
|
+
{ name: ALGORITHM },
|
|
92
|
+
false,
|
|
93
|
+
["decrypt"]
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const decryptedData = await window.crypto.subtle.decrypt(
|
|
97
|
+
{
|
|
98
|
+
name: ALGORITHM,
|
|
99
|
+
iv,
|
|
100
|
+
},
|
|
101
|
+
key,
|
|
102
|
+
encryptedData
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
return decryptedData;
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.error("Failed to decrypt file:", error);
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {
|
|
2
|
+
encodeEncryptionKey,
|
|
3
|
+
encryptFile,
|
|
4
|
+
EncryptionKey,
|
|
5
|
+
generateRandomAESKey,
|
|
6
|
+
} from "./encryption.js";
|
|
7
|
+
|
|
8
|
+
export const encryptAndUploadFile = async (
|
|
9
|
+
file: File,
|
|
10
|
+
presignedUrl: string
|
|
11
|
+
): Promise<EncryptionKey> => {
|
|
12
|
+
const encryptionKey = await generateRandomAESKey();
|
|
13
|
+
const { encryptedData, iv } = await encryptFile(file, encryptionKey);
|
|
14
|
+
|
|
15
|
+
// Upload encrypted file to S3
|
|
16
|
+
const uploadResponse = await fetch(presignedUrl, {
|
|
17
|
+
method: "PUT",
|
|
18
|
+
body: encryptedData,
|
|
19
|
+
headers: {
|
|
20
|
+
"Content-Type": file.type,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (!uploadResponse.ok) {
|
|
25
|
+
throw new Error(`Failed to upload file ${uploadResponse.status}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return encodeEncryptionKey(encryptionKey, iv);
|
|
29
|
+
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { EncryptionKey } from "./lib/encryption.js";
|
|
2
|
+
|
|
1
3
|
export interface DialogMessage {
|
|
2
4
|
type: string;
|
|
3
5
|
payload?: any;
|
|
@@ -18,8 +20,41 @@ export interface SubmissionPayload<T> {
|
|
|
18
20
|
submission: T;
|
|
19
21
|
}
|
|
20
22
|
|
|
23
|
+
export type UploadCredentials = {
|
|
24
|
+
presignedUrl: string;
|
|
25
|
+
uploadKey: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface RequestUploadCredentialsPayload {
|
|
29
|
+
timestamp?: Date;
|
|
30
|
+
applicationId: string;
|
|
31
|
+
reference: string;
|
|
32
|
+
fileName: string;
|
|
33
|
+
fileType: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface BinaryFileSubmissionPayload extends RequestUploadCredentialsPayload {
|
|
37
|
+
encryptionKey: EncryptionKey;
|
|
38
|
+
/// in bytes
|
|
39
|
+
fileSize: number;
|
|
40
|
+
url: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface RunningFileUpload extends RequestUploadCredentialsPayload {
|
|
44
|
+
credentials?: UploadCredentials;
|
|
45
|
+
file: File;
|
|
46
|
+
reference: string;
|
|
47
|
+
uploadPromise: {
|
|
48
|
+
resolve: (result: { url: string; binaryFileUid: string }) => void;
|
|
49
|
+
reject: (error: Error) => void;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
21
53
|
export interface DataSubmissionDialogMessage extends DialogMessage {
|
|
22
|
-
payload:
|
|
54
|
+
payload:
|
|
55
|
+
| SubmissionPayload<unknown>
|
|
56
|
+
| BinaryFileSubmissionPayload
|
|
57
|
+
| RequestUploadCredentialsPayload;
|
|
23
58
|
}
|
|
24
59
|
|
|
25
60
|
export interface WelshareConnectionOptions {
|
|
@@ -28,6 +63,7 @@ export interface WelshareConnectionOptions {
|
|
|
28
63
|
apiBaseUrl?: string;
|
|
29
64
|
environment?: WelshareEnvironment;
|
|
30
65
|
callbacks: {
|
|
66
|
+
onFileUploaded?: (insertedUid: string, url: string) => void;
|
|
31
67
|
onUploaded?: (payload: SubmissionPayload<unknown>) => void;
|
|
32
68
|
onError?: (error: string) => void;
|
|
33
69
|
onSessionReady?: (sessionPubKey: string) => void;
|