@secrecy/lib 1.29.0-feat-rename-file.2 → 1.29.0-feat-add-manage-user-data.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/dist/lib/cache.js +2 -2
- package/dist/lib/client/SecrecyAppClient.js +2 -2
- package/dist/lib/client/SecrecyCloudClient.js +91 -91
- package/dist/lib/client/SecrecyMailClient.js +25 -25
- package/dist/lib/client/convert/file.js +29 -0
- package/dist/lib/client/convert/mail.js +4 -4
- package/dist/lib/client/convert/node.js +7 -7
- package/dist/lib/client/index.js +3 -3
- package/dist/types/cache.d.ts +3 -3
- package/dist/types/client/SecrecyCloudClient.d.ts +15 -15
- package/dist/types/client/convert/file.d.ts +4 -0
- package/dist/types/client/index.d.ts +1 -1
- package/dist/types/client/types/file.d.ts +6 -0
- package/dist/types/client/types/index.d.ts +1 -1
- package/dist/types/client/types/mail.d.ts +5 -5
- package/dist/types/client/types/node.d.ts +4 -4
- package/dist/types/client.d.ts +445 -507
- package/dist/types/index.d.ts +2 -2
- package/dist/types/worker/sodium.d.ts +1 -1
- package/package.json +2 -2
- package/dist/lib/client/convert/data.js +0 -29
- package/dist/types/client/convert/data.d.ts +0 -4
- package/dist/types/client/types/data.d.ts +0 -6
- /package/dist/lib/client/types/{data.js → file.js} +0 -0
- /package/dist/lib/crypto/{data.js → file.js} +0 -0
- /package/dist/types/crypto/{data.d.ts → file.d.ts} +0 -0
package/dist/lib/cache.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { LRUCache } from 'lru-cache';
|
|
2
2
|
import { gigaToBytes } from './utils.js';
|
|
3
|
-
export const
|
|
3
|
+
export const filesCache = new Map();
|
|
4
4
|
export const nodesCache = new Map();
|
|
5
5
|
export const usersCache = new Map();
|
|
6
6
|
export const publicKeysCache = new Map();
|
|
7
|
-
export const
|
|
7
|
+
export const dataCache = new LRUCache({
|
|
8
8
|
max: 500,
|
|
9
9
|
maxSize: gigaToBytes(0.5),
|
|
10
10
|
sizeCalculation: (value) => {
|
|
@@ -82,8 +82,8 @@ export class SecrecyAppClient {
|
|
|
82
82
|
async updateSettings(settings) {
|
|
83
83
|
const updateAppSettings = await this.#apiClient.application.updateSettings.mutate({
|
|
84
84
|
cloudNodeDaysForDelete: settings.cloudNodeDaysForDelete,
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
historyFileDaysForDelete: settings.historyFileDaysForDelete,
|
|
86
|
+
historyMaxFileCount: settings.historyMaxFileCount,
|
|
87
87
|
});
|
|
88
88
|
return updateAppSettings;
|
|
89
89
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
2
|
import ky from 'ky';
|
|
3
3
|
import { encryptName } from '../index.js';
|
|
4
|
-
import { nodesCache,
|
|
5
|
-
import { secretStreamKeygen } from '../crypto/
|
|
4
|
+
import { nodesCache, filesCache, dataCache } from '../cache.js';
|
|
5
|
+
import { secretStreamKeygen } from '../crypto/file.js';
|
|
6
6
|
import { decryptCryptoBox, encryptCryptoBox } from '../crypto/index.js';
|
|
7
7
|
import { compress, decompress } from '../minify/index.js';
|
|
8
8
|
import { sodium } from '../sodium.js';
|
|
9
9
|
import { enumerate, chunks, concatenate } from '../utils/array.js';
|
|
10
10
|
import { md5 } from '../worker/md5.js';
|
|
11
11
|
import { decrypt, encrypt } from '../worker/sodium.js';
|
|
12
|
-
import {
|
|
12
|
+
import { apiFileToExternal } from './convert/file.js';
|
|
13
13
|
import { apiNodeFullToInternalFull, apiNodeToExternal, apiNodeToExternalNodeFull, internalNodeFullToNodeFull, } from './convert/node.js';
|
|
14
14
|
import { promiseAllLimit } from '../utils/promise.js';
|
|
15
15
|
// import { md5 } from "../worker/index.js";
|
|
@@ -23,71 +23,71 @@ export class SecrecyCloudClient {
|
|
|
23
23
|
this.#keys = keys;
|
|
24
24
|
this.#apiClient = apiClient;
|
|
25
25
|
}
|
|
26
|
-
async
|
|
27
|
-
const
|
|
28
|
-
|
|
26
|
+
async addFileToHistory({ fileId, nodeId, }) {
|
|
27
|
+
const addFileToHistory = await this.#apiClient.cloud.addFileToHistory.mutate({
|
|
28
|
+
fileId,
|
|
29
29
|
nodeId,
|
|
30
30
|
});
|
|
31
|
-
const node = await apiNodeFullToInternalFull(
|
|
32
|
-
const
|
|
33
|
-
if (
|
|
31
|
+
const node = await apiNodeFullToInternalFull(addFileToHistory, this.#keys);
|
|
32
|
+
const file = node.history.find((f) => f.id === fileId);
|
|
33
|
+
if (file !== undefined) {
|
|
34
34
|
const users = node.users.filter(([u]) => u.id !== this.#client.app.userId);
|
|
35
|
-
await this.#apiClient.cloud.
|
|
36
|
-
|
|
35
|
+
await this.#apiClient.cloud.shareFileInHistory.mutate({
|
|
36
|
+
fileId: file.id,
|
|
37
37
|
nodeId,
|
|
38
38
|
users: await Promise.all(users.map(async ([u]) => {
|
|
39
39
|
const userPubKey = await this.#client.app.userPublicKey(u.id);
|
|
40
40
|
return {
|
|
41
41
|
id: u.id,
|
|
42
|
-
key: sodium.to_hex(encryptCryptoBox(sodium.from_hex(
|
|
42
|
+
key: sodium.to_hex(encryptCryptoBox(sodium.from_hex(file.key), userPubKey, this.#keys.privateKey)),
|
|
43
43
|
};
|
|
44
44
|
})),
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
return internalNodeFullToNodeFull(node);
|
|
48
48
|
}
|
|
49
|
-
async
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
const compressed = compress(
|
|
53
|
-
const { data:
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
async uploadFile({ file, encryptProgress, uploadProgress, signal, }) {
|
|
50
|
+
const fileKey = secretStreamKeygen();
|
|
51
|
+
const fileBuffer = file instanceof File ? new Uint8Array(await file.arrayBuffer()) : file;
|
|
52
|
+
const compressed = compress(fileBuffer);
|
|
53
|
+
const { data: encryptedFile, md5: md5File, md5Encrypted, } = await encrypt(fileKey, compressed, encryptProgress, signal);
|
|
54
|
+
const encryptedFileKey = encryptCryptoBox(fileKey, this.#keys.publicKey, this.#keys.privateKey);
|
|
55
|
+
const uploadFile = await this.#apiClient.cloud.uploadFile.mutate({
|
|
56
|
+
fileSize: BigInt(encryptedFile.byteLength),
|
|
57
|
+
fileSizeBefore: BigInt(fileBuffer.byteLength),
|
|
58
|
+
fileKey: sodium.to_hex(encryptedFileKey),
|
|
59
59
|
md5Encrypted,
|
|
60
|
-
md5:
|
|
60
|
+
md5: md5File,
|
|
61
61
|
});
|
|
62
62
|
await uploadProgress?.({
|
|
63
|
-
total:
|
|
63
|
+
total: encryptedFile.byteLength,
|
|
64
64
|
current: 0,
|
|
65
65
|
percent: 0,
|
|
66
66
|
});
|
|
67
|
-
if (
|
|
67
|
+
if (uploadFile.parts.length === 0) {
|
|
68
68
|
await uploadProgress?.({
|
|
69
|
-
total:
|
|
70
|
-
current:
|
|
69
|
+
total: encryptedFile.byteLength,
|
|
70
|
+
current: encryptedFile.byteLength,
|
|
71
71
|
percent: 1,
|
|
72
72
|
});
|
|
73
|
-
return
|
|
73
|
+
return uploadFile.fileId;
|
|
74
74
|
}
|
|
75
75
|
const uploadPartEnded = async (md5, order) => {
|
|
76
|
-
const { isUploadPartEnded } = await this.#apiClient.cloud.
|
|
77
|
-
|
|
76
|
+
const { isUploadPartEnded } = await this.#apiClient.cloud.uploadFilePartEnd.mutate({
|
|
77
|
+
fileId: uploadFile.fileId,
|
|
78
78
|
md5,
|
|
79
79
|
order,
|
|
80
80
|
});
|
|
81
81
|
return isUploadPartEnded;
|
|
82
82
|
};
|
|
83
83
|
const uploadEnded = async () => {
|
|
84
|
-
const { isUploadEnded } = await this.#apiClient.cloud.
|
|
85
|
-
|
|
84
|
+
const { isUploadEnded } = await this.#apiClient.cloud.uploadFileEnd.mutate({
|
|
85
|
+
fileId: uploadFile.fileId,
|
|
86
86
|
});
|
|
87
87
|
return isUploadEnded;
|
|
88
88
|
};
|
|
89
89
|
const chunkParts = new Array();
|
|
90
|
-
for (const [index, chunk] of enumerate(chunks(
|
|
90
|
+
for (const [index, chunk] of enumerate(chunks(encryptedFile, Number(uploadFile.filePartSize)))) {
|
|
91
91
|
chunkParts.push({
|
|
92
92
|
order: index + 1,
|
|
93
93
|
data: chunk,
|
|
@@ -99,8 +99,8 @@ export class SecrecyCloudClient {
|
|
|
99
99
|
progressParts[part] = progressEvent;
|
|
100
100
|
const current = Object.values(progressParts).reduce((prv, cur) => prv + cur.loaded, 0);
|
|
101
101
|
void uploadProgress?.({
|
|
102
|
-
percent: current /
|
|
103
|
-
total:
|
|
102
|
+
percent: current / encryptedFile.byteLength,
|
|
103
|
+
total: encryptedFile.byteLength,
|
|
104
104
|
current,
|
|
105
105
|
});
|
|
106
106
|
};
|
|
@@ -113,7 +113,7 @@ export class SecrecyCloudClient {
|
|
|
113
113
|
for (const [key, value] of Object.entries(part.fields)) {
|
|
114
114
|
formData.append(key, value);
|
|
115
115
|
}
|
|
116
|
-
formData.append('
|
|
116
|
+
formData.append('file', new Blob([chunk.data]), `${uploadFile.fileId}-${chunk.order}`);
|
|
117
117
|
await axios.post(part.url, formData, {
|
|
118
118
|
onUploadProgress: (progressEvent) => {
|
|
119
119
|
onProgress(part.order, progressEvent);
|
|
@@ -122,22 +122,22 @@ export class SecrecyCloudClient {
|
|
|
122
122
|
});
|
|
123
123
|
await uploadPartEnded(chunk.md5, chunk.order);
|
|
124
124
|
};
|
|
125
|
-
await promiseAllLimit(3,
|
|
125
|
+
await promiseAllLimit(3, uploadFile.parts.map((p) => async () => {
|
|
126
126
|
await byPart(p);
|
|
127
127
|
}));
|
|
128
128
|
await uploadEnded();
|
|
129
|
-
|
|
130
|
-
return
|
|
129
|
+
dataCache.set(uploadFile.fileId, fileBuffer);
|
|
130
|
+
return uploadFile.fileId;
|
|
131
131
|
}
|
|
132
|
-
async
|
|
133
|
-
const
|
|
134
|
-
|
|
132
|
+
async uploadFileInCloud({ file, name, nodeId, encryptProgress, uploadProgress, signal, }) {
|
|
133
|
+
const fileId = await this.uploadFile({
|
|
134
|
+
file,
|
|
135
135
|
encryptProgress,
|
|
136
136
|
uploadProgress,
|
|
137
137
|
signal,
|
|
138
138
|
});
|
|
139
139
|
return await this.saveInCloud({
|
|
140
|
-
|
|
140
|
+
fileId,
|
|
141
141
|
name,
|
|
142
142
|
nodeId,
|
|
143
143
|
});
|
|
@@ -220,11 +220,11 @@ export class SecrecyCloudClient {
|
|
|
220
220
|
});
|
|
221
221
|
return await apiNodeToExternalNodeFull(node, this.#keys);
|
|
222
222
|
}
|
|
223
|
-
async
|
|
224
|
-
const
|
|
223
|
+
async fileMetadata({ id }) {
|
|
224
|
+
const file = await this.#apiClient.cloud.fileById.query({
|
|
225
225
|
id,
|
|
226
226
|
});
|
|
227
|
-
return
|
|
227
|
+
return apiFileToExternal(file, this.#keys);
|
|
228
228
|
}
|
|
229
229
|
async shareNode({ nodeId, userId, rights, }) {
|
|
230
230
|
const publicKey = await this.#client.app.userPublicKey(userId);
|
|
@@ -270,26 +270,26 @@ export class SecrecyCloudClient {
|
|
|
270
270
|
});
|
|
271
271
|
return await apiNodeToExternalNodeFull(updateNode, this.#keys);
|
|
272
272
|
}
|
|
273
|
-
async
|
|
274
|
-
const cached =
|
|
273
|
+
async fileContent({ fileId, onDownloadProgress, progressDecrypt, signal, }) {
|
|
274
|
+
const cached = dataCache.get(fileId);
|
|
275
275
|
if (cached !== undefined) {
|
|
276
276
|
return cached;
|
|
277
277
|
}
|
|
278
|
-
const
|
|
279
|
-
id:
|
|
278
|
+
const fileContent = await this.#apiClient.cloud.fileContentById.query({
|
|
279
|
+
id: fileId,
|
|
280
280
|
});
|
|
281
281
|
const progressParts = {};
|
|
282
282
|
const onProgress = (part, progressEvent) => {
|
|
283
283
|
progressParts[part] = progressEvent;
|
|
284
284
|
const transferredBytes = Object.values(progressParts).reduce((prv, cur) => prv + cur.transferredBytes, 0);
|
|
285
|
-
const totalBytes = Number(
|
|
285
|
+
const totalBytes = Number(fileContent.totalSize);
|
|
286
286
|
onDownloadProgress?.({
|
|
287
287
|
percent: transferredBytes / totalBytes,
|
|
288
288
|
totalBytes,
|
|
289
289
|
transferredBytes,
|
|
290
290
|
});
|
|
291
291
|
};
|
|
292
|
-
const encryptedContentFromParts = async (
|
|
292
|
+
const encryptedContentFromParts = async (fileParts) => {
|
|
293
293
|
const parts = new Array();
|
|
294
294
|
const byPart = async (part) => {
|
|
295
295
|
const buf = new Uint8Array(await ky
|
|
@@ -303,14 +303,14 @@ export class SecrecyCloudClient {
|
|
|
303
303
|
.arrayBuffer());
|
|
304
304
|
const md5Part = await md5(buf);
|
|
305
305
|
if (md5Part !== part.md5) {
|
|
306
|
-
throw new Error(`Invalid md5 for part ${part.order} of
|
|
306
|
+
throw new Error(`Invalid md5 for part ${part.order} of file ${fileId}`);
|
|
307
307
|
}
|
|
308
308
|
parts.push({
|
|
309
309
|
data: buf,
|
|
310
310
|
order: part.order,
|
|
311
311
|
});
|
|
312
312
|
};
|
|
313
|
-
await promiseAllLimit(3,
|
|
313
|
+
await promiseAllLimit(3, fileParts.map((p) => async () => {
|
|
314
314
|
await byPart(p);
|
|
315
315
|
}));
|
|
316
316
|
return concatenate(...parts.sort((a, b) => a.order - b.order).map((p) => p.data));
|
|
@@ -318,42 +318,42 @@ export class SecrecyCloudClient {
|
|
|
318
318
|
const finalize = async (encryptedContent) => {
|
|
319
319
|
// const md5Encrypted = await firstValueFrom(md5(of(encryptedContent)));
|
|
320
320
|
const md5Encrypted = await md5(encryptedContent);
|
|
321
|
-
if (md5Encrypted !==
|
|
321
|
+
if (md5Encrypted !== fileContent.md5Encrypted) {
|
|
322
322
|
throw new Error(`Encrypted content does not match`);
|
|
323
323
|
}
|
|
324
|
-
const key = decryptCryptoBox(sodium.from_hex(
|
|
325
|
-
?
|
|
326
|
-
:
|
|
327
|
-
?
|
|
324
|
+
const key = decryptCryptoBox(sodium.from_hex(fileContent.key), fileContent.type === 'received_mail'
|
|
325
|
+
? fileContent.senderPublicKey
|
|
326
|
+
: fileContent.type === 'cloud'
|
|
327
|
+
? fileContent.publicKey
|
|
328
328
|
: this.#keys.publicKey, this.#keys.privateKey);
|
|
329
329
|
const src = await decrypt(key, encryptedContent, progressDecrypt, signal);
|
|
330
330
|
// const md5Content = await firstValueFrom(md5(of(src)));
|
|
331
331
|
const md5Content = await md5(src);
|
|
332
|
-
if (md5Content !==
|
|
332
|
+
if (md5Content !== fileContent.md5) {
|
|
333
333
|
throw new Error(`Content does not match`);
|
|
334
334
|
}
|
|
335
335
|
return decompress(src);
|
|
336
336
|
};
|
|
337
|
-
const encryptedContent =
|
|
338
|
-
?
|
|
339
|
-
:
|
|
340
|
-
? await encryptedContentFromParts(
|
|
337
|
+
const encryptedContent = fileContent.type === 'lite'
|
|
338
|
+
? fileContent.content
|
|
339
|
+
: fileContent.type === 'cloud'
|
|
340
|
+
? await encryptedContentFromParts(fileContent.parts)
|
|
341
341
|
: // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
342
|
-
|
|
343
|
-
?
|
|
344
|
-
:
|
|
345
|
-
? await encryptedContentFromParts(
|
|
342
|
+
fileContent.maybeContent !== null
|
|
343
|
+
? fileContent.maybeContent
|
|
344
|
+
: fileContent.maybeParts !== null
|
|
345
|
+
? await encryptedContentFromParts(fileContent.maybeParts)
|
|
346
346
|
: null;
|
|
347
347
|
if (encryptedContent === null) {
|
|
348
|
-
throw `Can't find content for
|
|
348
|
+
throw `Can't find content for file ${fileId}`;
|
|
349
349
|
}
|
|
350
350
|
const data = await finalize(encryptedContent);
|
|
351
|
-
|
|
351
|
+
dataCache.set(fileId, data);
|
|
352
352
|
return data;
|
|
353
353
|
}
|
|
354
|
-
async
|
|
355
|
-
const { isDeleted } = await this.#apiClient.cloud.
|
|
356
|
-
|
|
354
|
+
async deleteFile({ fileId, nodeId, }) {
|
|
355
|
+
const { isDeleted } = await this.#apiClient.cloud.deleteFile.mutate({
|
|
356
|
+
fileId,
|
|
357
357
|
nodeId,
|
|
358
358
|
});
|
|
359
359
|
return isDeleted;
|
|
@@ -381,7 +381,7 @@ export class SecrecyCloudClient {
|
|
|
381
381
|
});
|
|
382
382
|
return isMoved;
|
|
383
383
|
}
|
|
384
|
-
async saveInCloud({
|
|
384
|
+
async saveInCloud({ fileId, name, nodeId, }) {
|
|
385
385
|
if (nodeId !== undefined && !nodesCache.has(nodeId)) {
|
|
386
386
|
await this.node({ id: nodeId });
|
|
387
387
|
if (!nodesCache.has(nodeId)) {
|
|
@@ -394,50 +394,50 @@ export class SecrecyCloudClient {
|
|
|
394
394
|
}
|
|
395
395
|
}
|
|
396
396
|
let key = '';
|
|
397
|
-
const
|
|
398
|
-
if (
|
|
399
|
-
await this.
|
|
400
|
-
const
|
|
401
|
-
if (
|
|
397
|
+
const file = filesCache.get(fileId);
|
|
398
|
+
if (file === undefined) {
|
|
399
|
+
await this.fileMetadata({ id: fileId });
|
|
400
|
+
const file = filesCache.get(fileId);
|
|
401
|
+
if (file === undefined) {
|
|
402
402
|
const receivedMails = await this.#client.mail.receivedMails();
|
|
403
|
-
const mail = receivedMails.find((m) => m.
|
|
403
|
+
const mail = receivedMails.find((m) => m.files.some((f) => f.id === fileId));
|
|
404
404
|
if (mail === undefined) {
|
|
405
405
|
const err = {
|
|
406
406
|
name: 'ClientError',
|
|
407
407
|
code: 'NOT_FOUND',
|
|
408
|
-
message: `Can't find mail with the
|
|
408
|
+
message: `Can't find mail with the file ${fileId}`,
|
|
409
409
|
};
|
|
410
410
|
throw err;
|
|
411
411
|
}
|
|
412
|
-
const
|
|
413
|
-
if (
|
|
412
|
+
const fileMail = mail.files.find((f) => f.id === fileId);
|
|
413
|
+
if (fileMail === undefined) {
|
|
414
414
|
const err = {
|
|
415
415
|
name: 'ClientError',
|
|
416
416
|
code: 'NOT_FOUND',
|
|
417
|
-
message: `Can't find mail with the
|
|
417
|
+
message: `Can't find mail with the file ${fileId}`,
|
|
418
418
|
};
|
|
419
419
|
throw err;
|
|
420
420
|
}
|
|
421
421
|
const senderPubKey = await this.#client.app.userPublicKey(mail.sender.id);
|
|
422
|
-
const
|
|
423
|
-
key = sodium.to_hex(
|
|
422
|
+
const fileKey = decryptCryptoBox(sodium.from_hex(fileMail.key), senderPubKey, this.#keys.privateKey);
|
|
423
|
+
key = sodium.to_hex(fileKey);
|
|
424
424
|
}
|
|
425
425
|
else {
|
|
426
|
-
key =
|
|
426
|
+
key = file.key;
|
|
427
427
|
}
|
|
428
428
|
}
|
|
429
429
|
else {
|
|
430
|
-
key =
|
|
430
|
+
key = file.key;
|
|
431
431
|
}
|
|
432
432
|
key = sodium.to_hex(encryptCryptoBox(sodium.from_hex(key), this.#keys.publicKey, this.#keys.privateKey));
|
|
433
433
|
const nameKey = secretStreamKeygen();
|
|
434
434
|
const encryptedName = await encryptName(name, sodium.to_hex(nameKey));
|
|
435
435
|
const encryptedNameKey = sodium.to_hex(encryptCryptoBox(nameKey, this.#keys.publicKey, this.#keys.privateKey));
|
|
436
436
|
const saveInCloud = await this.#apiClient.cloud.saveInCloud.mutate({
|
|
437
|
-
|
|
437
|
+
fileId,
|
|
438
438
|
key,
|
|
439
439
|
nodeId: nodeId ?? null,
|
|
440
|
-
|
|
440
|
+
filename: encryptedName,
|
|
441
441
|
nameKey: encryptedNameKey,
|
|
442
442
|
});
|
|
443
443
|
const node = await apiNodeToExternalNodeFull(saveInCloud, this.#keys);
|
|
@@ -468,7 +468,7 @@ export class SecrecyCloudClient {
|
|
|
468
468
|
nameKey: nameKey !== null
|
|
469
469
|
? sodium.to_hex(encryptCryptoBox(sodium.from_hex(nameKey), publicKey, this.#keys.privateKey))
|
|
470
470
|
: null,
|
|
471
|
-
|
|
471
|
+
files: 'history' in node
|
|
472
472
|
? node.history.map((f) => ({
|
|
473
473
|
id: f.id,
|
|
474
474
|
key: sodium.to_hex(encryptCryptoBox(sodium.from_hex(f.key), publicKey, this.#keys.privateKey)),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { filesCache } from '../cache.js';
|
|
2
2
|
import { encryptCryptoBox, sodium } from '../index.js';
|
|
3
3
|
import { convertInternalMailToExternal } from './convert/mail.js';
|
|
4
4
|
export class SecrecyMailClient {
|
|
@@ -67,11 +67,11 @@ export class SecrecyMailClient {
|
|
|
67
67
|
}
|
|
68
68
|
if (senderFiles !== undefined) {
|
|
69
69
|
for (const f of senderFiles) {
|
|
70
|
-
let
|
|
71
|
-
if (
|
|
72
|
-
await this.#client.cloud.
|
|
73
|
-
|
|
74
|
-
if (
|
|
70
|
+
let file = filesCache.get(f.id);
|
|
71
|
+
if (file === undefined) {
|
|
72
|
+
await this.#client.cloud.fileMetadata({ id: f.id });
|
|
73
|
+
file = filesCache.get(f.id);
|
|
74
|
+
if (file === undefined) {
|
|
75
75
|
throw new Error(`File ${f.name} (${f.id}) does not exists`);
|
|
76
76
|
}
|
|
77
77
|
}
|
|
@@ -132,7 +132,7 @@ export class SecrecyMailClient {
|
|
|
132
132
|
if (email === undefined) {
|
|
133
133
|
continue;
|
|
134
134
|
}
|
|
135
|
-
const input = await this._eachUser(draft.
|
|
135
|
+
const input = await this._eachUser(draft.files, draft.subject, draft.body, email);
|
|
136
136
|
if (input === null) {
|
|
137
137
|
temporaryRecipients.push(email);
|
|
138
138
|
}
|
|
@@ -141,7 +141,7 @@ export class SecrecyMailClient {
|
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
for (const { id } of draft.recipients) {
|
|
144
|
-
const input = await this._eachUser(draft.
|
|
144
|
+
const input = await this._eachUser(draft.files, draft.subject, draft.body, id);
|
|
145
145
|
if (input === null) {
|
|
146
146
|
temporaryRecipients.push(id);
|
|
147
147
|
}
|
|
@@ -167,7 +167,7 @@ export class SecrecyMailClient {
|
|
|
167
167
|
continue;
|
|
168
168
|
}
|
|
169
169
|
try {
|
|
170
|
-
const input = await this._eachUser(mail.
|
|
170
|
+
const input = await this._eachUser(mail.files, mail.subject, mail.body, email);
|
|
171
171
|
if (input === null) {
|
|
172
172
|
continue;
|
|
173
173
|
}
|
|
@@ -186,16 +186,16 @@ export class SecrecyMailClient {
|
|
|
186
186
|
const hashKey = sodium.randombytes_buf(sodium.crypto_generichash_KEYBYTES, 'hex');
|
|
187
187
|
const hash = sodium.crypto_generichash(sodium.crypto_generichash_BYTES, JSON.stringify({ body, subject }), hashKey, 'hex');
|
|
188
188
|
for (const f of senderFiles) {
|
|
189
|
-
let
|
|
190
|
-
if (
|
|
191
|
-
await this.#client.cloud.
|
|
192
|
-
|
|
193
|
-
if (
|
|
189
|
+
let file = filesCache.get(f.id);
|
|
190
|
+
if (file === undefined) {
|
|
191
|
+
await this.#client.cloud.fileMetadata({ id: f.id });
|
|
192
|
+
file = filesCache.get(f.id);
|
|
193
|
+
if (file === undefined) {
|
|
194
194
|
throw new Error(`File ${f.name} (${f.id}) does not exists`);
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
senderFiles.push({
|
|
198
|
-
id:
|
|
198
|
+
id: file.id,
|
|
199
199
|
name: sodium.to_hex(encryptCryptoBox(sodium.from_string(f.name), this.#keys.publicKey, this.#keys.privateKey)),
|
|
200
200
|
});
|
|
201
201
|
}
|
|
@@ -254,25 +254,25 @@ export class SecrecyMailClient {
|
|
|
254
254
|
const unreadReceivedMailsCount = await this.#apiClient.mail.unreadReceivedCount.query({});
|
|
255
255
|
return unreadReceivedMailsCount;
|
|
256
256
|
}
|
|
257
|
-
_eachUser = async (
|
|
257
|
+
_eachUser = async (files, subject, body, idOrMail) => {
|
|
258
258
|
// const pubKey = await this.#client.app.userPublicKey(userId)
|
|
259
259
|
const pubKey = await this.#client.app.userPublicKey(idOrMail);
|
|
260
260
|
// const recipientsFiles = new Array<MailFileInput>()
|
|
261
261
|
const recipientsFiles = new Array();
|
|
262
|
-
for (const f of
|
|
263
|
-
let
|
|
264
|
-
if (
|
|
265
|
-
await this.#client.cloud.
|
|
266
|
-
|
|
267
|
-
if (
|
|
262
|
+
for (const f of files) {
|
|
263
|
+
let fileInHistory = filesCache.get(f.id);
|
|
264
|
+
if (fileInHistory === undefined) {
|
|
265
|
+
await this.#client.cloud.fileMetadata({ id: f.id });
|
|
266
|
+
fileInHistory = filesCache.get(f.id);
|
|
267
|
+
if (fileInHistory === undefined) {
|
|
268
268
|
throw new Error(`File ${f.name} (${f.id}) does not exists`);
|
|
269
269
|
}
|
|
270
270
|
}
|
|
271
|
-
const key =
|
|
271
|
+
const key = fileInHistory.key;
|
|
272
272
|
recipientsFiles.push({
|
|
273
273
|
id: f.id,
|
|
274
274
|
name: sodium.to_hex(encryptCryptoBox(sodium.from_string(f.name), pubKey, this.#keys.privateKey)),
|
|
275
|
-
|
|
275
|
+
fileKey: sodium.to_hex(encryptCryptoBox(sodium.from_hex(key), pubKey, this.#keys.privateKey)),
|
|
276
276
|
});
|
|
277
277
|
}
|
|
278
278
|
return {
|
|
@@ -280,7 +280,7 @@ export class SecrecyMailClient {
|
|
|
280
280
|
recipientId: idOrMail,
|
|
281
281
|
body: sodium.to_hex(encryptCryptoBox(sodium.from_string(body), pubKey, this.#keys.privateKey)),
|
|
282
282
|
subject: sodium.to_hex(encryptCryptoBox(sodium.from_string(subject), pubKey, this.#keys.privateKey)),
|
|
283
|
-
|
|
283
|
+
files: recipientsFiles,
|
|
284
284
|
};
|
|
285
285
|
};
|
|
286
286
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { filesCache } from '../../cache.js';
|
|
2
|
+
import { decryptCryptoBox } from '../../crypto/index.js';
|
|
3
|
+
import { sodium } from '../../sodium.js';
|
|
4
|
+
export function apiFileToInternal(apiFile, keyPair) {
|
|
5
|
+
const file = {
|
|
6
|
+
id: apiFile.id,
|
|
7
|
+
md5: apiFile.md5,
|
|
8
|
+
md5Encrypted: apiFile.md5Encrypted,
|
|
9
|
+
createdAt: apiFile.createdAt,
|
|
10
|
+
size: apiFile.size,
|
|
11
|
+
sizeBefore: apiFile.sizeBefore,
|
|
12
|
+
key: sodium.to_hex(decryptCryptoBox(sodium.from_hex(apiFile.access.key), apiFile.access.sharedByPubKey, keyPair.privateKey)),
|
|
13
|
+
};
|
|
14
|
+
filesCache.set(file.id, file);
|
|
15
|
+
return file;
|
|
16
|
+
}
|
|
17
|
+
export function internalFileToFile(internal) {
|
|
18
|
+
return {
|
|
19
|
+
id: internal.id,
|
|
20
|
+
md5: internal.md5,
|
|
21
|
+
md5Encrypted: internal.md5Encrypted,
|
|
22
|
+
createdAt: internal.createdAt,
|
|
23
|
+
size: internal.size,
|
|
24
|
+
sizeBefore: internal.sizeBefore,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function apiFileToExternal(apiFile, keyPair) {
|
|
28
|
+
return internalFileToFile(apiFileToInternal(apiFile, keyPair));
|
|
29
|
+
}
|
|
@@ -30,12 +30,12 @@ export async function convertInternalMailToExternal({ client, mail, keyPair, })
|
|
|
30
30
|
temporaryRecipients: [], // TODO
|
|
31
31
|
recipients: mI.recipients,
|
|
32
32
|
sender: mail.sender,
|
|
33
|
-
|
|
34
|
-
const name = sodium.to_string(decryptCryptoBox(sodium.from_hex(f.
|
|
33
|
+
files: mail.files.map((f) => {
|
|
34
|
+
const name = sodium.to_string(decryptCryptoBox(sodium.from_hex(f.filename), pubKey, privateKey));
|
|
35
35
|
return {
|
|
36
|
-
id: f.
|
|
36
|
+
id: f.fileId,
|
|
37
37
|
name,
|
|
38
|
-
key: f.
|
|
38
|
+
key: f.fileKey,
|
|
39
39
|
};
|
|
40
40
|
}),
|
|
41
41
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { sodium } from '../../sodium.js';
|
|
2
2
|
import { decryptCryptoBox } from '../../crypto/index.js';
|
|
3
3
|
import { nodesCache } from '../../cache.js';
|
|
4
|
-
import { decryptSecretStream } from '../../crypto/
|
|
5
|
-
import {
|
|
4
|
+
import { decryptSecretStream } from '../../crypto/file.js';
|
|
5
|
+
import { apiFileToInternal, internalFileToFile } from './file.js';
|
|
6
6
|
export async function apiNodeToInternal(apiNode, keyPair) {
|
|
7
7
|
const internal = {
|
|
8
8
|
id: apiNode.id,
|
|
@@ -23,7 +23,7 @@ export async function apiNodeToInternal(apiNode, keyPair) {
|
|
|
23
23
|
deletedAt: apiNode.deletedAt,
|
|
24
24
|
users: apiNode.users,
|
|
25
25
|
parentId: apiNode.parentId ?? null,
|
|
26
|
-
|
|
26
|
+
currentFileId: apiNode.currentFileId ?? null,
|
|
27
27
|
};
|
|
28
28
|
internal.access = { ...apiNode.access };
|
|
29
29
|
if (apiNode.access.nameKey !== null) {
|
|
@@ -47,13 +47,13 @@ export async function apiNodeFullToInternalFull(apiNodeFull, keyPair) {
|
|
|
47
47
|
return {
|
|
48
48
|
...f,
|
|
49
49
|
current: apiNodeFull.current !== null
|
|
50
|
-
?
|
|
50
|
+
? apiFileToInternal(apiNodeFull.current, keyPair)
|
|
51
51
|
: undefined,
|
|
52
52
|
parent: apiNodeFull.parent !== null
|
|
53
53
|
? await apiNodeToInternal(apiNodeFull.parent, keyPair)
|
|
54
54
|
: null,
|
|
55
55
|
children: await Promise.all(apiNodeFull.children.map(async (s) => await apiNodeToInternal(s, keyPair))),
|
|
56
|
-
history: apiNodeFull.history.map((f) =>
|
|
56
|
+
history: apiNodeFull.history.map((f) => apiFileToInternal(f, keyPair)),
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
59
|
export function internalNodeToNode(internal) {
|
|
@@ -76,9 +76,9 @@ export function internalNodeFullToNodeFull(internal) {
|
|
|
76
76
|
...internalNodeToNode(internal),
|
|
77
77
|
parent: internal.parent !== null ? internalNodeToNode(internal.parent) : null,
|
|
78
78
|
children: internal.children.map(internalNodeToNode),
|
|
79
|
-
history: internal.history.map((f) =>
|
|
79
|
+
history: internal.history.map((f) => internalFileToFile(f)),
|
|
80
80
|
current: internal.current !== null && internal.current !== undefined
|
|
81
|
-
?
|
|
81
|
+
? internalFileToFile(internal.current)
|
|
82
82
|
: undefined,
|
|
83
83
|
};
|
|
84
84
|
}
|
package/dist/lib/client/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { BaseClient } from '../base-client.js';
|
|
2
|
-
import { encryptSecretStream } from '../crypto/
|
|
2
|
+
import { encryptSecretStream } from '../crypto/file.js';
|
|
3
3
|
import { sodium } from '../sodium.js';
|
|
4
4
|
import { SecrecyCloudClient } from './SecrecyCloudClient.js';
|
|
5
5
|
import { SecrecyMailClient } from './SecrecyMailClient.js';
|
|
6
6
|
import { SecrecyAppClient } from './SecrecyAppClient.js';
|
|
7
|
-
import { nodesCache,
|
|
7
|
+
import { nodesCache, filesCache, publicKeysCache } from '../cache.js';
|
|
8
8
|
import { SecrecyDbClient } from './SecrecyDbClient.js';
|
|
9
9
|
import { SecrecyWalletClient } from './SecrecyWalletClient.js';
|
|
10
10
|
import { SecrecyPayClient } from './SecrecyPayClient.js';
|
|
@@ -53,7 +53,7 @@ export class SecrecyClient extends BaseClient {
|
|
|
53
53
|
}
|
|
54
54
|
async logout(sessionId) {
|
|
55
55
|
nodesCache.clear();
|
|
56
|
-
|
|
56
|
+
filesCache.clear();
|
|
57
57
|
publicKeysCache.clear();
|
|
58
58
|
await super.logout(sessionId);
|
|
59
59
|
}
|