@welshare/react 0.4.0 → 0.5.1
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 +3 -55
- package/dist/esm/index.d.ts +1 -3
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +3 -4
- package/dist/esm/lib/uploads.d.ts +1 -1
- package/dist/esm/lib/uploads.d.ts.map +1 -1
- package/dist/esm/lib/uploads.js +1 -1
- package/dist/esm/types.d.ts +1 -1
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/node_modules/@welshare/react/.turbo/turbo-lint.log +1 -1
- package/dist/node_modules/@welshare/react/README.md +3 -55
- package/dist/node_modules/@welshare/react/dist/esm/index.d.ts +1 -3
- package/dist/node_modules/@welshare/react/dist/esm/index.d.ts.map +1 -1
- package/dist/node_modules/@welshare/react/dist/esm/index.js +3 -4
- package/dist/node_modules/@welshare/react/dist/esm/lib/uploads.d.ts +1 -1
- package/dist/node_modules/@welshare/react/dist/esm/lib/uploads.d.ts.map +1 -1
- package/dist/node_modules/@welshare/react/dist/esm/lib/uploads.js +1 -1
- package/dist/node_modules/@welshare/react/dist/esm/types.d.ts +1 -1
- package/dist/node_modules/@welshare/react/dist/esm/types.d.ts.map +1 -1
- package/dist/node_modules/@welshare/react/package.json +1 -8
- package/dist/node_modules/@welshare/react/src/index.ts +7 -8
- package/dist/node_modules/@welshare/react/src/lib/uploads.ts +2 -2
- package/dist/node_modules/@welshare/react/src/types.ts +1 -1
- package/package.json +2 -9
- package/dist/esm/hooks/use-binary-uploads.d.ts +0 -59
- package/dist/esm/hooks/use-binary-uploads.d.ts.map +0 -1
- package/dist/esm/hooks/use-binary-uploads.js +0 -93
- package/dist/esm/lib/encryption.d.ts +0 -9
- package/dist/esm/lib/encryption.d.ts.map +0 -1
- package/dist/esm/lib/encryption.js +0 -44
- package/dist/esm/utils.d.ts +0 -12
- package/dist/esm/utils.d.ts.map +0 -1
- package/dist/esm/utils.js +0 -8
- package/dist/node_modules/@welshare/react/dist/esm/hooks/use-binary-uploads.d.ts +0 -59
- package/dist/node_modules/@welshare/react/dist/esm/hooks/use-binary-uploads.d.ts.map +0 -1
- package/dist/node_modules/@welshare/react/dist/esm/hooks/use-binary-uploads.js +0 -93
- package/dist/node_modules/@welshare/react/dist/esm/lib/encryption.d.ts +0 -9
- package/dist/node_modules/@welshare/react/dist/esm/lib/encryption.d.ts.map +0 -1
- package/dist/node_modules/@welshare/react/dist/esm/lib/encryption.js +0 -44
- package/dist/node_modules/@welshare/react/dist/esm/utils.d.ts +0 -12
- package/dist/node_modules/@welshare/react/dist/esm/utils.d.ts.map +0 -1
- package/dist/node_modules/@welshare/react/dist/esm/utils.js +0 -8
- package/dist/node_modules/@welshare/react/src/hooks/use-binary-uploads.ts +0 -181
- package/dist/node_modules/@welshare/react/src/lib/encryption.ts +0 -79
- package/dist/node_modules/@welshare/react/src/utils.ts +0 -18
package/README.md
CHANGED
|
@@ -84,16 +84,9 @@ export function QuestionnaireForm() {
|
|
|
84
84
|
|
|
85
85
|
### Binary file uploads (e.g. images)
|
|
86
86
|
|
|
87
|
-
The package provides two ways to upload binary files:
|
|
88
|
-
|
|
89
|
-
1. **`useWelshare` hook** - Upload via the wallet dialog (external wallet flow)
|
|
90
|
-
2. **`useBinaryUploads` hook** - Upload directly with your own session keypair
|
|
91
|
-
|
|
92
87
|
Before data hits any server, the SDK encrypts all files with a new random symmetric AES (GCM / 256 bits) key. Users request a presigned upload url and post the encrypted file to an S3 compatible API that's currently operated by Welshare. Ultimately, they encrypt the encryption key for a Nillion _owned_ BinaryData collection and store it across Nillion nodes (no single node can recover the key). At the time of insertion, they currently also grant ACL read rights for the application (Technically, this is the welshare builder keypair at the moment).
|
|
93
88
|
|
|
94
|
-
####
|
|
95
|
-
|
|
96
|
-
This works best for applications that **don't** manage their own keypairs. It delegates the heavy lifting to the welshare wallet dialog frame.
|
|
89
|
+
#### Upload via Wallet Dialog
|
|
97
90
|
|
|
98
91
|
```ts
|
|
99
92
|
const { isConnected, openWallet, uploadFile, submitData } = useWelshare({
|
|
@@ -122,55 +115,10 @@ const responseItem = {
|
|
|
122
115
|
};
|
|
123
116
|
```
|
|
124
117
|
|
|
125
|
-
#### Option 2: Direct Upload (`useBinaryUploads`)
|
|
126
|
-
|
|
127
|
-
For applications that manage storage keypairs directly:
|
|
128
|
-
|
|
129
|
-
```ts
|
|
130
|
-
import {
|
|
131
|
-
useBinaryUploads,
|
|
132
|
-
encryptAndUploadFile,
|
|
133
|
-
Schemas,
|
|
134
|
-
} from "@welshare/react";
|
|
135
|
-
import { WelshareApi } from "@welshare/sdk";
|
|
136
|
-
|
|
137
|
-
const { createUploadCredentials, downloadAndDecryptFile, isRunning, error } =
|
|
138
|
-
useBinaryUploads({
|
|
139
|
-
keypair: storageKeyPair, // storage keypairs are derived by users
|
|
140
|
-
environment: "production",
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// Get presigned URL
|
|
144
|
-
const { presignedUrl, uploadKey } = await createUploadCredentials({
|
|
145
|
-
applicationId: "your-app-id",
|
|
146
|
-
reference: `questionnaire/${questionnaireId}/photo`,
|
|
147
|
-
fileName: file.name,
|
|
148
|
-
fileType: file.type,
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
// Encrypt & upload to S3, then submit metadata to Nillion
|
|
152
|
-
const { encryptionKey } = await encryptAndUploadFile(file, presignedUrl);
|
|
153
|
-
await WelshareApi.submitBinaryData(
|
|
154
|
-
storageKeyPair,
|
|
155
|
-
{
|
|
156
|
-
encryption_key: JSON.stringify(encryptionKey),
|
|
157
|
-
reference: `questionnaire/${questionnaireId}/photo`,
|
|
158
|
-
file_name: file.name,
|
|
159
|
-
file_size: file.size,
|
|
160
|
-
file_type: file.type,
|
|
161
|
-
controller_did: storageKeyPair.toDidString(),
|
|
162
|
-
url: `welshare://${uploadKey}`,
|
|
163
|
-
},
|
|
164
|
-
"production",
|
|
165
|
-
applicationId
|
|
166
|
-
);
|
|
167
|
-
|
|
168
|
-
// Download and decrypt owned files
|
|
169
|
-
const decryptedFile = await downloadAndDecryptFile(documentId);
|
|
170
|
-
```
|
|
171
|
-
|
|
172
118
|
Binary files are addressed as `valueAttachment` items in FHIR. See https://www.hl7.org/fhir/questionnaireresponse.html
|
|
173
119
|
|
|
120
|
+
For applications that manage storage keypairs directly and need more control over the upload process, see the [Binary File Uploads section in the SDK documentation](../welshare/README.md#binary-file-uploads).
|
|
121
|
+
|
|
174
122
|
## API
|
|
175
123
|
|
|
176
124
|
### supported callbacks
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
export { ConnectWelshareButton } from "./components/connect-button.js";
|
|
2
2
|
export { WelshareLogo } from "./components/welshare-logo.js";
|
|
3
3
|
export { useWelshare } from "./hooks/use-welshare.js";
|
|
4
|
-
export { useBinaryUploads, type UseBinaryUploadsOptions, type UseBinaryUploadsResult, } from "./hooks/use-binary-uploads.js";
|
|
5
4
|
export { WELSHARE_API_ENVIRONMENT, resolveEnvironment, getBaseUrl, type WelshareApiEnvironment, type WelshareEnvironmentName, type NillionClusterConfig, } from "@welshare/sdk/environment";
|
|
6
|
-
export { decrypt, encodeEncryptionKey, encryptFile, generateRandomAESKey, } from "
|
|
7
|
-
export { decodeEncryptionKey, type EncryptionKey } from "./utils.js";
|
|
5
|
+
export { decrypt, encodeEncryptionKey, encryptFile, generateRandomAESKey, decodeEncryptionKey, ALGORITHM, type EncryptionKey, type Algorithm, } from "@welshare/sdk";
|
|
8
6
|
export { browserDownload, encryptAndUploadFile } from "./lib/uploads.js";
|
|
9
7
|
export declare const Schemas: {
|
|
10
8
|
QuestionnaireResponse: string;
|
package/dist/esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAG7D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAG7D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAGtD,OAAO,EACL,wBAAwB,EACxB,kBAAkB,EAClB,UAAU,EACV,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,GAC1B,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EACL,OAAO,EACP,mBAAmB,EACnB,WAAW,EACX,oBAAoB,EACpB,mBAAmB,EACnB,SAAS,EACT,KAAK,aAAa,EAClB,KAAK,SAAS,GACf,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAGzE,eAAO,MAAM,OAAO;;;;CAInB,CAAC"}
|
package/dist/esm/index.js
CHANGED
|
@@ -3,12 +3,11 @@ export { ConnectWelshareButton } from "./components/connect-button.js";
|
|
|
3
3
|
export { WelshareLogo } from "./components/welshare-logo.js";
|
|
4
4
|
// ---- Hooks ----
|
|
5
5
|
export { useWelshare } from "./hooks/use-welshare.js";
|
|
6
|
-
export { useBinaryUploads, } from "./hooks/use-binary-uploads.js";
|
|
7
6
|
// ---- Environment (re-exported from @welshare/sdk) ----
|
|
8
7
|
export { WELSHARE_API_ENVIRONMENT, resolveEnvironment, getBaseUrl, } from "@welshare/sdk/environment";
|
|
9
|
-
// ----
|
|
10
|
-
export { decrypt, encodeEncryptionKey, encryptFile, generateRandomAESKey, } from "
|
|
11
|
-
|
|
8
|
+
// ---- Encryption utilities (re-exported from @welshare/sdk) ----
|
|
9
|
+
export { decrypt, encodeEncryptionKey, encryptFile, generateRandomAESKey, decodeEncryptionKey, ALGORITHM, } from "@welshare/sdk";
|
|
10
|
+
// ---- Upload utilities (local, for frame-based upload flow) ----
|
|
12
11
|
export { browserDownload, encryptAndUploadFile } from "./lib/uploads.js";
|
|
13
12
|
//todo: import them from the SDK
|
|
14
13
|
export const Schemas = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EncryptionKey } from "
|
|
1
|
+
import { type EncryptionKey } from "@welshare/sdk";
|
|
2
2
|
export declare const encryptAndUploadFile: (file: File, presignedUrl: string) => Promise<EncryptionKey>;
|
|
3
3
|
export declare const browserDownload: (decryptedFile: File) => void;
|
|
4
4
|
//# sourceMappingURL=uploads.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploads.d.ts","sourceRoot":"","sources":["../../../src/lib/uploads.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"uploads.d.ts","sourceRoot":"","sources":["../../../src/lib/uploads.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAInB,MAAM,eAAe,CAAC;AAEvB,eAAO,MAAM,oBAAoB,SACzB,IAAI,gBACI,MAAM,KACnB,OAAO,CAAC,aAAa,CAgBvB,CAAC;AAEF,eAAO,MAAM,eAAe,kBAAmB,IAAI,SASlD,CAAC"}
|
package/dist/esm/lib/uploads.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { encodeEncryptionKey, encryptFile, generateRandomAESKey, } from "
|
|
1
|
+
import { encodeEncryptionKey, encryptFile, generateRandomAESKey, } from "@welshare/sdk";
|
|
2
2
|
export const encryptAndUploadFile = async (file, presignedUrl) => {
|
|
3
3
|
const encryptionKey = await generateRandomAESKey();
|
|
4
4
|
const { encryptedData, iv } = await encryptFile(file, encryptionKey);
|
package/dist/esm/types.d.ts
CHANGED
package/dist/esm/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EACV,sBAAsB,EACtB,uBAAuB,EACxB,MAAM,2BAA2B,CAAC;AAEnC,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAExC,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC;IAChB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,UAAU,EAAE,CAAC,CAAC;CACf;AAED,MAAM,MAAM,iBAAiB,GAAG;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5E,MAAM,WAAW,+BAA+B;IAC9C,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,2BACf,SAAQ,+BAA+B;IACvC,aAAa,EAAE,aAAa,CAAC;IAE7B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,iBAAkB,SAAQ,+BAA+B;IACxE,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE;QACb,OAAO,EAAE,CAAC,MAAM,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,aAAa,EAAE,MAAM,CAAA;SAAE,KAAK,IAAI,CAAC;QAClE,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;KAChC,CAAC;CACH;AAED,MAAM,WAAW,2BAA4B,SAAQ,aAAa;IAChE,OAAO,EACH,iBAAiB,CAAC,OAAO,CAAC,GAC1B,2BAA2B,GAC3B,+BAA+B,CAAC;CACrC;AAED,MAAM,WAAW,yBAAyB;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF;;;;OAIG;IACH,WAAW,CAAC,EAAE,sBAAsB,GAAG,uBAAuB,CAAC;IAC/D;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE;QACT,cAAc,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAC5D,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;QAC3D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;QAClC,cAAc,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;QACjD,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;QAC7B,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;KAC5B,CAAC;CACH"}
|
|
@@ -84,16 +84,9 @@ export function QuestionnaireForm() {
|
|
|
84
84
|
|
|
85
85
|
### Binary file uploads (e.g. images)
|
|
86
86
|
|
|
87
|
-
The package provides two ways to upload binary files:
|
|
88
|
-
|
|
89
|
-
1. **`useWelshare` hook** - Upload via the wallet dialog (external wallet flow)
|
|
90
|
-
2. **`useBinaryUploads` hook** - Upload directly with your own session keypair
|
|
91
|
-
|
|
92
87
|
Before data hits any server, the SDK encrypts all files with a new random symmetric AES (GCM / 256 bits) key. Users request a presigned upload url and post the encrypted file to an S3 compatible API that's currently operated by Welshare. Ultimately, they encrypt the encryption key for a Nillion _owned_ BinaryData collection and store it across Nillion nodes (no single node can recover the key). At the time of insertion, they currently also grant ACL read rights for the application (Technically, this is the welshare builder keypair at the moment).
|
|
93
88
|
|
|
94
|
-
####
|
|
95
|
-
|
|
96
|
-
This works best for applications that **don't** manage their own keypairs. It delegates the heavy lifting to the welshare wallet dialog frame.
|
|
89
|
+
#### Upload via Wallet Dialog
|
|
97
90
|
|
|
98
91
|
```ts
|
|
99
92
|
const { isConnected, openWallet, uploadFile, submitData } = useWelshare({
|
|
@@ -122,55 +115,10 @@ const responseItem = {
|
|
|
122
115
|
};
|
|
123
116
|
```
|
|
124
117
|
|
|
125
|
-
#### Option 2: Direct Upload (`useBinaryUploads`)
|
|
126
|
-
|
|
127
|
-
For applications that manage storage keypairs directly:
|
|
128
|
-
|
|
129
|
-
```ts
|
|
130
|
-
import {
|
|
131
|
-
useBinaryUploads,
|
|
132
|
-
encryptAndUploadFile,
|
|
133
|
-
Schemas,
|
|
134
|
-
} from "@welshare/react";
|
|
135
|
-
import { WelshareApi } from "@welshare/sdk";
|
|
136
|
-
|
|
137
|
-
const { createUploadCredentials, downloadAndDecryptFile, isRunning, error } =
|
|
138
|
-
useBinaryUploads({
|
|
139
|
-
keypair: storageKeyPair, // storage keypairs are derived by users
|
|
140
|
-
environment: "production",
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// Get presigned URL
|
|
144
|
-
const { presignedUrl, uploadKey } = await createUploadCredentials({
|
|
145
|
-
applicationId: "your-app-id",
|
|
146
|
-
reference: `questionnaire/${questionnaireId}/photo`,
|
|
147
|
-
fileName: file.name,
|
|
148
|
-
fileType: file.type,
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
// Encrypt & upload to S3, then submit metadata to Nillion
|
|
152
|
-
const { encryptionKey } = await encryptAndUploadFile(file, presignedUrl);
|
|
153
|
-
await WelshareApi.submitBinaryData(
|
|
154
|
-
storageKeyPair,
|
|
155
|
-
{
|
|
156
|
-
encryption_key: JSON.stringify(encryptionKey),
|
|
157
|
-
reference: `questionnaire/${questionnaireId}/photo`,
|
|
158
|
-
file_name: file.name,
|
|
159
|
-
file_size: file.size,
|
|
160
|
-
file_type: file.type,
|
|
161
|
-
controller_did: storageKeyPair.toDidString(),
|
|
162
|
-
url: `welshare://${uploadKey}`,
|
|
163
|
-
},
|
|
164
|
-
"production",
|
|
165
|
-
applicationId
|
|
166
|
-
);
|
|
167
|
-
|
|
168
|
-
// Download and decrypt owned files
|
|
169
|
-
const decryptedFile = await downloadAndDecryptFile(documentId);
|
|
170
|
-
```
|
|
171
|
-
|
|
172
118
|
Binary files are addressed as `valueAttachment` items in FHIR. See https://www.hl7.org/fhir/questionnaireresponse.html
|
|
173
119
|
|
|
120
|
+
For applications that manage storage keypairs directly and need more control over the upload process, see the [Binary File Uploads section in the SDK documentation](../welshare/README.md#binary-file-uploads).
|
|
121
|
+
|
|
174
122
|
## API
|
|
175
123
|
|
|
176
124
|
### supported callbacks
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
export { ConnectWelshareButton } from "./components/connect-button.js";
|
|
2
2
|
export { WelshareLogo } from "./components/welshare-logo.js";
|
|
3
3
|
export { useWelshare } from "./hooks/use-welshare.js";
|
|
4
|
-
export { useBinaryUploads, type UseBinaryUploadsOptions, type UseBinaryUploadsResult, } from "./hooks/use-binary-uploads.js";
|
|
5
4
|
export { WELSHARE_API_ENVIRONMENT, resolveEnvironment, getBaseUrl, type WelshareApiEnvironment, type WelshareEnvironmentName, type NillionClusterConfig, } from "@welshare/sdk/environment";
|
|
6
|
-
export { decrypt, encodeEncryptionKey, encryptFile, generateRandomAESKey, } from "
|
|
7
|
-
export { decodeEncryptionKey, type EncryptionKey } from "./utils.js";
|
|
5
|
+
export { decrypt, encodeEncryptionKey, encryptFile, generateRandomAESKey, decodeEncryptionKey, ALGORITHM, type EncryptionKey, type Algorithm, } from "@welshare/sdk";
|
|
8
6
|
export { browserDownload, encryptAndUploadFile } from "./lib/uploads.js";
|
|
9
7
|
export declare const Schemas: {
|
|
10
8
|
QuestionnaireResponse: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAG7D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAG7D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAGtD,OAAO,EACL,wBAAwB,EACxB,kBAAkB,EAClB,UAAU,EACV,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,GAC1B,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EACL,OAAO,EACP,mBAAmB,EACnB,WAAW,EACX,oBAAoB,EACpB,mBAAmB,EACnB,SAAS,EACT,KAAK,aAAa,EAClB,KAAK,SAAS,GACf,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAGzE,eAAO,MAAM,OAAO;;;;CAInB,CAAC"}
|
|
@@ -3,12 +3,11 @@ export { ConnectWelshareButton } from "./components/connect-button.js";
|
|
|
3
3
|
export { WelshareLogo } from "./components/welshare-logo.js";
|
|
4
4
|
// ---- Hooks ----
|
|
5
5
|
export { useWelshare } from "./hooks/use-welshare.js";
|
|
6
|
-
export { useBinaryUploads, } from "./hooks/use-binary-uploads.js";
|
|
7
6
|
// ---- Environment (re-exported from @welshare/sdk) ----
|
|
8
7
|
export { WELSHARE_API_ENVIRONMENT, resolveEnvironment, getBaseUrl, } from "@welshare/sdk/environment";
|
|
9
|
-
// ----
|
|
10
|
-
export { decrypt, encodeEncryptionKey, encryptFile, generateRandomAESKey, } from "
|
|
11
|
-
|
|
8
|
+
// ---- Encryption utilities (re-exported from @welshare/sdk) ----
|
|
9
|
+
export { decrypt, encodeEncryptionKey, encryptFile, generateRandomAESKey, decodeEncryptionKey, ALGORITHM, } from "@welshare/sdk";
|
|
10
|
+
// ---- Upload utilities (local, for frame-based upload flow) ----
|
|
12
11
|
export { browserDownload, encryptAndUploadFile } from "./lib/uploads.js";
|
|
13
12
|
//todo: import them from the SDK
|
|
14
13
|
export const Schemas = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EncryptionKey } from "
|
|
1
|
+
import { type EncryptionKey } from "@welshare/sdk";
|
|
2
2
|
export declare const encryptAndUploadFile: (file: File, presignedUrl: string) => Promise<EncryptionKey>;
|
|
3
3
|
export declare const browserDownload: (decryptedFile: File) => void;
|
|
4
4
|
//# sourceMappingURL=uploads.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uploads.d.ts","sourceRoot":"","sources":["../../../src/lib/uploads.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"uploads.d.ts","sourceRoot":"","sources":["../../../src/lib/uploads.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAInB,MAAM,eAAe,CAAC;AAEvB,eAAO,MAAM,oBAAoB,SACzB,IAAI,gBACI,MAAM,KACnB,OAAO,CAAC,aAAa,CAgBvB,CAAC;AAEF,eAAO,MAAM,eAAe,kBAAmB,IAAI,SASlD,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { encodeEncryptionKey, encryptFile, generateRandomAESKey, } from "
|
|
1
|
+
import { encodeEncryptionKey, encryptFile, generateRandomAESKey, } from "@welshare/sdk";
|
|
2
2
|
export const encryptAndUploadFile = async (file, presignedUrl) => {
|
|
3
3
|
const encryptionKey = await generateRandomAESKey();
|
|
4
4
|
const { encryptedData, iv } = await encryptFile(file, encryptionKey);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EACV,sBAAsB,EACtB,uBAAuB,EACxB,MAAM,2BAA2B,CAAC;AAEnC,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAExC,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC;IAChB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,UAAU,EAAE,CAAC,CAAC;CACf;AAED,MAAM,MAAM,iBAAiB,GAAG;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5E,MAAM,WAAW,+BAA+B;IAC9C,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,2BACf,SAAQ,+BAA+B;IACvC,aAAa,EAAE,aAAa,CAAC;IAE7B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,iBAAkB,SAAQ,+BAA+B;IACxE,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE;QACb,OAAO,EAAE,CAAC,MAAM,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,aAAa,EAAE,MAAM,CAAA;SAAE,KAAK,IAAI,CAAC;QAClE,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;KAChC,CAAC;CACH;AAED,MAAM,WAAW,2BAA4B,SAAQ,aAAa;IAChE,OAAO,EACH,iBAAiB,CAAC,OAAO,CAAC,GAC1B,2BAA2B,GAC3B,+BAA+B,CAAC;CACrC;AAED,MAAM,WAAW,yBAAyB;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF;;;;OAIG;IACH,WAAW,CAAC,EAAE,sBAAsB,GAAG,uBAAuB,CAAC;IAC/D;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE;QACT,cAAc,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAC5D,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;QAC3D,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;QAClC,cAAc,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;QACjD,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;QAC7B,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;KAC5B,CAAC;CACH"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@welshare/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "React library for integrating with Welshare's sovereign data sharing platform",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -61,7 +61,6 @@
|
|
|
61
61
|
"exports": {
|
|
62
62
|
"./package.json": "./package.json",
|
|
63
63
|
"./types": "./src/types.ts",
|
|
64
|
-
"./utils": "./src/utils.ts",
|
|
65
64
|
".": "./src/index.ts"
|
|
66
65
|
}
|
|
67
66
|
},
|
|
@@ -73,12 +72,6 @@
|
|
|
73
72
|
"default": "./dist/esm/types.js"
|
|
74
73
|
}
|
|
75
74
|
},
|
|
76
|
-
"./utils": {
|
|
77
|
-
"import": {
|
|
78
|
-
"types": "./dist/esm/utils.d.ts",
|
|
79
|
-
"default": "./dist/esm/utils.js"
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
75
|
".": {
|
|
83
76
|
"import": {
|
|
84
77
|
"types": "./dist/esm/index.d.ts",
|
|
@@ -4,11 +4,6 @@ export { WelshareLogo } from "./components/welshare-logo.js";
|
|
|
4
4
|
|
|
5
5
|
// ---- Hooks ----
|
|
6
6
|
export { useWelshare } from "./hooks/use-welshare.js";
|
|
7
|
-
export {
|
|
8
|
-
useBinaryUploads,
|
|
9
|
-
type UseBinaryUploadsOptions,
|
|
10
|
-
type UseBinaryUploadsResult,
|
|
11
|
-
} from "./hooks/use-binary-uploads.js";
|
|
12
7
|
|
|
13
8
|
// ---- Environment (re-exported from @welshare/sdk) ----
|
|
14
9
|
export {
|
|
@@ -20,15 +15,19 @@ export {
|
|
|
20
15
|
type NillionClusterConfig,
|
|
21
16
|
} from "@welshare/sdk/environment";
|
|
22
17
|
|
|
23
|
-
// ----
|
|
18
|
+
// ---- Encryption utilities (re-exported from @welshare/sdk) ----
|
|
24
19
|
export {
|
|
25
20
|
decrypt,
|
|
26
21
|
encodeEncryptionKey,
|
|
27
22
|
encryptFile,
|
|
28
23
|
generateRandomAESKey,
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
decodeEncryptionKey,
|
|
25
|
+
ALGORITHM,
|
|
26
|
+
type EncryptionKey,
|
|
27
|
+
type Algorithm,
|
|
28
|
+
} from "@welshare/sdk";
|
|
31
29
|
|
|
30
|
+
// ---- Upload utilities (local, for frame-based upload flow) ----
|
|
32
31
|
export { browserDownload, encryptAndUploadFile } from "./lib/uploads.js";
|
|
33
32
|
|
|
34
33
|
//todo: import them from the SDK
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@welshare/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "React library for integrating with Welshare's sovereign data sharing platform",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"homepage": "https://welshare.health",
|
|
24
24
|
"type": "module",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@welshare/sdk": "0.
|
|
26
|
+
"@welshare/sdk": "0.3.1"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
29
|
"react": "^19",
|
|
@@ -52,7 +52,6 @@
|
|
|
52
52
|
"exports": {
|
|
53
53
|
"./package.json": "./package.json",
|
|
54
54
|
"./types": "./src/types.ts",
|
|
55
|
-
"./utils": "./src/utils.ts",
|
|
56
55
|
".": "./src/index.ts"
|
|
57
56
|
}
|
|
58
57
|
},
|
|
@@ -64,12 +63,6 @@
|
|
|
64
63
|
"default": "./dist/esm/types.js"
|
|
65
64
|
}
|
|
66
65
|
},
|
|
67
|
-
"./utils": {
|
|
68
|
-
"import": {
|
|
69
|
-
"types": "./dist/esm/utils.d.ts",
|
|
70
|
-
"default": "./dist/esm/utils.js"
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
66
|
".": {
|
|
74
67
|
"import": {
|
|
75
68
|
"types": "./dist/esm/index.d.ts",
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import type { RequestUploadCredentialsPayload, UploadCredentials } from "../types.js";
|
|
2
|
-
import { Nillion, type WelshareApiEnvironment, type WelshareEnvironmentName } from "@welshare/sdk";
|
|
3
|
-
export interface UseBinaryUploadsOptions {
|
|
4
|
-
/**
|
|
5
|
-
* The user's session keypair for authentication.
|
|
6
|
-
*/
|
|
7
|
-
keypair: Nillion.Keypair | null | undefined;
|
|
8
|
-
/**
|
|
9
|
-
* The Welshare environment to use for API calls.
|
|
10
|
-
*/
|
|
11
|
-
environment: WelshareApiEnvironment | WelshareEnvironmentName;
|
|
12
|
-
}
|
|
13
|
-
export interface UseBinaryUploadsResult {
|
|
14
|
-
/**
|
|
15
|
-
* Request upload credentials (presigned URL) for uploading an encrypted file.
|
|
16
|
-
*/
|
|
17
|
-
createUploadCredentials: (payload: RequestUploadCredentialsPayload) => Promise<UploadCredentials>;
|
|
18
|
-
/**
|
|
19
|
-
* Download and decrypt a file by its document ID.
|
|
20
|
-
* Only works for files owned by the current user.
|
|
21
|
-
*/
|
|
22
|
-
downloadAndDecryptFile: (documentId: string) => Promise<File | undefined>;
|
|
23
|
-
/**
|
|
24
|
-
* Whether an operation is currently running.
|
|
25
|
-
*/
|
|
26
|
-
isRunning: boolean;
|
|
27
|
-
/**
|
|
28
|
-
* Error message if the last operation failed.
|
|
29
|
-
*/
|
|
30
|
-
error: string | null;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Hook for managing binary file uploads and downloads with Welshare.
|
|
34
|
-
*
|
|
35
|
-
* This hook provides functionality to:
|
|
36
|
-
* - Request presigned URLs for uploading encrypted files to S3
|
|
37
|
-
* - Download and decrypt files from Nillion storage
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* ```tsx
|
|
41
|
-
* const { createUploadCredentials, downloadAndDecryptFile, isRunning, error } = useBinaryUploads({
|
|
42
|
-
* keypair: sessionKeyPair,
|
|
43
|
-
* environment: 'production',
|
|
44
|
-
* });
|
|
45
|
-
*
|
|
46
|
-
* // Upload flow: get credentials, encrypt, upload to S3, then submit metadata via submitBinaryData
|
|
47
|
-
* const credentials = await createUploadCredentials({
|
|
48
|
-
* applicationId: 'my-app',
|
|
49
|
-
* reference: 'user-photo',
|
|
50
|
-
* fileName: 'photo.jpg',
|
|
51
|
-
* fileType: 'image/jpeg',
|
|
52
|
-
* });
|
|
53
|
-
*
|
|
54
|
-
* // Download and decrypt
|
|
55
|
-
* const file = await downloadAndDecryptFile(documentId);
|
|
56
|
-
* ```
|
|
57
|
-
*/
|
|
58
|
-
export declare const useBinaryUploads: (options: UseBinaryUploadsOptions) => UseBinaryUploadsResult;
|
|
59
|
-
//# sourceMappingURL=use-binary-uploads.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use-binary-uploads.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-binary-uploads.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,+BAA+B,EAC/B,iBAAiB,EAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,OAAO,EAGP,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC7B,MAAM,eAAe,CAAC;AAEvB,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;IAC5C;;OAEG;IACH,WAAW,EAAE,sBAAsB,GAAG,uBAAuB,CAAC;CAC/D;AAED,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,uBAAuB,EAAE,CACvB,OAAO,EAAE,+BAA+B,KACrC,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAEhC;;;OAGG;IACH,sBAAsB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC,CAAC;IAC1E;;OAEG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,gBAAgB,YAClB,uBAAuB,KAC/B,sBAuGF,CAAC"}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
-
import { decrypt } from "../lib/encryption.js";
|
|
3
|
-
import { WelshareApi, resolveEnvironment, } from "@welshare/sdk";
|
|
4
|
-
/**
|
|
5
|
-
* Hook for managing binary file uploads and downloads with Welshare.
|
|
6
|
-
*
|
|
7
|
-
* This hook provides functionality to:
|
|
8
|
-
* - Request presigned URLs for uploading encrypted files to S3
|
|
9
|
-
* - Download and decrypt files from Nillion storage
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```tsx
|
|
13
|
-
* const { createUploadCredentials, downloadAndDecryptFile, isRunning, error } = useBinaryUploads({
|
|
14
|
-
* keypair: sessionKeyPair,
|
|
15
|
-
* environment: 'production',
|
|
16
|
-
* });
|
|
17
|
-
*
|
|
18
|
-
* // Upload flow: get credentials, encrypt, upload to S3, then submit metadata via submitBinaryData
|
|
19
|
-
* const credentials = await createUploadCredentials({
|
|
20
|
-
* applicationId: 'my-app',
|
|
21
|
-
* reference: 'user-photo',
|
|
22
|
-
* fileName: 'photo.jpg',
|
|
23
|
-
* fileType: 'image/jpeg',
|
|
24
|
-
* });
|
|
25
|
-
*
|
|
26
|
-
* // Download and decrypt
|
|
27
|
-
* const file = await downloadAndDecryptFile(documentId);
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
export const useBinaryUploads = (options) => {
|
|
31
|
-
const [isRunning, setIsRunning] = useState(false);
|
|
32
|
-
const [error, setError] = useState(null);
|
|
33
|
-
const mountedRef = useRef(true);
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
mountedRef.current = true;
|
|
36
|
-
return () => {
|
|
37
|
-
mountedRef.current = false;
|
|
38
|
-
};
|
|
39
|
-
}, []);
|
|
40
|
-
const resolvedEnvironment = useMemo(() => resolveEnvironment(options.environment), [options.environment]);
|
|
41
|
-
const { keypair } = options;
|
|
42
|
-
const createUploadCredentials = useCallback(async (payload) => {
|
|
43
|
-
if (!keypair) {
|
|
44
|
-
throw new Error("No keypair available");
|
|
45
|
-
}
|
|
46
|
-
const { reference, fileName, fileType } = payload;
|
|
47
|
-
const { presignedUrl, uploadKey } = await WelshareApi.fetchS3WriteDelegation(keypair, { reference, fileName, fileType }, resolvedEnvironment);
|
|
48
|
-
return { presignedUrl, uploadKey };
|
|
49
|
-
}, [keypair, resolvedEnvironment]);
|
|
50
|
-
/**
|
|
51
|
-
* Downloads and decrypts a file by its document ID from Nillion.
|
|
52
|
-
*/
|
|
53
|
-
const downloadAndDecryptFile = useCallback(async (documentId) => {
|
|
54
|
-
if (!keypair) {
|
|
55
|
-
throw new Error("No keypair available");
|
|
56
|
-
}
|
|
57
|
-
try {
|
|
58
|
-
setIsRunning(true);
|
|
59
|
-
setError(null);
|
|
60
|
-
const { binaryFile, data: downloadResponse } = await WelshareApi.fetchBinaryData(keypair, resolvedEnvironment, documentId);
|
|
61
|
-
const encodedEncryptionKey = JSON.parse(binaryFile.encryption_key);
|
|
62
|
-
const decryptedData = await decrypt(await downloadResponse, encodedEncryptionKey);
|
|
63
|
-
if (!decryptedData) {
|
|
64
|
-
throw new Error("Failed to decrypt file (received null)");
|
|
65
|
-
}
|
|
66
|
-
const decryptedFile = new File([decryptedData], binaryFile.file_name, {
|
|
67
|
-
type: binaryFile.file_type,
|
|
68
|
-
});
|
|
69
|
-
return decryptedFile;
|
|
70
|
-
}
|
|
71
|
-
catch (err) {
|
|
72
|
-
console.error("Error during file download/decryption:", err);
|
|
73
|
-
const errorMessage = err instanceof Error
|
|
74
|
-
? err.message
|
|
75
|
-
: "Failed to download/decrypt file";
|
|
76
|
-
if (mountedRef.current) {
|
|
77
|
-
setError(errorMessage);
|
|
78
|
-
}
|
|
79
|
-
return undefined;
|
|
80
|
-
}
|
|
81
|
-
finally {
|
|
82
|
-
if (mountedRef.current) {
|
|
83
|
-
setIsRunning(false);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}, [keypair, resolvedEnvironment]);
|
|
87
|
-
return {
|
|
88
|
-
createUploadCredentials,
|
|
89
|
-
downloadAndDecryptFile,
|
|
90
|
-
isRunning,
|
|
91
|
-
error,
|
|
92
|
-
};
|
|
93
|
-
};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { EncryptionKey } from "../utils.js";
|
|
2
|
-
export declare const generateRandomAESKey: () => Promise<CryptoKey>;
|
|
3
|
-
export declare const encryptFile: (file: File, key: CryptoKey) => Promise<{
|
|
4
|
-
encryptedData: ArrayBuffer;
|
|
5
|
-
iv: Uint8Array;
|
|
6
|
-
}>;
|
|
7
|
-
export declare const encodeEncryptionKey: (key: CryptoKey, iv: Uint8Array) => Promise<EncryptionKey>;
|
|
8
|
-
export declare const decrypt: (encryptedData: ArrayBuffer, encryptionKey: EncryptionKey) => Promise<ArrayBuffer | null>;
|
|
9
|
-
//# sourceMappingURL=encryption.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../../../src/lib/encryption.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,aAAa,EAAuB,MAAM,aAAa,CAAC;AAE5E,eAAO,MAAM,oBAAoB,QAAa,OAAO,CAAC,SAAS,CAU9D,CAAC;AAIF,eAAO,MAAM,WAAW,SAChB,IAAI,OACL,SAAS,KACb,OAAO,CAAC;IAAE,aAAa,EAAE,WAAW,CAAC;IAAC,EAAE,EAAE,UAAU,CAAA;CAAE,CAaxD,CAAC;AAEF,eAAO,MAAM,mBAAmB,QACzB,SAAS,MACV,UAAU,KACb,OAAO,CAAC,aAAa,CAYvB,CAAC;AAGF,eAAO,MAAM,OAAO,kBACH,WAAW,iBACX,aAAa,KAC3B,OAAO,CAAC,WAAW,GAAG,IAAI,CAuB5B,CAAC"}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { ALGORITHM, decodeEncryptionKey } from "../utils.js";
|
|
2
|
-
export const generateRandomAESKey = async () => {
|
|
3
|
-
// Generate a 256-bit AES-GCM key for file encryption
|
|
4
|
-
return window.crypto.subtle.generateKey({
|
|
5
|
-
name: ALGORITHM,
|
|
6
|
-
length: 256, // 256-bit key
|
|
7
|
-
}, true, // Key is extractable (needed for storage/transmission)
|
|
8
|
-
["encrypt", "decrypt"] // Key usage
|
|
9
|
-
);
|
|
10
|
-
};
|
|
11
|
-
/// also Generates random IV (12 bytes for AES-GCM)
|
|
12
|
-
/// @return {arraybuffer ciphertext, uint8array iv}
|
|
13
|
-
export const encryptFile = async (file, key) => {
|
|
14
|
-
// Read file as ArrayBuffer
|
|
15
|
-
const fileData = await file.arrayBuffer();
|
|
16
|
-
const iv = window.crypto.getRandomValues(new Uint8Array(12));
|
|
17
|
-
// Encrypt the file data
|
|
18
|
-
const encryptedData = await window.crypto.subtle.encrypt({ name: ALGORITHM, iv: iv }, key, fileData);
|
|
19
|
-
return { encryptedData, iv };
|
|
20
|
-
};
|
|
21
|
-
export const encodeEncryptionKey = async (key, iv) => {
|
|
22
|
-
// Export the key as raw bytes
|
|
23
|
-
const exportedKey = await window.crypto.subtle.exportKey("raw", key);
|
|
24
|
-
const keyHex = Array.from(new Uint8Array(exportedKey))
|
|
25
|
-
.map((b) => b.toString(16).padStart(2, "0"))
|
|
26
|
-
.join("");
|
|
27
|
-
const ivHex = Array.from(iv)
|
|
28
|
-
.map((b) => b.toString(16).padStart(2, "0"))
|
|
29
|
-
.join("");
|
|
30
|
-
return { algorithm: ALGORITHM, key: keyHex, iv: ivHex };
|
|
31
|
-
};
|
|
32
|
-
// Helper function to decrypt a file using encoded encryption key
|
|
33
|
-
export const decrypt = async (encryptedData, encryptionKey) => {
|
|
34
|
-
try {
|
|
35
|
-
const { key: keyBytes, iv } = decodeEncryptionKey(encryptionKey);
|
|
36
|
-
const key = await window.crypto.subtle.importKey("raw", keyBytes, { name: ALGORITHM }, false, ["decrypt"]);
|
|
37
|
-
const decryptedData = await window.crypto.subtle.decrypt({ name: ALGORITHM, iv: iv }, key, encryptedData);
|
|
38
|
-
return decryptedData;
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
console.error("Failed to decrypt file:", error);
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
};
|
package/dist/esm/utils.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export declare const ALGORITHM = "AES-GCM";
|
|
2
|
-
export type Algorithm = "AES-GCM";
|
|
3
|
-
export type EncryptionKey = {
|
|
4
|
-
algorithm: Algorithm;
|
|
5
|
-
key: string;
|
|
6
|
-
iv: string;
|
|
7
|
-
};
|
|
8
|
-
export declare const decodeEncryptionKey: (encryptionKey: EncryptionKey) => {
|
|
9
|
-
key: BufferSource;
|
|
10
|
-
iv: BufferSource;
|
|
11
|
-
};
|
|
12
|
-
//# sourceMappingURL=utils.d.ts.map
|
package/dist/esm/utils.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,YAAY,CAAC;AACnC,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC;AAElC,MAAM,MAAM,aAAa,GAAG;IAAE,SAAS,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9E,eAAO,MAAM,mBAAmB,kBACf,aAAa,KAC3B;IAAE,GAAG,EAAE,YAAY,CAAC;IAAC,EAAE,EAAE,YAAY,CAAA;CAUvC,CAAC"}
|
package/dist/esm/utils.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export const ALGORITHM = "AES-GCM";
|
|
2
|
-
export const decodeEncryptionKey = (encryptionKey) => {
|
|
3
|
-
const keyBytes = new Uint8Array(encryptionKey.key
|
|
4
|
-
.match(/.{1,2}/g)
|
|
5
|
-
.map((byte) => parseInt(byte, 16)));
|
|
6
|
-
const ivBytes = new Uint8Array(encryptionKey.iv.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
|
|
7
|
-
return { key: keyBytes, iv: ivBytes };
|
|
8
|
-
};
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import type { RequestUploadCredentialsPayload, UploadCredentials } from "../types.js";
|
|
2
|
-
import { Nillion, type WelshareApiEnvironment, type WelshareEnvironmentName } from "@welshare/sdk";
|
|
3
|
-
export interface UseBinaryUploadsOptions {
|
|
4
|
-
/**
|
|
5
|
-
* The user's session keypair for authentication.
|
|
6
|
-
*/
|
|
7
|
-
keypair: Nillion.Keypair | null | undefined;
|
|
8
|
-
/**
|
|
9
|
-
* The Welshare environment to use for API calls.
|
|
10
|
-
*/
|
|
11
|
-
environment: WelshareApiEnvironment | WelshareEnvironmentName;
|
|
12
|
-
}
|
|
13
|
-
export interface UseBinaryUploadsResult {
|
|
14
|
-
/**
|
|
15
|
-
* Request upload credentials (presigned URL) for uploading an encrypted file.
|
|
16
|
-
*/
|
|
17
|
-
createUploadCredentials: (payload: RequestUploadCredentialsPayload) => Promise<UploadCredentials>;
|
|
18
|
-
/**
|
|
19
|
-
* Download and decrypt a file by its document ID.
|
|
20
|
-
* Only works for files owned by the current user.
|
|
21
|
-
*/
|
|
22
|
-
downloadAndDecryptFile: (documentId: string) => Promise<File | undefined>;
|
|
23
|
-
/**
|
|
24
|
-
* Whether an operation is currently running.
|
|
25
|
-
*/
|
|
26
|
-
isRunning: boolean;
|
|
27
|
-
/**
|
|
28
|
-
* Error message if the last operation failed.
|
|
29
|
-
*/
|
|
30
|
-
error: string | null;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Hook for managing binary file uploads and downloads with Welshare.
|
|
34
|
-
*
|
|
35
|
-
* This hook provides functionality to:
|
|
36
|
-
* - Request presigned URLs for uploading encrypted files to S3
|
|
37
|
-
* - Download and decrypt files from Nillion storage
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* ```tsx
|
|
41
|
-
* const { createUploadCredentials, downloadAndDecryptFile, isRunning, error } = useBinaryUploads({
|
|
42
|
-
* keypair: sessionKeyPair,
|
|
43
|
-
* environment: 'production',
|
|
44
|
-
* });
|
|
45
|
-
*
|
|
46
|
-
* // Upload flow: get credentials, encrypt, upload to S3, then submit metadata via submitBinaryData
|
|
47
|
-
* const credentials = await createUploadCredentials({
|
|
48
|
-
* applicationId: 'my-app',
|
|
49
|
-
* reference: 'user-photo',
|
|
50
|
-
* fileName: 'photo.jpg',
|
|
51
|
-
* fileType: 'image/jpeg',
|
|
52
|
-
* });
|
|
53
|
-
*
|
|
54
|
-
* // Download and decrypt
|
|
55
|
-
* const file = await downloadAndDecryptFile(documentId);
|
|
56
|
-
* ```
|
|
57
|
-
*/
|
|
58
|
-
export declare const useBinaryUploads: (options: UseBinaryUploadsOptions) => UseBinaryUploadsResult;
|
|
59
|
-
//# sourceMappingURL=use-binary-uploads.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use-binary-uploads.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-binary-uploads.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,+BAA+B,EAC/B,iBAAiB,EAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,OAAO,EAGP,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC7B,MAAM,eAAe,CAAC;AAEvB,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;IAC5C;;OAEG;IACH,WAAW,EAAE,sBAAsB,GAAG,uBAAuB,CAAC;CAC/D;AAED,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,uBAAuB,EAAE,CACvB,OAAO,EAAE,+BAA+B,KACrC,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAEhC;;;OAGG;IACH,sBAAsB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC,CAAC;IAC1E;;OAEG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,gBAAgB,YAClB,uBAAuB,KAC/B,sBAuGF,CAAC"}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
-
import { decrypt } from "../lib/encryption.js";
|
|
3
|
-
import { WelshareApi, resolveEnvironment, } from "@welshare/sdk";
|
|
4
|
-
/**
|
|
5
|
-
* Hook for managing binary file uploads and downloads with Welshare.
|
|
6
|
-
*
|
|
7
|
-
* This hook provides functionality to:
|
|
8
|
-
* - Request presigned URLs for uploading encrypted files to S3
|
|
9
|
-
* - Download and decrypt files from Nillion storage
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```tsx
|
|
13
|
-
* const { createUploadCredentials, downloadAndDecryptFile, isRunning, error } = useBinaryUploads({
|
|
14
|
-
* keypair: sessionKeyPair,
|
|
15
|
-
* environment: 'production',
|
|
16
|
-
* });
|
|
17
|
-
*
|
|
18
|
-
* // Upload flow: get credentials, encrypt, upload to S3, then submit metadata via submitBinaryData
|
|
19
|
-
* const credentials = await createUploadCredentials({
|
|
20
|
-
* applicationId: 'my-app',
|
|
21
|
-
* reference: 'user-photo',
|
|
22
|
-
* fileName: 'photo.jpg',
|
|
23
|
-
* fileType: 'image/jpeg',
|
|
24
|
-
* });
|
|
25
|
-
*
|
|
26
|
-
* // Download and decrypt
|
|
27
|
-
* const file = await downloadAndDecryptFile(documentId);
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
export const useBinaryUploads = (options) => {
|
|
31
|
-
const [isRunning, setIsRunning] = useState(false);
|
|
32
|
-
const [error, setError] = useState(null);
|
|
33
|
-
const mountedRef = useRef(true);
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
mountedRef.current = true;
|
|
36
|
-
return () => {
|
|
37
|
-
mountedRef.current = false;
|
|
38
|
-
};
|
|
39
|
-
}, []);
|
|
40
|
-
const resolvedEnvironment = useMemo(() => resolveEnvironment(options.environment), [options.environment]);
|
|
41
|
-
const { keypair } = options;
|
|
42
|
-
const createUploadCredentials = useCallback(async (payload) => {
|
|
43
|
-
if (!keypair) {
|
|
44
|
-
throw new Error("No keypair available");
|
|
45
|
-
}
|
|
46
|
-
const { reference, fileName, fileType } = payload;
|
|
47
|
-
const { presignedUrl, uploadKey } = await WelshareApi.fetchS3WriteDelegation(keypair, { reference, fileName, fileType }, resolvedEnvironment);
|
|
48
|
-
return { presignedUrl, uploadKey };
|
|
49
|
-
}, [keypair, resolvedEnvironment]);
|
|
50
|
-
/**
|
|
51
|
-
* Downloads and decrypts a file by its document ID from Nillion.
|
|
52
|
-
*/
|
|
53
|
-
const downloadAndDecryptFile = useCallback(async (documentId) => {
|
|
54
|
-
if (!keypair) {
|
|
55
|
-
throw new Error("No keypair available");
|
|
56
|
-
}
|
|
57
|
-
try {
|
|
58
|
-
setIsRunning(true);
|
|
59
|
-
setError(null);
|
|
60
|
-
const { binaryFile, data: downloadResponse } = await WelshareApi.fetchBinaryData(keypair, resolvedEnvironment, documentId);
|
|
61
|
-
const encodedEncryptionKey = JSON.parse(binaryFile.encryption_key);
|
|
62
|
-
const decryptedData = await decrypt(await downloadResponse, encodedEncryptionKey);
|
|
63
|
-
if (!decryptedData) {
|
|
64
|
-
throw new Error("Failed to decrypt file (received null)");
|
|
65
|
-
}
|
|
66
|
-
const decryptedFile = new File([decryptedData], binaryFile.file_name, {
|
|
67
|
-
type: binaryFile.file_type,
|
|
68
|
-
});
|
|
69
|
-
return decryptedFile;
|
|
70
|
-
}
|
|
71
|
-
catch (err) {
|
|
72
|
-
console.error("Error during file download/decryption:", err);
|
|
73
|
-
const errorMessage = err instanceof Error
|
|
74
|
-
? err.message
|
|
75
|
-
: "Failed to download/decrypt file";
|
|
76
|
-
if (mountedRef.current) {
|
|
77
|
-
setError(errorMessage);
|
|
78
|
-
}
|
|
79
|
-
return undefined;
|
|
80
|
-
}
|
|
81
|
-
finally {
|
|
82
|
-
if (mountedRef.current) {
|
|
83
|
-
setIsRunning(false);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}, [keypair, resolvedEnvironment]);
|
|
87
|
-
return {
|
|
88
|
-
createUploadCredentials,
|
|
89
|
-
downloadAndDecryptFile,
|
|
90
|
-
isRunning,
|
|
91
|
-
error,
|
|
92
|
-
};
|
|
93
|
-
};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { EncryptionKey } from "../utils.js";
|
|
2
|
-
export declare const generateRandomAESKey: () => Promise<CryptoKey>;
|
|
3
|
-
export declare const encryptFile: (file: File, key: CryptoKey) => Promise<{
|
|
4
|
-
encryptedData: ArrayBuffer;
|
|
5
|
-
iv: Uint8Array;
|
|
6
|
-
}>;
|
|
7
|
-
export declare const encodeEncryptionKey: (key: CryptoKey, iv: Uint8Array) => Promise<EncryptionKey>;
|
|
8
|
-
export declare const decrypt: (encryptedData: ArrayBuffer, encryptionKey: EncryptionKey) => Promise<ArrayBuffer | null>;
|
|
9
|
-
//# sourceMappingURL=encryption.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"encryption.d.ts","sourceRoot":"","sources":["../../../src/lib/encryption.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,aAAa,EAAuB,MAAM,aAAa,CAAC;AAE5E,eAAO,MAAM,oBAAoB,QAAa,OAAO,CAAC,SAAS,CAU9D,CAAC;AAIF,eAAO,MAAM,WAAW,SAChB,IAAI,OACL,SAAS,KACb,OAAO,CAAC;IAAE,aAAa,EAAE,WAAW,CAAC;IAAC,EAAE,EAAE,UAAU,CAAA;CAAE,CAaxD,CAAC;AAEF,eAAO,MAAM,mBAAmB,QACzB,SAAS,MACV,UAAU,KACb,OAAO,CAAC,aAAa,CAYvB,CAAC;AAGF,eAAO,MAAM,OAAO,kBACH,WAAW,iBACX,aAAa,KAC3B,OAAO,CAAC,WAAW,GAAG,IAAI,CAuB5B,CAAC"}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { ALGORITHM, decodeEncryptionKey } from "../utils.js";
|
|
2
|
-
export const generateRandomAESKey = async () => {
|
|
3
|
-
// Generate a 256-bit AES-GCM key for file encryption
|
|
4
|
-
return window.crypto.subtle.generateKey({
|
|
5
|
-
name: ALGORITHM,
|
|
6
|
-
length: 256, // 256-bit key
|
|
7
|
-
}, true, // Key is extractable (needed for storage/transmission)
|
|
8
|
-
["encrypt", "decrypt"] // Key usage
|
|
9
|
-
);
|
|
10
|
-
};
|
|
11
|
-
/// also Generates random IV (12 bytes for AES-GCM)
|
|
12
|
-
/// @return {arraybuffer ciphertext, uint8array iv}
|
|
13
|
-
export const encryptFile = async (file, key) => {
|
|
14
|
-
// Read file as ArrayBuffer
|
|
15
|
-
const fileData = await file.arrayBuffer();
|
|
16
|
-
const iv = window.crypto.getRandomValues(new Uint8Array(12));
|
|
17
|
-
// Encrypt the file data
|
|
18
|
-
const encryptedData = await window.crypto.subtle.encrypt({ name: ALGORITHM, iv: iv }, key, fileData);
|
|
19
|
-
return { encryptedData, iv };
|
|
20
|
-
};
|
|
21
|
-
export const encodeEncryptionKey = async (key, iv) => {
|
|
22
|
-
// Export the key as raw bytes
|
|
23
|
-
const exportedKey = await window.crypto.subtle.exportKey("raw", key);
|
|
24
|
-
const keyHex = Array.from(new Uint8Array(exportedKey))
|
|
25
|
-
.map((b) => b.toString(16).padStart(2, "0"))
|
|
26
|
-
.join("");
|
|
27
|
-
const ivHex = Array.from(iv)
|
|
28
|
-
.map((b) => b.toString(16).padStart(2, "0"))
|
|
29
|
-
.join("");
|
|
30
|
-
return { algorithm: ALGORITHM, key: keyHex, iv: ivHex };
|
|
31
|
-
};
|
|
32
|
-
// Helper function to decrypt a file using encoded encryption key
|
|
33
|
-
export const decrypt = async (encryptedData, encryptionKey) => {
|
|
34
|
-
try {
|
|
35
|
-
const { key: keyBytes, iv } = decodeEncryptionKey(encryptionKey);
|
|
36
|
-
const key = await window.crypto.subtle.importKey("raw", keyBytes, { name: ALGORITHM }, false, ["decrypt"]);
|
|
37
|
-
const decryptedData = await window.crypto.subtle.decrypt({ name: ALGORITHM, iv: iv }, key, encryptedData);
|
|
38
|
-
return decryptedData;
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
console.error("Failed to decrypt file:", error);
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export declare const ALGORITHM = "AES-GCM";
|
|
2
|
-
export type Algorithm = "AES-GCM";
|
|
3
|
-
export type EncryptionKey = {
|
|
4
|
-
algorithm: Algorithm;
|
|
5
|
-
key: string;
|
|
6
|
-
iv: string;
|
|
7
|
-
};
|
|
8
|
-
export declare const decodeEncryptionKey: (encryptionKey: EncryptionKey) => {
|
|
9
|
-
key: BufferSource;
|
|
10
|
-
iv: BufferSource;
|
|
11
|
-
};
|
|
12
|
-
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,YAAY,CAAC;AACnC,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC;AAElC,MAAM,MAAM,aAAa,GAAG;IAAE,SAAS,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9E,eAAO,MAAM,mBAAmB,kBACf,aAAa,KAC3B;IAAE,GAAG,EAAE,YAAY,CAAC;IAAC,EAAE,EAAE,YAAY,CAAA;CAUvC,CAAC"}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export const ALGORITHM = "AES-GCM";
|
|
2
|
-
export const decodeEncryptionKey = (encryptionKey) => {
|
|
3
|
-
const keyBytes = new Uint8Array(encryptionKey.key
|
|
4
|
-
.match(/.{1,2}/g)
|
|
5
|
-
.map((byte) => parseInt(byte, 16)));
|
|
6
|
-
const ivBytes = new Uint8Array(encryptionKey.iv.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
|
|
7
|
-
return { key: keyBytes, iv: ivBytes };
|
|
8
|
-
};
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
-
import { decrypt } from "../lib/encryption.js";
|
|
3
|
-
import { type EncryptionKey } from "../utils.js";
|
|
4
|
-
import type {
|
|
5
|
-
RequestUploadCredentialsPayload,
|
|
6
|
-
UploadCredentials,
|
|
7
|
-
} from "../types.js";
|
|
8
|
-
import {
|
|
9
|
-
Nillion,
|
|
10
|
-
WelshareApi,
|
|
11
|
-
resolveEnvironment,
|
|
12
|
-
type WelshareApiEnvironment,
|
|
13
|
-
type WelshareEnvironmentName,
|
|
14
|
-
} from "@welshare/sdk";
|
|
15
|
-
|
|
16
|
-
export interface UseBinaryUploadsOptions {
|
|
17
|
-
/**
|
|
18
|
-
* The user's session keypair for authentication.
|
|
19
|
-
*/
|
|
20
|
-
keypair: Nillion.Keypair | null | undefined;
|
|
21
|
-
/**
|
|
22
|
-
* The Welshare environment to use for API calls.
|
|
23
|
-
*/
|
|
24
|
-
environment: WelshareApiEnvironment | WelshareEnvironmentName;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface UseBinaryUploadsResult {
|
|
28
|
-
/**
|
|
29
|
-
* Request upload credentials (presigned URL) for uploading an encrypted file.
|
|
30
|
-
*/
|
|
31
|
-
createUploadCredentials: (
|
|
32
|
-
payload: RequestUploadCredentialsPayload
|
|
33
|
-
) => Promise<UploadCredentials>;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Download and decrypt a file by its document ID.
|
|
37
|
-
* Only works for files owned by the current user.
|
|
38
|
-
*/
|
|
39
|
-
downloadAndDecryptFile: (documentId: string) => Promise<File | undefined>;
|
|
40
|
-
/**
|
|
41
|
-
* Whether an operation is currently running.
|
|
42
|
-
*/
|
|
43
|
-
isRunning: boolean;
|
|
44
|
-
/**
|
|
45
|
-
* Error message if the last operation failed.
|
|
46
|
-
*/
|
|
47
|
-
error: string | null;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Hook for managing binary file uploads and downloads with Welshare.
|
|
52
|
-
*
|
|
53
|
-
* This hook provides functionality to:
|
|
54
|
-
* - Request presigned URLs for uploading encrypted files to S3
|
|
55
|
-
* - Download and decrypt files from Nillion storage
|
|
56
|
-
*
|
|
57
|
-
* @example
|
|
58
|
-
* ```tsx
|
|
59
|
-
* const { createUploadCredentials, downloadAndDecryptFile, isRunning, error } = useBinaryUploads({
|
|
60
|
-
* keypair: sessionKeyPair,
|
|
61
|
-
* environment: 'production',
|
|
62
|
-
* });
|
|
63
|
-
*
|
|
64
|
-
* // Upload flow: get credentials, encrypt, upload to S3, then submit metadata via submitBinaryData
|
|
65
|
-
* const credentials = await createUploadCredentials({
|
|
66
|
-
* applicationId: 'my-app',
|
|
67
|
-
* reference: 'user-photo',
|
|
68
|
-
* fileName: 'photo.jpg',
|
|
69
|
-
* fileType: 'image/jpeg',
|
|
70
|
-
* });
|
|
71
|
-
*
|
|
72
|
-
* // Download and decrypt
|
|
73
|
-
* const file = await downloadAndDecryptFile(documentId);
|
|
74
|
-
* ```
|
|
75
|
-
*/
|
|
76
|
-
export const useBinaryUploads = (
|
|
77
|
-
options: UseBinaryUploadsOptions
|
|
78
|
-
): UseBinaryUploadsResult => {
|
|
79
|
-
const [isRunning, setIsRunning] = useState(false);
|
|
80
|
-
const [error, setError] = useState<string | null>(null);
|
|
81
|
-
const mountedRef = useRef(true);
|
|
82
|
-
|
|
83
|
-
useEffect(() => {
|
|
84
|
-
mountedRef.current = true;
|
|
85
|
-
return () => {
|
|
86
|
-
mountedRef.current = false;
|
|
87
|
-
};
|
|
88
|
-
}, []);
|
|
89
|
-
|
|
90
|
-
const resolvedEnvironment = useMemo(
|
|
91
|
-
() => resolveEnvironment(options.environment),
|
|
92
|
-
[options.environment]
|
|
93
|
-
);
|
|
94
|
-
const { keypair } = options;
|
|
95
|
-
|
|
96
|
-
const createUploadCredentials = useCallback(
|
|
97
|
-
async (
|
|
98
|
-
payload: RequestUploadCredentialsPayload
|
|
99
|
-
): Promise<UploadCredentials> => {
|
|
100
|
-
if (!keypair) {
|
|
101
|
-
throw new Error("No keypair available");
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const { reference, fileName, fileType } = payload;
|
|
105
|
-
|
|
106
|
-
const { presignedUrl, uploadKey } =
|
|
107
|
-
await WelshareApi.fetchS3WriteDelegation(
|
|
108
|
-
keypair,
|
|
109
|
-
{ reference, fileName, fileType },
|
|
110
|
-
resolvedEnvironment
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
return { presignedUrl, uploadKey };
|
|
114
|
-
},
|
|
115
|
-
[keypair, resolvedEnvironment]
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Downloads and decrypts a file by its document ID from Nillion.
|
|
120
|
-
*/
|
|
121
|
-
const downloadAndDecryptFile = useCallback(
|
|
122
|
-
async (documentId: string): Promise<File | undefined> => {
|
|
123
|
-
if (!keypair) {
|
|
124
|
-
throw new Error("No keypair available");
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
try {
|
|
128
|
-
setIsRunning(true);
|
|
129
|
-
setError(null);
|
|
130
|
-
|
|
131
|
-
const { binaryFile, data: downloadResponse } =
|
|
132
|
-
await WelshareApi.fetchBinaryData(
|
|
133
|
-
keypair,
|
|
134
|
-
resolvedEnvironment,
|
|
135
|
-
documentId
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
const encodedEncryptionKey: EncryptionKey = JSON.parse(
|
|
139
|
-
binaryFile.encryption_key
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
const decryptedData = await decrypt(
|
|
143
|
-
await downloadResponse,
|
|
144
|
-
encodedEncryptionKey
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
if (!decryptedData) {
|
|
148
|
-
throw new Error("Failed to decrypt file (received null)");
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const decryptedFile = new File([decryptedData], binaryFile.file_name, {
|
|
152
|
-
type: binaryFile.file_type,
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
return decryptedFile;
|
|
156
|
-
} catch (err) {
|
|
157
|
-
console.error("Error during file download/decryption:", err);
|
|
158
|
-
const errorMessage =
|
|
159
|
-
err instanceof Error
|
|
160
|
-
? err.message
|
|
161
|
-
: "Failed to download/decrypt file";
|
|
162
|
-
if (mountedRef.current) {
|
|
163
|
-
setError(errorMessage);
|
|
164
|
-
}
|
|
165
|
-
return undefined;
|
|
166
|
-
} finally {
|
|
167
|
-
if (mountedRef.current) {
|
|
168
|
-
setIsRunning(false);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
},
|
|
172
|
-
[keypair, resolvedEnvironment]
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
return {
|
|
176
|
-
createUploadCredentials,
|
|
177
|
-
downloadAndDecryptFile,
|
|
178
|
-
isRunning,
|
|
179
|
-
error,
|
|
180
|
-
};
|
|
181
|
-
};
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { ALGORITHM, EncryptionKey, decodeEncryptionKey } from "../utils.js";
|
|
2
|
-
|
|
3
|
-
export const generateRandomAESKey = async (): Promise<CryptoKey> => {
|
|
4
|
-
// Generate a 256-bit AES-GCM key for file encryption
|
|
5
|
-
return window.crypto.subtle.generateKey(
|
|
6
|
-
{
|
|
7
|
-
name: ALGORITHM,
|
|
8
|
-
length: 256, // 256-bit key
|
|
9
|
-
},
|
|
10
|
-
true, // Key is extractable (needed for storage/transmission)
|
|
11
|
-
["encrypt", "decrypt"] // Key usage
|
|
12
|
-
);
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
/// also Generates random IV (12 bytes for AES-GCM)
|
|
16
|
-
/// @return {arraybuffer ciphertext, uint8array iv}
|
|
17
|
-
export const encryptFile = async (
|
|
18
|
-
file: File,
|
|
19
|
-
key: CryptoKey
|
|
20
|
-
): Promise<{ encryptedData: ArrayBuffer; iv: Uint8Array }> => {
|
|
21
|
-
// Read file as ArrayBuffer
|
|
22
|
-
const fileData = await file.arrayBuffer();
|
|
23
|
-
const iv = window.crypto.getRandomValues(new Uint8Array(12));
|
|
24
|
-
|
|
25
|
-
// Encrypt the file data
|
|
26
|
-
const encryptedData = await window.crypto.subtle.encrypt(
|
|
27
|
-
{ name: ALGORITHM, iv: iv },
|
|
28
|
-
key,
|
|
29
|
-
fileData
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
return { encryptedData, iv };
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export const encodeEncryptionKey = async (
|
|
36
|
-
key: CryptoKey,
|
|
37
|
-
iv: Uint8Array
|
|
38
|
-
): Promise<EncryptionKey> => {
|
|
39
|
-
// Export the key as raw bytes
|
|
40
|
-
const exportedKey = await window.crypto.subtle.exportKey("raw", key);
|
|
41
|
-
const keyHex = Array.from(new Uint8Array(exportedKey))
|
|
42
|
-
.map((b) => b.toString(16).padStart(2, "0"))
|
|
43
|
-
.join("");
|
|
44
|
-
|
|
45
|
-
const ivHex = Array.from(iv)
|
|
46
|
-
.map((b) => b.toString(16).padStart(2, "0"))
|
|
47
|
-
.join("");
|
|
48
|
-
|
|
49
|
-
return { algorithm: ALGORITHM, key: keyHex, iv: ivHex };
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
// Helper function to decrypt a file using encoded encryption key
|
|
53
|
-
export const decrypt = async (
|
|
54
|
-
encryptedData: ArrayBuffer,
|
|
55
|
-
encryptionKey: EncryptionKey
|
|
56
|
-
): Promise<ArrayBuffer | null> => {
|
|
57
|
-
try {
|
|
58
|
-
const { key: keyBytes, iv } = decodeEncryptionKey(encryptionKey);
|
|
59
|
-
|
|
60
|
-
const key = await window.crypto.subtle.importKey(
|
|
61
|
-
"raw",
|
|
62
|
-
keyBytes as BufferSource,
|
|
63
|
-
{ name: ALGORITHM },
|
|
64
|
-
false,
|
|
65
|
-
["decrypt"]
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
const decryptedData = await window.crypto.subtle.decrypt(
|
|
69
|
-
{ name: ALGORITHM, iv: iv as BufferSource },
|
|
70
|
-
key,
|
|
71
|
-
encryptedData
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
return decryptedData;
|
|
75
|
-
} catch (error) {
|
|
76
|
-
console.error("Failed to decrypt file:", error);
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export const ALGORITHM = "AES-GCM";
|
|
2
|
-
export type Algorithm = "AES-GCM";
|
|
3
|
-
|
|
4
|
-
export type EncryptionKey = { algorithm: Algorithm; key: string; iv: string };
|
|
5
|
-
|
|
6
|
-
export const decodeEncryptionKey = (
|
|
7
|
-
encryptionKey: EncryptionKey
|
|
8
|
-
): { key: BufferSource; iv: BufferSource } => {
|
|
9
|
-
const keyBytes = new Uint8Array(
|
|
10
|
-
encryptionKey.key
|
|
11
|
-
.match(/.{1,2}/g)!
|
|
12
|
-
.map((byte: string) => parseInt(byte, 16))
|
|
13
|
-
);
|
|
14
|
-
const ivBytes = new Uint8Array(
|
|
15
|
-
encryptionKey.iv.match(/.{1,2}/g)!.map((byte: string) => parseInt(byte, 16))
|
|
16
|
-
);
|
|
17
|
-
return { key: keyBytes, iv: ivBytes };
|
|
18
|
-
};
|