wolfronix-sdk 1.1.0 → 1.3.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 +43 -36
- package/dist/index.d.mts +5 -25
- package/dist/index.d.ts +5 -25
- package/dist/index.js +52 -116
- package/dist/index.mjs +52 -116
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,35 +1,44 @@
|
|
|
1
|
-
#
|
|
1
|
+
# wolfronix-sdk
|
|
2
2
|
|
|
3
3
|
Official JavaScript/TypeScript SDK for Wolfronix - Zero-knowledge encryption made simple.
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/wolfronix-sdk)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
8
|
## Features
|
|
9
9
|
|
|
10
10
|
- 🔐 **Zero-Knowledge Encryption** - Keys generated client-side, never leave your device
|
|
11
11
|
- 🏢 **Enterprise Ready** - Seamless integration with your existing storage
|
|
12
|
-
|
|
13
12
|
- 🚀 **Simple API** - Encrypt files in 2 lines of code
|
|
14
13
|
- 📦 **TypeScript Native** - Full type definitions included
|
|
15
|
-
- 🌐 **Universal** - Works in Node.js and browsers
|
|
16
|
-
- ⚡ **Streaming** - Handle large files with progress tracking
|
|
14
|
+
- 🌐 **Universal** - Works in Node.js 16+ and modern browsers
|
|
17
15
|
- 🔄 **Auto Retry** - Built-in retry logic with exponential backoff
|
|
18
16
|
|
|
17
|
+
## Backend Integration (Enterprise Mode)
|
|
18
|
+
|
|
19
|
+
To use this SDK, your backend API must implement 3 storage endpoints that Wolfronix will call:
|
|
20
|
+
|
|
21
|
+
1. **POST** `/wolfronix/files/upload` - Store encrypted file + metadata
|
|
22
|
+
2. **GET** `/wolfronix/files/{id}` - Retrieve metadata
|
|
23
|
+
3. **GET** `/wolfronix/files/{id}/data` - Retrieve encrypted file blob
|
|
24
|
+
|
|
25
|
+
Wolfronix handles all encryption/decryption keys and logic; you only handle the encrypted blobs.
|
|
26
|
+
|
|
27
|
+
|
|
19
28
|
## Installation
|
|
20
29
|
|
|
21
30
|
```bash
|
|
22
|
-
npm install
|
|
31
|
+
npm install wolfronix-sdk
|
|
23
32
|
# or
|
|
24
|
-
yarn add
|
|
33
|
+
yarn add wolfronix-sdk
|
|
25
34
|
# or
|
|
26
|
-
pnpm add
|
|
35
|
+
pnpm add wolfronix-sdk
|
|
27
36
|
```
|
|
28
37
|
|
|
29
38
|
## Quick Start
|
|
30
39
|
|
|
31
40
|
```typescript
|
|
32
|
-
import Wolfronix from '
|
|
41
|
+
import Wolfronix from 'wolfronix-sdk';
|
|
33
42
|
|
|
34
43
|
// Initialize client
|
|
35
44
|
const wfx = new Wolfronix({
|
|
@@ -37,7 +46,10 @@ const wfx = new Wolfronix({
|
|
|
37
46
|
clientId: 'your-enterprise-client-id'
|
|
38
47
|
});
|
|
39
48
|
|
|
40
|
-
//
|
|
49
|
+
// Register (First time only) - Generates keys client-side
|
|
50
|
+
await wfx.register('user@example.com', 'password123');
|
|
51
|
+
|
|
52
|
+
// Login (Subsequent visits)
|
|
41
53
|
await wfx.login('user@example.com', 'password123');
|
|
42
54
|
|
|
43
55
|
// Encrypt a file
|
|
@@ -53,7 +65,7 @@ const decrypted = await wfx.decrypt(result.file_id);
|
|
|
53
65
|
### Browser (React, Vue, Angular, etc.)
|
|
54
66
|
|
|
55
67
|
```typescript
|
|
56
|
-
import Wolfronix from '
|
|
68
|
+
import Wolfronix from 'wolfronix-sdk';
|
|
57
69
|
|
|
58
70
|
const wfx = new Wolfronix('https://wolfronix-server:5002');
|
|
59
71
|
|
|
@@ -63,7 +75,6 @@ const handleFileUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
|
63
75
|
if (!file) return;
|
|
64
76
|
|
|
65
77
|
try {
|
|
66
|
-
// Keys are automatically handled by the SDK
|
|
67
78
|
const { file_id } = await wfx.encrypt(file);
|
|
68
79
|
console.log('File encrypted with your private key:', file_id);
|
|
69
80
|
} catch (error) {
|
|
@@ -88,7 +99,7 @@ const handleDownload = async (fileId: string, filename: string) => {
|
|
|
88
99
|
### Node.js
|
|
89
100
|
|
|
90
101
|
```typescript
|
|
91
|
-
import Wolfronix from '
|
|
102
|
+
import Wolfronix from 'wolfronix-sdk';
|
|
92
103
|
import * as fs from 'fs';
|
|
93
104
|
|
|
94
105
|
const wfx = new Wolfronix({
|
|
@@ -118,23 +129,6 @@ async function main() {
|
|
|
118
129
|
main();
|
|
119
130
|
```
|
|
120
131
|
|
|
121
|
-
### Large File Streaming with Progress
|
|
122
|
-
|
|
123
|
-
```typescript
|
|
124
|
-
const wfx = new Wolfronix('https://wolfronix-server:5002');
|
|
125
|
-
|
|
126
|
-
// Encrypt with progress
|
|
127
|
-
const result = await wfx.encryptStream(largeFile, (percent) => {
|
|
128
|
-
console.log(`Uploading: ${percent}%`);
|
|
129
|
-
progressBar.value = percent;
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
// Decrypt with progress
|
|
133
|
-
const blob = await wfx.decryptStream(fileId, (percent) => {
|
|
134
|
-
console.log(`Downloading: ${percent}%`);
|
|
135
|
-
});
|
|
136
|
-
```
|
|
137
|
-
|
|
138
132
|
## API Reference
|
|
139
133
|
|
|
140
134
|
### Constructor
|
|
@@ -167,10 +161,8 @@ new Wolfronix(config: WolfronixConfig | string)
|
|
|
167
161
|
| Method | Description |
|
|
168
162
|
|--------|-------------|
|
|
169
163
|
| `encrypt(file, filename?)` | Encrypt and store file |
|
|
170
|
-
| `encryptStream(file, onProgress?)` | Encrypt large file with progress |
|
|
171
164
|
| `decrypt(fileId)` | Decrypt file (returns Blob) |
|
|
172
165
|
| `decryptToBuffer(fileId)` | Decrypt file (returns ArrayBuffer) |
|
|
173
|
-
| `decryptStream(fileId, onProgress?)` | Decrypt large file with progress |
|
|
174
166
|
| `listFiles()` | List user's encrypted files |
|
|
175
167
|
| `deleteFile(fileId)` | Delete encrypted file |
|
|
176
168
|
|
|
@@ -193,7 +185,7 @@ import Wolfronix, {
|
|
|
193
185
|
PermissionDeniedError,
|
|
194
186
|
NetworkError,
|
|
195
187
|
ValidationError
|
|
196
|
-
} from '
|
|
188
|
+
} from 'wolfronix-sdk';
|
|
197
189
|
|
|
198
190
|
try {
|
|
199
191
|
await wfx.encrypt(file);
|
|
@@ -232,7 +224,7 @@ import Wolfronix, {
|
|
|
232
224
|
FileInfo,
|
|
233
225
|
ListFilesResponse,
|
|
234
226
|
MetricsResponse
|
|
235
|
-
} from '
|
|
227
|
+
} from 'wolfronix-sdk';
|
|
236
228
|
|
|
237
229
|
// All methods are fully typed
|
|
238
230
|
const config: WolfronixConfig = {
|
|
@@ -249,7 +241,7 @@ const response: EncryptResponse = await wfx.encrypt(file);
|
|
|
249
241
|
```typescript
|
|
250
242
|
// useWolfronix.ts
|
|
251
243
|
import { useState, useCallback, useMemo } from 'react';
|
|
252
|
-
import Wolfronix, { FileInfo as WolfronixFile } from '
|
|
244
|
+
import Wolfronix, { FileInfo as WolfronixFile } from 'wolfronix-sdk';
|
|
253
245
|
|
|
254
246
|
export function useWolfronix(baseUrl: string, clientId?: string) {
|
|
255
247
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -337,10 +329,25 @@ function FileManager() {
|
|
|
337
329
|
}
|
|
338
330
|
```
|
|
339
331
|
|
|
332
|
+
## Real-World Use Cases
|
|
333
|
+
|
|
334
|
+
Wolfronix can be integrated into **any application** that handles sensitive data:
|
|
335
|
+
|
|
336
|
+
| Industry | Application | How Wolfronix Helps |
|
|
337
|
+
|----------|------------|---------------------|
|
|
338
|
+
| 🏥 **Healthcare** | Patient records, lab reports | HIPAA-compliant encryption at rest |
|
|
339
|
+
| 🏦 **Finance** | Invoices, tax docs, receipts | End-to-end encrypted banking documents |
|
|
340
|
+
| ⚖️ **Legal** | Contracts, case files | Zero-knowledge confidential storage |
|
|
341
|
+
| ☁️ **Cloud Storage** | Drive/Dropbox alternatives | Encrypted file vault with user-owned keys |
|
|
342
|
+
| 🏢 **Enterprise** | HR records, internal docs | Per-employee encryption isolation |
|
|
343
|
+
| 🎓 **Education** | Exam papers, student data | Tamper-proof academic records |
|
|
344
|
+
| 💬 **Messaging** | File attachments | Encrypted file sharing in chat apps |
|
|
345
|
+
| 🛒 **E-commerce** | Order docs, payment receipts | PCI-compliant document storage |
|
|
346
|
+
|
|
340
347
|
## Requirements
|
|
341
348
|
|
|
342
349
|
- Node.js 16+ (for Node.js usage)
|
|
343
|
-
- Modern browser with
|
|
350
|
+
- Modern browser with Web Crypto API support
|
|
344
351
|
|
|
345
352
|
## License
|
|
346
353
|
|
package/dist/index.d.mts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Zero-knowledge encryption made simple
|
|
4
4
|
*
|
|
5
5
|
* @package @wolfronix/sdk
|
|
6
|
-
* @version 1.
|
|
6
|
+
* @version 1.3.0
|
|
7
7
|
*/
|
|
8
8
|
interface WolfronixConfig {
|
|
9
9
|
/** Wolfronix server base URL */
|
|
@@ -24,11 +24,10 @@ interface AuthResponse {
|
|
|
24
24
|
message: string;
|
|
25
25
|
}
|
|
26
26
|
interface EncryptResponse {
|
|
27
|
-
|
|
27
|
+
status: string;
|
|
28
28
|
file_id: string;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
message: string;
|
|
29
|
+
file_size: number;
|
|
30
|
+
enc_time_ms: number;
|
|
32
31
|
}
|
|
33
32
|
interface FileInfo {
|
|
34
33
|
file_id: string;
|
|
@@ -52,10 +51,6 @@ interface MetricsResponse {
|
|
|
52
51
|
total_bytes_encrypted: number;
|
|
53
52
|
total_bytes_decrypted: number;
|
|
54
53
|
}
|
|
55
|
-
interface StreamToken {
|
|
56
|
-
token: string;
|
|
57
|
-
expires_at: string;
|
|
58
|
-
}
|
|
59
54
|
declare class WolfronixError extends Error {
|
|
60
55
|
readonly code: string;
|
|
61
56
|
readonly statusCode?: number;
|
|
@@ -155,17 +150,6 @@ declare class Wolfronix {
|
|
|
155
150
|
* ```
|
|
156
151
|
*/
|
|
157
152
|
encrypt(file: File | Blob | ArrayBuffer | Uint8Array, filename?: string): Promise<EncryptResponse>;
|
|
158
|
-
/**
|
|
159
|
-
* Encrypt a file using streaming (for large files)
|
|
160
|
-
*
|
|
161
|
-
* @example
|
|
162
|
-
* ```typescript
|
|
163
|
-
* const result = await wfx.encryptStream(largeFile, (progress) => {
|
|
164
|
-
* console.log(`Progress: ${progress}%`);
|
|
165
|
-
* });
|
|
166
|
-
* ```
|
|
167
|
-
*/
|
|
168
|
-
encryptStream(file: File | Blob, onProgress?: (percent: number) => void): Promise<EncryptResponse>;
|
|
169
153
|
/**
|
|
170
154
|
* Decrypt and retrieve a file
|
|
171
155
|
*
|
|
@@ -185,10 +169,6 @@ declare class Wolfronix {
|
|
|
185
169
|
* Decrypt and return as ArrayBuffer
|
|
186
170
|
*/
|
|
187
171
|
decryptToBuffer(fileId: string): Promise<ArrayBuffer>;
|
|
188
|
-
/**
|
|
189
|
-
* Decrypt using streaming (for large files)
|
|
190
|
-
*/
|
|
191
|
-
decryptStream(fileId: string, onProgress?: (percent: number) => void): Promise<Blob>;
|
|
192
172
|
/**
|
|
193
173
|
* List all encrypted files for current user
|
|
194
174
|
*
|
|
@@ -238,4 +218,4 @@ declare class Wolfronix {
|
|
|
238
218
|
*/
|
|
239
219
|
declare function createClient(config: WolfronixConfig | string): Wolfronix;
|
|
240
220
|
|
|
241
|
-
export { type AuthResponse, AuthenticationError, type DeleteResponse, type EncryptResponse, type FileInfo, FileNotFoundError, type ListFilesResponse, type MetricsResponse, NetworkError, PermissionDeniedError,
|
|
221
|
+
export { type AuthResponse, AuthenticationError, type DeleteResponse, type EncryptResponse, type FileInfo, FileNotFoundError, type ListFilesResponse, type MetricsResponse, NetworkError, PermissionDeniedError, ValidationError, Wolfronix, type WolfronixConfig, WolfronixError, createClient, Wolfronix as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Zero-knowledge encryption made simple
|
|
4
4
|
*
|
|
5
5
|
* @package @wolfronix/sdk
|
|
6
|
-
* @version 1.
|
|
6
|
+
* @version 1.3.0
|
|
7
7
|
*/
|
|
8
8
|
interface WolfronixConfig {
|
|
9
9
|
/** Wolfronix server base URL */
|
|
@@ -24,11 +24,10 @@ interface AuthResponse {
|
|
|
24
24
|
message: string;
|
|
25
25
|
}
|
|
26
26
|
interface EncryptResponse {
|
|
27
|
-
|
|
27
|
+
status: string;
|
|
28
28
|
file_id: string;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
message: string;
|
|
29
|
+
file_size: number;
|
|
30
|
+
enc_time_ms: number;
|
|
32
31
|
}
|
|
33
32
|
interface FileInfo {
|
|
34
33
|
file_id: string;
|
|
@@ -52,10 +51,6 @@ interface MetricsResponse {
|
|
|
52
51
|
total_bytes_encrypted: number;
|
|
53
52
|
total_bytes_decrypted: number;
|
|
54
53
|
}
|
|
55
|
-
interface StreamToken {
|
|
56
|
-
token: string;
|
|
57
|
-
expires_at: string;
|
|
58
|
-
}
|
|
59
54
|
declare class WolfronixError extends Error {
|
|
60
55
|
readonly code: string;
|
|
61
56
|
readonly statusCode?: number;
|
|
@@ -155,17 +150,6 @@ declare class Wolfronix {
|
|
|
155
150
|
* ```
|
|
156
151
|
*/
|
|
157
152
|
encrypt(file: File | Blob | ArrayBuffer | Uint8Array, filename?: string): Promise<EncryptResponse>;
|
|
158
|
-
/**
|
|
159
|
-
* Encrypt a file using streaming (for large files)
|
|
160
|
-
*
|
|
161
|
-
* @example
|
|
162
|
-
* ```typescript
|
|
163
|
-
* const result = await wfx.encryptStream(largeFile, (progress) => {
|
|
164
|
-
* console.log(`Progress: ${progress}%`);
|
|
165
|
-
* });
|
|
166
|
-
* ```
|
|
167
|
-
*/
|
|
168
|
-
encryptStream(file: File | Blob, onProgress?: (percent: number) => void): Promise<EncryptResponse>;
|
|
169
153
|
/**
|
|
170
154
|
* Decrypt and retrieve a file
|
|
171
155
|
*
|
|
@@ -185,10 +169,6 @@ declare class Wolfronix {
|
|
|
185
169
|
* Decrypt and return as ArrayBuffer
|
|
186
170
|
*/
|
|
187
171
|
decryptToBuffer(fileId: string): Promise<ArrayBuffer>;
|
|
188
|
-
/**
|
|
189
|
-
* Decrypt using streaming (for large files)
|
|
190
|
-
*/
|
|
191
|
-
decryptStream(fileId: string, onProgress?: (percent: number) => void): Promise<Blob>;
|
|
192
172
|
/**
|
|
193
173
|
* List all encrypted files for current user
|
|
194
174
|
*
|
|
@@ -238,4 +218,4 @@ declare class Wolfronix {
|
|
|
238
218
|
*/
|
|
239
219
|
declare function createClient(config: WolfronixConfig | string): Wolfronix;
|
|
240
220
|
|
|
241
|
-
export { type AuthResponse, AuthenticationError, type DeleteResponse, type EncryptResponse, type FileInfo, FileNotFoundError, type ListFilesResponse, type MetricsResponse, NetworkError, PermissionDeniedError,
|
|
221
|
+
export { type AuthResponse, AuthenticationError, type DeleteResponse, type EncryptResponse, type FileInfo, FileNotFoundError, type ListFilesResponse, type MetricsResponse, NetworkError, PermissionDeniedError, ValidationError, Wolfronix, type WolfronixConfig, WolfronixError, createClient, Wolfronix as default };
|
package/dist/index.js
CHANGED
|
@@ -33,6 +33,14 @@ __export(index_exports, {
|
|
|
33
33
|
module.exports = __toCommonJS(index_exports);
|
|
34
34
|
|
|
35
35
|
// src/crypto.ts
|
|
36
|
+
var getCrypto = () => {
|
|
37
|
+
if (typeof globalThis.crypto !== "undefined") {
|
|
38
|
+
return globalThis.crypto;
|
|
39
|
+
}
|
|
40
|
+
throw new Error(
|
|
41
|
+
"Web Crypto API not available. Requires a modern browser or Node.js 16+."
|
|
42
|
+
);
|
|
43
|
+
};
|
|
36
44
|
var RSA_ALG = {
|
|
37
45
|
name: "RSA-OAEP",
|
|
38
46
|
modulusLength: 2048,
|
|
@@ -42,7 +50,7 @@ var RSA_ALG = {
|
|
|
42
50
|
var WRAP_ALG = "AES-GCM";
|
|
43
51
|
var PBKDF2_ITERATIONS = 1e5;
|
|
44
52
|
async function generateKeyPair() {
|
|
45
|
-
return await
|
|
53
|
+
return await getCrypto().subtle.generateKey(
|
|
46
54
|
RSA_ALG,
|
|
47
55
|
true,
|
|
48
56
|
// extractable
|
|
@@ -51,7 +59,7 @@ async function generateKeyPair() {
|
|
|
51
59
|
}
|
|
52
60
|
async function exportKeyToPEM(key, type) {
|
|
53
61
|
const format = type === "public" ? "spki" : "pkcs8";
|
|
54
|
-
const exported = await
|
|
62
|
+
const exported = await getCrypto().subtle.exportKey(format, key);
|
|
55
63
|
const exportedAsBase64 = arrayBufferToBase64(exported);
|
|
56
64
|
const pemHeader = type === "public" ? "-----BEGIN PUBLIC KEY-----" : "-----BEGIN PRIVATE KEY-----";
|
|
57
65
|
const pemFooter = type === "public" ? "-----END PUBLIC KEY-----" : "-----END PRIVATE KEY-----";
|
|
@@ -66,7 +74,7 @@ async function importKeyFromPEM(pem, type) {
|
|
|
66
74
|
const binaryDer = base64ToArrayBuffer(pemContents);
|
|
67
75
|
const format = type === "public" ? "spki" : "pkcs8";
|
|
68
76
|
const usage = type === "public" ? ["encrypt", "wrapKey"] : ["decrypt", "unwrapKey"];
|
|
69
|
-
return await
|
|
77
|
+
return await getCrypto().subtle.importKey(
|
|
70
78
|
format,
|
|
71
79
|
binaryDer,
|
|
72
80
|
RSA_ALG,
|
|
@@ -76,7 +84,7 @@ async function importKeyFromPEM(pem, type) {
|
|
|
76
84
|
}
|
|
77
85
|
async function deriveWrappingKey(password, saltHex) {
|
|
78
86
|
const enc = new TextEncoder();
|
|
79
|
-
const passwordKey = await
|
|
87
|
+
const passwordKey = await getCrypto().subtle.importKey(
|
|
80
88
|
"raw",
|
|
81
89
|
enc.encode(password),
|
|
82
90
|
"PBKDF2",
|
|
@@ -84,7 +92,7 @@ async function deriveWrappingKey(password, saltHex) {
|
|
|
84
92
|
["deriveKey"]
|
|
85
93
|
);
|
|
86
94
|
const salt = hexToArrayBuffer(saltHex);
|
|
87
|
-
return await
|
|
95
|
+
return await getCrypto().subtle.deriveKey(
|
|
88
96
|
{
|
|
89
97
|
name: "PBKDF2",
|
|
90
98
|
salt,
|
|
@@ -98,12 +106,13 @@ async function deriveWrappingKey(password, saltHex) {
|
|
|
98
106
|
);
|
|
99
107
|
}
|
|
100
108
|
async function wrapPrivateKey(privateKey, password) {
|
|
101
|
-
const
|
|
109
|
+
const crypto = getCrypto();
|
|
110
|
+
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
102
111
|
const saltHex = arrayBufferToHex(salt.buffer);
|
|
103
112
|
const wrappingKey = await deriveWrappingKey(password, saltHex);
|
|
104
|
-
const exportedKey = await
|
|
105
|
-
const iv =
|
|
106
|
-
const encryptedContent = await
|
|
113
|
+
const exportedKey = await crypto.subtle.exportKey("pkcs8", privateKey);
|
|
114
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
115
|
+
const encryptedContent = await crypto.subtle.encrypt(
|
|
107
116
|
{
|
|
108
117
|
name: WRAP_ALG,
|
|
109
118
|
iv
|
|
@@ -125,7 +134,7 @@ async function unwrapPrivateKey(encryptedKeyBase64, password, saltHex) {
|
|
|
125
134
|
const iv = combinedArray.slice(0, 12);
|
|
126
135
|
const data = combinedArray.slice(12);
|
|
127
136
|
const wrappingKey = await deriveWrappingKey(password, saltHex);
|
|
128
|
-
const decryptedKeyData = await
|
|
137
|
+
const decryptedKeyData = await getCrypto().subtle.decrypt(
|
|
129
138
|
{
|
|
130
139
|
name: WRAP_ALG,
|
|
131
140
|
iv
|
|
@@ -133,7 +142,7 @@ async function unwrapPrivateKey(encryptedKeyBase64, password, saltHex) {
|
|
|
133
142
|
wrappingKey,
|
|
134
143
|
data
|
|
135
144
|
);
|
|
136
|
-
return await
|
|
145
|
+
return await getCrypto().subtle.importKey(
|
|
137
146
|
"pkcs8",
|
|
138
147
|
decryptedKeyData,
|
|
139
148
|
RSA_ALG,
|
|
@@ -142,16 +151,23 @@ async function unwrapPrivateKey(encryptedKeyBase64, password, saltHex) {
|
|
|
142
151
|
);
|
|
143
152
|
}
|
|
144
153
|
function arrayBufferToBase64(buffer) {
|
|
145
|
-
let binary = "";
|
|
146
154
|
const bytes = new Uint8Array(buffer);
|
|
155
|
+
if (typeof Buffer !== "undefined") {
|
|
156
|
+
return Buffer.from(bytes).toString("base64");
|
|
157
|
+
}
|
|
158
|
+
let binary = "";
|
|
147
159
|
const len = bytes.byteLength;
|
|
148
160
|
for (let i = 0; i < len; i++) {
|
|
149
161
|
binary += String.fromCharCode(bytes[i]);
|
|
150
162
|
}
|
|
151
|
-
return
|
|
163
|
+
return btoa(binary);
|
|
152
164
|
}
|
|
153
165
|
function base64ToArrayBuffer(base64) {
|
|
154
|
-
|
|
166
|
+
if (typeof Buffer !== "undefined") {
|
|
167
|
+
const buf = Buffer.from(base64, "base64");
|
|
168
|
+
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
169
|
+
}
|
|
170
|
+
const binary_string = atob(base64);
|
|
155
171
|
const len = binary_string.length;
|
|
156
172
|
const bytes = new Uint8Array(len);
|
|
157
173
|
for (let i = 0; i < len; i++) {
|
|
@@ -261,6 +277,9 @@ var Wolfronix = class {
|
|
|
261
277
|
}
|
|
262
278
|
if (includeAuth && this.token) {
|
|
263
279
|
headers["Authorization"] = `Bearer ${this.token}`;
|
|
280
|
+
if (this.userId) {
|
|
281
|
+
headers["X-User-ID"] = this.userId;
|
|
282
|
+
}
|
|
264
283
|
}
|
|
265
284
|
return headers;
|
|
266
285
|
}
|
|
@@ -489,60 +508,15 @@ var Wolfronix = class {
|
|
|
489
508
|
throw new Error("Public key not available. Is user logged in?");
|
|
490
509
|
}
|
|
491
510
|
formData.append("client_public_key", this.publicKeyPEM);
|
|
492
|
-
|
|
493
|
-
formData
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* Encrypt a file using streaming (for large files)
|
|
498
|
-
*
|
|
499
|
-
* @example
|
|
500
|
-
* ```typescript
|
|
501
|
-
* const result = await wfx.encryptStream(largeFile, (progress) => {
|
|
502
|
-
* console.log(`Progress: ${progress}%`);
|
|
503
|
-
* });
|
|
504
|
-
* ```
|
|
505
|
-
*/
|
|
506
|
-
async encryptStream(file, onProgress) {
|
|
507
|
-
this.ensureAuthenticated();
|
|
508
|
-
const tokenResponse = await this.request("POST", "/api/v1/stream/token", {
|
|
509
|
-
body: {
|
|
510
|
-
user_id: this.userId,
|
|
511
|
-
client_id: this.config.clientId
|
|
512
|
-
}
|
|
513
|
-
});
|
|
514
|
-
const formData = new FormData();
|
|
515
|
-
formData.append("file", file);
|
|
516
|
-
formData.append("user_id", this.userId || "");
|
|
517
|
-
formData.append("stream_token", tokenResponse.token);
|
|
518
|
-
if (onProgress && typeof XMLHttpRequest !== "undefined") {
|
|
519
|
-
return new Promise((resolve, reject) => {
|
|
520
|
-
const xhr = new XMLHttpRequest();
|
|
521
|
-
xhr.upload.onprogress = (event) => {
|
|
522
|
-
if (event.lengthComputable) {
|
|
523
|
-
onProgress(Math.round(event.loaded / event.total * 100));
|
|
524
|
-
}
|
|
525
|
-
};
|
|
526
|
-
xhr.onload = () => {
|
|
527
|
-
if (xhr.status >= 200 && xhr.status < 300) {
|
|
528
|
-
resolve(JSON.parse(xhr.responseText));
|
|
529
|
-
} else {
|
|
530
|
-
reject(new WolfronixError("Upload failed", "UPLOAD_ERROR", xhr.status));
|
|
531
|
-
}
|
|
532
|
-
};
|
|
533
|
-
xhr.onerror = () => reject(new NetworkError("Upload failed"));
|
|
534
|
-
xhr.open("POST", `${this.config.baseUrl}/api/v1/stream/encrypt`);
|
|
535
|
-
const headers = this.getHeaders();
|
|
536
|
-
Object.entries(headers).forEach(([key, value]) => {
|
|
537
|
-
xhr.setRequestHeader(key, value);
|
|
538
|
-
});
|
|
539
|
-
xhr.send(formData);
|
|
540
|
-
});
|
|
541
|
-
}
|
|
542
|
-
return this.request("POST", "/api/v1/stream/encrypt", {
|
|
511
|
+
const response = await this.request("POST", "/api/v1/encrypt", {
|
|
543
512
|
formData
|
|
544
|
-
// Stream endpoint might need update to accept public key too if re-implemented
|
|
545
513
|
});
|
|
514
|
+
return {
|
|
515
|
+
status: response.status,
|
|
516
|
+
file_id: String(response.file_id),
|
|
517
|
+
file_size: response.file_size,
|
|
518
|
+
enc_time_ms: response.enc_time_ms
|
|
519
|
+
};
|
|
546
520
|
}
|
|
547
521
|
/**
|
|
548
522
|
* Decrypt and retrieve a file
|
|
@@ -567,9 +541,6 @@ var Wolfronix = class {
|
|
|
567
541
|
throw new Error("Private key not available. Is user logged in?");
|
|
568
542
|
}
|
|
569
543
|
const privateKeyPEM = await exportKeyToPEM(this.privateKey, "private");
|
|
570
|
-
const headers = this.getHeaders();
|
|
571
|
-
headers["X-Private-Key"] = privateKeyPEM;
|
|
572
|
-
headers["X-User-Role"] = "owner";
|
|
573
544
|
return this.request("POST", `/api/v1/files/${fileId}/decrypt`, {
|
|
574
545
|
responseType: "blob",
|
|
575
546
|
headers: {
|
|
@@ -598,51 +569,6 @@ var Wolfronix = class {
|
|
|
598
569
|
}
|
|
599
570
|
});
|
|
600
571
|
}
|
|
601
|
-
/**
|
|
602
|
-
* Decrypt using streaming (for large files)
|
|
603
|
-
*/
|
|
604
|
-
async decryptStream(fileId, onProgress) {
|
|
605
|
-
this.ensureAuthenticated();
|
|
606
|
-
if (!fileId) {
|
|
607
|
-
throw new ValidationError("File ID is required");
|
|
608
|
-
}
|
|
609
|
-
if (!this.privateKey) {
|
|
610
|
-
throw new Error("Private key not available. Is user logged in?");
|
|
611
|
-
}
|
|
612
|
-
const privateKeyPEM = await exportKeyToPEM(this.privateKey, "private");
|
|
613
|
-
if (onProgress && typeof XMLHttpRequest !== "undefined") {
|
|
614
|
-
return new Promise((resolve, reject) => {
|
|
615
|
-
const xhr = new XMLHttpRequest();
|
|
616
|
-
xhr.responseType = "blob";
|
|
617
|
-
xhr.onprogress = (event) => {
|
|
618
|
-
if (event.lengthComputable) {
|
|
619
|
-
onProgress(Math.round(event.loaded / event.total * 100));
|
|
620
|
-
}
|
|
621
|
-
};
|
|
622
|
-
xhr.onload = () => {
|
|
623
|
-
if (xhr.status >= 200 && xhr.status < 300) {
|
|
624
|
-
resolve(xhr.response);
|
|
625
|
-
} else {
|
|
626
|
-
reject(new WolfronixError("Download failed", "DOWNLOAD_ERROR", xhr.status));
|
|
627
|
-
}
|
|
628
|
-
};
|
|
629
|
-
xhr.onerror = () => reject(new NetworkError("Download failed"));
|
|
630
|
-
xhr.open("POST", `${this.config.baseUrl}/api/v1/files/${fileId}/decrypt`);
|
|
631
|
-
const headers = { ...this.getHeaders(), "X-Private-Key": privateKeyPEM, "X-User-Role": "owner" };
|
|
632
|
-
Object.entries(headers).forEach(([key, value]) => {
|
|
633
|
-
xhr.setRequestHeader(key, value);
|
|
634
|
-
});
|
|
635
|
-
xhr.send();
|
|
636
|
-
});
|
|
637
|
-
}
|
|
638
|
-
return this.request("POST", `/api/v1/files/${fileId}/decrypt`, {
|
|
639
|
-
responseType: "blob",
|
|
640
|
-
headers: {
|
|
641
|
-
"X-Private-Key": privateKeyPEM,
|
|
642
|
-
"X-User-Role": "owner"
|
|
643
|
-
}
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
572
|
/**
|
|
647
573
|
* List all encrypted files for current user
|
|
648
574
|
*
|
|
@@ -654,7 +580,17 @@ var Wolfronix = class {
|
|
|
654
580
|
*/
|
|
655
581
|
async listFiles() {
|
|
656
582
|
this.ensureAuthenticated();
|
|
657
|
-
|
|
583
|
+
const files = await this.request("GET", "/api/v1/files");
|
|
584
|
+
return {
|
|
585
|
+
success: true,
|
|
586
|
+
files: (files || []).map((f) => ({
|
|
587
|
+
file_id: f.id,
|
|
588
|
+
original_name: f.name,
|
|
589
|
+
encrypted_size: f.size_bytes,
|
|
590
|
+
created_at: f.date
|
|
591
|
+
})),
|
|
592
|
+
total: (files || []).length
|
|
593
|
+
};
|
|
658
594
|
}
|
|
659
595
|
/**
|
|
660
596
|
* Delete an encrypted file
|
|
@@ -685,7 +621,7 @@ var Wolfronix = class {
|
|
|
685
621
|
*/
|
|
686
622
|
async getMetrics() {
|
|
687
623
|
this.ensureAuthenticated();
|
|
688
|
-
return this.request("GET", "/api/v1/metrics");
|
|
624
|
+
return this.request("GET", "/api/v1/metrics/summary");
|
|
689
625
|
}
|
|
690
626
|
/**
|
|
691
627
|
* Check if server is healthy
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
// src/crypto.ts
|
|
2
|
+
var getCrypto = () => {
|
|
3
|
+
if (typeof globalThis.crypto !== "undefined") {
|
|
4
|
+
return globalThis.crypto;
|
|
5
|
+
}
|
|
6
|
+
throw new Error(
|
|
7
|
+
"Web Crypto API not available. Requires a modern browser or Node.js 16+."
|
|
8
|
+
);
|
|
9
|
+
};
|
|
2
10
|
var RSA_ALG = {
|
|
3
11
|
name: "RSA-OAEP",
|
|
4
12
|
modulusLength: 2048,
|
|
@@ -8,7 +16,7 @@ var RSA_ALG = {
|
|
|
8
16
|
var WRAP_ALG = "AES-GCM";
|
|
9
17
|
var PBKDF2_ITERATIONS = 1e5;
|
|
10
18
|
async function generateKeyPair() {
|
|
11
|
-
return await
|
|
19
|
+
return await getCrypto().subtle.generateKey(
|
|
12
20
|
RSA_ALG,
|
|
13
21
|
true,
|
|
14
22
|
// extractable
|
|
@@ -17,7 +25,7 @@ async function generateKeyPair() {
|
|
|
17
25
|
}
|
|
18
26
|
async function exportKeyToPEM(key, type) {
|
|
19
27
|
const format = type === "public" ? "spki" : "pkcs8";
|
|
20
|
-
const exported = await
|
|
28
|
+
const exported = await getCrypto().subtle.exportKey(format, key);
|
|
21
29
|
const exportedAsBase64 = arrayBufferToBase64(exported);
|
|
22
30
|
const pemHeader = type === "public" ? "-----BEGIN PUBLIC KEY-----" : "-----BEGIN PRIVATE KEY-----";
|
|
23
31
|
const pemFooter = type === "public" ? "-----END PUBLIC KEY-----" : "-----END PRIVATE KEY-----";
|
|
@@ -32,7 +40,7 @@ async function importKeyFromPEM(pem, type) {
|
|
|
32
40
|
const binaryDer = base64ToArrayBuffer(pemContents);
|
|
33
41
|
const format = type === "public" ? "spki" : "pkcs8";
|
|
34
42
|
const usage = type === "public" ? ["encrypt", "wrapKey"] : ["decrypt", "unwrapKey"];
|
|
35
|
-
return await
|
|
43
|
+
return await getCrypto().subtle.importKey(
|
|
36
44
|
format,
|
|
37
45
|
binaryDer,
|
|
38
46
|
RSA_ALG,
|
|
@@ -42,7 +50,7 @@ async function importKeyFromPEM(pem, type) {
|
|
|
42
50
|
}
|
|
43
51
|
async function deriveWrappingKey(password, saltHex) {
|
|
44
52
|
const enc = new TextEncoder();
|
|
45
|
-
const passwordKey = await
|
|
53
|
+
const passwordKey = await getCrypto().subtle.importKey(
|
|
46
54
|
"raw",
|
|
47
55
|
enc.encode(password),
|
|
48
56
|
"PBKDF2",
|
|
@@ -50,7 +58,7 @@ async function deriveWrappingKey(password, saltHex) {
|
|
|
50
58
|
["deriveKey"]
|
|
51
59
|
);
|
|
52
60
|
const salt = hexToArrayBuffer(saltHex);
|
|
53
|
-
return await
|
|
61
|
+
return await getCrypto().subtle.deriveKey(
|
|
54
62
|
{
|
|
55
63
|
name: "PBKDF2",
|
|
56
64
|
salt,
|
|
@@ -64,12 +72,13 @@ async function deriveWrappingKey(password, saltHex) {
|
|
|
64
72
|
);
|
|
65
73
|
}
|
|
66
74
|
async function wrapPrivateKey(privateKey, password) {
|
|
67
|
-
const
|
|
75
|
+
const crypto = getCrypto();
|
|
76
|
+
const salt = crypto.getRandomValues(new Uint8Array(16));
|
|
68
77
|
const saltHex = arrayBufferToHex(salt.buffer);
|
|
69
78
|
const wrappingKey = await deriveWrappingKey(password, saltHex);
|
|
70
|
-
const exportedKey = await
|
|
71
|
-
const iv =
|
|
72
|
-
const encryptedContent = await
|
|
79
|
+
const exportedKey = await crypto.subtle.exportKey("pkcs8", privateKey);
|
|
80
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
81
|
+
const encryptedContent = await crypto.subtle.encrypt(
|
|
73
82
|
{
|
|
74
83
|
name: WRAP_ALG,
|
|
75
84
|
iv
|
|
@@ -91,7 +100,7 @@ async function unwrapPrivateKey(encryptedKeyBase64, password, saltHex) {
|
|
|
91
100
|
const iv = combinedArray.slice(0, 12);
|
|
92
101
|
const data = combinedArray.slice(12);
|
|
93
102
|
const wrappingKey = await deriveWrappingKey(password, saltHex);
|
|
94
|
-
const decryptedKeyData = await
|
|
103
|
+
const decryptedKeyData = await getCrypto().subtle.decrypt(
|
|
95
104
|
{
|
|
96
105
|
name: WRAP_ALG,
|
|
97
106
|
iv
|
|
@@ -99,7 +108,7 @@ async function unwrapPrivateKey(encryptedKeyBase64, password, saltHex) {
|
|
|
99
108
|
wrappingKey,
|
|
100
109
|
data
|
|
101
110
|
);
|
|
102
|
-
return await
|
|
111
|
+
return await getCrypto().subtle.importKey(
|
|
103
112
|
"pkcs8",
|
|
104
113
|
decryptedKeyData,
|
|
105
114
|
RSA_ALG,
|
|
@@ -108,16 +117,23 @@ async function unwrapPrivateKey(encryptedKeyBase64, password, saltHex) {
|
|
|
108
117
|
);
|
|
109
118
|
}
|
|
110
119
|
function arrayBufferToBase64(buffer) {
|
|
111
|
-
let binary = "";
|
|
112
120
|
const bytes = new Uint8Array(buffer);
|
|
121
|
+
if (typeof Buffer !== "undefined") {
|
|
122
|
+
return Buffer.from(bytes).toString("base64");
|
|
123
|
+
}
|
|
124
|
+
let binary = "";
|
|
113
125
|
const len = bytes.byteLength;
|
|
114
126
|
for (let i = 0; i < len; i++) {
|
|
115
127
|
binary += String.fromCharCode(bytes[i]);
|
|
116
128
|
}
|
|
117
|
-
return
|
|
129
|
+
return btoa(binary);
|
|
118
130
|
}
|
|
119
131
|
function base64ToArrayBuffer(base64) {
|
|
120
|
-
|
|
132
|
+
if (typeof Buffer !== "undefined") {
|
|
133
|
+
const buf = Buffer.from(base64, "base64");
|
|
134
|
+
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
135
|
+
}
|
|
136
|
+
const binary_string = atob(base64);
|
|
121
137
|
const len = binary_string.length;
|
|
122
138
|
const bytes = new Uint8Array(len);
|
|
123
139
|
for (let i = 0; i < len; i++) {
|
|
@@ -227,6 +243,9 @@ var Wolfronix = class {
|
|
|
227
243
|
}
|
|
228
244
|
if (includeAuth && this.token) {
|
|
229
245
|
headers["Authorization"] = `Bearer ${this.token}`;
|
|
246
|
+
if (this.userId) {
|
|
247
|
+
headers["X-User-ID"] = this.userId;
|
|
248
|
+
}
|
|
230
249
|
}
|
|
231
250
|
return headers;
|
|
232
251
|
}
|
|
@@ -455,60 +474,15 @@ var Wolfronix = class {
|
|
|
455
474
|
throw new Error("Public key not available. Is user logged in?");
|
|
456
475
|
}
|
|
457
476
|
formData.append("client_public_key", this.publicKeyPEM);
|
|
458
|
-
|
|
459
|
-
formData
|
|
460
|
-
});
|
|
461
|
-
}
|
|
462
|
-
/**
|
|
463
|
-
* Encrypt a file using streaming (for large files)
|
|
464
|
-
*
|
|
465
|
-
* @example
|
|
466
|
-
* ```typescript
|
|
467
|
-
* const result = await wfx.encryptStream(largeFile, (progress) => {
|
|
468
|
-
* console.log(`Progress: ${progress}%`);
|
|
469
|
-
* });
|
|
470
|
-
* ```
|
|
471
|
-
*/
|
|
472
|
-
async encryptStream(file, onProgress) {
|
|
473
|
-
this.ensureAuthenticated();
|
|
474
|
-
const tokenResponse = await this.request("POST", "/api/v1/stream/token", {
|
|
475
|
-
body: {
|
|
476
|
-
user_id: this.userId,
|
|
477
|
-
client_id: this.config.clientId
|
|
478
|
-
}
|
|
479
|
-
});
|
|
480
|
-
const formData = new FormData();
|
|
481
|
-
formData.append("file", file);
|
|
482
|
-
formData.append("user_id", this.userId || "");
|
|
483
|
-
formData.append("stream_token", tokenResponse.token);
|
|
484
|
-
if (onProgress && typeof XMLHttpRequest !== "undefined") {
|
|
485
|
-
return new Promise((resolve, reject) => {
|
|
486
|
-
const xhr = new XMLHttpRequest();
|
|
487
|
-
xhr.upload.onprogress = (event) => {
|
|
488
|
-
if (event.lengthComputable) {
|
|
489
|
-
onProgress(Math.round(event.loaded / event.total * 100));
|
|
490
|
-
}
|
|
491
|
-
};
|
|
492
|
-
xhr.onload = () => {
|
|
493
|
-
if (xhr.status >= 200 && xhr.status < 300) {
|
|
494
|
-
resolve(JSON.parse(xhr.responseText));
|
|
495
|
-
} else {
|
|
496
|
-
reject(new WolfronixError("Upload failed", "UPLOAD_ERROR", xhr.status));
|
|
497
|
-
}
|
|
498
|
-
};
|
|
499
|
-
xhr.onerror = () => reject(new NetworkError("Upload failed"));
|
|
500
|
-
xhr.open("POST", `${this.config.baseUrl}/api/v1/stream/encrypt`);
|
|
501
|
-
const headers = this.getHeaders();
|
|
502
|
-
Object.entries(headers).forEach(([key, value]) => {
|
|
503
|
-
xhr.setRequestHeader(key, value);
|
|
504
|
-
});
|
|
505
|
-
xhr.send(formData);
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
return this.request("POST", "/api/v1/stream/encrypt", {
|
|
477
|
+
const response = await this.request("POST", "/api/v1/encrypt", {
|
|
509
478
|
formData
|
|
510
|
-
// Stream endpoint might need update to accept public key too if re-implemented
|
|
511
479
|
});
|
|
480
|
+
return {
|
|
481
|
+
status: response.status,
|
|
482
|
+
file_id: String(response.file_id),
|
|
483
|
+
file_size: response.file_size,
|
|
484
|
+
enc_time_ms: response.enc_time_ms
|
|
485
|
+
};
|
|
512
486
|
}
|
|
513
487
|
/**
|
|
514
488
|
* Decrypt and retrieve a file
|
|
@@ -533,9 +507,6 @@ var Wolfronix = class {
|
|
|
533
507
|
throw new Error("Private key not available. Is user logged in?");
|
|
534
508
|
}
|
|
535
509
|
const privateKeyPEM = await exportKeyToPEM(this.privateKey, "private");
|
|
536
|
-
const headers = this.getHeaders();
|
|
537
|
-
headers["X-Private-Key"] = privateKeyPEM;
|
|
538
|
-
headers["X-User-Role"] = "owner";
|
|
539
510
|
return this.request("POST", `/api/v1/files/${fileId}/decrypt`, {
|
|
540
511
|
responseType: "blob",
|
|
541
512
|
headers: {
|
|
@@ -564,51 +535,6 @@ var Wolfronix = class {
|
|
|
564
535
|
}
|
|
565
536
|
});
|
|
566
537
|
}
|
|
567
|
-
/**
|
|
568
|
-
* Decrypt using streaming (for large files)
|
|
569
|
-
*/
|
|
570
|
-
async decryptStream(fileId, onProgress) {
|
|
571
|
-
this.ensureAuthenticated();
|
|
572
|
-
if (!fileId) {
|
|
573
|
-
throw new ValidationError("File ID is required");
|
|
574
|
-
}
|
|
575
|
-
if (!this.privateKey) {
|
|
576
|
-
throw new Error("Private key not available. Is user logged in?");
|
|
577
|
-
}
|
|
578
|
-
const privateKeyPEM = await exportKeyToPEM(this.privateKey, "private");
|
|
579
|
-
if (onProgress && typeof XMLHttpRequest !== "undefined") {
|
|
580
|
-
return new Promise((resolve, reject) => {
|
|
581
|
-
const xhr = new XMLHttpRequest();
|
|
582
|
-
xhr.responseType = "blob";
|
|
583
|
-
xhr.onprogress = (event) => {
|
|
584
|
-
if (event.lengthComputable) {
|
|
585
|
-
onProgress(Math.round(event.loaded / event.total * 100));
|
|
586
|
-
}
|
|
587
|
-
};
|
|
588
|
-
xhr.onload = () => {
|
|
589
|
-
if (xhr.status >= 200 && xhr.status < 300) {
|
|
590
|
-
resolve(xhr.response);
|
|
591
|
-
} else {
|
|
592
|
-
reject(new WolfronixError("Download failed", "DOWNLOAD_ERROR", xhr.status));
|
|
593
|
-
}
|
|
594
|
-
};
|
|
595
|
-
xhr.onerror = () => reject(new NetworkError("Download failed"));
|
|
596
|
-
xhr.open("POST", `${this.config.baseUrl}/api/v1/files/${fileId}/decrypt`);
|
|
597
|
-
const headers = { ...this.getHeaders(), "X-Private-Key": privateKeyPEM, "X-User-Role": "owner" };
|
|
598
|
-
Object.entries(headers).forEach(([key, value]) => {
|
|
599
|
-
xhr.setRequestHeader(key, value);
|
|
600
|
-
});
|
|
601
|
-
xhr.send();
|
|
602
|
-
});
|
|
603
|
-
}
|
|
604
|
-
return this.request("POST", `/api/v1/files/${fileId}/decrypt`, {
|
|
605
|
-
responseType: "blob",
|
|
606
|
-
headers: {
|
|
607
|
-
"X-Private-Key": privateKeyPEM,
|
|
608
|
-
"X-User-Role": "owner"
|
|
609
|
-
}
|
|
610
|
-
});
|
|
611
|
-
}
|
|
612
538
|
/**
|
|
613
539
|
* List all encrypted files for current user
|
|
614
540
|
*
|
|
@@ -620,7 +546,17 @@ var Wolfronix = class {
|
|
|
620
546
|
*/
|
|
621
547
|
async listFiles() {
|
|
622
548
|
this.ensureAuthenticated();
|
|
623
|
-
|
|
549
|
+
const files = await this.request("GET", "/api/v1/files");
|
|
550
|
+
return {
|
|
551
|
+
success: true,
|
|
552
|
+
files: (files || []).map((f) => ({
|
|
553
|
+
file_id: f.id,
|
|
554
|
+
original_name: f.name,
|
|
555
|
+
encrypted_size: f.size_bytes,
|
|
556
|
+
created_at: f.date
|
|
557
|
+
})),
|
|
558
|
+
total: (files || []).length
|
|
559
|
+
};
|
|
624
560
|
}
|
|
625
561
|
/**
|
|
626
562
|
* Delete an encrypted file
|
|
@@ -651,7 +587,7 @@ var Wolfronix = class {
|
|
|
651
587
|
*/
|
|
652
588
|
async getMetrics() {
|
|
653
589
|
this.ensureAuthenticated();
|
|
654
|
-
return this.request("GET", "/api/v1/metrics");
|
|
590
|
+
return this.request("GET", "/api/v1/metrics/summary");
|
|
655
591
|
}
|
|
656
592
|
/**
|
|
657
593
|
* Check if server is healthy
|